You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by yo...@apache.org on 2011/02/17 00:01:41 UTC

svn commit: r1071435 - in /lucene/dev/trunk/solr: ./ contrib/dataimporthandler/src/main/java/org/apache/solr/handler/dataimport/ contrib/dataimporthandler/src/test/java/org/apache/solr/handler/dataimport/

Author: yonik
Date: Wed Feb 16 23:01:41 2011
New Revision: 1071435

URL: http://svn.apache.org/viewvc?rev=1071435&view=rev
Log:
SOLR-1191: fix DIH deltaQyery when pk has prefix, change NPE to better error reporting

Added:
    lucene/dev/trunk/solr/contrib/dataimporthandler/src/test/java/org/apache/solr/handler/dataimport/TestSqlEntityProcessorDeltaPrefixedPk.java   (with props)
Modified:
    lucene/dev/trunk/solr/CHANGES.txt
    lucene/dev/trunk/solr/contrib/dataimporthandler/src/main/java/org/apache/solr/handler/dataimport/DocBuilder.java

Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1071435&r1=1071434&r2=1071435&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Wed Feb 16 23:01:41 2011
@@ -679,6 +679,13 @@ Bug Fixes
   DocumentAnalysisRequestHandler to respect charset from XML file and only
   use HTTP header's "Content-Type" as a "hint". (Uwe Schindler)
 
+* SOLR-1191: resolve DataImportHandler deltaQuery column against pk when pk
+  has a prefix (e.g. pk="book.id" deltaQuery="select id from ..."). More
+  useful error reporting when no match found (previously failed with a
+  NullPointerException in log and no clear user feedback). (gthb via yonik)
+
+
+
 Other Changes
 ----------------------
 

Modified: lucene/dev/trunk/solr/contrib/dataimporthandler/src/main/java/org/apache/solr/handler/dataimport/DocBuilder.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/contrib/dataimporthandler/src/main/java/org/apache/solr/handler/dataimport/DocBuilder.java?rev=1071435&r1=1071434&r2=1071435&view=diff
==============================================================================
--- lucene/dev/trunk/solr/contrib/dataimporthandler/src/main/java/org/apache/solr/handler/dataimport/DocBuilder.java (original)
+++ lucene/dev/trunk/solr/contrib/dataimporthandler/src/main/java/org/apache/solr/handler/dataimport/DocBuilder.java Wed Feb 16 23:01:41 2011
@@ -318,7 +318,11 @@ public class DocBuilder {
       String keyName = root.isDocRoot ? root.getPk() : root.getSchemaPk();
       Object key = map.get(keyName);
       if(key == null) {
-        LOG.warn("no key was available for deleteted pk query. keyName = " + keyName);
+        keyName = findMatchingPkColumn(keyName, map);
+        key = map.get(keyName);
+      }
+      if(key == null) {
+        LOG.warn("no key was available for deleted pk query. keyName = " + keyName);
         continue;
       }
       writer.deleteDoc(key);
@@ -816,6 +820,28 @@ public class DocBuilder {
     return entity.processor = new EntityProcessorWrapper(entityProcessor, this);
   }
 
+  private String findMatchingPkColumn(String pk, Map<String, Object> row) {
+    if (row.containsKey(pk))
+      throw new IllegalArgumentException(
+        String.format("deltaQuery returned a row with null for primary key %s", pk));
+    String resolvedPk = null;
+    for (String columnName : row.keySet()) {
+      if (columnName.endsWith("." + pk) || pk.endsWith("." + columnName)) {
+        if (resolvedPk != null)
+          throw new IllegalArgumentException(
+            String.format(
+              "deltaQuery has more than one column (%s and %s) that might resolve to declared primary key pk='%s'",
+              resolvedPk, columnName, pk));
+        resolvedPk = columnName;
+      }
+    }
+    if (resolvedPk == null)
+      throw new IllegalArgumentException(
+        String.format("deltaQuery has no column to resolve to declared primary key pk='%s'", pk));
+    LOG.info(String.format("Resolving deltaQuery column '%s' to match entity's declared pk '%s'", resolvedPk, pk));
+    return resolvedPk;
+  }
+
   /**
    * <p> Collects unique keys of all Solr documents for whom one or more source tables have been changed since the last
    * indexed time. </p> <p> Note: In our definition, unique key of Solr document is the primary key of the top level
@@ -852,13 +878,20 @@ public class DocBuilder {
     Map<String, Map<String, Object>> deltaSet = new HashMap<String, Map<String, Object>>();
     LOG.info("Running ModifiedRowKey() for Entity: " + entity.name);
     //get the modified rows in this entity
+    String pk = entity.getPk();
     while (true) {
       Map<String, Object> row = entityProcessor.nextModifiedRowKey();
 
       if (row == null)
         break;
 
-      deltaSet.put(row.get(entity.getPk()).toString(), row);
+      Object pkValue = row.get(pk);
+      if (pkValue == null) {
+        pk = findMatchingPkColumn(pk, row);
+        pkValue = row.get(pk);
+      }
+
+      deltaSet.put(pkValue.toString(), row);
       importStatistics.rowsCount.incrementAndGet();
       // check for abort
       if (stop.get())
@@ -873,8 +906,14 @@ public class DocBuilder {
 
       deletedSet.add(row);
       
+      Object pkValue = row.get(pk);
+      if (pkValue == null) {
+        pk = findMatchingPkColumn(pk, row);
+        pkValue = row.get(pk);
+      }
+
       // Remove deleted rows from the delta rows
-      String deletedRowPk = row.get(entity.getPk()).toString();
+      String deletedRowPk = pkValue.toString();
       if (deltaSet.containsKey(deletedRowPk)) {
         deltaSet.remove(deletedRowPk);
       }

Added: lucene/dev/trunk/solr/contrib/dataimporthandler/src/test/java/org/apache/solr/handler/dataimport/TestSqlEntityProcessorDeltaPrefixedPk.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/contrib/dataimporthandler/src/test/java/org/apache/solr/handler/dataimport/TestSqlEntityProcessorDeltaPrefixedPk.java?rev=1071435&view=auto
==============================================================================
--- lucene/dev/trunk/solr/contrib/dataimporthandler/src/test/java/org/apache/solr/handler/dataimport/TestSqlEntityProcessorDeltaPrefixedPk.java (added)
+++ lucene/dev/trunk/solr/contrib/dataimporthandler/src/test/java/org/apache/solr/handler/dataimport/TestSqlEntityProcessorDeltaPrefixedPk.java Wed Feb 16 23:01:41 2011
@@ -0,0 +1,145 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.handler.dataimport;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.*;
+
+/**
+ * <p>
+ * Test for SqlEntityProcessorDelta verifying fix for SOLR-1191
+ * </p>
+ * 
+ *
+ * @version $Id$
+ * @since solr 3.1
+ */
+public class TestSqlEntityProcessorDeltaPrefixedPk extends AbstractDataImportHandlerTestCase {
+  private static final String FULLIMPORT_QUERY = "select * from x";
+
+  private static final String DELTA_QUERY = "select id from x where last_modified > NOW";
+
+  private static final String DELETED_PK_QUERY = "select id from x where last_modified > NOW AND deleted='true'";
+
+  private static final String dataConfig_delta =
+    "<dataConfig>" +
+    "  <dataSource  type=\"MockDataSource\"/>\n" +
+    "  <document>\n" +
+    "    <entity name=\"x\" transformer=\"TemplateTransformer\" pk=\"x.id\"" +
+    "            query=\"" + FULLIMPORT_QUERY + "\"" +
+    "            deletedPkQuery=\"" + DELETED_PK_QUERY + "\"" +
+    "            deltaImportQuery=\"select * from x where id='${dih.delta.id}'\"" +
+    "            deltaQuery=\"" + DELTA_QUERY + "\">\n" +
+    "      <field column=\"id\" name=\"id\"/>\n" +
+    "      <field column=\"desc\" name=\"desc\"/>\n" +
+    "    </entity>\n" +
+    "  </document>\n" +
+    "</dataConfig>\n";
+  
+  private static final List EMPTY_LIST = Collections.EMPTY_LIST;
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    initCore("dataimport-solrconfig.xml", "dataimport-schema.xml");
+  }
+
+  @Before @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    clearIndex();
+    assertU(commit());
+    //Logger.getLogger("").setLevel(Level.ALL);
+  }
+
+  @SuppressWarnings("unchecked")
+  private void add1document() throws Exception {
+    List row = new ArrayList();
+    row.add(createMap("id", "1", "desc", "bar"));
+    MockDataSource.setIterator(FULLIMPORT_QUERY, row.iterator());
+
+    runFullImport(dataConfig_delta);
+
+    assertQ(req("*:* OR add1document"), "//*[@numFound='1']");
+    assertQ(req("id:1"), "//*[@numFound='1']");
+    assertQ(req("desc:bar"), "//*[@numFound='1']");
+  }
+
+  @Test
+  @SuppressWarnings("unchecked")
+  public void testDeltaImport_deleteResolvesUnprefixedPk() throws Exception {
+    add1document();
+    MockDataSource.clearCache();
+    List deletedRows = new ArrayList();
+    deletedRows.add(createMap("id", "1"));
+    MockDataSource.setIterator(DELETED_PK_QUERY, deletedRows.iterator());
+    MockDataSource.setIterator(DELTA_QUERY, EMPTY_LIST.iterator());
+    runDeltaImport(dataConfig_delta);
+
+    assertQ(req("*:* OR testDeltaImport_deleteResolvesUnprefixedPk"), "//*[@numFound='0']");
+  }
+
+  @Test
+  @SuppressWarnings("unchecked")
+  public void testDeltaImport_replace_resolvesUnprefixedPk() throws Exception {
+    add1document();
+    MockDataSource.clearCache();
+    List deltaRows = new ArrayList();
+    deltaRows.add(createMap("id", "1"));
+    MockDataSource.setIterator(DELTA_QUERY, deltaRows.iterator());
+    MockDataSource.setIterator(DELETED_PK_QUERY, EMPTY_LIST.iterator());
+    List rows = new ArrayList();
+    rows.add(createMap("id", "1", "desc", "baz"));
+    MockDataSource.setIterator("select * from x where id='1'", rows.iterator());
+
+    runDeltaImport(dataConfig_delta);
+
+    assertQ(req("*:* OR testDeltaImport_replace_resolvesUnprefixedPk"), "//*[@numFound='1']");
+    assertQ(req("id:1"), "//*[@numFound='1']");
+    assertQ(req("desc:bar"), "//*[@numFound='0']");
+    assertQ(req("desc:baz"), "//*[@numFound='1']");
+  }
+
+  @Test
+  @SuppressWarnings("unchecked")
+  public void testDeltaImport_addResolvesUnprefixedPk() throws Exception {
+    add1document();
+    MockDataSource.clearCache();
+
+    List deltaRows = new ArrayList();
+    deltaRows.add(createMap("id", "2"));
+    MockDataSource.setIterator(DELTA_QUERY, deltaRows.iterator());
+
+    List rows = new ArrayList();
+    rows.add(createMap("id", "2", "desc", "xyzzy"));
+    MockDataSource.setIterator("select * from x where id='2'", rows.iterator());
+
+    runDeltaImport(dataConfig_delta);
+
+    assertQ(req("*:* OR testDeltaImport_addResolvesUnprefixedPk"), "//*[@numFound='2']");
+    assertQ(req("id:1"), "//*[@numFound='1']");
+    assertQ(req("id:2"), "//*[@numFound='1']");
+    assertQ(req("desc:bar"), "//*[@numFound='1']");
+    assertQ(req("desc:xyzzy"), "//*[@numFound='1']");
+  }
+
+}