blob: 2941f589617ff3a377f07d92d01a349883c8d9dd [file] [log] [blame]
/*
* 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 java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.solr.request.LocalSolrQueryRequest;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
/**
* <p>
* Test for DocBuilder using the test harness
* </p>
*
*
* @since solr 1.3
*/
public class TestDocBuilder2 extends AbstractDataImportHandlerTestCase {
@BeforeClass
public static void beforeClass() throws Exception {
initCore("dataimport-solrconfig.xml", "dataimport-schema.xml");
}
@Test
@SuppressWarnings("unchecked")
public void testSingleEntity() throws Exception {
@SuppressWarnings({"rawtypes"})
List rows = new ArrayList();
rows.add(createMap("id", "1", "desc", "one"));
MockDataSource.setIterator("select * from x", rows.iterator());
runFullImport(loadDataConfig("single-entity-data-config.xml"));
assertQ(req("id:1"), "//*[@numFound='1']");
assertTrue("Update request processor processAdd was not called", TestUpdateRequestProcessor.processAddCalled);
assertTrue("Update request processor processCommit was not callled", TestUpdateRequestProcessor.processCommitCalled);
assertTrue("Update request processor finish was not called", TestUpdateRequestProcessor.finishCalled);
}
@Test
@SuppressWarnings("unchecked")
public void testSingleEntity_CaseInsensitive() throws Exception {
@SuppressWarnings({"rawtypes"})
List rows = new ArrayList();
rows.add(createMap("id", "1", "desC", "one"));
MockDataSource.setIterator("select * from x", rows.iterator());
runFullImport(DATA_CONFIG_WITH_CASE_INSENSITIVE_FIELDS);
assertQ(req("id:1"), "//*[@numFound='1']");
assertTrue("Start event listener was not called", StartEventListener.executed);
assertTrue("End event listener was not called", EndEventListener.executed);
assertTrue("Update request processor processAdd was not called", TestUpdateRequestProcessor.processAddCalled);
assertTrue("Update request processor finish was not called", TestUpdateRequestProcessor.finishCalled);
}
@Test
@SuppressWarnings({"unchecked"})
public void testErrorHandler() throws Exception {
@SuppressWarnings({"rawtypes"})
List rows = new ArrayList();
rows.add(createMap("id", "1", "FORCE_ERROR", "true"));
MockDataSource.setIterator("select * from x", rows.iterator());
runFullImport(DATA_CONFIG_WITH_ERROR_HANDLER);
assertTrue("Error event listener was not called", ErrorEventListener.executed);
assertTrue(ErrorEventListener.lastException.getMessage().contains("ForcedException"));
}
@Test
@SuppressWarnings("unchecked")
public void testDynamicFields() throws Exception {
@SuppressWarnings({"rawtypes"})
List rows = new ArrayList();
rows.add(createMap("id", "1", "desc", "one"));
MockDataSource.setIterator("select * from x", rows.iterator());
runFullImport(DATA_CONFIG_WITH_DYNAMIC_TRANSFORMER);
assertQ(req("id:1"), "//*[@numFound='1']");
assertQ(req("dynamic_s:test"), "//*[@numFound='1']");
}
@Test
@SuppressWarnings("unchecked")
public void testRequestParamsAsVariable() throws Exception {
@SuppressWarnings({"rawtypes"})
List rows = new ArrayList();
rows.add(createMap("id", "101", "desc", "ApacheSolr"));
MockDataSource.setIterator("select * from books where category='search'", rows.iterator());
LocalSolrQueryRequest request = lrf.makeRequest("command", "full-import",
"debug", "on", "clean", "true", "commit", "true",
"category", "search",
"dataConfig", REQUEST_PARAM_AS_VARIABLE);
h.query("/dataimport", request);
assertQ(req("desc:ApacheSolr"), "//*[@numFound='1']");
}
@Test
@SuppressWarnings("unchecked")
public void testDynamicFieldNames() throws Exception {
@SuppressWarnings({"rawtypes"})
List rows = new ArrayList();
rows.add(createMap("mypk", "101", "text", "ApacheSolr"));
MockDataSource.setIterator("select * from x", rows.iterator());
LocalSolrQueryRequest request = lrf.makeRequest("command", "full-import",
"debug", "on", "clean", "true", "commit", "true",
"dataConfig", DATA_CONFIG_WITH_DYNAMIC_FIELD_NAMES);
h.query("/dataimport", request);
assertQ(req("id:101"), "//*[@numFound='1']", "//*[@name='101_s']");
}
@Test
@SuppressWarnings("unchecked")
public void testRequestParamsAsFieldName() throws Exception {
@SuppressWarnings({"rawtypes"})
List rows = new ArrayList();
rows.add(createMap("mypk", "101", "text", "ApacheSolr"));
MockDataSource.setIterator("select * from x", rows.iterator());
LocalSolrQueryRequest request = lrf.makeRequest("command", "full-import",
"debug", "on", "clean", "true", "commit", "true",
"mypk", "id", "text", "desc",
"dataConfig", DATA_CONFIG_WITH_TEMPLATIZED_FIELD_NAMES);
h.query("/dataimport", request);
assertQ(req("id:101"), "//*[@numFound='1']");
}
@Test
@SuppressWarnings("unchecked")
public void testContext() throws Exception {
@SuppressWarnings({"rawtypes"})
List rows = new ArrayList();
rows.add(createMap("id", "1", "desc", "one"));
MockDataSource.setIterator("select * from x", rows.iterator());
runFullImport(loadDataConfig("data-config-with-transformer.xml"));
}
@Test
@SuppressWarnings("unchecked")
public void testSkipDoc() throws Exception {
@SuppressWarnings({"rawtypes"})
List rows = new ArrayList();
rows.add(createMap("id", "1", "desc", "one"));
rows.add(createMap("id", "2", "desc", "two", DocBuilder.SKIP_DOC, "true"));
MockDataSource.setIterator("select * from x", rows.iterator());
runFullImport(DATA_CONFIG_WITH_DYNAMIC_TRANSFORMER);
assertQ(req("id:1"), "//*[@numFound='1']");
assertQ(req("id:2"), "//*[@numFound='0']");
}
@Test
@SuppressWarnings({"unchecked", "rawtypes"})
public void testSkipRow() throws Exception {
List rows = new ArrayList();
rows.add(createMap("id", "1", "desc", "one"));
rows.add(createMap("id", "2", "desc", "two", DocBuilder.SKIP_ROW, "true"));
MockDataSource.setIterator("select * from x", rows.iterator());
runFullImport(DATA_CONFIG_WITH_DYNAMIC_TRANSFORMER);
assertQ(req("id:1"), "//*[@numFound='1']");
assertQ(req("id:2"), "//*[@numFound='0']");
MockDataSource.clearCache();
rows = new ArrayList();
rows.add(createMap("id", "3", "desc", "one"));
rows.add(createMap("id", "4", "desc", "two"));
MockDataSource.setIterator("select * from x", rows.iterator());
rows = new ArrayList();
rows.add(createMap("name_s", "abcd"));
MockDataSource.setIterator("3", rows.iterator());
rows = new ArrayList();
rows.add(createMap("name_s", "xyz", DocBuilder.SKIP_ROW, "true"));
MockDataSource.setIterator("4", rows.iterator());
runFullImport(DATA_CONFIG_WITH_TWO_ENTITIES);
assertQ(req("id:3"), "//*[@numFound='1']");
assertQ(req("id:4"), "//*[@numFound='1']");
assertQ(req("name_s:abcd"), "//*[@numFound='1']");
assertQ(req("name_s:xyz"), "//*[@numFound='0']");
}
@Test
@SuppressWarnings("unchecked")
public void testStopTransform() throws Exception {
@SuppressWarnings({"rawtypes"})
List rows = new ArrayList();
rows.add(createMap("id", "1", "desc", "one"));
rows.add(createMap("id", "2", "desc", "two", "$stopTransform", "true"));
MockDataSource.setIterator("select * from x", rows.iterator());
runFullImport(DATA_CONFIG_FOR_SKIP_TRANSFORM);
assertQ(req("id:1"), "//*[@numFound='1']");
assertQ(req("id:2"), "//*[@numFound='1']");
assertQ(req("name_s:xyz"), "//*[@numFound='1']");
}
@Test
@SuppressWarnings({"unchecked", "rawtypes"})
public void testDeleteDocs() throws Exception {
List rows = new ArrayList();
rows.add(createMap("id", "1", "desc", "one"));
rows.add(createMap("id", "2", "desc", "two"));
rows.add(createMap("id", "3", "desc", "two", DocBuilder.DELETE_DOC_BY_ID, "2"));
MockDataSource.setIterator("select * from x", rows.iterator());
runFullImport(DATA_CONFIG_FOR_SKIP_TRANSFORM);
assertQ(req("id:1"), "//*[@numFound='1']");
assertQ(req("id:2"), "//*[@numFound='0']");
assertQ(req("id:3"), "//*[@numFound='1']");
assertTrue("Update request processor processDelete was not called", TestUpdateRequestProcessor.processDeleteCalled);
assertTrue("Update request processor finish was not called", TestUpdateRequestProcessor.finishCalled);
MockDataSource.clearCache();
rows = new ArrayList();
rows.add(createMap("id", "1", "desc", "one"));
rows.add(createMap("id", "2", "desc", "one"));
rows.add(createMap("id", "3", "desc", "two", DocBuilder.DELETE_DOC_BY_QUERY, "desc:one"));
MockDataSource.setIterator("select * from x", rows.iterator());
runFullImport(DATA_CONFIG_FOR_SKIP_TRANSFORM);
assertQ(req("id:1"), "//*[@numFound='0']");
assertQ(req("id:2"), "//*[@numFound='0']");
assertQ(req("id:3"), "//*[@numFound='1']");
assertTrue("Update request processor processDelete was not called", TestUpdateRequestProcessor.processDeleteCalled);
assertTrue("Update request processor finish was not called", TestUpdateRequestProcessor.finishCalled);
MockDataSource.clearCache();
rows = new ArrayList();
rows.add(createMap(DocBuilder.DELETE_DOC_BY_ID, "3"));
MockDataSource.setIterator("select * from x", rows.iterator());
runFullImport(DATA_CONFIG_FOR_SKIP_TRANSFORM, createMap("clean","false"));
assertQ(req("id:3"), "//*[@numFound='0']");
assertTrue("Update request processor processDelete was not called", TestUpdateRequestProcessor.processDeleteCalled);
assertTrue("Update request processor finish was not called", TestUpdateRequestProcessor.finishCalled);
}
@Test
@Ignore("Fix Me. See SOLR-4103.")
public void testFileListEntityProcessor_lastIndexTime() throws Exception {
File tmpdir = createTempDir().toFile();
@SuppressWarnings({"unchecked"})
Map<String, String> params = createMap("baseDir", tmpdir.getAbsolutePath());
createFile(tmpdir, "a.xml", "a.xml".getBytes(StandardCharsets.UTF_8), true);
createFile(tmpdir, "b.xml", "b.xml".getBytes(StandardCharsets.UTF_8), true);
createFile(tmpdir, "c.props", "c.props".getBytes(StandardCharsets.UTF_8), true);
runFullImport(DATA_CONFIG_FILE_LIST, params);
assertQ(req("*:*"), "//*[@numFound='3']");
// Add a new file after a full index is done
createFile(tmpdir, "t.xml", "t.xml".getBytes(StandardCharsets.UTF_8), false);
runFullImport(DATA_CONFIG_FILE_LIST, params);
// we should find only 1 because by default clean=true is passed
// and this particular import should find only one file t.xml
assertQ(req("*:*"), "//*[@numFound='1']");
}
public static class MockTransformer extends Transformer {
@Override
public Object transformRow(Map<String, Object> row, Context context) {
assertTrue("Context gave incorrect data source", context.getDataSource("mockDs") instanceof MockDataSource2);
return row;
}
}
public static class AddDynamicFieldTransformer extends Transformer {
@Override
public Object transformRow(Map<String, Object> row, Context context) {
// Add a dynamic field
row.put("dynamic_s", "test");
return row;
}
}
public static class ForcedExceptionTransformer extends Transformer {
@Override
public Object transformRow(Map<String, Object> row, Context context) {
throw new DataImportHandlerException(DataImportHandlerException.SEVERE, "ForcedException");
}
}
public static class MockDataSource2 extends MockDataSource {
}
public static class StartEventListener implements EventListener {
public static boolean executed = false;
@Override
public void onEvent(Context ctx) {
executed = true;
}
}
public static class EndEventListener implements EventListener {
public static boolean executed = false;
@Override
public void onEvent(Context ctx) {
executed = true;
}
}
public static class ErrorEventListener implements EventListener {
public static boolean executed = false;
public static Exception lastException = null;
@Override
public void onEvent(Context ctx) {
executed = true;
lastException = ((ContextImpl) ctx).getLastException();
}
}
private static final String REQUEST_PARAM_AS_VARIABLE = "<dataConfig>\n" +
" <dataSource type=\"MockDataSource\" />\n" +
" <document>\n" +
" <entity name=\"books\" query=\"select * from books where category='${dataimporter.request.category}'\">\n" +
" <field column=\"id\" />\n" +
" <field column=\"desc\" />\n" +
" </entity>\n" +
" </document>\n" +
"</dataConfig>";
private static final String DATA_CONFIG_WITH_DYNAMIC_TRANSFORMER = "<dataConfig> <dataSource type=\"MockDataSource\"/>\n" +
" <document>\n" +
" <entity name=\"books\" query=\"select * from x\"" +
" transformer=\"TestDocBuilder2$AddDynamicFieldTransformer\">\n" +
" <field column=\"id\" />\n" +
" <field column=\"desc\" />\n" +
" </entity>\n" +
" </document>\n" +
"</dataConfig>";
private static final String DATA_CONFIG_FOR_SKIP_TRANSFORM = "<dataConfig> <dataSource type=\"MockDataSource\"/>\n" +
" <document>\n" +
" <entity name=\"books\" query=\"select * from x\"" +
" transformer=\"TemplateTransformer\">\n" +
" <field column=\"id\" />\n" +
" <field column=\"desc\" />\n" +
" <field column=\"name_s\" template=\"xyz\" />\n" +
" </entity>\n" +
" </document>\n" +
"</dataConfig>";
private static final String DATA_CONFIG_WITH_TWO_ENTITIES = "<dataConfig><dataSource type=\"MockDataSource\"/>\n" +
" <document>\n" +
" <entity name=\"books\" query=\"select * from x\">" +
" <field column=\"id\" />\n" +
" <field column=\"desc\" />\n" +
" <entity name=\"authors\" query=\"${books.id}\">" +
" <field column=\"name_s\" />" +
" </entity>" +
" </entity>\n" +
" </document>\n" +
"</dataConfig>";
private static final String DATA_CONFIG_WITH_CASE_INSENSITIVE_FIELDS = "<dataConfig> <dataSource type=\"MockDataSource\"/>\n" +
" <document onImportStart=\"TestDocBuilder2$StartEventListener\" onImportEnd=\"TestDocBuilder2$EndEventListener\">\n" +
" <entity name=\"books\" query=\"select * from x\">\n" +
" <field column=\"ID\" />\n" +
" <field column=\"Desc\" />\n" +
" </entity>\n" +
" </document>\n" +
"</dataConfig>";
private static final String DATA_CONFIG_WITH_ERROR_HANDLER = "<dataConfig> <dataSource type=\"MockDataSource\"/>\n" +
" <document onError=\"TestDocBuilder2$ErrorEventListener\">\n" +
" <entity name=\"books\" query=\"select * from x\" transformer=\"TestDocBuilder2$ForcedExceptionTransformer\">\n" +
" <field column=\"id\" />\n" +
" <field column=\"FORCE_ERROR\" />\n" +
" </entity>\n" +
" </document>\n" +
"</dataConfig>";
private static final String DATA_CONFIG_WITH_TEMPLATIZED_FIELD_NAMES = "<dataConfig><dataSource type=\"MockDataSource\"/>\n" +
" <document>\n" +
" <entity name=\"books\" query=\"select * from x\">\n" +
" <field column=\"mypk\" name=\"${dih.request.mypk}\" />\n" +
" <field column=\"text\" name=\"${dih.request.text}\" />\n" +
" </entity>\n" +
" </document>\n" +
"</dataConfig>";
private static final String DATA_CONFIG_WITH_DYNAMIC_FIELD_NAMES = "<dataConfig><dataSource type=\"MockDataSource\"/>\n" +
" <document>\n" +
" <entity name=\"books\" query=\"select * from x\">\n" +
" <field column=\"mypk\" name=\"id\" />\n" +
" <field column=\"text\" name=\"${books.mypk}_s\" />\n" +
" </entity>\n" +
" </document>\n" +
"</dataConfig>";
private static final String DATA_CONFIG_FILE_LIST = "<dataConfig>\n" +
"\t<document>\n" +
"\t\t<entity name=\"x\" processor=\"FileListEntityProcessor\" \n" +
"\t\t\t\tfileName=\".*\" newerThan=\"${dih.last_index_time}\" \n" +
"\t\t\t\tbaseDir=\"${dih.request.baseDir}\" transformer=\"TemplateTransformer\">\n" +
"\t\t\t<field column=\"id\" template=\"${x.file}\" />\n" +
"\t\t</entity>\n" +
"\t</document>\n" +
"</dataConfig>";
}