From d9a96ea831626edb13694067a79474ca3b5353d2 Mon Sep 17 00:00:00 2001 From: Roberto Franchini Date: Thu, 15 Dec 2016 13:58:01 +0100 Subject: [PATCH] adds support for range query on Lucene module refs https://github.com/orientechnologies/orientdb/issues/6534 --- .../lucene/OLuceneIndexType.java | 17 ++- .../lucene/builder/OLuceneQueryBuilder.java | 35 ++--- .../lucene/operator/OLuceneOperatorUtil.java | 95 ++++++------- .../lucene/operator/OLuceneTextOperator.java | 82 ++++++------ .../parser/OLuceneMultiFieldQueryParser.java | 88 ++++++++++++ .../lucene/test/LuceneMultiFieldTest.java | 12 +- .../lucene/test/LuceneNumericRangeTest.java | 48 ------- .../lucene/test/LuceneRangeTest.java | 125 ++++++++++++++++++ .../lucene/test/LuceneReuseTest.java | 13 +- lucene/src/test/resources/testLuceneIndex.sql | 13 +- 10 files changed, 353 insertions(+), 175 deletions(-) create mode 100644 lucene/src/main/java/com/orientechnologies/lucene/parser/OLuceneMultiFieldQueryParser.java delete mode 100644 lucene/src/test/java/com/orientechnologies/lucene/test/LuceneNumericRangeTest.java create mode 100644 lucene/src/test/java/com/orientechnologies/lucene/test/LuceneRangeTest.java diff --git a/lucene/src/main/java/com/orientechnologies/lucene/OLuceneIndexType.java b/lucene/src/main/java/com/orientechnologies/lucene/OLuceneIndexType.java index 33a63a80b8a..19ba2c468ca 100644 --- a/lucene/src/main/java/com/orientechnologies/lucene/OLuceneIndexType.java +++ b/lucene/src/main/java/com/orientechnologies/lucene/OLuceneIndexType.java @@ -54,15 +54,14 @@ public static Field createField(String fieldName, Object value, Field.Store stor if (value instanceof Number) { Number number = (Number) value; - if (value instanceof Long) + if (value instanceof Long) { return new LongField(fieldName, number.longValue(), store); - else if (value instanceof Float) + } else if (value instanceof Float) { return new FloatField(fieldName, number.floatValue(), store); - else if (value instanceof Double) + } else if (value instanceof Double) { return new DoubleField(fieldName, number.doubleValue(), store); - + } return new IntField(fieldName, number.intValue(), store); - } else if (value instanceof Date) { return new LongField(fieldName, ((Date) value).getTime(), store); } @@ -104,10 +103,6 @@ public static Query createExactQuery(OIndexDefinition index, Object key) { return query; } - public static Query createQueryId(OIdentifiable value) { - return new TermQuery(new Term(OLuceneIndexEngineAbstract.RID, value.getIdentity().toString())); - } - public static Query createDeleteQuery(OIdentifiable value, List fields, Object key) { final BooleanQuery.Builder queryBuilder = new BooleanQuery.Builder(); @@ -127,6 +122,10 @@ public static Query createDeleteQuery(OIdentifiable value, List fields, return queryBuilder.build(); } + public static Query createQueryId(OIdentifiable value) { + return new TermQuery(new Term(OLuceneIndexEngineAbstract.RID, value.getIdentity().toString())); + } + public static Query createFullQuery(OIndexDefinition index, Object key, Analyzer analyzer) throws ParseException { String query = ""; diff --git a/lucene/src/main/java/com/orientechnologies/lucene/builder/OLuceneQueryBuilder.java b/lucene/src/main/java/com/orientechnologies/lucene/builder/OLuceneQueryBuilder.java index 904c4b812af..9c1548fdcf6 100644 --- a/lucene/src/main/java/com/orientechnologies/lucene/builder/OLuceneQueryBuilder.java +++ b/lucene/src/main/java/com/orientechnologies/lucene/builder/OLuceneQueryBuilder.java @@ -19,15 +19,16 @@ package com.orientechnologies.lucene.builder; import com.orientechnologies.common.log.OLogManager; +import com.orientechnologies.lucene.parser.OLuceneMultiFieldQueryParser; import com.orientechnologies.orient.core.index.OCompositeKey; import com.orientechnologies.orient.core.index.OIndexDefinition; +import com.orientechnologies.orient.core.metadata.schema.OType; import com.orientechnologies.orient.core.record.impl.ODocument; import com.orientechnologies.orient.core.sql.parser.ParseException; import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.queryparser.classic.MultiFieldQueryParser; -import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.Query; +import java.util.HashMap; import java.util.Map; /** @@ -82,26 +83,30 @@ public Query query(OIndexDefinition index, Object key, Analyzer analyzer) throws } protected Query getQueryParser(OIndexDefinition index, String key, Analyzer analyzer) throws ParseException { - QueryParser queryParser; - if ((key).startsWith("(")) { - queryParser = new QueryParser("", analyzer); + String[] fields; + if (index.isAutomatic()) { + fields = index.getFields().toArray(new String[index.getFields().size()]); } else { - String[] fields; - if (index.isAutomatic()) { - fields = index.getFields().toArray(new String[index.getFields().size()]); - } else { - int length = index.getTypes().length; + int length = index.getTypes().length; - fields = new String[length]; - for (int i = 0; i < length; i++) { - fields[i] = "k" + i; - } + fields = new String[length]; + for (int i = 0; i < length; i++) { + fields[i] = "k" + i; } + } + + Map types = new HashMap(); + for (int i = 0; i < fields.length; i++) { + String field = fields[i]; + types.put(field, index.getTypes()[i]); - queryParser = new MultiFieldQueryParser(fields, analyzer); } +// for (Map.Entry typeEntry : types.entrySet()) { +// System.out.println("typeEntry = " + typeEntry); +// } + final OLuceneMultiFieldQueryParser queryParser = new OLuceneMultiFieldQueryParser(types, fields, analyzer); queryParser.setAllowLeadingWildcard(allowLeadingWildcard); queryParser.setLowercaseExpandedTerms(lowercaseExpandedTerms); diff --git a/lucene/src/main/java/com/orientechnologies/lucene/operator/OLuceneOperatorUtil.java b/lucene/src/main/java/com/orientechnologies/lucene/operator/OLuceneOperatorUtil.java index d89709875f7..b633a200c70 100644 --- a/lucene/src/main/java/com/orientechnologies/lucene/operator/OLuceneOperatorUtil.java +++ b/lucene/src/main/java/com/orientechnologies/lucene/operator/OLuceneOperatorUtil.java @@ -31,53 +31,11 @@ public class OLuceneOperatorUtil { - public static boolean checkIndexExistence(final OClass iSchemaClass, final OIndexSearchResult result) { - if (!iSchemaClass.areIndexed(result.fields())) - return false; - - if (result.lastField.isLong()) { - final int fieldCount = result.lastField.getItemCount(); - OClass cls = iSchemaClass.getProperty(result.lastField.getItemName(0)).getLinkedClass(); - - for (int i = 1; i < fieldCount; i++) { - if (cls == null || !cls.areIndexed(result.lastField.getItemName(i))) { - return false; - } + public static OIndexSearchResult buildOIndexSearchResult(OClass iSchemaClass, + OSQLFilterCondition iCondition, + List iIndexSearchResults, + OCommandContext context) { - cls = cls.getProperty(result.lastField.getItemName(i)).getLinkedClass(); - } - } - return true; - } - - public static OIndexSearchResult createIndexedProperty(final OSQLFilterCondition iCondition, final Object iItem) { - if (iItem == null || !(iItem instanceof OSQLFilterItemField)) - return null; - - if (iCondition.getLeft() instanceof OSQLFilterItemField && iCondition.getRight() instanceof OSQLFilterItemField) - return null; - - final OSQLFilterItemField item = (OSQLFilterItemField) iItem; - - if (item.hasChainOperators() && !item.isFieldChain()) - return null; - - final Object origValue = iCondition.getLeft() == iItem ? iCondition.getRight() : iCondition.getLeft(); - - if (iCondition.getOperator() instanceof OQueryOperatorBetween || iCondition.getOperator() instanceof OQueryOperatorIn) { - return new OIndexSearchResult(iCondition.getOperator(), item.getFieldChain(), origValue); - } - - final Object value = OSQLHelper.getValue(origValue); - - if (value == null) - return null; - - return new OIndexSearchResult(iCondition.getOperator(), item.getFieldChain(), value); - } - - public static OIndexSearchResult buildOIndexSearchResult(OClass iSchemaClass, OSQLFilterCondition iCondition, - List iIndexSearchResults, OCommandContext context) { if (iCondition.getLeft() instanceof Collection) { OIndexSearchResult lastResult = null; @@ -132,4 +90,49 @@ public static OIndexSearchResult buildOIndexSearchResult(OClass iSchemaClass, OS } } + public static boolean checkIndexExistence(final OClass iSchemaClass, final OIndexSearchResult result) { + if (!iSchemaClass.areIndexed(result.fields())) + return false; + + if (result.lastField.isLong()) { + final int fieldCount = result.lastField.getItemCount(); + OClass cls = iSchemaClass.getProperty(result.lastField.getItemName(0)).getLinkedClass(); + + for (int i = 1; i < fieldCount; i++) { + if (cls == null || !cls.areIndexed(result.lastField.getItemName(i))) { + return false; + } + + cls = cls.getProperty(result.lastField.getItemName(i)).getLinkedClass(); + } + } + return true; + } + + public static OIndexSearchResult createIndexedProperty(final OSQLFilterCondition iCondition, final Object iItem) { + if (iItem == null || !(iItem instanceof OSQLFilterItemField)) + return null; + + if (iCondition.getLeft() instanceof OSQLFilterItemField && iCondition.getRight() instanceof OSQLFilterItemField) + return null; + + final OSQLFilterItemField item = (OSQLFilterItemField) iItem; + + if (item.hasChainOperators() && !item.isFieldChain()) + return null; + + final Object origValue = iCondition.getLeft() == iItem ? iCondition.getRight() : iCondition.getLeft(); + + if (iCondition.getOperator() instanceof OQueryOperatorBetween || iCondition.getOperator() instanceof OQueryOperatorIn) { + return new OIndexSearchResult(iCondition.getOperator(), item.getFieldChain(), origValue); + } + + final Object value = OSQLHelper.getValue(origValue); + + if (value == null) + return null; + + return new OIndexSearchResult(iCondition.getOperator(), item.getFieldChain(), value); + } + } diff --git a/lucene/src/main/java/com/orientechnologies/lucene/operator/OLuceneTextOperator.java b/lucene/src/main/java/com/orientechnologies/lucene/operator/OLuceneTextOperator.java index 0cf68fc6c26..400519c86e3 100644 --- a/lucene/src/main/java/com/orientechnologies/lucene/operator/OLuceneTextOperator.java +++ b/lucene/src/main/java/com/orientechnologies/lucene/operator/OLuceneTextOperator.java @@ -38,15 +38,21 @@ import com.orientechnologies.orient.core.sql.operator.OIndexReuseType; import com.orientechnologies.orient.core.sql.operator.OQueryTargetOperator; import com.orientechnologies.orient.core.sql.parser.ParseException; -import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.memory.MemoryIndex; -import org.apache.lucene.search.Query; -import java.util.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; public class OLuceneTextOperator extends OQueryTargetOperator { + public static final String MEMORY_INDEX = "_memoryIndex"; + public OLuceneTextOperator() { this("LUCENE", 5, false); } @@ -55,24 +61,30 @@ public OLuceneTextOperator(String iKeyword, int iPrecedence, boolean iLogical) { super(iKeyword, iPrecedence, iLogical); } - protected static ODatabaseDocumentInternal getDatabase() { - return ODatabaseRecordThreadLocal.INSTANCE.get(); + @Override + public OIndexReuseType getIndexReuseType(Object iLeft, Object iRight) { + return OIndexReuseType.INDEX_OPERATOR; + } + + @Override + public OIndexSearchResult getOIndexSearchResult(OClass iSchemaClass, OSQLFilterCondition iCondition, + List iIndexSearchResults, OCommandContext context) { + // FIXME questo non trova l'indice se l'ordine e' errato + OIndexSearchResult result = OLuceneOperatorUtil.buildOIndexSearchResult(iSchemaClass, iCondition, + iIndexSearchResults, context); + + return result; } @Override public OIndexCursor executeIndexQuery(OCommandContext iContext, OIndex index, List keyParams, boolean ascSortOrder) { - OIndexCursor cursor; + Object indexResult = index.get(new OFullTextCompositeKey(keyParams).setContext(iContext)); + if (indexResult == null || indexResult instanceof OIdentifiable) - cursor = new OIndexCursorSingleValue((OIdentifiable) indexResult, new OFullTextCompositeKey(keyParams)); - else - cursor = new OIndexCursorCollectionValue(((Collection) indexResult), new OFullTextCompositeKey(keyParams)); - return cursor; - } + return new OIndexCursorSingleValue((OIdentifiable) indexResult, new OFullTextCompositeKey(keyParams)); - @Override - public OIndexReuseType getIndexReuseType(Object iLeft, Object iRight) { - return OIndexReuseType.INDEX_OPERATOR; + return new OIndexCursorCollectionValue(((Collection) indexResult), new OFullTextCompositeKey(keyParams)); } @Override @@ -86,11 +98,8 @@ public ORID getEndRidRange(Object iLeft, Object iRight) { } @Override - public OIndexSearchResult getOIndexSearchResult(OClass iSchemaClass, OSQLFilterCondition iCondition, - List iIndexSearchResults, OCommandContext context) { - - // FIXME questo non trova l'indice se l'ordine e' errato - return OLuceneOperatorUtil.buildOIndexSearchResult(iSchemaClass, iCondition, iIndexSearchResults, context); + public boolean canBeMerged() { + return false; } @Override @@ -104,35 +113,31 @@ public Object evaluateRecord(OIdentifiable iRecord, ODocument iCurrentResult, OS Object iRight, OCommandContext iContext) { OLuceneFullTextIndex index = involvedIndex(iRecord, iCurrentResult, iCondition, iLeft, iRight); - if (index == null) { throw new OCommandExecutionException("Cannot evaluate lucene condition without index configuration."); } - MemoryIndex memoryIndex = (MemoryIndex) iContext.getVariable("_memoryIndex"); + MemoryIndex memoryIndex = (MemoryIndex) iContext.getVariable(MEMORY_INDEX); if (memoryIndex == null) { memoryIndex = new MemoryIndex(); - iContext.setVariable("_memoryIndex", memoryIndex); + iContext.setVariable(MEMORY_INDEX, memoryIndex); } memoryIndex.reset(); - Document doc = null; try { - doc = index.buildDocument(iLeft); - } catch (Exception e) { - e.printStackTrace(); - } + for (IndexableField field : index.buildDocument(iLeft).getFields()) { + memoryIndex.addField(field.name(), field.tokenStream(index.indexAnalyzer(), null)); + } + return memoryIndex.search(index.buildQuery(iRight)) > 0.0f; - for (IndexableField field : doc.getFields()) { - memoryIndex.addField(field.name(), field.stringValue(), index.indexAnalyzer()); - } - Query query = null; - try { - query = index.buildQuery(iRight); } catch (ParseException e) { OLogManager.instance().error(this, "error occurred while building query", e); + + } catch (IOException e) { + OLogManager.instance().error(this, "error occurred while building memory index", e); + } - return memoryIndex.search(query) > 0.0f; + return null; } protected OLuceneFullTextIndex involvedIndex(OIdentifiable iRecord, ODocument iCurrentResult, OSQLFilterCondition iCondition, @@ -166,6 +171,10 @@ protected OLuceneFullTextIndex involvedIndex(OIdentifiable iRecord, ODocument iC return idx; } + protected static ODatabaseDocumentInternal getDatabase() { + return ODatabaseRecordThreadLocal.INSTANCE.get(); + } + private boolean isChained(Object left) { if (left instanceof OSQLFilterItemField) { OSQLFilterItemField field = (OSQLFilterItemField) left; @@ -204,9 +213,4 @@ protected Collection fields(OSQLFilterCondition iCondition) { } return Collections.emptyList(); } - - @Override - public boolean canBeMerged() { - return false; - } } diff --git a/lucene/src/main/java/com/orientechnologies/lucene/parser/OLuceneMultiFieldQueryParser.java b/lucene/src/main/java/com/orientechnologies/lucene/parser/OLuceneMultiFieldQueryParser.java new file mode 100644 index 00000000000..fc9d98b28b1 --- /dev/null +++ b/lucene/src/main/java/com/orientechnologies/lucene/parser/OLuceneMultiFieldQueryParser.java @@ -0,0 +1,88 @@ +package com.orientechnologies.lucene.parser; + +import com.orientechnologies.orient.core.metadata.schema.OType; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.document.DateTools; +import org.apache.lucene.queryparser.classic.MultiFieldQueryParser; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.search.NumericRangeQuery; +import org.apache.lucene.search.Query; + +import java.util.Map; + +/** + * Created by frank on 13/12/2016. + */ +public class OLuceneMultiFieldQueryParser extends MultiFieldQueryParser { + private final Map types; + + public OLuceneMultiFieldQueryParser(Map types, String[] fields, + Analyzer analyzer, + Map boosts) { + super(fields, analyzer, boosts); + this.types = types; + } + + public OLuceneMultiFieldQueryParser(Map types, String[] fields, Analyzer analyzer) { + super(fields, analyzer); + this.types = types; + } + + @Override + protected Query getFieldQuery(String field, String queryText, int slop) throws ParseException { + Query query = getQuery(field, queryText, queryText, true, true); + if (query != null) + return query; + + return super.getFieldQuery(field, queryText, slop); + } + + @Override + protected Query getFieldQuery(String field, String queryText, boolean quoted) throws ParseException { + + Query query = getQuery(field, queryText, queryText, true, true); + if (query != null) + return query; + + return super.getFieldQuery(field, queryText, quoted); + } + + @Override + protected Query getRangeQuery(String field, String part1, String part2, boolean startInclusive, boolean endInclusive) + throws ParseException { + + Query query = getQuery(field, part1, part2, startInclusive, endInclusive); + if (query != null) + return query; + + return super.getRangeQuery(field, part1, part2, startInclusive, endInclusive); + } + + private Query getQuery(String field, String part1, String part2, boolean startInclusive, boolean endInclusive) + throws ParseException { + + if (types.containsKey(field)) { + switch (types.get(field)) { + case LONG: + return NumericRangeQuery.newLongRange(field, Long.parseLong(part1), Long.parseLong(part2), + startInclusive, endInclusive); + case INTEGER: + return NumericRangeQuery.newIntRange(field, Integer.parseInt(part1), Integer.parseInt(part2), + startInclusive, endInclusive); + case DOUBLE: + return NumericRangeQuery.newDoubleRange(field, Double.parseDouble(part1), Double.parseDouble(part2), + startInclusive, endInclusive); + case DATE: + case DATETIME: + try { + return NumericRangeQuery.newLongRange(field, DateTools.stringToTime(part1), DateTools.stringToTime(part2), + startInclusive, endInclusive); + } catch (java.text.ParseException e) { + throw new ParseException(e.getMessage()); + } + } + } + return null; + } + +} diff --git a/lucene/src/test/java/com/orientechnologies/lucene/test/LuceneMultiFieldTest.java b/lucene/src/test/java/com/orientechnologies/lucene/test/LuceneMultiFieldTest.java index ddd0c03e010..8a83f140384 100644 --- a/lucene/src/test/java/com/orientechnologies/lucene/test/LuceneMultiFieldTest.java +++ b/lucene/src/test/java/com/orientechnologies/lucene/test/LuceneMultiFieldTest.java @@ -19,15 +19,11 @@ package com.orientechnologies.lucene.test; import com.orientechnologies.orient.core.command.script.OCommandScript; -import com.orientechnologies.orient.core.metadata.schema.OClass; -import com.orientechnologies.orient.core.metadata.schema.OSchema; -import com.orientechnologies.orient.core.metadata.schema.OType; import com.orientechnologies.orient.core.record.impl.ODocument; import com.orientechnologies.orient.core.sql.OCommandSQL; import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery; import org.apache.lucene.analysis.en.EnglishAnalyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; -import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -35,7 +31,7 @@ import java.io.InputStream; import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; /** * Created by enricorisa on 19/09/14. @@ -49,7 +45,6 @@ public LuceneMultiFieldTest() { @Before public void init() { - InputStream stream = ClassLoader.getSystemResourceAsStream("testLuceneIndex.sql"); db.command(new OCommandScript("sql", getScriptFromStream(stream))).execute(); @@ -66,7 +61,6 @@ public void init() { } - @Test public void testSelectSingleDocumentWithAndOperator() { @@ -121,7 +115,9 @@ public void testSelectOnAuthorWithMatchOnAuthor() { @Test public void testSelectOnIndexWithIgnoreNullValuesToFalse() { //#5579 - String script = "create class Item\n" + "create property Item.title string\n" + "create property Item.summary string\n" + String script = "create class Item\n" + + "create property Item.title string\n" + + "create property Item.summary string\n" + "create property Item.content string\n" + "create index Item.i_lucene on Item(title, summary, content) fulltext engine lucene METADATA {ignoreNullValues:false}\n" + "insert into Item set title = 'test', content = 'this is a test'\n"; diff --git a/lucene/src/test/java/com/orientechnologies/lucene/test/LuceneNumericRangeTest.java b/lucene/src/test/java/com/orientechnologies/lucene/test/LuceneNumericRangeTest.java deleted file mode 100644 index f8352f6b86c..00000000000 --- a/lucene/src/test/java/com/orientechnologies/lucene/test/LuceneNumericRangeTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.orientechnologies.lucene.test; - -import com.orientechnologies.orient.core.metadata.schema.OClass; -import com.orientechnologies.orient.core.metadata.schema.OSchemaProxy; -import com.orientechnologies.orient.core.metadata.schema.OType; -import com.orientechnologies.orient.core.record.impl.ODocument; -import com.orientechnologies.orient.core.sql.OCommandSQL; -import org.junit.Test; - -import java.util.Collection; -import java.util.Date; - -import static org.assertj.core.api.Assertions.*; - -/** - * Created by frank on 13/12/2016. - */ -public class LuceneNumericRangeTest extends BaseLuceneTest { - - @Test - public void shouldUseRangeQuery() throws Exception { - - OSchemaProxy schema = db.getMetadata().getSchema(); - - OClass cls = schema.createClass("Person"); - cls.createProperty("name", OType.STRING); - cls.createProperty("surname", OType.STRING); - cls.createProperty("date", OType.DATETIME); - cls.createProperty("age", OType.LONG); - - db.command(new OCommandSQL("create index Person.age on Person(age) FULLTEXT ENGINE LUCENE")).execute(); - - for (int i = 0; i < 10; i++) { - db.save(new ODocument("Reuse") - .field("name", "John") - .field("date", new Date()) - .field("surname", "Reese") - .field("age", i)); - } - - long size = db.getMetadata().getIndexManager().getIndex("Person.age").getSize(); - System.out.println("size = " + size); - Collection results = db.command(new OCommandSQL("SELECT FROM Person WHERE age LUCENE '[5 TO 6]'")) - .execute(); - -// assertThat(results).hasSize(2); - } -} diff --git a/lucene/src/test/java/com/orientechnologies/lucene/test/LuceneRangeTest.java b/lucene/src/test/java/com/orientechnologies/lucene/test/LuceneRangeTest.java new file mode 100644 index 00000000000..e8a06cf7d09 --- /dev/null +++ b/lucene/src/test/java/com/orientechnologies/lucene/test/LuceneRangeTest.java @@ -0,0 +1,125 @@ +package com.orientechnologies.lucene.test; + +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OSchemaProxy; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.sql.OCommandSQL; +import org.apache.lucene.document.DateTools; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +/** + * Created by frank on 13/12/2016. + */ +public class LuceneRangeTest extends BaseLuceneTest { + + @Before + public void setUp() throws Exception { + OSchemaProxy schema = db.getMetadata().getSchema(); + + OClass cls = schema.createClass("Person"); + cls.createProperty("name", OType.STRING); + cls.createProperty("surname", OType.STRING); + cls.createProperty("date", OType.DATETIME); + cls.createProperty("age", OType.INTEGER); + + List names = Arrays.asList("John", "Robert", "Jane", "andrew", "Scott", "luke", "Enriquez", "Luis", "Gabriel", "Sara"); + for (int i = 0; i < 10; i++) { + db.save(new ODocument("Person") + .field("name", names.get(i)) + .field("surname", "Reese") + //from today back one day a time + .field("date", System.currentTimeMillis() - (i * 3600 * 24 * 1000)) + .field("age", i)); + } + + } + + @Test + public void shouldUseRangeQueryOnSingleIntegerField() throws Exception { + + db.command(new OCommandSQL("create index Person.age on Person(age) FULLTEXT ENGINE LUCENE")).execute(); + + assertThat(db.getMetadata().getIndexManager().getIndex("Person.age").getSize()).isEqualTo(10); + + //range + Collection results = db.command( + new OCommandSQL("SELECT FROM Person WHERE age LUCENE 'age:[5 TO 6]'")) + .execute(); + + assertThat(results).hasSize(2); + + //single value + results = db.command( + new OCommandSQL("SELECT FROM Person WHERE age LUCENE 'age:5'")) + .execute(); + + assertThat(results).hasSize(1); + } + + @Test + public void shouldUseRangeQueryOnSingleDateField() throws Exception { + + db.commit(); + db.command(new OCommandSQL("create index Person.date on Person(date) FULLTEXT ENGINE LUCENE")).execute(); + db.commit(); + + assertThat(db.getMetadata().getIndexManager().getIndex("Person.date").getSize()).isEqualTo(10); + + String today = DateTools.timeToString(System.currentTimeMillis(), DateTools.Resolution.MINUTE); + String fiveDaysAgo = DateTools.timeToString(System.currentTimeMillis() - (5 * 3600 * 24 * 1000), DateTools.Resolution.MINUTE); + + //range + Collection results = db.command( + new OCommandSQL("SELECT FROM Person WHERE date LUCENE 'date:[" + fiveDaysAgo + " TO " + today + "]'")) + .execute(); + + assertThat(results).hasSize(5); + + } + + @Test + public void shouldUseRangeQueryMultipleField() throws Exception { + + db.command(new OCommandSQL("create index Person.composite on Person(name,surname,date,age) FULLTEXT ENGINE LUCENE")).execute(); + + assertThat(db.getMetadata().getIndexManager().getIndex("Person.composite").getSize()).isEqualTo(10); + + db.commit(); + + String today = DateTools.timeToString(System.currentTimeMillis(), DateTools.Resolution.MINUTE); + String fiveDaysAgo = DateTools.timeToString(System.currentTimeMillis() - (5 * 3600 * 24 * 1000), DateTools.Resolution.MINUTE); + + //anme and age range + Collection results = db.command( + new OCommandSQL("SELECT * FROM Person WHERE [name,surname,date,age] LUCENE 'name:luke age:[5 TO 6]'")) + .execute(); + + assertThat(results).hasSize(2); + + //date range + results = db.command( + new OCommandSQL("SELECT FROM Person WHERE [name,surname,date,age] LUCENE 'date:[" + fiveDaysAgo + " TO " + today + "]'")) + .execute(); + + assertThat(results).hasSize(5); + + //age and date range with MUST + results = db.command( + new OCommandSQL( + "SELECT FROM Person WHERE [name,surname,date,age] LUCENE '+age:[4 TO 7] +date:[" + fiveDaysAgo + " TO " + today + + "]'")) + .execute(); + + assertThat(results).hasSize(2); + + } + +} diff --git a/lucene/src/test/java/com/orientechnologies/lucene/test/LuceneReuseTest.java b/lucene/src/test/java/com/orientechnologies/lucene/test/LuceneReuseTest.java index a81bb085b88..a1ee440509e 100644 --- a/lucene/src/test/java/com/orientechnologies/lucene/test/LuceneReuseTest.java +++ b/lucene/src/test/java/com/orientechnologies/lucene/test/LuceneReuseTest.java @@ -1,6 +1,5 @@ package com.orientechnologies.lucene.test; -import com.orientechnologies.lucene.test.BaseLuceneTest; import com.orientechnologies.orient.core.metadata.schema.OClass; import com.orientechnologies.orient.core.metadata.schema.OSchemaProxy; import com.orientechnologies.orient.core.metadata.schema.OType; @@ -62,11 +61,19 @@ public void shouldUseTheRightLuceneIndex() { db.command(new OCommandSQL("create index Reuse.name_surname on Reuse (name,surname) FULLTEXT ENGINE LUCENE")).execute(); for (int i = 0; i < 10; i++) { - db.save(new ODocument("Reuse").field("name", "John").field("date", new Date()).field("surname", "Reese").field("age", i)); + db.save(new ODocument("Reuse") + .field("name", "John") + .field("date", new Date()) + .field("surname", "Reese") + .field("age", i)); } //additional record - db.save(new ODocument("Reuse").field("name", "John").field("date", new Date()).field("surname", "Franklin").field("age", 11)); + db.save(new ODocument("Reuse") + .field("name", "John") + .field("date", new Date()) + .field("surname", "Franklin") + .field("age", 11)); Collection results = db .command(new OCommandSQL("SELECT FROM Reuse WHERE name='John' and [name,surname] LUCENE 'Reese'")).execute(); diff --git a/lucene/src/test/resources/testLuceneIndex.sql b/lucene/src/test/resources/testLuceneIndex.sql index ad3ec1e9c3d..52f1fd98f08 100644 --- a/lucene/src/test/resources/testLuceneIndex.sql +++ b/lucene/src/test/resources/testLuceneIndex.sql @@ -8,15 +8,14 @@ create property Song.description STRING create class Author extends V create property Author.name STRING +create property Author.score INTEGER begin - - -create vertex Author set name="Bob Dylan" -create vertex Author set name="Grateful Dead" -create vertex Author set name="Lennon McCartney" -create vertex Author set name="Chuck Berry" -create vertex Author set name="Jack Mountain" +create vertex Author set name="Bob Dylan", score =10 +create vertex Author set name="Grateful Dead", score =5 +create vertex Author set name="Lennon McCartney", score =7 +create vertex Author set name="Chuck Berry", score =10 +create vertex Author set name="Jack Mountain", score =4 create vertex Song set title="BELIEVE IT OR NOT", author="Hunter"