/* * 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.lucene.luke.models.search; import java.io.IOException; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; import java.util.Optional; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.DoubleDocValuesField; import org.apache.lucene.document.DoublePoint; import org.apache.lucene.document.Field; import org.apache.lucene.document.FloatDocValuesField; import org.apache.lucene.document.FloatPoint; import org.apache.lucene.document.IntPoint; import org.apache.lucene.document.LongPoint; import org.apache.lucene.document.NumericDocValuesField; import org.apache.lucene.document.SortedDocValuesField; import org.apache.lucene.document.SortedNumericDocValuesField; import org.apache.lucene.document.SortedSetDocValuesField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.RandomIndexWriter; import org.apache.lucene.luke.models.LukeException; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.PointRangeQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; import org.apache.lucene.search.SortedNumericSortField; import org.apache.lucene.search.SortedSetSortField; import org.apache.lucene.store.Directory; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.LuceneTestCase; import org.junit.Test; public class SearchImplTest extends LuceneTestCase { private IndexReader reader; private Directory dir; private Path indexDir; @Override public void setUp() throws Exception { super.setUp(); createIndex(); dir = newFSDirectory(indexDir); reader = DirectoryReader.open(dir); } private void createIndex() throws IOException { indexDir = createTempDir("testIndex"); Directory dir = newFSDirectory(indexDir); RandomIndexWriter writer = new RandomIndexWriter(random(), dir, new StandardAnalyzer()); for (int i = 0; i < 10; i++) { Document doc1 = new Document(); doc1.add(newTextField("f1", "Apple Pie", Field.Store.YES)); doc1.add(new SortedDocValuesField("f2", new BytesRef("a" + (i * 10 + 1)))); doc1.add(new SortedSetDocValuesField("f3", new BytesRef("a" + (i * 10 + 1)))); doc1.add(new NumericDocValuesField("f4", i * 10 + 1L)); doc1.add(new FloatDocValuesField("f5", i * 10 + 1.0f)); doc1.add(new DoubleDocValuesField("f6", i * 10 + 1.0)); doc1.add(new SortedNumericDocValuesField("f7", i * 10 + 1L)); doc1.add(new IntPoint("f8", i * 10 + 1)); doc1.add(new LongPoint("f9", i * 10 + 1L)); doc1.add(new FloatPoint("f10", i * 10 + 1.0f)); doc1.add(new DoublePoint("f11", i * 10 + 1.0)); writer.addDocument(doc1); Document doc2 = new Document(); doc2.add(newTextField("f1", "Brownie", Field.Store.YES)); doc2.add(new SortedDocValuesField("f2", new BytesRef("b" + (i * 10 + 2)))); doc2.add(new SortedSetDocValuesField("f3", new BytesRef("b" + (i * 10 + 2)))); doc2.add(new NumericDocValuesField("f4", i * 10 + 2L)); doc2.add(new FloatDocValuesField("f5", i * 10 + 2.0f)); doc2.add(new DoubleDocValuesField("f6", i * 10 + 2.0)); doc2.add(new SortedNumericDocValuesField("f7", i * 10 + 2L)); doc2.add(new IntPoint("f8", i * 10 + 2)); doc2.add(new LongPoint("f9", i * 10 + 2L)); doc2.add(new FloatPoint("f10", i * 10 + 2.0f)); doc2.add(new DoublePoint("f11", i * 10 + 2.0)); writer.addDocument(doc2); Document doc3 = new Document(); doc3.add(newTextField("f1", "Chocolate Pie", Field.Store.YES)); doc3.add(new SortedDocValuesField("f2", new BytesRef("c" + (i * 10 + 3)))); doc3.add(new SortedSetDocValuesField("f3", new BytesRef("c" + (i * 10 + 3)))); doc3.add(new NumericDocValuesField("f4", i * 10 + 3L)); doc3.add(new FloatDocValuesField("f5", i * 10 + 3.0f)); doc3.add(new DoubleDocValuesField("f6", i * 10 + 3.0)); doc3.add(new SortedNumericDocValuesField("f7", i * 10 + 3L)); doc3.add(new IntPoint("f8", i * 10 + 3)); doc3.add(new LongPoint("f9", i * 10 + 3L)); doc3.add(new FloatPoint("f10", i * 10 + 3.0f)); doc3.add(new DoublePoint("f11", i * 10 + 3.0)); writer.addDocument(doc3); Document doc4 = new Document(); doc4.add(newTextField("f1", "Doughnut", Field.Store.YES)); doc4.add(new SortedDocValuesField("f2", new BytesRef("d" + (i * 10 + 4)))); doc4.add(new SortedSetDocValuesField("f3", new BytesRef("d" + (i * 10 + 4)))); doc4.add(new NumericDocValuesField("f4", i * 10 + 4L)); doc4.add(new FloatDocValuesField("f5", i * 10 + 4.0f)); doc4.add(new DoubleDocValuesField("f6", i * 10 + 4.0)); doc4.add(new SortedNumericDocValuesField("f7", i * 10 + 4L)); doc4.add(new IntPoint("f8", i * 10 + 4)); doc4.add(new LongPoint("f9", i * 10 + 4L)); doc4.add(new FloatPoint("f10", i * 10 + 4.0f)); doc4.add(new DoublePoint("f11", i * 10 + 4.0)); writer.addDocument(doc4); Document doc5 = new Document(); doc5.add(newTextField("f1", "Eclair", Field.Store.YES)); doc5.add(new SortedDocValuesField("f2", new BytesRef("e" + (i * 10 + 5)))); doc5.add(new SortedSetDocValuesField("f3", new BytesRef("e" + (i * 10 + 5)))); doc5.add(new NumericDocValuesField("f4", i * 10 + 5L)); doc5.add(new FloatDocValuesField("f5", i * 10 + 5.0f)); doc5.add(new DoubleDocValuesField("f6", i * 10 + 5.0)); doc5.add(new SortedNumericDocValuesField("f7", i * 10 + 5L)); doc5.add(new IntPoint("f8", i * 10 + 5)); doc5.add(new LongPoint("f9", i * 10 + 5L)); doc5.add(new FloatPoint("f10", i * 10 + 5.0f)); doc5.add(new DoublePoint("f11", i * 10 + 5.0)); writer.addDocument(doc5); } writer.commit(); writer.close(); dir.close(); } @Override public void tearDown() throws Exception { super.tearDown(); reader.close(); dir.close(); } @Test public void testGetSortableFieldNames() { SearchImpl search = new SearchImpl(reader); assertArrayEquals(new String[]{"f2", "f3", "f4", "f5", "f6", "f7"}, search.getSortableFieldNames().toArray()); } @Test public void testGetSearchableFieldNames() { SearchImpl search = new SearchImpl(reader); assertArrayEquals(new String[]{"f1"}, search.getSearchableFieldNames().toArray()); } @Test public void testGetRangeSearchableFieldNames() { SearchImpl search = new SearchImpl(reader); assertArrayEquals(new String[]{"f8", "f9", "f10", "f11"}, search.getRangeSearchableFieldNames().toArray()); } @Test public void testParseClassic() { SearchImpl search = new SearchImpl(reader); QueryParserConfig config = new QueryParserConfig.Builder() .allowLeadingWildcard(true) .defaultOperator(QueryParserConfig.Operator.AND) .fuzzyMinSim(1.0f) .build(); Query q = search.parseQuery("app~ f2:*ie", "f1", new StandardAnalyzer(), config, false); assertEquals("+f1:app~1 +f2:*ie", q.toString()); } @Test public void testParsePointRange() { SearchImpl search = new SearchImpl(reader); Map<String, Class<? extends Number>> types = new HashMap<>(); types.put("f8", Integer.class); QueryParserConfig config = new QueryParserConfig.Builder() .useClassicParser(false) .typeMap(types) .build(); Query q = search.parseQuery("f8:[10 TO 20]", "f1", new StandardAnalyzer(), config, false); assertEquals("f8:[10 TO 20]", q.toString()); assertTrue(q instanceof PointRangeQuery); } @Test public void testGuessSortTypes() { SearchImpl search = new SearchImpl(reader); assertTrue(search.guessSortTypes("f1").isEmpty()); assertArrayEquals( new SortField[]{ new SortField("f2", SortField.Type.STRING), new SortField("f2", SortField.Type.STRING_VAL)}, search.guessSortTypes("f2").toArray()); assertArrayEquals( new SortField[]{new SortedSetSortField("f3", false)}, search.guessSortTypes("f3").toArray()); assertArrayEquals( new SortField[]{ new SortField("f4", SortField.Type.INT), new SortField("f4", SortField.Type.LONG), new SortField("f4", SortField.Type.FLOAT), new SortField("f4", SortField.Type.DOUBLE)}, search.guessSortTypes("f4").toArray()); assertArrayEquals( new SortField[]{ new SortField("f5", SortField.Type.INT), new SortField("f5", SortField.Type.LONG), new SortField("f5", SortField.Type.FLOAT), new SortField("f5", SortField.Type.DOUBLE)}, search.guessSortTypes("f5").toArray()); assertArrayEquals( new SortField[]{ new SortField("f6", SortField.Type.INT), new SortField("f6", SortField.Type.LONG), new SortField("f6", SortField.Type.FLOAT), new SortField("f6", SortField.Type.DOUBLE)}, search.guessSortTypes("f6").toArray()); assertArrayEquals( new SortField[]{ new SortedNumericSortField("f7", SortField.Type.INT), new SortedNumericSortField("f7", SortField.Type.LONG), new SortedNumericSortField("f7", SortField.Type.FLOAT), new SortedNumericSortField("f7", SortField.Type.DOUBLE)}, search.guessSortTypes("f7").toArray()); } @Test(expected = LukeException.class) public void testGuessSortTypesNoSuchField() { SearchImpl search = new SearchImpl(reader); search.guessSortTypes("unknown"); } @Test public void testGetSortType() { SearchImpl search = new SearchImpl(reader); assertFalse(search.getSortType("f1", "STRING", false).isPresent()); assertEquals(new SortField("f2", SortField.Type.STRING, false), search.getSortType("f2", "STRING", false).get()); assertFalse(search.getSortType("f2", "INT", false).isPresent()); assertEquals(new SortedSetSortField("f3", false), search.getSortType("f3", "CUSTOM", false).get()); assertEquals(new SortField("f4", SortField.Type.LONG, false), search.getSortType("f4", "LONG", false).get()); assertFalse(search.getSortType("f4", "STRING", false).isPresent()); assertEquals(new SortField("f5", SortField.Type.FLOAT, false), search.getSortType("f5", "FLOAT", false).get()); assertFalse(search.getSortType("f5", "STRING", false).isPresent()); assertEquals(new SortField("f6", SortField.Type.DOUBLE, false), search.getSortType("f6", "DOUBLE", false).get()); assertFalse(search.getSortType("f6", "STRING", false).isPresent()); assertEquals(new SortedNumericSortField("f7", SortField.Type.LONG, false), search.getSortType("f7", "LONG", false).get()); assertFalse(search.getSortType("f7", "STRING", false).isPresent()); } @Test(expected = LukeException.class) public void testGetSortTypeNoSuchField() { SearchImpl search = new SearchImpl(reader); search.getSortType("unknown", "STRING", false); } @Test public void testSearch() throws Exception { SearchImpl search = new SearchImpl(reader); Query query = new QueryParser("f1", new StandardAnalyzer()).parse("apple"); SearchResults res = search.search(query, new SimilarityConfig.Builder().build(), null, 10, true); assertEquals(10, res.getTotalHits().value); assertEquals(10, res.size()); assertEquals(0, res.getOffset()); } @Test public void testSearchWithSort() throws Exception { SearchImpl search = new SearchImpl(reader); Query query = new QueryParser("f1", new StandardAnalyzer()).parse("apple"); Sort sort = new Sort(new SortField("f2", SortField.Type.STRING, true)); SearchResults res = search.search(query, new SimilarityConfig.Builder().build(), sort, null, 10, true); assertEquals(10, res.getTotalHits().value); assertEquals(10, res.size()); assertEquals(0, res.getOffset()); } @Test public void testNextPage() throws Exception { SearchImpl search = new SearchImpl(reader); Query query = new QueryParser("f1", new StandardAnalyzer()).parse("pie"); search.search(query, new SimilarityConfig.Builder().build(), null, 10, true); Optional<SearchResults> opt = search.nextPage(); assertTrue(opt.isPresent()); SearchResults res = opt.get(); assertEquals(20, res.getTotalHits().value); assertEquals(10, res.size()); assertEquals(10, res.getOffset()); } @Test(expected = LukeException.class) public void testNextPageSearchNotStarted() { SearchImpl search = new SearchImpl(reader); search.nextPage(); } @Test public void testNextPageNoMoreResults() throws Exception { SearchImpl search = new SearchImpl(reader); Query query = new QueryParser("f1", new StandardAnalyzer()).parse("pie"); search.search(query, new SimilarityConfig.Builder().build(), null, 10, true); search.nextPage(); assertFalse(search.nextPage().isPresent()); } @Test public void testPrevPage() throws Exception { SearchImpl search = new SearchImpl(reader); Query query = new QueryParser("f1", new StandardAnalyzer()).parse("pie"); search.search(query, new SimilarityConfig.Builder().build(), null, 10, true); search.nextPage(); Optional<SearchResults> opt = search.prevPage(); assertTrue(opt.isPresent()); SearchResults res = opt.get(); assertEquals(20, res.getTotalHits().value); assertEquals(10, res.size()); assertEquals(0, res.getOffset()); } @Test(expected = LukeException.class) public void testPrevPageSearchNotStarted() { SearchImpl search = new SearchImpl(reader); search.prevPage(); } @Test public void testPrevPageNoMoreResults() throws Exception { SearchImpl search = new SearchImpl(reader); Query query = new QueryParser("f1", new StandardAnalyzer()).parse("pie"); search.search(query, new SimilarityConfig.Builder().build(), null, 10, true); assertFalse(search.prevPage().isPresent()); } }