From 37b2aa4eca61f9aed32f2a5af883e88c215cd400 Mon Sep 17 00:00:00 2001 From: David Clement Date: Mon, 15 Jan 2018 11:01:39 +0100 Subject: [PATCH] Add better OR support for mixed indexes. Issue : #163 Signed-off-by: David Clement --- docs/basics.adoc | 3 + docs/elasticsearch.adoc | 3 +- docs/lucene.adoc | 1 + .../org/janusgraph/core/JanusGraphQuery.java | 2 + .../diskstorage/indexing/IndexFeatures.java | 25 +- .../diskstorage/indexing/IndexQuery.java | 7 +- .../graphdb/database/IndexSerializer.java | 173 +++--- .../graphdb/predicate/AndJanusPredicate.java | 54 ++ .../predicate/ConnectiveJanusGraphP.java | 65 +++ .../predicate/ConnectiveJanusPredicate.java | 77 +++ .../graphdb/predicate/OrJanusPredicate.java | 50 ++ .../graphdb/query/JanusGraphPredicate.java | 57 +- .../janusgraph/graphdb/query/QueryUtil.java | 103 +++- .../query/graph/GraphCentricQuery.java | 6 +- .../query/graph/GraphCentricQueryBuilder.java | 124 +++-- .../tinkerpop/optimize/HasStepFolder.java | 168 ++++-- ...JanusGraphLocalQueryOptimizerStrategy.java | 38 +- .../optimize/JanusGraphPropertiesStep.java | 51 +- .../tinkerpop/optimize/JanusGraphStep.java | 190 ++++++- .../optimize/JanusGraphStepStrategy.java | 9 +- .../optimize/JanusGraphVertexStep.java | 50 +- .../graphdb/tinkerpop/optimize/QueryInfo.java | 61 +++ .../util/MultiDistinctOrderedIterator.java | 85 +++ .../es/compat/AbstractESCompat.java | 3 +- .../diskstorage/lucene/LuceneIndex.java | 6 +- .../src/test/resources/collections.txt | 2 +- .../graphdb/JanusGraphBaseTest.java | 57 +- .../graphdb/JanusGraphIndexTest.java | 492 ++++++++++++------ .../janusgraph/testutil/JanusGraphAssert.java | 11 + .../JanusGraphStepStrategyTest.java | 56 +- .../predicate/AndJanusPredicateTest.java | 52 ++ .../predicate/ConnectiveJanusGraphPTest.java | 37 ++ .../ConnectiveJanusPredicateTest.java | 113 ++++ .../predicate/OrJanusPredicateTest.java | 52 ++ 34 files changed, 1830 insertions(+), 453 deletions(-) create mode 100644 janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/AndJanusPredicate.java create mode 100644 janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/ConnectiveJanusGraphP.java create mode 100644 janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/ConnectiveJanusPredicate.java create mode 100644 janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/OrJanusPredicate.java create mode 100644 janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/QueryInfo.java create mode 100644 janusgraph-core/src/main/java/org/janusgraph/graphdb/util/MultiDistinctOrderedIterator.java create mode 100644 janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/AndJanusPredicateTest.java create mode 100644 janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/ConnectiveJanusGraphPTest.java create mode 100644 janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/ConnectiveJanusPredicateTest.java create mode 100644 janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/OrJanusPredicateTest.java diff --git a/docs/basics.adoc b/docs/basics.adoc index 2af9bffc80d..a7ec55ae3b0 100644 --- a/docs/basics.adoc +++ b/docs/basics.adoc @@ -997,6 +997,9 @@ While the index definition example looks similar to the composite index above, i g.V().has('name', textContains('hercules')).has('age', inside(20, 50)) g.V().has('name', textContains('hercules')) g.V().has('age', lt(50)) +g.V().has('age', outside(20, 50)) +g.V().has('age', lt(50).or(gte(60))) +g.V().or(__.has('name', textContains('hercules')), __.has('age', inside(20, 50))) Mixed indexes support full-text search, range search, geo search and others. Refer to <> for a list of predicates supported by a particular indexing backend. diff --git a/docs/elasticsearch.adoc b/docs/elasticsearch.adoc index df083b938fc..deb754239f3 100644 --- a/docs/elasticsearch.adoc +++ b/docs/elasticsearch.adoc @@ -12,7 +12,8 @@ JanusGraph supports https://www.elastic.co/[Elasticsearch] as an index backend. * *Flexible Configuration*: Supports remote operation and open-ended settings customization. * *Collections*: Supports indexing SET and LIST cardinality properties. * *Temporal*: Nanosecond granularity temporal indexing. -* *Custom Analyzer*: Choose to use a custom analyzer +* *Custom Analyzer*: Choose to use a custom analyzer. +* *Not Query-normal-form*: Supports queries other than Query-normal-form (QNF). QNF for JanusGraph is a variant of CNF (conjunctive normal form) with negation inlined where possible. Please see <> for details on what versions of Elasticsearch will work with JanusGraph. diff --git a/docs/lucene.adoc b/docs/lucene.adoc index a6d34fa6a93..baef4063897 100644 --- a/docs/lucene.adoc +++ b/docs/lucene.adoc @@ -25,6 +25,7 @@ In the above configuration, the index backend is named `search`. Replace `search * *Numeric Range*: Supports all numeric comparisons in `Compare`. * *Temporal*: Nanosecond granularity temporal indexing. * *Custom Analyzer*: Choose to use a custom analyzer +* *Not Query-normal-form*: Supports queries other than Query-normal-form (QNF). QNF for JanusGraph is a variant of CNF (conjunctive normal form) with negation inlined where possible. === Configuration Options diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/JanusGraphQuery.java b/janusgraph-core/src/main/java/org/janusgraph/core/JanusGraphQuery.java index 7546d6072d4..ba315edb6e1 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/core/JanusGraphQuery.java +++ b/janusgraph-core/src/main/java/org/janusgraph/core/JanusGraphQuery.java @@ -55,6 +55,8 @@ public interface JanusGraphQuery> { Q hasNot(String key, Object value); + Q or(Q subQuery); + > Q interval(String key, T startValue, T endValue); /** diff --git a/janusgraph-core/src/main/java/org/janusgraph/diskstorage/indexing/IndexFeatures.java b/janusgraph-core/src/main/java/org/janusgraph/diskstorage/indexing/IndexFeatures.java index 6da24dbbef6..0ee1dcd490d 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/diskstorage/indexing/IndexFeatures.java +++ b/janusgraph-core/src/main/java/org/janusgraph/diskstorage/indexing/IndexFeatures.java @@ -37,11 +37,12 @@ public class IndexFeatures { private final boolean supportsNanoseconds; private final boolean supportsCustomAnalyzer; private final boolean supportsGeoContains; + private final boolean supportsNotQueryNormalForm; private final ImmutableSet supportedCardinalities; public IndexFeatures(boolean supportsDocumentTTL, Mapping defaultMap, ImmutableSet supportedMap, String wildcardField, ImmutableSet supportedCardinalities, boolean supportsNanoseconds, - boolean supportCustomAnalyzer, boolean supportsGeoContains) { + boolean supportCustomAnalyzer, boolean supportsGeoContains, boolean supportsNotQueryNormalForm) { Preconditions.checkArgument(defaultMap!=null && defaultMap!=Mapping.DEFAULT); Preconditions.checkArgument(supportedMap!=null && !supportedMap.isEmpty() @@ -54,6 +55,7 @@ public IndexFeatures(boolean supportsDocumentTTL, Mapping defaultMap, ImmutableS this.supportsNanoseconds = supportsNanoseconds; this.supportsCustomAnalyzer = supportCustomAnalyzer; this.supportsGeoContains = supportsGeoContains; + this.supportsNotQueryNormalForm = supportsNotQueryNormalForm; } public boolean supportsDocumentTTL() { @@ -79,7 +81,7 @@ public boolean supportsCardinality(Cardinality cardinality) { public boolean supportsNanoseconds() { return supportsNanoseconds; } - + public boolean supportsCustomAnalyzer() { return supportsCustomAnalyzer; } @@ -88,6 +90,10 @@ public boolean supportsGeoContains() { return supportsGeoContains; } + public boolean supportNotQueryNormalForm() { + return supportsNotQueryNormalForm; + } + public static class Builder { private boolean supportsDocumentTTL = false; @@ -97,7 +103,8 @@ public static class Builder { private String wildcardField = "*"; private boolean supportsNanoseconds; private boolean supportsCustomAnalyzer; - private boolean supportsGeoContains = false; + private boolean supportsGeoContains; + private boolean supportNotQueryNormalForm; public Builder supportsDocumentTTL() { supportsDocumentTTL=true; @@ -128,7 +135,7 @@ public Builder supportsNanoseconds() { supportsNanoseconds = true; return this; } - + public Builder supportsCustomAnalyzer() { supportsCustomAnalyzer = true; return this; @@ -139,13 +146,15 @@ public Builder supportsGeoContains() { return this; } + public Builder supportNotQueryNormalForm() { + this.supportNotQueryNormalForm = true; + return this; + } + public IndexFeatures build() { return new IndexFeatures(supportsDocumentTTL, defaultStringMapping, ImmutableSet.copyOf(supportedMappings), wildcardField, ImmutableSet.copyOf(supportedCardinalities), supportsNanoseconds, supportsCustomAnalyzer, - supportsGeoContains); + supportsGeoContains, supportNotQueryNormalForm); } - - } - } diff --git a/janusgraph-core/src/main/java/org/janusgraph/diskstorage/indexing/IndexQuery.java b/janusgraph-core/src/main/java/org/janusgraph/diskstorage/indexing/IndexQuery.java index 074fd5095a1..debe617847a 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/diskstorage/indexing/IndexQuery.java +++ b/janusgraph-core/src/main/java/org/janusgraph/diskstorage/indexing/IndexQuery.java @@ -51,7 +51,6 @@ public IndexQuery(String store, Condition condition, ImmutableList o Preconditions.checkNotNull(store); Preconditions.checkNotNull(condition); Preconditions.checkArgument(orders != null); - Preconditions.checkArgument(QueryUtil.isQueryNormalForm(condition)); this.condition = condition; this.orders = orders; this.store = store; @@ -103,14 +102,14 @@ public boolean equals(Object other) { if (this == other) return true; else if (other == null) return false; else if (!getClass().isInstance(other)) return false; - IndexQuery oth = (IndexQuery) other; + final IndexQuery oth = (IndexQuery) other; return store.equals(oth.store) && orders.equals(oth.orders) && condition.equals(oth.condition) && getLimit() == oth.getLimit(); } @Override public String toString() { - StringBuilder b = new StringBuilder(); + final StringBuilder b = new StringBuilder(); b.append("[").append(condition.toString()).append("]"); if (!orders.isEmpty()) b.append(orders); if (hasLimit()) b.append("(").append(getLimit()).append(")"); @@ -155,7 +154,7 @@ public boolean equals(Object oth) { if (this == oth) return true; else if (oth == null) return false; else if (!getClass().isInstance(oth)) return false; - OrderEntry o = (OrderEntry) oth; + final OrderEntry o = (OrderEntry) oth; return key.equals(o.key) && order == o.order; } diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/IndexSerializer.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/IndexSerializer.java index 2f925c80219..cc513daee56 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/IndexSerializer.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/IndexSerializer.java @@ -53,6 +53,7 @@ import javax.annotation.Nullable; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import java.util.stream.Stream; import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.INDEX_NAME_MAPPING; @@ -95,32 +96,31 @@ public boolean containsIndex(final String indexName) { public String getDefaultFieldName(final PropertyKey key, final Parameter[] parameters, final String indexName) { Preconditions.checkArgument(!ParameterType.MAPPED_NAME.hasParameter(parameters),"A field name mapping has been specified for key: %s",key); Preconditions.checkArgument(containsIndex(indexName),"Unknown backing index: %s",indexName); - String fieldname = configuration.get(INDEX_NAME_MAPPING,indexName)?key.name():keyID2Name(key); + final String fieldname = configuration.get(INDEX_NAME_MAPPING,indexName)?key.name():keyID2Name(key); return mixedIndexes.get(indexName).mapKey2Field(fieldname, new StandardKeyInformation(key,parameters)); } public static void register(final MixedIndexType index, final PropertyKey key, final BackendTransaction tx) throws BackendException { tx.getIndexTransaction(index.getBackingIndexName()).register(index.getStoreName(), key2Field(index,key), getKeyInformation(index.getField(key))); - } -// public boolean supports(final String indexName, final Class dataType, final Parameter[] parameters) { -// IndexInformation indexinfo = indexes.get(indexName); -// Preconditions.checkArgument(indexinfo != null, "Index is unknown or not configured: %s", indexName); -// return indexinfo.supports(new StandardKeyInformation(dataType,parameters)); -// } - public boolean supports(final MixedIndexType index, final ParameterIndexField field) { - IndexInformation indexinfo = mixedIndexes.get(index.getBackingIndexName()); - Preconditions.checkArgument(indexinfo != null, "Index is unknown or not configured: %s", index.getBackingIndexName()); - return indexinfo.supports(getKeyInformation(field)); + return getMixedIndex(index).supports(getKeyInformation(field)); } public boolean supports(final MixedIndexType index, final ParameterIndexField field, final JanusGraphPredicate predicate) { - IndexInformation indexinfo = mixedIndexes.get(index.getBackingIndexName()); - Preconditions.checkArgument(indexinfo != null, "Index is unknown or not configured: %s", index.getBackingIndexName()); - return indexinfo.supports(getKeyInformation(field),predicate); + return getMixedIndex(index).supports(getKeyInformation(field),predicate); + } + + public IndexFeatures features(final MixedIndexType index) { + return getMixedIndex(index).getFeatures(); + } + + private IndexInformation getMixedIndex(final MixedIndexType index) { + final IndexInformation indexinfo = mixedIndexes.get(index.getBackingIndexName()); + Preconditions.checkArgument(indexinfo != null, "Index is unknown or not configured: " + index.getBackingIndexName()); + return indexinfo; } private static StandardKeyInformation getKeyInformation(final ParameterIndexField field) { @@ -157,8 +157,8 @@ public KeyInformation.StoreRetriever get(final String store) { Preconditions.checkState(transaction!=null,"Retriever has not been initialized"); final MixedIndexType extIndex = getMixedIndex(store, transaction); assert extIndex.getBackingIndexName().equals(index); - ImmutableMap.Builder b = ImmutableMap.builder(); - for (ParameterIndexField field : extIndex.getFieldKeys()) b.put(key2Field(field),getKeyInformation(field)); + final ImmutableMap.Builder b = ImmutableMap.builder(); + for (final ParameterIndexField field : extIndex.getFieldKeys()) b.put(key2Field(field),getKeyInformation(field)); final ImmutableMap infoMap = b.build(); final KeyInformation.StoreRetriever storeRetriever = infoMap::get; indexes.put(store,storeRetriever); @@ -245,7 +245,7 @@ public int hashCode() { public boolean equals(Object other) { if (this==other) return true; else if (other==null || !(other instanceof IndexUpdate)) return false; - IndexUpdate oth = (IndexUpdate)other; + final IndexUpdate oth = (IndexUpdate)other; return index.equals(oth.index) && mutationType==oth.mutationType && key.equals(oth.key) && entry.equals(oth.entry); } } @@ -264,18 +264,18 @@ private static boolean indexAppliesTo(IndexType index, JanusGraphElement element public Collection getIndexUpdates(InternalRelation relation) { assert relation.isNew() || relation.isRemoved(); - Set updates = Sets.newHashSet(); - IndexUpdate.Type updateType = getUpdateType(relation); - int ttl = updateType==IndexUpdate.Type.ADD?StandardJanusGraph.getTTL(relation):0; - for (RelationType type : relation.getPropertyKeysDirect()) { + final Set updates = Sets.newHashSet(); + final IndexUpdate.Type updateType = getUpdateType(relation); + final int ttl = updateType==IndexUpdate.Type.ADD?StandardJanusGraph.getTTL(relation):0; + for (final RelationType type : relation.getPropertyKeysDirect()) { if (!(type instanceof PropertyKey)) continue; - PropertyKey key = (PropertyKey)type; - for (IndexType index : ((InternalRelationType)key).getKeyIndexes()) { + final PropertyKey key = (PropertyKey)type; + for (final IndexType index : ((InternalRelationType)key).getKeyIndexes()) { if (!indexAppliesTo(index,relation)) continue; IndexUpdate update; if (index instanceof CompositeIndexType) { - CompositeIndexType iIndex= (CompositeIndexType) index; - RecordEntry[] record = indexMatch(relation, iIndex); + final CompositeIndexType iIndex= (CompositeIndexType) index; + final RecordEntry[] record = indexMatch(relation, iIndex); if (record==null) continue; update = new IndexUpdate<>(iIndex, updateType, getIndexKey(iIndex, record), getIndexEntry(iIndex, record, relation), relation); } else { @@ -291,15 +291,15 @@ public Collection getIndexUpdates(InternalRelation relation) { } private static PropertyKey[] getKeysOfRecords(RecordEntry[] record) { - PropertyKey[] keys = new PropertyKey[record.length]; + final PropertyKey[] keys = new PropertyKey[record.length]; for (int i=0;i 0 && (kttl < ttl || ttl <= 0)) ttl = kttl; } return ttl; @@ -307,28 +307,28 @@ private static int getIndexTTL(InternalVertex vertex, PropertyKey... keys) { public Collection getIndexUpdates(InternalVertex vertex, Collection updatedProperties) { if (updatedProperties.isEmpty()) return Collections.emptyList(); - Set updates = Sets.newHashSet(); + final Set updates = Sets.newHashSet(); - for (InternalRelation rel : updatedProperties) { + for (final InternalRelation rel : updatedProperties) { assert rel.isProperty(); - JanusGraphVertexProperty p = (JanusGraphVertexProperty)rel; + final JanusGraphVertexProperty p = (JanusGraphVertexProperty)rel; assert rel.isNew() || rel.isRemoved(); assert rel.getVertex(0).equals(vertex); - IndexUpdate.Type updateType = getUpdateType(rel); - for (IndexType index : ((InternalRelationType)p.propertyKey()).getKeyIndexes()) { + final IndexUpdate.Type updateType = getUpdateType(rel); + for (final IndexType index : ((InternalRelationType)p.propertyKey()).getKeyIndexes()) { if (!indexAppliesTo(index,vertex)) continue; if (index.isCompositeIndex()) { //Gather composite indexes - CompositeIndexType cIndex = (CompositeIndexType)index; - IndexRecords updateRecords = indexMatches(vertex,cIndex,updateType==IndexUpdate.Type.DELETE,p.propertyKey(),new RecordEntry(p)); - for (RecordEntry[] record : updateRecords) { + final CompositeIndexType cIndex = (CompositeIndexType)index; + final IndexRecords updateRecords = indexMatches(vertex,cIndex,updateType==IndexUpdate.Type.DELETE,p.propertyKey(),new RecordEntry(p)); + for (final RecordEntry[] record : updateRecords) { final IndexUpdate update = new IndexUpdate<>(cIndex, updateType, getIndexKey(cIndex, record), getIndexEntry(cIndex, record, vertex), vertex); - int ttl = getIndexTTL(vertex,getKeysOfRecords(record)); + final int ttl = getIndexTTL(vertex,getKeysOfRecords(record)); if (ttl>0 && updateType== IndexUpdate.Type.ADD) update.setTTL(ttl); updates.add(update); } } else { //Update mixed indexes if (((MixedIndexType)index).getField(p.propertyKey()).getStatus()== SchemaStatus.DISABLED) continue; final IndexUpdate update = getMixedIndexUpdate(vertex, p.propertyKey(), p.value(), (MixedIndexType) index, updateType); - int ttl = getIndexTTL(vertex,p.propertyKey()); + final int ttl = getIndexTTL(vertex,p.propertyKey()); if (ttl>0 && updateType== IndexUpdate.Type.ADD) update.setTTL(ttl); updates.add(update); } @@ -344,9 +344,9 @@ private IndexUpdate getMixedIndexUpdate(JanusGraphElement ele public void reindexElement(JanusGraphElement element, MixedIndexType index, Map>> documentsPerStore) { if (!indexAppliesTo(index,element)) return; - List entries = Lists.newArrayList(); - for (ParameterIndexField field: index.getFieldKeys()) { - PropertyKey key = field.getFieldKey(); + final List entries = Lists.newArrayList(); + for (final ParameterIndexField field: index.getFieldKeys()) { + final PropertyKey key = field.getFieldKey(); if (field.getStatus()==SchemaStatus.DISABLED) continue; if (element.properties(key.name()).hasNext()) { element.values(key.name()).forEachRemaining(value->entries.add(new IndexEntry(key2Field(field), value))); @@ -367,28 +367,28 @@ public void removeElement(Object elementId, MixedIndexType index, Map> reindexElement(JanusGraphElement element, CompositeIndexType index) { - Set> indexEntries = Sets.newHashSet(); + final Set> indexEntries = Sets.newHashSet(); if (!indexAppliesTo(index,element)) return indexEntries; Iterable records; if (element instanceof JanusGraphVertex) records = indexMatches((JanusGraphVertex)element,index); else { assert element instanceof JanusGraphRelation; records = Collections.EMPTY_LIST; - RecordEntry[] record = indexMatch((JanusGraphRelation)element,index); + final RecordEntry[] record = indexMatch((JanusGraphRelation)element,index); if (record!=null) records = ImmutableList.of(record); } - for (RecordEntry[] record : records) { + for (final RecordEntry[] record : records) { indexEntries.add(new IndexUpdate<>(index, IndexUpdate.Type.ADD, getIndexKey(index, record), getIndexEntry(index, record, element), element)); } return indexEntries; } public static RecordEntry[] indexMatch(JanusGraphRelation relation, CompositeIndexType index) { - IndexField[] fields = index.getFieldKeys(); - RecordEntry[] match = new RecordEntry[fields.length]; + final IndexField[] fields = index.getFieldKeys(); + final RecordEntry[] match = new RecordEntry[fields.length]; for (int i = 0; i { + @Override public boolean add(RecordEntry[] record) { return super.add(Arrays.copyOf(record,record.length)); } @@ -412,7 +413,7 @@ public Object[] apply(final RecordEntry[] record) { } private static Object[] getValues(RecordEntry[] record) { - Object[] values = new Object[record.length]; + final Object[] values = new Object[record.length]; for (int i = 0; i < values.length; i++) { values[i]=record[i].value; } @@ -444,8 +445,8 @@ public static IndexRecords indexMatches(JanusGraphVertex vertex, CompositeIndexT public static IndexRecords indexMatches(JanusGraphVertex vertex, CompositeIndexType index, PropertyKey replaceKey, Object replaceValue) { - IndexRecords matches = new IndexRecords(); - IndexField[] fields = index.getFieldKeys(); + final IndexRecords matches = new IndexRecords(); + final IndexField[] fields = index.getFieldKeys(); if (indexAppliesTo(index,vertex)) { indexMatches(vertex,new RecordEntry[fields.length],matches,fields,0,false, replaceKey,new RecordEntry(0,replaceValue,replaceKey)); @@ -455,8 +456,8 @@ public static IndexRecords indexMatches(JanusGraphVertex vertex, CompositeIndexT private static IndexRecords indexMatches(JanusGraphVertex vertex, CompositeIndexType index, boolean onlyLoaded, PropertyKey replaceKey, RecordEntry replaceValue) { - IndexRecords matches = new IndexRecords(); - IndexField[] fields = index.getFieldKeys(); + final IndexRecords matches = new IndexRecords(); + final IndexField[] fields = index.getFieldKeys(); indexMatches(vertex,new RecordEntry[fields.length],matches,fields,0,onlyLoaded,replaceKey,replaceValue); return matches; } @@ -469,7 +470,7 @@ private static void indexMatches(JanusGraphVertex vertex, RecordEntry[] current, return; } - PropertyKey key = fields[pos].getFieldKey(); + final PropertyKey key = fields[pos].getFieldKey(); List values; if (key.equals(replaceKey)) { @@ -480,20 +481,20 @@ private static void indexMatches(JanusGraphVertex vertex, RecordEntry[] current, if (onlyLoaded || (!vertex.isNew() && IDManager.VertexIDType.PartitionedVertex.is(vertex.longId()))) { //going through transaction so we can query deleted vertices - VertexCentricQueryBuilder qb = ((InternalVertex)vertex).tx().query(vertex); + final VertexCentricQueryBuilder qb = ((InternalVertex)vertex).tx().query(vertex); qb.noPartitionRestriction().type(key); if (onlyLoaded) qb.queryOnlyLoaded(); props = qb.properties(); } else { props = vertex.query().keys(key.name()).properties(); } - for (JanusGraphVertexProperty p : props) { + for (final JanusGraphVertexProperty p : props) { assert !onlyLoaded || p.isLoaded() || p.isRemoved(); assert key.dataType().equals(p.value().getClass()) : key + " -> " + p; values.add(new RecordEntry(p)); } } - for (RecordEntry value : values) { + for (final RecordEntry value : values) { current[pos]=value; indexMatches(vertex,current,matches,fields,pos+1,onlyLoaded,replaceKey,replaceValue); } @@ -510,8 +511,8 @@ public Stream query(final JointIndexQuery.Subquery query, final BackendT final MultiKeySliceQuery sq = query.getCompositeQuery(); final List rs = sq.execute(tx); final List results = new ArrayList<>(rs.get(0).size()); - for (EntryList r : rs) { - for (java.util.Iterator iterator = r.reuseIterator(); iterator.hasNext(); ) { + for (final EntryList r : rs) { + for (final java.util.Iterator iterator = r.reuseIterator(); iterator.hasNext(); ) { final Entry entry = iterator.next(); final ReadBuffer entryValue = entry.asReadBuffer(); entryValue.movePositionTo(entry.getValuePosition()); @@ -532,27 +533,27 @@ public Stream query(final JointIndexQuery.Subquery query, final BackendT public MultiKeySliceQuery getQuery(final CompositeIndexType index, List values) { final List ksqs = new ArrayList<>(values.size()); - for (Object[] value : values) { + for (final Object[] value : values) { ksqs.add(new KeySliceQuery(getIndexKey(index,value), BufferUtil.zeroBuffer(1), BufferUtil.oneBuffer(1))); } return new MultiKeySliceQuery(ksqs); } public IndexQuery getQuery(final MixedIndexType index, final Condition condition, final OrderList orders) { - Condition newCondition = ConditionUtil.literalTransformation(condition, + final Condition newCondition = ConditionUtil.literalTransformation(condition, new Function, Condition>() { @Nullable @Override public Condition apply(final Condition condition) { Preconditions.checkArgument(condition instanceof PredicateCondition); - PredicateCondition pc = (PredicateCondition) condition; - PropertyKey key = (PropertyKey) pc.getKey(); + final PredicateCondition pc = (PredicateCondition) condition; + final PropertyKey key = (PropertyKey) pc.getKey(); return new PredicateCondition<>(key2Field(index, key), pc.getPredicate(), pc.getValue()); } }); ImmutableList newOrders = IndexQuery.NO_ORDER; if (!orders.isEmpty() && GraphCentricQueryBuilder.indexCoversOrder(index,orders)) { - ImmutableList.Builder lb = ImmutableList.builder(); + final ImmutableList.Builder lb = ImmutableList.builder(); for (int i = 0; i < orders.size(); i++) { lb.add(new IndexQuery.OrderEntry(key2Field(index,orders.getKey(i)), orders.getOrder(i), orders.getKey(i).dataType())); } @@ -568,10 +569,10 @@ public Condition apply(final Condition con private String createQueryString(IndexQueryBuilder query, final ElementCategory resultType, final StandardJanusGraphTx transaction, MixedIndexType index) { Preconditions.checkArgument(index.getElement()==resultType,"Index is not configured for the desired result type: %s",resultType); - String backingIndexName = index.getBackingIndexName(); - IndexProvider indexInformation = (IndexProvider) mixedIndexes.get(backingIndexName); + final String backingIndexName = index.getBackingIndexName(); + final IndexProvider indexInformation = (IndexProvider) mixedIndexes.get(backingIndexName); - StringBuilder qB = new StringBuilder(query.getQuery()); + final StringBuilder qB = new StringBuilder(query.getQuery()); final String prefix = query.getPrefix(); Preconditions.checkNotNull(prefix); //Convert query string by replacing @@ -581,10 +582,10 @@ private String createQueryString(IndexQueryBuilder query, final ElementCategory pos = qB.indexOf(prefix,pos); if (pos<0) break; - int startPos = pos; + final int startPos = pos; pos += prefix.length(); - StringBuilder keyBuilder = new StringBuilder(); - boolean quoteTerminated = qB.charAt(pos)=='"'; + final StringBuilder keyBuilder = new StringBuilder(); + final boolean quoteTerminated = qB.charAt(pos)=='"'; if (quoteTerminated) pos++; while (pos [{}]",replacements,query.getQuery(),queryStr); return queryStr; } - + public Stream executeQuery(IndexQueryBuilder query, final ElementCategory resultType, final BackendTransaction backendTx, final StandardJanusGraphTx transaction) { final MixedIndexType index = getMixedIndex(query.getIndex(), transaction); @@ -649,7 +650,7 @@ public Long executeTotals(IndexQueryBuilder query, final ElementCategory resultT ################################################### */ private static MixedIndexType getMixedIndex(String indexName, StandardJanusGraphTx transaction) { - IndexType index = ManagementSystem.getGraphIndexDirect(indexName, transaction); + final IndexType index = ManagementSystem.getGraphIndexDirect(indexName, transaction); Preconditions.checkArgument(index!=null,"Index with name [%s] is unknown or not configured properly",indexName); Preconditions.checkArgument(index.isMixedIndex()); return (MixedIndexType)index; @@ -698,13 +699,13 @@ private StaticBuffer getIndexKey(CompositeIndexType index, RecordEntry[] record) } private StaticBuffer getIndexKey(CompositeIndexType index, Object[] values) { - DataOutput out = serializer.getDataOutput(8*DEFAULT_OBJECT_BYTELEN + 8); + final DataOutput out = serializer.getDataOutput(8*DEFAULT_OBJECT_BYTELEN + 8); VariableLong.writePositive(out, index.getID()); - IndexField[] fields = index.getFieldKeys(); + final IndexField[] fields = index.getFieldKeys(); Preconditions.checkArgument(fields.length>0 && fields.length==values.length); for (int i = 0; i < fields.length; i++) { - IndexField f = fields[i]; - Object value = values[i]; + final IndexField f = fields[i]; + final Object value = values[i]; Preconditions.checkNotNull(value); if (AttributeUtil.hasGenericDataType(f.getFieldKey())) { out.writeClassAndObject(value); @@ -724,25 +725,25 @@ public long getIndexIdFromKey(StaticBuffer key) { } private Entry getIndexEntry(CompositeIndexType index, RecordEntry[] record, JanusGraphElement element) { - DataOutput out = serializer.getDataOutput(1+8+8*record.length+4*8); + final DataOutput out = serializer.getDataOutput(1+8+8*record.length+4*8); out.putByte(FIRST_INDEX_COLUMN_BYTE); if (index.getCardinality()!=Cardinality.SINGLE) { VariableLong.writePositive(out,element.longId()); if (index.getCardinality()!=Cardinality.SET) { - for (RecordEntry re : record) { + for (final RecordEntry re : record) { VariableLong.writePositive(out,re.relationId); } } } - int valuePosition=out.getPosition(); + final int valuePosition=out.getPosition(); if (element instanceof JanusGraphVertex) { VariableLong.writePositive(out,element.longId()); } else { assert element instanceof JanusGraphRelation; - RelationIdentifier rid = (RelationIdentifier)element.id(); - long[] longs = rid.getLongRepresentation(); + final RelationIdentifier rid = (RelationIdentifier)element.id(); + final long[] longs = rid.getLongRepresentation(); Preconditions.checkArgument(longs.length == 3 || longs.length == 4); - for (long aLong : longs) VariableLong.writePositive(out, aLong); + for (final long aLong : longs) VariableLong.writePositive(out, aLong); } return new StaticArrayEntry(out.getStaticBuffer(),valuePosition); } diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/AndJanusPredicate.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/AndJanusPredicate.java new file mode 100644 index 00000000000..ee4503c09af --- /dev/null +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/AndJanusPredicate.java @@ -0,0 +1,54 @@ +// Copyright 2017 JanusGraph Authors +// +// Licensed 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.janusgraph.graphdb.predicate; + +import java.util.List; +import org.janusgraph.graphdb.query.JanusGraphPredicate; + +/** + * @author David Clement (david.clement90@laposte.net) + */ +public class AndJanusPredicate extends ConnectiveJanusPredicate { + + private static final long serialVersionUID = -4249282297056862327L; + + public AndJanusPredicate(){ + super(); + } + + public AndJanusPredicate(final List predicates) { + super(predicates); + } + + @Override + ConnectiveJanusPredicate getNewNegateIntance() { + return new OrJanusPredicate(); + } + + @Override + boolean isOr() { + return false; + } + + @Override + public boolean isQNF() { + for (final JanusGraphPredicate internalCondition : this) { + if (!internalCondition.isQNF()){ + return false; + } + } + return true; + } +} diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/ConnectiveJanusGraphP.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/ConnectiveJanusGraphP.java new file mode 100644 index 00000000000..494d395f001 --- /dev/null +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/ConnectiveJanusGraphP.java @@ -0,0 +1,65 @@ +// Copyright 2017 JanusGraph Authors +// +// Licensed 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.janusgraph.graphdb.predicate; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.janusgraph.graphdb.query.JanusGraphPredicate; + +/** + * @author David Clement (david.clement90@laposte.net) + */ +public class ConnectiveJanusGraphP extends P{ + + private static final long serialVersionUID = 1737489543643777182L; + + public ConnectiveJanusGraphP(ConnectiveJanusPredicate biPredicate, List value) { + super(biPredicate, value); + } + @Override + public String toString() { + return toString((ConnectiveJanusPredicate) this.biPredicate, this.originalValue).toString(); + } + + private StringBuilder toString(final JanusGraphPredicate predicate, final Object value) { + final StringBuilder toReturn = new StringBuilder(); + if (!(predicate instanceof ConnectiveJanusPredicate)) { + toReturn.append(predicate); + if (value != null) { + toReturn.append("(").append(value).append(")"); + } + return toReturn; + } + final ConnectiveJanusPredicate connectivePredicate = (ConnectiveJanusPredicate) predicate; + final List values = null == value ? new ArrayList<>() : (List) value; + if (connectivePredicate.size() == 1) { + return toString(connectivePredicate.get(0), values.get(0)); + } + if (predicate instanceof AndJanusPredicate){ + toReturn.append("and("); + } else if (predicate instanceof OrJanusPredicate){ + toReturn.append("or("); + } else { + throw new IllegalArgumentException("JanusGraph does not support the given predicate: " + predicate); + } + final Iterator itValues = values.iterator(); + toReturn.append(connectivePredicate.stream().map(p -> toString(p, itValues.next())).collect(Collectors.joining(", "))).append(")"); + return toReturn; + } +} diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/ConnectiveJanusPredicate.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/ConnectiveJanusPredicate.java new file mode 100644 index 00000000000..0286ebc9d56 --- /dev/null +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/ConnectiveJanusPredicate.java @@ -0,0 +1,77 @@ +// Copyright 2017 JanusGraph Authors +// +// Licensed 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.janusgraph.graphdb.predicate; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import org.janusgraph.graphdb.query.JanusGraphPredicate; + +/** + * @author David Clement (david.clement90@laposte.net) + */ +public abstract class ConnectiveJanusPredicate extends ArrayList implements JanusGraphPredicate { + + private static final long serialVersionUID = 1558788908114391360L; + + public ConnectiveJanusPredicate(){ + super(); + } + + public ConnectiveJanusPredicate(final List predicates) { + super(predicates); + } + + abstract ConnectiveJanusPredicate getNewNegateIntance(); + + abstract boolean isOr(); + + @Override + @SuppressWarnings("unchecked") + public boolean isValidCondition(Object condition) { + if (!(condition instanceof List) || ((List)condition).size() != this.size()){ + return false; + } + final Iterator itConditions = ((List) condition).iterator(); + return !this.stream().anyMatch(internalCondition -> !internalCondition.isValidCondition(itConditions.next())); + } + + @Override + public boolean isValidValueType(Class clazz) { + return !this.stream().anyMatch(internalCondition -> !(internalCondition.isValidValueType(clazz))); + } + + @Override + public boolean hasNegation() { + return !this.stream().anyMatch(internalCondition -> !(internalCondition.hasNegation())); + } + + @Override + public JanusGraphPredicate negate() { + final ConnectiveJanusPredicate toReturn = getNewNegateIntance(); + this.stream().map(JanusGraphPredicate::negate).forEach(toReturn::add); + return toReturn; + } + + @Override + @SuppressWarnings("unchecked") + public boolean test(Object value, Object condition) { + if (!(condition instanceof List) || ((List) condition).size() != this.size()){ + return false; + } + final Iterator itConditions = ((List) condition).iterator(); + return this.stream().anyMatch(internalCondition -> isOr() == internalCondition.test(value, itConditions.next())) ? isOr() : !isOr(); + } +} diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/OrJanusPredicate.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/OrJanusPredicate.java new file mode 100644 index 00000000000..2281d132305 --- /dev/null +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/predicate/OrJanusPredicate.java @@ -0,0 +1,50 @@ +// Copyright 2017 JanusGraph Authors +// +// Licensed 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.janusgraph.graphdb.predicate; + +import java.util.List; + +import org.janusgraph.graphdb.query.JanusGraphPredicate; + +/** + * @author David Clement (david.clement90@laposte.net) + */ +public class OrJanusPredicate extends ConnectiveJanusPredicate { + + private static final long serialVersionUID = 8069102813023214045L; + + public OrJanusPredicate(){ + super(); + } + + public OrJanusPredicate(final List predicates) { + super(predicates); + } + + @Override + ConnectiveJanusPredicate getNewNegateIntance() { + return new AndJanusPredicate(); + } + + @Override + boolean isOr() { + return true; + } + + @Override + public boolean isQNF() { + return ! this.stream().anyMatch(internalCondition -> internalCondition instanceof AndJanusPredicate || !internalCondition.isQNF()); + } +} diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/query/JanusGraphPredicate.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/query/JanusGraphPredicate.java index a6e105ba4a2..300e9d237f8 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/query/JanusGraphPredicate.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/query/JanusGraphPredicate.java @@ -16,9 +16,21 @@ import org.janusgraph.core.attribute.Cmp; import org.janusgraph.core.attribute.Contain; +import org.janusgraph.graphdb.predicate.AndJanusPredicate; +import org.janusgraph.graphdb.predicate.ConnectiveJanusGraphP; +import org.janusgraph.graphdb.predicate.ConnectiveJanusPredicate; +import org.janusgraph.graphdb.predicate.OrJanusPredicate; + import org.apache.tinkerpop.gremlin.process.traversal.Compare; import org.apache.tinkerpop.gremlin.process.traversal.Contains; - +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer; +import org.apache.tinkerpop.gremlin.process.traversal.util.AndP; +import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP; +import org.apache.tinkerpop.gremlin.process.traversal.util.OrP; + +import java.util.ArrayList; +import java.util.List; import java.util.function.BiPredicate; /** @@ -64,6 +76,7 @@ public interface JanusGraphPredicate extends BiPredicate { * Returns the negation of this predicate if it exists, otherwise an exception is thrown. Check {@link #hasNegation()} first. * @return */ + @Override JanusGraphPredicate negate(); /** @@ -90,7 +103,7 @@ public static JanusGraphPredicate convertInternal(BiPredicate p) { if (p instanceof JanusGraphPredicate) { return (JanusGraphPredicate)p; } else if (p instanceof Compare) { - Compare comp = (Compare)p; + final Compare comp = (Compare)p; switch(comp) { case eq: return Cmp.EQUAL; case neq: return Cmp.NOT_EQUAL; @@ -101,7 +114,7 @@ public static JanusGraphPredicate convertInternal(BiPredicate p) { default: throw new IllegalArgumentException("Unexpected comparator: " + comp); } } else if (p instanceof Contains) { - Contains con = (Contains)p; + final Contains con = (Contains)p; switch (con) { case within: return Contain.IN; case without: return Contain.NOT_IN; @@ -112,7 +125,7 @@ public static JanusGraphPredicate convertInternal(BiPredicate p) { } public static JanusGraphPredicate convert(BiPredicate p) { - JanusGraphPredicate janusgraphPredicate = convertInternal(p); + final JanusGraphPredicate janusgraphPredicate = convertInternal(p); if (janusgraphPredicate==null) throw new IllegalArgumentException("JanusGraph does not support the given predicate: " + p); return janusgraphPredicate; } @@ -120,6 +133,40 @@ public static JanusGraphPredicate convert(BiPredicate p) { public static boolean supports(BiPredicate p) { return convertInternal(p)!=null; } - } + public static HasContainer convert(final HasContainer container){ + if (!(container.getPredicate() instanceof ConnectiveP)) { + return container; + } + final ConnectiveJanusPredicate connectivePredicate = instanceConnectiveJanusPredicate(container.getPredicate()); + return new HasContainer(container.getKey(), new ConnectiveJanusGraphP(connectivePredicate, convert(((ConnectiveP) container.getPredicate()), connectivePredicate))); + } + + public static ConnectiveJanusPredicate instanceConnectiveJanusPredicate(final P predicate) { + final ConnectiveJanusPredicate connectivePredicate; + if (predicate.getClass().isAssignableFrom(AndP.class)){ + connectivePredicate = new AndJanusPredicate(); + } else if (predicate.getClass().isAssignableFrom(OrP.class)){ + connectivePredicate = new OrJanusPredicate(); + } else { + throw new IllegalArgumentException("JanusGraph does not support the given predicate: " + predicate); + } + return connectivePredicate; + } + + public static List convert(final ConnectiveP predicate, final ConnectiveJanusPredicate connectivePredicate) { + final List toReturn = new ArrayList<>(); + for (final P p : predicate.getPredicates()){ + if (p instanceof ConnectiveP) { + final ConnectiveJanusPredicate subPredicate = instanceConnectiveJanusPredicate(p); + toReturn.add(convert((ConnectiveP)p, subPredicate)); + connectivePredicate.add(subPredicate); + } else { + connectivePredicate.add(Converter.convert(p.getBiPredicate())); + toReturn.add(p.getValue()); + } + } + return toReturn; + } + } } diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/query/QueryUtil.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/query/QueryUtil.java index 8ff505d7412..18494dbbe5b 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/query/QueryUtil.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/query/QueryUtil.java @@ -20,6 +20,9 @@ import org.janusgraph.core.attribute.Cmp; import org.janusgraph.core.attribute.Contain; import org.janusgraph.graphdb.internal.InternalRelationType; +import org.janusgraph.graphdb.predicate.AndJanusPredicate; +import org.janusgraph.graphdb.predicate.ConnectiveJanusPredicate; +import org.janusgraph.graphdb.predicate.OrJanusPredicate; import org.janusgraph.graphdb.query.condition.*; import org.janusgraph.graphdb.transaction.StandardJanusGraphTx; import java.util.*; @@ -36,7 +39,7 @@ public static int adjustLimitForTxModifications(StandardJanusGraphTx tx, int unc assert uncoveredAndConditions >= 0; if (uncoveredAndConditions > 0) { - int maxMultiplier = Integer.MAX_VALUE / limit; + final int maxMultiplier = Integer.MAX_VALUE / limit; limit = limit * Math.min(maxMultiplier, (int) Math.pow(2, uncoveredAndConditions)); //(limit*3)/2+1; } @@ -52,13 +55,18 @@ public static int convertLimit(long limit) { else return (int)limit; } - public static int mergeLimits(int limit1, int limit2) { + public static int mergeLowLimits(int limit1, int limit2) { + assert limit1>=0 && limit2>=0; + return Math.max(limit1,limit2); + } + + public static int mergeHighLimits(int limit1, int limit2) { assert limit1>=0 && limit2>=0; return Math.min(limit1,limit2); } public static InternalRelationType getType(StandardJanusGraphTx tx, String typeName) { - RelationType t = tx.getRelationType(typeName); + final RelationType t = tx.getRelationType(typeName); if (t == null && !tx.getConfiguration().getAutoSchemaMaker().ignoreUndefinedQueryTypes()) { throw new IllegalArgumentException("Undefined type used in query: " + typeName); } @@ -101,7 +109,7 @@ public static boolean isQueryNormalForm(Condition condition) { for (final Condition child : ((And) condition).getChildren()) { if (!isQNFLiteralOrNot(child)) { if (child instanceof Or) { - for (Condition child2 : ((Or) child).getChildren()) { + for (final Condition child2 : ((Or) child).getChildren()) { if (!isQNFLiteralOrNot(child2)) return false; } } else { @@ -127,7 +135,7 @@ private static boolean isQNFLiteral(Condition condition) { public static Condition simplifyQNF(Condition condition) { Preconditions.checkArgument(isQueryNormalForm(condition)); if (condition.numChildren() == 1) { - Condition child = ((And) condition).get(0); + final Condition child = ((And) condition).get(0); if (child.getType() == Condition.Type.LITERAL) return child; } return condition; @@ -149,8 +157,8 @@ public static boolean isEmpty(Condition condition) { */ public static And constraints2QNF(StandardJanusGraphTx tx, List> constraints) { final And conditions = new And<>(constraints.size() + 4); - for (PredicateCondition atom : constraints) { - RelationType type = getType(tx, atom.getKey()); + for (final PredicateCondition atom : constraints) { + final RelationType type = getType(tx, atom.getKey()); if (type == null) { if (atom.getPredicate() == Cmp.EQUAL && atom.getValue() == null || @@ -160,12 +168,12 @@ public static And constraints2QNF(StandardJanus return null; } - Object value = atom.getValue(); - JanusGraphPredicate predicate = atom.getPredicate(); + final Object value = atom.getValue(); + final JanusGraphPredicate predicate = atom.getPredicate(); if (type.isPropertyKey()) { - PropertyKey key = (PropertyKey) type; + final PropertyKey key = (PropertyKey) type; assert predicate.isValidCondition(value); Preconditions.checkArgument(key.dataType()==Object.class || predicate.isValidValueType(key.dataType()), "Data type of key is not compatible with condition"); } else { //its a label @@ -175,10 +183,10 @@ public static And constraints2QNF(StandardJanus if (predicate instanceof Contain) { //Rewrite contains conditions - Collection values = (Collection) value; + final Collection values = (Collection) value; if (predicate == Contain.NOT_IN) { if (values.isEmpty()) continue; //Simply ignore since trivially satisfied - for (Object inValue : values) + for (final Object inValue : values) addConstraint(type, Cmp.NOT_EQUAL, inValue, conditions, tx); } else { Preconditions.checkArgument(predicate == Contain.IN); @@ -188,11 +196,22 @@ public static And constraints2QNF(StandardJanus addConstraint(type, Cmp.EQUAL, values.iterator().next(), conditions, tx); } else { final Or nested = new Or<>(values.size()); - for (Object invalue : values) + for (final Object invalue : values) addConstraint(type, Cmp.EQUAL, invalue, nested, tx); conditions.add(nested); } } + } else if (predicate instanceof AndJanusPredicate) { + if (addConstraint(type, (AndJanusPredicate) (predicate), (List) (value), conditions, tx) == null) { + return null; + } + } else if (predicate instanceof OrJanusPredicate) { + final List values = (List) (value); + final Or nested = addConstraint(type, (OrJanusPredicate) predicate, values, new Or<>(values.size()), tx); + if (nested == null) { + return null; + } + conditions.add(nested); } else { addConstraint(type, predicate, value, conditions, tx); } @@ -200,6 +219,48 @@ public static And constraints2QNF(StandardJanus return conditions; } + private static And addConstraint(final RelationType type, AndJanusPredicate predicate, List values, And and, StandardJanusGraphTx tx) { + for (int i = 0 ; i < values.size(); i++) { + final JanusGraphPredicate janusGraphPredicate = predicate.get(i); + if (janusGraphPredicate instanceof AndJanusPredicate) { + if (addConstraint(type, (AndJanusPredicate) (janusGraphPredicate), (List) (values.get(i)), and, tx) == null) { + return null; + } + } else if (predicate.get(i) instanceof OrJanusPredicate) { + final List childValues = (List) (values.get(i)); + final Or nested = addConstraint(type, (OrJanusPredicate) (janusGraphPredicate), childValues, new Or<>(childValues.size()), tx); + if (nested == null) { + return null; + } + and.add(nested); + } else { + addConstraint(type, janusGraphPredicate, values.get(i), and, tx); + } + } + return and; + } + + private static Or addConstraint(final RelationType type, OrJanusPredicate predicate, List values, Or or, StandardJanusGraphTx tx) { + for (int i = 0 ; i < values.size(); i++) { + final JanusGraphPredicate janusGraphPredicate = predicate.get(i); + if (janusGraphPredicate instanceof AndJanusPredicate) { + final List childValues = (List) (values.get(i)); + final And nested = addConstraint(type, (AndJanusPredicate) janusGraphPredicate, childValues, new And<>(childValues.size()), tx); + if (nested == null) { + return null; + } + or.add(nested); + } else if (janusGraphPredicate instanceof OrJanusPredicate) { + if (addConstraint(type, (OrJanusPredicate) janusGraphPredicate, (List) (values.get(i)), or, tx) == null) { + return null; + } + } else { + addConstraint(type, janusGraphPredicate, values.get(i), or, tx); + } + } + return or; + } + private static void addConstraint(RelationType type, JanusGraphPredicate predicate, Object value, MultiCondition conditions, StandardJanusGraphTx tx) { if (type.isPropertyKey()) { @@ -216,13 +277,13 @@ private static void addConstraint(RelationType typ public static Map.Entry extractOrCondition(Or condition) { RelationType masterType = null; final List values = new ArrayList<>(); - for (Condition c : condition.getChildren()) { + for (final Condition c : condition.getChildren()) { if (!(c instanceof PredicateCondition)) return null; - PredicateCondition atom = (PredicateCondition)c; + final PredicateCondition atom = (PredicateCondition)c; if (atom.getPredicate()!=Cmp.EQUAL) return null; - Object value = atom.getValue(); + final Object value = atom.getValue(); if (value==null) return null; - RelationType type = atom.getKey(); + final RelationType type = atom.getKey(); if (masterType==null) masterType=type; else if (!masterType.equals(type)) return null; values.add(value); @@ -243,18 +304,18 @@ public static List processIntersectingRetrievals(List> retri * of current results with cumulative results on each iteration. */ //TODO: smarter limit estimation - int multiplier = Math.min(16, (int) Math.pow(2, retrievals.size() - 1)); + final int multiplier = Math.min(16, (int) Math.pow(2, retrievals.size() - 1)); int subLimit = Integer.MAX_VALUE; if (Integer.MAX_VALUE / multiplier >= limit) subLimit = limit * multiplier; boolean exhaustedResults; do { exhaustedResults = true; results = null; - for (IndexCall call : retrievals) { + for (final IndexCall call : retrievals) { Collection subResult; try { subResult = call.call(subLimit); - } catch (Exception e) { + } catch (final Exception e) { throw new JanusGraphException("Could not process individual retrieval call ", e); } @@ -262,7 +323,7 @@ public static List processIntersectingRetrievals(List> retri if (results == null) { results = Lists.newArrayList(subResult); } else { - Set subResultSet = ImmutableSet.copyOf(subResult); + final Set subResultSet = ImmutableSet.copyOf(subResult); results.removeIf(o -> !subResultSet.contains(o)); } } diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/query/graph/GraphCentricQuery.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/query/graph/GraphCentricQuery.java index 209502fb973..93c911a258b 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/query/graph/GraphCentricQuery.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/query/graph/GraphCentricQuery.java @@ -21,7 +21,6 @@ import org.janusgraph.graphdb.query.BackendQueryHolder; import org.janusgraph.graphdb.query.BaseQuery; import org.janusgraph.graphdb.query.ElementQuery; -import org.janusgraph.graphdb.query.QueryUtil; import org.janusgraph.graphdb.query.condition.Condition; import org.janusgraph.graphdb.query.condition.FixedCondition; import org.janusgraph.graphdb.query.profile.ProfileObservable; @@ -64,7 +63,6 @@ public GraphCentricQuery(ElementCategory resultType, Condition> constraints; + private final List> constraints = new ArrayList<>(5); + + /** + * List of constraints added to an Or query. None by defautl + */ + private final List>> globalConstraints = new ArrayList<>(); /** * The order in which the elements should be returned. None by default. */ @@ -79,7 +85,6 @@ public GraphCentricQueryBuilder(StandardJanusGraphTx tx, IndexSerializer seriali Preconditions.checkNotNull(serializer); this.tx = tx; this.serializer = serializer; - this.constraints = new ArrayList<>(5); } /* --------------------------------------------------------------- @@ -87,6 +92,10 @@ public GraphCentricQueryBuilder(StandardJanusGraphTx tx, IndexSerializer seriali * --------------------------------------------------------------- */ + public List> getConstraints() { + return constraints; + } + public GraphCentricQueryBuilder profiler(QueryProfiler profiler) { Preconditions.checkNotNull(profiler); this.profiler=profiler; @@ -144,7 +153,7 @@ public GraphCentricQueryBuilder limit(final int limit) { @Override public GraphCentricQueryBuilder orderBy(String keyName, org.apache.tinkerpop.gremlin.process.traversal.Order order) { Preconditions.checkArgument(tx.containsPropertyKey(keyName),"Provided key does not exist: %s",keyName); - PropertyKey key = tx.getPropertyKey(keyName); + final PropertyKey key = tx.getPropertyKey(keyName); Preconditions.checkArgument(key!=null && order!=null,"Need to specify and key and an order"); Preconditions.checkArgument(Comparable.class.isAssignableFrom(key.dataType()), "Can only order on keys with comparable data type. [%s] has datatype [%s]", key.name(), key.dataType()); @@ -155,6 +164,12 @@ public GraphCentricQueryBuilder orderBy(String keyName, org.apache.tinkerpop.gr return this; } + @Override + public GraphCentricQueryBuilder or(GraphCentricQueryBuilder subQuery) { + this.globalConstraints.add(subQuery.getConstraints()); + return this; + } + /* --------------------------------------------------------------- * Query Execution * --------------------------------------------------------------- @@ -162,20 +177,21 @@ public GraphCentricQueryBuilder orderBy(String keyName, org.apache.tinkerpop.gr @Override public Iterable vertices() { - GraphCentricQuery query = constructQuery(ElementCategory.VERTEX); - return Iterables.filter(new QueryProcessor<>(query, tx.elementProcessor), JanusGraphVertex.class); + return iterables(constructQuery(ElementCategory.VERTEX), JanusGraphVertex.class); } @Override public Iterable edges() { - GraphCentricQuery query = constructQuery(ElementCategory.EDGE); - return Iterables.filter(new QueryProcessor<>(query, tx.elementProcessor), JanusGraphEdge.class); + return iterables(constructQuery(ElementCategory.EDGE), JanusGraphEdge.class); } @Override public Iterable properties() { - GraphCentricQuery query = constructQuery(ElementCategory.PROPERTY); - return Iterables.filter(new QueryProcessor<>(query, tx.elementProcessor), JanusGraphVertexProperty.class); + return iterables(constructQuery(ElementCategory.PROPERTY), JanusGraphVertexProperty.class); + } + + public Iterable iterables(final GraphCentricQuery query, final Class aClass) { + return Iterables.filter(new QueryProcessor<>(query, tx.elementProcessor), aClass); } @@ -197,9 +213,12 @@ public Iterable properties() { public GraphCentricQuery constructQuery(final ElementCategory resultType) { - QueryProfiler optProfiler = profiler.addNested(QueryProfiler.OPTIMIZATION); + final QueryProfiler optProfiler = profiler.addNested(QueryProfiler.OPTIMIZATION); optProfiler.startTimer(); - GraphCentricQuery query = constructQueryWithoutProfile(resultType); + if (this.globalConstraints.isEmpty()) { + this.globalConstraints.add(this.constraints); + } + final GraphCentricQuery query = constructQueryWithoutProfile(resultType); optProfiler.stopTimer(); query.observeWith(profiler); return query; @@ -210,8 +229,19 @@ public GraphCentricQuery constructQueryWithoutProfile(final ElementCategory resu if (limit == 0) return GraphCentricQuery.emptyQuery(resultType); //Prepare constraints - And conditions = QueryUtil.constraints2QNF(tx, constraints); - if (conditions == null) return GraphCentricQuery.emptyQuery(resultType); + final MultiCondition conditions; + if (this.globalConstraints.size() == 1) { + conditions = QueryUtil.constraints2QNF(tx, constraints); + if (conditions == null) return GraphCentricQuery.emptyQuery(resultType); + } else { + conditions = new Or<>(); + for (final List> child : this.globalConstraints){ + final And localconditions = QueryUtil.constraints2QNF(tx, child); + if (localconditions == null) return GraphCentricQuery.emptyQuery(resultType); + conditions.add(localconditions); + } + } + //Prepare orders orders.makeImmutable(); @@ -224,7 +254,7 @@ public GraphCentricQuery constructQueryWithoutProfile(final ElementCategory resu final RelationType type = ((PredicateCondition) condition).getKey(); Preconditions.checkArgument(type != null && type.isPropertyKey()); Iterables.addAll(indexCandidates, Iterables.filter(((InternalRelationType) type).getKeyIndexes(), - indexType -> indexType.getElement() == resultType)); + indexType -> indexType.getElement() == resultType && !(conditions instanceof Or && (indexType.isCompositeIndex() || !serializer.features((MixedIndexType) indexType).supportNotQueryNormalForm())))); } return true; }); @@ -235,9 +265,9 @@ Iterate over all potential indexes (as compiled above) and compute a score based this index covers. The index with the highest score (as long as it covers at least one additional clause) is picked and added to the joint query for as long as such exist. */ - JointIndexQuery jointQuery = new JointIndexQuery(); + final JointIndexQuery jointQuery = new JointIndexQuery(); boolean isSorted = orders.isEmpty(); - Set coveredClauses = Sets.newHashSet(); + final Set coveredClauses = Sets.newHashSet(); while (true) { IndexType bestCandidate = null; double candidateScore = 0.0; @@ -245,17 +275,17 @@ this index covers. The index with the highest score (as long as it covers at lea boolean candidateSupportsSort = false; Object candidateSubCondition = null; - for (IndexType index : indexCandidates) { - Set subcover = Sets.newHashSet(); + for (final IndexType index : indexCandidates) { + final Set subcover = Sets.newHashSet(); Object subCondition; boolean supportsSort = orders.isEmpty(); //Check that this index actually applies in case of a schema constraint if (index.hasSchemaTypeConstraint()) { - JanusGraphSchemaType type = index.getSchemaTypeConstraint(); - Map.Entry> equalCon + final JanusGraphSchemaType type = index.getSchemaTypeConstraint(); + final Map.Entry> equalCon = getEqualityConditionValues(conditions,ImplicitKey.LABEL); if (equalCon==null) continue; - Collection labels = equalCon.getValue(); + final Collection labels = equalCon.getValue(); assert labels.size() >= 1; if (labels.size()>1) { log.warn("The query optimizer currently does not support multiple label constraints in query: {}",this); @@ -274,11 +304,10 @@ this index covers. The index with the highest score (as long as it covers at lea if (coveredClauses.isEmpty() && !supportsSort && indexCoversOrder((MixedIndexType)index,orders)) supportsSort=true; } - if (subCondition==null) continue; - assert !subcover.isEmpty(); + if (subCondition==null || subcover.isEmpty()) continue; double score = 0.0; boolean coversAdditionalClause = false; - for (Condition c : subcover) { + for (final Condition c : subcover) { double s = (c instanceof PredicateCondition && ((PredicateCondition)c).getPredicate()==Cmp.EQUAL)? EQUAL_CONDITION_SCORE:OTHER_CONDITION_SCORE; if (coveredClauses.contains(c)) s=s*ALREADY_MATCHED_ADJUSTOR; @@ -343,11 +372,13 @@ public static boolean indexCoversOrder(MixedIndexType index, OrderList orders) { public static List indexCover(final CompositeIndexType index, Condition condition, Set covered) { - assert QueryUtil.isQueryNormalForm(condition); + if (!QueryUtil.isQueryNormalForm(condition)) { + return null; + } assert condition instanceof And; if (index.getStatus()!= SchemaStatus.ENABLED) return null; - IndexField[] fields = index.getFieldKeys(); - Object[] indexValues = new Object[fields.length]; + final IndexField[] fields = index.getFieldKeys(); + final Object[] indexValues = new Object[fields.length]; final Set coveredClauses = new HashSet<>(fields.length); final List indexCovers = new ArrayList<>(4); @@ -364,13 +395,13 @@ private static void constructIndexCover(Object[] indexValues, int position, Inde if (position>=fields.length) { indexCovers.add(indexValues); } else { - IndexField field = fields[position]; - Map.Entry> equalCon = getEqualityConditionValues(condition,field.getFieldKey()); + final IndexField field = fields[position]; + final Map.Entry> equalCon = getEqualityConditionValues(condition,field.getFieldKey()); if (equalCon!=null) { coveredClauses.add(equalCon.getKey()); assert equalCon.getValue().size()>0; - for (Object value : equalCon.getValue()) { - Object[] newValues = Arrays.copyOf(indexValues,fields.length); + for (final Object value : equalCon.getValue()) { + final Object[] newValues = Arrays.copyOf(indexValues,fields.length); newValues[position]=value; constructIndexCover(newValues,position+1,fields,condition,indexCovers,coveredClauses); } @@ -380,14 +411,14 @@ private static void constructIndexCover(Object[] indexValues, int position, Inde private static Map.Entry> getEqualityConditionValues( Condition condition, RelationType type) { - for (Condition c : condition.getChildren()) { + for (final Condition c : condition.getChildren()) { if (c instanceof Or) { - Map.Entry orEqual = QueryUtil.extractOrCondition((Or)c); + final Map.Entry orEqual = QueryUtil.extractOrCondition((Or)c); if (orEqual!=null && orEqual.getKey().equals(type) && !orEqual.getValue().isEmpty()) { return new AbstractMap.SimpleImmutableEntry(c,orEqual.getValue()); } } else if (c instanceof PredicateCondition) { - PredicateCondition atom = (PredicateCondition)c; + final PredicateCondition atom = (PredicateCondition)c; if (atom.getKey().equals(type) && atom.getPredicate()==Cmp.EQUAL && atom.getValue()!=null) { return new AbstractMap.SimpleImmutableEntry(c,ImmutableList.of(atom.getValue())); } @@ -401,10 +432,29 @@ public static Condition indexCover(final MixedIndexType index Condition condition, final IndexSerializer indexInfo, final Set covered) { - assert QueryUtil.isQueryNormalForm(condition); + if (!indexInfo.features(index).supportNotQueryNormalForm() && !QueryUtil.isQueryNormalForm(condition)) { + return null; + } + if (condition instanceof Or) { + for (final Condition subClause : condition.getChildren()) { + if (subClause instanceof And) { + for (final Condition subsubClause : condition.getChildren()) { + if (!coversAll(index, subsubClause,indexInfo)) { + return null; + } + } + } else { + if (!coversAll(index, subClause, indexInfo)) { + return null; + } + } + } + covered.add(condition); + return condition; + } assert condition instanceof And; final And subCondition = new And<>(condition.numChildren()); - for (Condition subClause : condition.getChildren()) { + for (final Condition subClause : condition.getChildren()) { if (coversAll(index,subClause,indexInfo)) { subCondition.add(subClause); covered.add(subClause); @@ -422,7 +472,7 @@ private static boolean coversAll(final MixedIndexType index, Condition atom = (PredicateCondition) condition; + final PredicateCondition atom = (PredicateCondition) condition; if (atom.getValue() == null) { return false; } diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/HasStepFolder.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/HasStepFolder.java index b8a1c0ee0f0..ca2dc36445c 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/HasStepFolder.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/HasStepFolder.java @@ -19,6 +19,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.NoOpBarrierStep; import org.apache.tinkerpop.gremlin.process.traversal.util.AndP; +import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP; import org.janusgraph.core.Cardinality; import org.janusgraph.core.PropertyKey; import org.janusgraph.core.JanusGraphTransaction; @@ -28,11 +29,13 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder; -import org.apache.tinkerpop.gremlin.process.traversal.step.Ranging; import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.filter.OrStep; import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeGlobalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.OrderGlobalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.IdentityStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.ElementValueComparator; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep; import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer; import org.javatuples.Pair; @@ -52,15 +55,27 @@ public interface HasStepFolder extends Step { void addAll(Iterable hasContainers); + List addLocalAll(Iterable hasContainers); + void orderBy(String key, Order order); - void setLimit(int limit); + void localOrderBy(List hasContainers, String key, Order order); + + void setLimit(int low, int high); + + void setLocalLimit(List hasContainers, int low, int high); + + int getLowLimit(); + + int getLocalLowLimit(List hasContainers); - int getLimit(); + int getHighLimit(); + + int getLocalHighLimit(List hasContainers); static boolean validJanusGraphHas(HasContainer has) { - if (has.getPredicate() instanceof AndP) { - final List> predicates = ((AndP) has.getPredicate()).getPredicates(); + if (has.getPredicate() instanceof ConnectiveP) { + final List> predicates = ((ConnectiveP) has.getPredicate()).getPredicates(); return predicates.stream().allMatch(p-> validJanusGraphHas(new HasContainer(has.getKey(), p))); } else { return JanusGraphPredicate.Converter.supports(has.getBiPredicate()); @@ -68,7 +83,7 @@ static boolean validJanusGraphHas(HasContainer has) { } static boolean validJanusGraphHas(Iterable has) { - for (HasContainer h : has) { + for (final HasContainer h : has) { if (!validJanusGraphHas(h)) return false; } return true; @@ -77,23 +92,27 @@ static boolean validJanusGraphHas(Iterable has) { static boolean validJanusGraphOrder(OrderGlobalStep orderGlobalStep, Traversal rootTraversal, boolean isVertexOrder) { final List> comparators = orderGlobalStep.getComparators(); - for(Pair comp : comparators) { + for(final Pair comp : comparators) { + final String key; if (comp.getValue0() instanceof ElementValueTraversal && comp.getValue1() instanceof Order) { - final String key = ((ElementValueTraversal) comp.getValue0()).getPropertyKey(); - final JanusGraphTransaction tx = JanusGraphTraversalUtil.getTx(rootTraversal.asAdmin()); - final PropertyKey pKey = tx.getPropertyKey(key); - if(pKey == null - || !(Comparable.class.isAssignableFrom(pKey.dataType())) - || (isVertexOrder && pKey.cardinality() != Cardinality.SINGLE)) { - return false; - } + key = ((ElementValueTraversal) comp.getValue0()).getPropertyKey(); + } else if (comp.getValue1() instanceof ElementValueComparator) { + final ElementValueComparator evc = (ElementValueComparator) comp.getValue1(); + if (!(evc.getValueComparator() instanceof Order)) return false; + key = evc.getPropertyKey(); } else { // do not fold comparators that include nested traversals that are not simple ElementValues return false; } + final JanusGraphTransaction tx = JanusGraphTraversalUtil.getTx(rootTraversal.asAdmin()); + final PropertyKey pKey = tx.getPropertyKey(key); + if (pKey == null + || !(Comparable.class.isAssignableFrom(pKey.dataType())) + || (isVertexOrder && pKey.cardinality() != Cardinality.SINGLE)) { + return false; + } } - return true; } @@ -135,16 +154,43 @@ else if (currentStep instanceof IdentityStep) { } } - static void foldInHasContainer(final HasStepFolder janusgraphStep, final Traversal.Admin traversal) { + static void foldInHasContainer(final HasStepFolder janusgraphStep, final Traversal.Admin traversal, + final Traversal rootTraversal) { Step currentStep = janusgraphStep.getNextStep(); while (true) { - if (currentStep instanceof HasContainerHolder) { - Iterable containers = ((HasContainerHolder) currentStep).getHasContainers(); - if (validJanusGraphHas(containers)) { + if (currentStep instanceof OrStep && janusgraphStep instanceof JanusGraphStep) { + for (final Traversal.Admin child : ((OrStep) currentStep).getLocalChildren()) { + if (!validFoldInHasContainer(child.getStartStep(), false)){ + return; + } + } + ((OrStep) currentStep).getLocalChildren().forEach(t ->localFoldInHasContainer(janusgraphStep, t.getStartStep(), t, rootTraversal)); + traversal.removeStep(currentStep); + } else if (currentStep instanceof HasContainerHolder){ + final Iterable containers = ((HasContainerHolder) currentStep).getHasContainers().stream().map(c -> JanusGraphPredicate.Converter.convert(c)).collect(Collectors.toList()); + if (validFoldInHasContainer(currentStep, true)) { janusgraphStep.addAll(containers); currentStep.getLabels().forEach(janusgraphStep::addLabel); traversal.removeStep(currentStep); } + } else if (!(currentStep instanceof IdentityStep) && !(currentStep instanceof NoOpBarrierStep) && !(currentStep instanceof HasContainerHolder)) { + break; + } + currentStep = currentStep.getNextStep(); + } + } + + static void localFoldInHasContainer(final HasStepFolder janusgraphStep, final Step tinkerpopStep, final Traversal.Admin traversal, + final Traversal rootTraversal){ + Step currentStep = tinkerpopStep; + while (true) { + if (currentStep instanceof HasContainerHolder) { + final Iterable containers = ((HasContainerHolder) currentStep).getHasContainers().stream().map(c -> JanusGraphPredicate.Converter.convert(c)).collect(Collectors.toList()); + final List hasContainers = janusgraphStep.addLocalAll(containers); + currentStep.getLabels().forEach(janusgraphStep::addLabel); + traversal.removeStep(currentStep); + currentStep = foldInOrder(janusgraphStep, currentStep, traversal, rootTraversal, janusgraphStep instanceof JanusGraphStep && ((JanusGraphStep)janusgraphStep).returnsVertex(), hasContainers); + foldInRange(janusgraphStep, currentStep, traversal, hasContainers); } else if (!(currentStep instanceof IdentityStep) && !(currentStep instanceof NoOpBarrierStep)) { break; } @@ -152,9 +198,25 @@ static void foldInHasContainer(final HasStepFolder janusgraphStep, final Travers } } - static void foldInOrder(final HasStepFolder janusgraphStep, final Traversal.Admin traversal, - final Traversal rootTraversal, boolean isVertexOrder) { - Step currentStep = janusgraphStep.getNextStep(); + static boolean validFoldInHasContainer(final Step tinkerpopStep, final boolean defaultValue){ + Step currentStep = tinkerpopStep; + Boolean toReturn = null; + while (!(currentStep instanceof EmptyStep)) { + if (currentStep instanceof HasContainerHolder) { + final Iterable containers = ((HasContainerHolder) currentStep).getHasContainers(); + toReturn = toReturn == null ? validJanusGraphHas(containers) : toReturn && validJanusGraphHas(containers); + } else if (!(currentStep instanceof IdentityStep) && !(currentStep instanceof NoOpBarrierStep) && !(currentStep instanceof RangeGlobalStep) && !(currentStep instanceof OrderGlobalStep)) { + toReturn = toReturn == null ? false : (toReturn && defaultValue); + break; + } + currentStep = currentStep.getNextStep(); + } + return Boolean.TRUE.equals(toReturn); + } + + static Step foldInOrder(final HasStepFolder janusgraphStep, final Step tinkerpopStep, final Traversal.Admin traversal, + final Traversal rootTraversal, boolean isVertexOrder, final List hasContainers) { + Step currentStep = tinkerpopStep; OrderGlobalStep lastOrder = null; while (true) { if (currentStep instanceof OrderGlobalStep) { @@ -169,21 +231,33 @@ static void foldInOrder(final HasStepFolder janusgraphStep, final Traversal.Admi currentStep = currentStep.getNextStep(); } - if (lastOrder != null) { - if (validJanusGraphOrder(lastOrder, rootTraversal, isVertexOrder)) { - //Add orders to HasStepFolder - for (Pair, Comparator> comp : - (List, Comparator>>) ((OrderGlobalStep) lastOrder).getComparators()) { - ElementValueTraversal evt = (ElementValueTraversal) comp.getValue0(); - janusgraphStep.orderBy(evt.getPropertyKey(), (Order) comp.getValue1()); + if (lastOrder != null && validJanusGraphOrder(lastOrder, rootTraversal, isVertexOrder)) { + //Add orders to HasStepFolder + for (final Pair, Comparator> comp : (List, Comparator>>) ((OrderGlobalStep) lastOrder).getComparators()) { + final String key; + final Order order; + if (comp.getValue0() instanceof ElementValueTraversal) { + final ElementValueTraversal evt = (ElementValueTraversal) comp.getValue0(); + key = evt.getPropertyKey(); + order = (Order) comp.getValue1(); + } else { + final ElementValueComparator evc = (ElementValueComparator) comp.getValue1(); + key = evc.getPropertyKey(); + order = (Order) evc.getValueComparator(); + } + if (hasContainers == null) { + janusgraphStep.orderBy(key, order); + } else { + janusgraphStep.localOrderBy(hasContainers, key, order); } - lastOrder.getLabels().forEach(janusgraphStep::addLabel); - traversal.removeStep(lastOrder); } + lastOrder.getLabels().forEach(janusgraphStep::addLabel); + traversal.removeStep(lastOrder); } + return currentStep; } - static void splitAndP(final List hasContainers, final Iterable has) { + static List splitAndP(final List hasContainers, final Iterable has) { has.forEach(hasContainer -> { if (hasContainer.getPredicate() instanceof AndP) { for (final P predicate : ((AndP) hasContainer.getPredicate()).getPredicates()) { @@ -192,6 +266,7 @@ static void splitAndP(final List hasContainers, final Iterable void foldInRange(final HasStepFolder janusgraphStep, final Traversal.Admin traversal) { - Step nextStep = JanusGraphTraversalUtil.getNextNonIdentityStep(janusgraphStep); - + static void foldInRange(final HasStepFolder janusgraphStep, final Step tinkerpopStep, final Traversal.Admin traversal, final List hasContainers) { + final Step nextStep = tinkerpopStep instanceof IdentityStep ? JanusGraphTraversalUtil.getNextNonIdentityStep(tinkerpopStep): tinkerpopStep; if (nextStep instanceof RangeGlobalStep) { - RangeGlobalStep range = (RangeGlobalStep) nextStep; - int limit = QueryUtil.convertLimit(range.getHighRange()); - janusgraphStep.setLimit(QueryUtil.mergeLimits(limit, janusgraphStep.getLimit())); - if (range.getLowRange() == 0) { //Range can be removed since there is no offset + final RangeGlobalStep range = (RangeGlobalStep) nextStep; + int low = 0; + if (janusgraphStep instanceof JanusGraphStep) { + low = QueryUtil.convertLimit(range.getLowRange()); + low = QueryUtil.mergeLowLimits(low, hasContainers == null ? janusgraphStep.getLowLimit(): janusgraphStep.getLocalLowLimit(hasContainers)); + } + int high = QueryUtil.convertLimit(range.getHighRange()); + high = QueryUtil.mergeHighLimits(high, hasContainers == null ? janusgraphStep.getHighLimit(): janusgraphStep.getLocalHighLimit(hasContainers)); + if (hasContainers == null) { + janusgraphStep.setLimit(low, high); + } else { + janusgraphStep.setLocalLimit(hasContainers, low, high); + } + if (janusgraphStep instanceof JanusGraphStep || range.getLowRange() == 0) { //Range can be removed since there is JanusGraphStep or no offset nextStep.getLabels().forEach(janusgraphStep::addLabel); traversal.removeStep(nextStep); } } } - - } diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphLocalQueryOptimizerStrategy.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphLocalQueryOptimizerStrategy.java index 0598f2fd399..e1639ed6261 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphLocalQueryOptimizerStrategy.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphLocalQueryOptimizerStrategy.java @@ -47,10 +47,10 @@ public void apply(final Traversal.Admin traversal) { if (!traversal.getGraph().isPresent()) return; - Graph graph = traversal.getGraph().get(); + final Graph graph = traversal.getGraph().get(); //If this is a compute graph then we can't apply local traversal optimisation at this stage. - StandardJanusGraph janusGraph = graph instanceof StandardJanusGraphTx ? ((StandardJanusGraphTx) graph).getGraph() : (StandardJanusGraph) graph; + final StandardJanusGraph janusGraph = graph instanceof StandardJanusGraphTx ? ((StandardJanusGraphTx) graph).getGraph() : (StandardJanusGraph) graph; final boolean useMultiQuery = !TraversalHelper.onGraphComputer(traversal) && janusGraph.getConfiguration().useMultiQuery(); /* @@ -58,20 +58,20 @@ public void apply(final Traversal.Admin traversal) { */ TraversalHelper.getStepsOfClass(VertexStep.class, traversal).forEach(originalStep -> { - JanusGraphVertexStep vertexStep = new JanusGraphVertexStep(originalStep); + final JanusGraphVertexStep vertexStep = new JanusGraphVertexStep(originalStep); TraversalHelper.replaceStep(originalStep, vertexStep, traversal); if (JanusGraphTraversalUtil.isEdgeReturnStep(vertexStep)) { - HasStepFolder.foldInHasContainer(vertexStep, traversal); + HasStepFolder.foldInHasContainer(vertexStep, traversal, traversal); //We cannot fold in orders or ranges since they are not local } assert JanusGraphTraversalUtil.isEdgeReturnStep(vertexStep) || JanusGraphTraversalUtil.isVertexReturnStep(vertexStep); - Step nextStep = JanusGraphTraversalUtil.getNextNonIdentityStep(vertexStep); + final Step nextStep = JanusGraphTraversalUtil.getNextNonIdentityStep(vertexStep); if (nextStep instanceof RangeGlobalStep) { - int limit = QueryUtil.convertLimit(((RangeGlobalStep) nextStep).getHighRange()); - vertexStep.setLimit(QueryUtil.mergeLimits(limit, vertexStep.getLimit())); + final int limit = QueryUtil.convertLimit(((RangeGlobalStep) nextStep).getHighRange()); + vertexStep.setLimit(0, QueryUtil.mergeHighLimits(limit, vertexStep.getHighLimit())); } if (useMultiQuery) { @@ -86,12 +86,12 @@ public void apply(final Traversal.Admin traversal) { TraversalHelper.getStepsOfClass(PropertiesStep.class, traversal).forEach(originalStep -> { - JanusGraphPropertiesStep propertiesStep = new JanusGraphPropertiesStep(originalStep); + final JanusGraphPropertiesStep propertiesStep = new JanusGraphPropertiesStep(originalStep); TraversalHelper.replaceStep(originalStep, propertiesStep, traversal); if (propertiesStep.getReturnType().forProperties()) { - HasStepFolder.foldInHasContainer(propertiesStep, traversal); + HasStepFolder.foldInHasContainer(propertiesStep, traversal, traversal); //We cannot fold in orders or ranges since they are not local } @@ -105,32 +105,32 @@ public void apply(final Traversal.Admin traversal) { */ TraversalHelper.getStepsOfClass(LocalStep.class, traversal).forEach(localStep -> { - Traversal.Admin localTraversal = ((LocalStep) localStep).getLocalChildren().get(0); - Step localStart = localTraversal.getStartStep(); + final Traversal.Admin localTraversal = ((LocalStep) localStep).getLocalChildren().get(0); + final Step localStart = localTraversal.getStartStep(); if (localStart instanceof VertexStep) { - JanusGraphVertexStep vertexStep = new JanusGraphVertexStep((VertexStep) localStart); + final JanusGraphVertexStep vertexStep = new JanusGraphVertexStep((VertexStep) localStart); TraversalHelper.replaceStep(localStart, vertexStep, localTraversal); if (JanusGraphTraversalUtil.isEdgeReturnStep(vertexStep)) { - HasStepFolder.foldInHasContainer(vertexStep, localTraversal); - HasStepFolder.foldInOrder(vertexStep, localTraversal, traversal, false); + HasStepFolder.foldInHasContainer(vertexStep, localTraversal, traversal); + HasStepFolder.foldInOrder(vertexStep, vertexStep.getNextStep(), localTraversal, traversal, false, null); } - HasStepFolder.foldInRange(vertexStep, localTraversal); + HasStepFolder.foldInRange(vertexStep, JanusGraphTraversalUtil.getNextNonIdentityStep(vertexStep), localTraversal, null); unfoldLocalTraversal(traversal,localStep,localTraversal,vertexStep,useMultiQuery); } if (localStart instanceof PropertiesStep) { - JanusGraphPropertiesStep propertiesStep = new JanusGraphPropertiesStep((PropertiesStep) localStart); + final JanusGraphPropertiesStep propertiesStep = new JanusGraphPropertiesStep((PropertiesStep) localStart); TraversalHelper.replaceStep(localStart, propertiesStep, localTraversal); if (propertiesStep.getReturnType().forProperties()) { - HasStepFolder.foldInHasContainer(propertiesStep, localTraversal); - HasStepFolder.foldInOrder(propertiesStep, localTraversal, traversal, false); + HasStepFolder.foldInHasContainer(propertiesStep, localTraversal, traversal); + HasStepFolder.foldInOrder(propertiesStep, propertiesStep.getNextStep(), localTraversal, traversal, false, null); } - HasStepFolder.foldInRange(propertiesStep, localTraversal); + HasStepFolder.foldInRange(propertiesStep, JanusGraphTraversalUtil.getNextNonIdentityStep(propertiesStep), localTraversal, null); unfoldLocalTraversal(traversal,localStep,localTraversal,propertiesStep,useMultiQuery); diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphPropertiesStep.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphPropertiesStep.java index 0a1d1984094..dc6ec090b21 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphPropertiesStep.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphPropertiesStep.java @@ -14,6 +14,7 @@ package org.janusgraph.graphdb.tinkerpop.optimize; +import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import org.apache.tinkerpop.gremlin.structure.Property; @@ -62,12 +63,12 @@ public void setUseMultiQuery(boolean useMultiQuery) { } private Q makeQuery(Q query) { - String[] keys = getPropertyKeys(); + final String[] keys = getPropertyKeys(); query.keys(keys); - for (HasContainer condition : hasContainers) { + for (final HasContainer condition : hasContainers) { query.has(condition.getKey(), JanusGraphPredicate.Converter.convert(condition.getBiPredicate()), condition.getValue()); } - for (OrderEntry order : orders) query.orderBy(order.key, order.order); + for (final OrderEntry order : orders) query.orderBy(order.key, order.order); if (limit != BaseQuery.NO_LIMIT) query.limit(limit); ((BasicVertexCentricQueryBuilder) query).profiler(queryProfiler); return query; @@ -88,7 +89,7 @@ private void initialize() { assert getReturnType().forProperties() || (orders.isEmpty() && hasContainers.isEmpty()); if (!starts.hasNext()) throw FastNoSuchElementException.instance(); - List> elements = new ArrayList<>(); + final List> elements = new ArrayList<>(); starts.forEachRemaining(elements::add); starts.add(elements.iterator()); assert elements.size() > 0; @@ -96,7 +97,7 @@ private void initialize() { useMultiQuery = useMultiQuery && elements.stream().allMatch(e -> e.get() instanceof Vertex); if (useMultiQuery) { - JanusGraphMultiVertexQuery multiQuery = JanusGraphTraversalUtil.getTx(traversal).multiQuery(); + final JanusGraphMultiVertexQuery multiQuery = JanusGraphTraversalUtil.getTx(traversal).multiQuery(); elements.forEach(e -> multiQuery.addVertex((Vertex) e.get())); makeQuery(multiQuery); @@ -116,7 +117,7 @@ protected Iterator flatMap(final Traverser.Admin traverser) { assert multiQueryResults != null; return convertIterator(multiQueryResults.get(traverser.get())); } else if (traverser.get() instanceof JanusGraphVertex || traverser.get() instanceof WrappedVertex) { - JanusGraphVertexQuery query = makeQuery((JanusGraphTraversalUtil.getJanusGraphVertex(traverser)).query()); + final JanusGraphVertexQuery query = makeQuery((JanusGraphTraversalUtil.getJanusGraphVertex(traverser)).query()); return convertIterator(query.properties()); } else { //It is some other element (edge or vertex property) @@ -163,21 +164,52 @@ public void addAll(Iterable has) { Iterables.addAll(hasContainers, has); } + @Override + public List addLocalAll(Iterable has) { + throw new UnsupportedOperationException("addLocalAll is not supported for properties step."); + } + @Override public void orderBy(String key, Order order) { orders.add(new HasStepFolder.OrderEntry(key, order)); } @Override - public void setLimit(int limit) { - this.limit = limit; + public void localOrderBy(List hasContainers, String key, Order order) { + throw new UnsupportedOperationException("LocalOrderBy is not supported for properties step."); + } + + @Override + public void setLimit(int low, int high) { + Preconditions.checkArgument(low == 0, "Offset is not supported for properties step."); + this.limit = high; } @Override - public int getLimit() { + public void setLocalLimit(List hasContainers, int low, int high) { + throw new UnsupportedOperationException("LocalLimit is not supported for properties step."); + } + + @Override + public int getLowLimit() { + throw new UnsupportedOperationException("getLowLimit is not supported for properties step."); + } + + @Override + public int getLocalLowLimit(List hasContainers) { + throw new UnsupportedOperationException("getLocalLowLimit is not supported for properties step."); + } + + @Override + public int getHighLimit() { return this.limit; } + @Override + public int getLocalHighLimit(List hasContainers) { + throw new UnsupportedOperationException("getLocalHighLimit is not supported for properties step."); + } + @Override public String toString() { return this.hasContainers.isEmpty() ? super.toString() : StringFactory.stepString(this, this.hasContainers); @@ -187,4 +219,5 @@ public String toString() { public void setMetrics(MutableMetrics metrics) { queryProfiler = new TP3ProfileWrapper(metrics); } + } diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphStep.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphStep.java index fac80415225..94206c2498d 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphStep.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphStep.java @@ -15,13 +15,25 @@ package org.janusgraph.graphdb.tinkerpop.optimize; import org.apache.tinkerpop.gremlin.structure.Graph; +import org.janusgraph.core.JanusGraphEdge; +import org.janusgraph.core.JanusGraphElement; import org.janusgraph.core.JanusGraphQuery; import org.janusgraph.core.JanusGraphTransaction; +import org.janusgraph.core.JanusGraphVertex; +import org.janusgraph.graphdb.internal.ElementCategory; import org.janusgraph.graphdb.query.BaseQuery; import org.janusgraph.graphdb.query.JanusGraphPredicate; +import org.janusgraph.graphdb.query.graph.GraphCentricQuery; import org.janusgraph.graphdb.query.graph.GraphCentricQueryBuilder; import org.janusgraph.graphdb.query.profile.QueryProfiler; import org.janusgraph.graphdb.tinkerpop.profile.TP3ProfileWrapper; +import org.janusgraph.graphdb.util.MultiDistinctOrderedIterator; +import org.javatuples.Triplet; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + import org.apache.tinkerpop.gremlin.process.traversal.Order; import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder; import org.apache.tinkerpop.gremlin.process.traversal.step.Profiling; @@ -36,7 +48,10 @@ import java.util.Arrays; import java.util.Collections; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; /** * @author Matthias Broecheler (me@matthiasb.com) @@ -44,7 +59,9 @@ public class JanusGraphStep extends GraphStep implements HasStepFolder, Profiling, HasContainerHolder { private final List hasContainers = new ArrayList<>(); - private int limit = BaseQuery.NO_LIMIT; + private final Map, QueryInfo> hasLocalContainers = new LinkedHashMap<>(); + private int lowLimit = 0; + private int highLimit = BaseQuery.NO_LIMIT; private final List orders = new ArrayList<>(); private QueryProfiler queryProfiler = QueryProfiler.NO_OP; @@ -60,22 +77,116 @@ else if (this.ids.length > 0) { final Graph graph = (Graph)traversal.asAdmin().getGraph().get(); return iteratorList((Iterator)graph.vertices(this.ids)); } - JanusGraphTransaction tx = JanusGraphTraversalUtil.getTx(traversal); - JanusGraphQuery query = tx.query(); - for (HasContainer condition : hasContainers) { - query.has(condition.getKey(), JanusGraphPredicate.Converter.convert(condition.getBiPredicate()), condition.getValue()); + if (hasLocalContainers.isEmpty()) { + hasLocalContainers.put(new ArrayList<>(), new QueryInfo(new ArrayList<>(), 0, BaseQuery.NO_LIMIT)); + } + final JanusGraphTransaction tx = JanusGraphTraversalUtil.getTx(traversal); + final GraphCentricQuery globalQuery = buildGlobalGraphCentricQuery(tx); + + final Multimap queries = ArrayListMultimap.create(); + if (globalQuery != null && !globalQuery.getSubQuery(0).getBackendQuery().isEmpty()) { + queries.put(0, globalQuery); + } else { + hasLocalContainers.entrySet().forEach(c -> queries.put(c.getValue().getLowLimit(), buildGraphCentricQuery(tx, c))); } - for (OrderEntry order : orders) query.orderBy(order.key, order.order); - if (limit != BaseQuery.NO_LIMIT) query.limit(limit); - ((GraphCentricQueryBuilder) query).profiler(queryProfiler); - return Vertex.class.isAssignableFrom(this.returnClass) ? query.vertices().iterator() : query.edges().iterator(); + + final GraphCentricQueryBuilder builder = (GraphCentricQueryBuilder) tx.query(); + final List> responses = new ArrayList<>(); + queries.entries().forEach(q -> executeGraphCentryQuery(builder, responses, q)); + + return new MultiDistinctOrderedIterator(lowLimit, highLimit, responses, orders); }); } + private GraphCentricQuery buildGlobalGraphCentricQuery(final JanusGraphTransaction tx) { + //If a query have a local offset or have a local order without a global order and if a query have a limit lower than the global different from other query we can not build globalquery + final Iterator itQueryInfo = hasLocalContainers.values().iterator(); + QueryInfo queryInfo = itQueryInfo.next(); + if (queryInfo.getLowLimit() > 0 || orders.isEmpty() && !queryInfo.getOrders().isEmpty()) { + return null; + } + final Integer limit = queryInfo.getHighLimit(); + while (itQueryInfo.hasNext()) { + queryInfo = itQueryInfo.next(); + if (queryInfo.getLowLimit() > 0 || (orders.isEmpty() && !queryInfo.getOrders().isEmpty()) || (queryInfo.getHighLimit() < highLimit && !limit.equals(queryInfo.getHighLimit()))) { + return null; + } + } + final JanusGraphQuery query = tx.query(); + for(final List localContainers : hasLocalContainers.keySet()) { + final JanusGraphQuery localQuery = tx.query(); + addConstraint(localQuery, localContainers); + query.or(localQuery); + } + for (final OrderEntry order : orders) query.orderBy(order.key, order.order); + if (highLimit != BaseQuery.NO_LIMIT || limit != BaseQuery.NO_LIMIT) query.limit(Math.min(limit, highLimit)); + Preconditions.checkArgument(query instanceof GraphCentricQueryBuilder); + final GraphCentricQueryBuilder centricQueryBuilder = ((GraphCentricQueryBuilder) query); + centricQueryBuilder.profiler(queryProfiler); + final GraphCentricQuery graphCentricQuery = centricQueryBuilder.constructQuery(Vertex.class.isAssignableFrom(this.returnClass) ? ElementCategory.VERTEX: ElementCategory.EDGE); + return graphCentricQuery; + } + + private void addConstraint(final JanusGraphQuery query, final List localContainers) { + for (final HasContainer condition : hasContainers) { + query.has(condition.getKey(), JanusGraphPredicate.Converter.convert(condition.getBiPredicate()), condition.getValue()); + } + for (final HasContainer condition : localContainers) { + query.has(condition.getKey(), JanusGraphPredicate.Converter.convert(condition.getBiPredicate()), condition.getValue()); + } + } + + private GraphCentricQuery buildGraphCentricQuery(final JanusGraphTransaction tx, + final Entry, QueryInfo> containers) { + final JanusGraphQuery query = tx.query(); + addConstraint(query, containers.getKey()); + final List realOrders = orders.isEmpty() ? containers.getValue().getOrders() : orders; + for (final OrderEntry order : realOrders) query.orderBy(order.key, order.order); + if (highLimit != BaseQuery.NO_LIMIT || containers.getValue().getHighLimit() != BaseQuery.NO_LIMIT) query.limit(Math.min(containers.getValue().getHighLimit(), highLimit)); + Preconditions.checkArgument(query instanceof GraphCentricQueryBuilder); + final GraphCentricQueryBuilder centricQueryBuilder = ((GraphCentricQueryBuilder) query); + centricQueryBuilder.profiler(queryProfiler); + final GraphCentricQuery graphCentricQuery = centricQueryBuilder.constructQuery(Vertex.class.isAssignableFrom(this.returnClass) ? ElementCategory.VERTEX: ElementCategory.EDGE); + return graphCentricQuery; + } + + private void executeGraphCentryQuery(final GraphCentricQueryBuilder builder, final List> responses, + final Entry query) { + final Class classe = Vertex.class.isAssignableFrom(this.returnClass) ? JanusGraphVertex.class: JanusGraphEdge.class; + final Iterator response = (Iterator) builder.iterables(query.getValue(), classe).iterator(); + long i = 0; + while (i < query.getKey() && response.hasNext()) { + response.next(); + i++; + } + responses.add(response); + } + @Override public String toString() { - return this.hasContainers.isEmpty() ? - super.toString() : StringFactory.stepString(this, Arrays.toString(this.ids), this.hasContainers, this.orders); + if (hasLocalContainers.isEmpty() && hasContainers.isEmpty()){ + return super.toString(); + } + if (hasLocalContainers.isEmpty()) { + return StringFactory.stepString(this, Arrays.toString(this.ids), hasContainers); + } + if (hasLocalContainers.size() == 1){ + final List containers = new ArrayList<>(hasContainers); + containers.addAll(hasLocalContainers.keySet().iterator().next()); + return StringFactory.stepString(this, Arrays.toString(this.ids), containers); + } + final StringBuilder sb = new StringBuilder(""); + if (!hasContainers.isEmpty()) { + sb.append(StringFactory.stepString(this, Arrays.toString(ids), hasContainers)).append("."); + } + sb.append("Or("); + final Iterator> itContainers = this.hasLocalContainers.keySet().iterator(); + sb.append(StringFactory.stepString(this, Arrays.toString(this.ids), itContainers.next())); + while(itContainers.hasNext()){ + sb.append(",").append(StringFactory.stepString(this, Arrays.toString(this.ids), itContainers.next())); + } + sb.append(")"); + return sb.toString(); } @Override @@ -83,19 +194,52 @@ public void addAll(Iterable has) { HasStepFolder.splitAndP(hasContainers, has); } + @Override + public List addLocalAll(Iterable has) { + final List containers = HasStepFolder.splitAndP(new ArrayList<>(), has); + hasLocalContainers.put(containers, new QueryInfo(new ArrayList<>(), 0, BaseQuery.NO_LIMIT)); + return containers; + } + @Override public void orderBy(String key, Order order) { orders.add(new OrderEntry(key, order)); } @Override - public void setLimit(int limit) { - this.limit = limit; + public void localOrderBy(List containers, String key, Order order) { + hasLocalContainers.get(containers).getOrders().add(new OrderEntry(key, order)); + } + + @Override + public void setLimit(int low, int high) { + this.lowLimit = low; + this.highLimit = high; + } + + @Override + public void setLocalLimit(List containers, int low, int high) { + hasLocalContainers.replace(containers, hasLocalContainers.get(containers).setLowLimit(low).setHighLimit(high)); } @Override - public int getLimit() { - return this.limit; + public int getLowLimit() { + return this.lowLimit; + } + + @Override + public int getLocalLowLimit(List containers) { + return hasLocalContainers.get(containers).getLowLimit(); + } + + @Override + public int getHighLimit() { + return this.highLimit; + } + + @Override + public int getLocalHighLimit(List containers) { + return hasLocalContainers.get(containers).getHighLimit(); } @Override @@ -105,7 +249,9 @@ public void setMetrics(MutableMetrics metrics) { @Override public List getHasContainers() { - return this.hasContainers; + final List toReturn = new ArrayList<>(this.hasContainers); + this.hasLocalContainers.keySet().stream().forEach(l -> l.stream().forEach(toReturn::add)); + return toReturn; } @Override @@ -113,11 +259,15 @@ public void addHasContainer(final HasContainer hasContainer) { this.addAll(Collections.singleton(hasContainer)); } + public List getOrders() { + return orders; + } + private Iterator iteratorList(final Iterator iterator) { final List list = new ArrayList<>(); while (iterator.hasNext()) { final A e = iterator.next(); - if (HasContainer.testAll(e, this.hasContainers)) + if (HasContainer.testAll(e, this.getHasContainers())) list.add(e); } return list.iterator(); @@ -126,8 +276,10 @@ private Iterator iteratorList(final Iterator iterator) @Override public int hashCode() { int result = super.hashCode(); - result = 31 * result + (hasContainers != null ? hasContainers.hashCode() : 0); - result = 31 * result + limit; + result = 31 * result + (hasContainers != null ? this.hasContainers.hashCode() : 0); + result = 31 * result + (hasLocalContainers != null ? this.hasLocalContainers.hashCode() : 0); + result = 31 * result + lowLimit; + result = 31 * result + highLimit; result = 31 * result + (orders != null ? orders.hashCode() : 0); return result; } diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphStepStrategy.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphStepStrategy.java index ba7fac1d909..52a0bc8bad8 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphStepStrategy.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphStepStrategy.java @@ -44,12 +44,12 @@ public void apply(final Traversal.Admin traversal) { final JanusGraphStep janusGraphStep = new JanusGraphStep<>(originalGraphStep); TraversalHelper.replaceStep(originalGraphStep, janusGraphStep, traversal); HasStepFolder.foldInIds(janusGraphStep, traversal); - HasStepFolder.foldInHasContainer(janusGraphStep, traversal); - HasStepFolder.foldInOrder(janusGraphStep, traversal, traversal, janusGraphStep.returnsVertex()); - HasStepFolder.foldInRange(janusGraphStep, traversal); + HasStepFolder.foldInHasContainer(janusGraphStep, traversal, traversal); + HasStepFolder.foldInOrder(janusGraphStep, janusGraphStep.getNextStep(), traversal, traversal, janusGraphStep.returnsVertex(), null); + HasStepFolder.foldInRange(janusGraphStep, JanusGraphTraversalUtil.getNextNonIdentityStep(janusGraphStep), traversal, null); } else { //Make sure that any provided "start" elements are instantiated in the current transaction - Object[] ids = originalGraphStep.getIds(); + final Object[] ids = originalGraphStep.getIds(); ElementUtils.verifyArgsMustBeEitherIdOrElement(ids); if (ids[0] instanceof Element) { //GraphStep constructor ensures that the entire array is elements @@ -62,6 +62,7 @@ public void apply(final Traversal.Admin traversal) { ((Graph) originalGraphStep.getTraversal().getGraph().get()).edges(elementIds)); } } + }); } diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphVertexStep.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphVertexStep.java index d26af09b546..b05f0416eaf 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphVertexStep.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/JanusGraphVertexStep.java @@ -41,6 +41,8 @@ import java.util.List; import java.util.Map; +import com.google.common.base.Preconditions; + /** * @author Matthias Broecheler (me@matthiasb.com) */ @@ -66,10 +68,10 @@ public void setUseMultiQuery(boolean useMultiQuery) { public Q makeQuery(Q query) { query.labels(getEdgeLabels()); query.direction(getDirection()); - for (HasContainer condition : hasContainers) { + for (final HasContainer condition : hasContainers) { query.has(condition.getKey(), JanusGraphPredicate.Converter.convert(condition.getBiPredicate()), condition.getValue()); } - for (OrderEntry order : orders) query.orderBy(order.key, order.order); + for (final OrderEntry order : orders) query.orderBy(order.key, order.order); if (limit != BaseQuery.NO_LIMIT) query.limit(limit); ((BasicVertexCentricQueryBuilder) query).profiler(queryProfiler); return query; @@ -81,8 +83,8 @@ private void initialize() { initialized = true; if (useMultiQuery) { if (!starts.hasNext()) throw FastNoSuchElementException.instance(); - JanusGraphMultiVertexQuery multiQuery = JanusGraphTraversalUtil.getTx(traversal).multiQuery(); - List> vertices = new ArrayList<>(); + final JanusGraphMultiVertexQuery multiQuery = JanusGraphTraversalUtil.getTx(traversal).multiQuery(); + final List> vertices = new ArrayList<>(); starts.forEachRemaining(v -> { vertices.add(v); multiQuery.addVertex(v.get()); @@ -107,7 +109,7 @@ protected Iterator flatMap(final Traverser.Admin traverser) { assert multiQueryResults != null; return (Iterator) multiQueryResults.get(traverser.get()).iterator(); } else { - JanusGraphVertexQuery query = makeQuery((JanusGraphTraversalUtil.getJanusGraphVertex(traverser)).query()); + final JanusGraphVertexQuery query = makeQuery((JanusGraphTraversalUtil.getJanusGraphVertex(traverser)).query()); return (Vertex.class.isAssignableFrom(getReturnClass())) ? query.vertices().iterator() : query.edges().iterator(); } } @@ -133,27 +135,57 @@ public JanusGraphVertexStep clone() { private int limit; private final List orders = new ArrayList<>(); - @Override public void addAll(Iterable has) { HasStepFolder.splitAndP(hasContainers, has); } + @Override + public List addLocalAll(Iterable has) { + throw new UnsupportedOperationException("addLocalAll is not supported for graph vertex step."); + } + @Override public void orderBy(String key, Order order) { orders.add(new OrderEntry(key, order)); } @Override - public void setLimit(int limit) { - this.limit = limit; + public void localOrderBy(List hasContainers, String key, Order order) { + throw new UnsupportedOperationException("localOrderBy is not supported for graph vertex step."); + } + + @Override + public void setLimit(int low, int high) { + Preconditions.checkArgument(low == 0, "Offset is not supported for properties step."); + this.limit = high; + } + + @Override + public void setLocalLimit(List hasContainers, int low, int high) { + throw new UnsupportedOperationException("setLocalLimit is not supported for graph vertex step."); + } + + @Override + public int getLowLimit() { + throw new UnsupportedOperationException("getLowLimit is not supported for properties step."); + } + + @Override + public int getLocalLowLimit(List hasContainers) { + throw new UnsupportedOperationException("getLocalLowLimit is not supported for properties step."); } @Override - public int getLimit() { + public int getHighLimit() { return this.limit; } + @Override + public int getLocalHighLimit(List hasContainers) { + throw new UnsupportedOperationException("getLocalHighLimit is not supported for graph vertex step."); + } + @Override public String toString() { return this.hasContainers.isEmpty() ? super.toString() : StringFactory.stepString(this, this.hasContainers); diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/QueryInfo.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/QueryInfo.java new file mode 100644 index 00000000000..e705ccd8afe --- /dev/null +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/tinkerpop/optimize/QueryInfo.java @@ -0,0 +1,61 @@ +// Copyright 2017 JanusGraph Authors +// +// Licensed 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.janusgraph.graphdb.tinkerpop.optimize; + +import java.util.List; + +import org.janusgraph.graphdb.tinkerpop.optimize.HasStepFolder.OrderEntry; + +/** + * + * @author David Clement (david.clement90@laposte.net) + * + */ +public class QueryInfo { + + private final List orders; + + private Integer lowLimit; + + private Integer highLimit; + + public QueryInfo(List orders, Integer lowLimit, Integer highLimit) { + this.orders = orders; + this.lowLimit = lowLimit; + this.highLimit = highLimit; + } + + public List getOrders() { + return orders; + } + + public Integer getLowLimit() { + return lowLimit; + } + + public Integer getHighLimit() { + return highLimit; + } + + public QueryInfo setLowLimit(Integer lowLimit) { + this.lowLimit = lowLimit; + return this; + } + + public QueryInfo setHighLimit(Integer highLimit) { + this.highLimit = highLimit; + return this; + } +} diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/util/MultiDistinctOrderedIterator.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/util/MultiDistinctOrderedIterator.java new file mode 100644 index 00000000000..04b6ebb58fc --- /dev/null +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/util/MultiDistinctOrderedIterator.java @@ -0,0 +1,85 @@ +package org.janusgraph.graphdb.util; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.TreeMap; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import org.apache.tinkerpop.gremlin.process.traversal.step.util.ElementValueComparator; +import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.util.function.MultiComparator; +import org.janusgraph.graphdb.tinkerpop.optimize.HasStepFolder.OrderEntry; + +import com.google.common.collect.Iterators; + +public class MultiDistinctOrderedIterator implements Iterator { + + private final Map> iterators = new LinkedHashMap<>(); + private final Map values = new LinkedHashMap<>(); + private final TreeMap currentElements; + private final Set allElements = new HashSet<>(); + private final Integer limit; + private long count = 0; + + public MultiDistinctOrderedIterator(final Integer lowLimit, final Integer highLimit, final List> iterators, final List orders) { + this.limit = highLimit; + Comparator comparator = null; + if (orders.isEmpty()) { + final Stream stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(Iterators.concat(iterators.iterator()), Spliterator.ORDERED), false); + this.iterators.put(0, stream.iterator()); + } else { + final List> comp = new ArrayList<>(); + orders.forEach(o -> comp.add(new ElementValueComparator(o.key, o.order))); + comparator = new MultiComparator<>(comp); + for (int i = 0; i < iterators.size(); i++) { + this.iterators.put(i, iterators.get(i)); + } + } + currentElements = new TreeMap<>(comparator); + long i = 0; + while (i < lowLimit && this.hasNext()) { + this.next(); + i++; + } + } + + @Override + public boolean hasNext() { + if (limit != null && count >= limit) { + return false; + } + for (int i = 0; i < iterators.size(); i++) { + if (!values.containsKey(i) && iterators.get(i).hasNext()){ + E element = null; + do { + element = iterators.get(i).next(); + if (allElements.contains(element)) { + element = null; + } + } while (element == null && iterators.get(i).hasNext()); + if (element != null) { + values.put(i, element); + currentElements.put(element, i); + allElements.add(element); + } + } + } + return !values.isEmpty(); + } + + @Override + public E next() { + count++; + return values.remove(currentElements.remove(currentElements.firstKey())); + } + +} diff --git a/janusgraph-es/src/main/java/org/janusgraph/diskstorage/es/compat/AbstractESCompat.java b/janusgraph-es/src/main/java/org/janusgraph/diskstorage/es/compat/AbstractESCompat.java index a08138389ac..ad5db2f4610 100644 --- a/janusgraph-es/src/main/java/org/janusgraph/diskstorage/es/compat/AbstractESCompat.java +++ b/janusgraph-es/src/main/java/org/janusgraph/diskstorage/es/compat/AbstractESCompat.java @@ -55,6 +55,7 @@ static IndexFeatures.Builder coreFeatures() { .supportsCardinality(Cardinality.SET) .supportsNanoseconds() .supportsCustomAnalyzer() + .supportNotQueryNormalForm() ; } @@ -180,7 +181,7 @@ public Map createRequestBody(ElasticSearchRequest request, Parame if (!request.getSorts().isEmpty()) { requestBody.put("sort", request.getSorts()); } - + Optional.ofNullable(request.getQuery()).ifPresent(parameter -> requestBody.put("query", parameter)); Optional.ofNullable(parameters).ifPresent(p -> Arrays.stream(p).forEachOrdered(parameter -> requestBody.put(parameter.key(), parameter.value()))); return requestBody; diff --git a/janusgraph-lucene/src/main/java/org/janusgraph/diskstorage/lucene/LuceneIndex.java b/janusgraph-lucene/src/main/java/org/janusgraph/diskstorage/lucene/LuceneIndex.java index d5150b02424..9e43d8ba12f 100644 --- a/janusgraph-lucene/src/main/java/org/janusgraph/diskstorage/lucene/LuceneIndex.java +++ b/janusgraph-lucene/src/main/java/org/janusgraph/diskstorage/lucene/LuceneIndex.java @@ -92,6 +92,7 @@ public class LuceneIndex implements IndexProvider { .supportsCustomAnalyzer() .supportsNanoseconds() .supportsGeoContains() + .supportNotQueryNormalForm() .build(); /** @@ -454,6 +455,7 @@ public Stream query(IndexQuery query, KeyInformation.IndexRetriever info final SearchParams searchParams = convertQuery(query.getCondition(), information.get(store), delegatingAnalyzer); try { +<<<<<<< HEAD final IndexSearcher searcher = ((Transaction) tx).getSearcher(query.getStore()); if (searcher == null) { return Collections.unmodifiableList(new ArrayList()).stream(); //Index does not yet exist @@ -821,8 +823,8 @@ public IndexFeatures getFeatures() { @Override public void close() throws BackendException { try { - for (IndexWriter w : writers.values()) w.close(); - } catch (IOException e) { + for (final IndexWriter w : writers.values()) w.close(); + } catch (final IOException e) { throw new PermanentBackendException("Could not close writers", e); } } diff --git a/janusgraph-solr/src/test/resources/collections.txt b/janusgraph-solr/src/test/resources/collections.txt index 26dd6017490..8b8a89f19c0 100644 --- a/janusgraph-solr/src/test/resources/collections.txt +++ b/janusgraph-solr/src/test/resources/collections.txt @@ -1 +1 @@ -store1 store2 vertex edge namev namee composite psearch esearch vsearch mi mixed index1 index2 index3 ecategory vcategory pcategory theIndex vertices edges booleanIndex dateIndex instantIndex uuidIndex randomMixedIndex collectionIndex +store1 store2 vertex edge namev namee composite psearch esearch vsearch mi mixed index1 index2 index3 ecategory vcategory pcategory theIndex vertices edges booleanIndex dateIndex instantIndex uuidIndex randomMixedIndex collectionIndex nameidx oridx otheridx lengthidx diff --git a/janusgraph-test/src/main/java/org/janusgraph/graphdb/JanusGraphBaseTest.java b/janusgraph-test/src/main/java/org/janusgraph/graphdb/JanusGraphBaseTest.java index c0b91999ac7..00eb1aac662 100644 --- a/janusgraph-test/src/main/java/org/janusgraph/graphdb/JanusGraphBaseTest.java +++ b/janusgraph-test/src/main/java/org/janusgraph/graphdb/JanusGraphBaseTest.java @@ -81,11 +81,11 @@ public static void clearGraph(WriteConfiguration config) throws BackendException getBackend(config, true).clearStorage(); } - public static Backend getBackend(WriteConfiguration config, boolean initialize) { - ModifiableConfiguration adjustedConfig = new ModifiableConfiguration(GraphDatabaseConfiguration.ROOT_NS,config.copy(), BasicConfiguration.Restriction.NONE); + public static Backend getBackend(WriteConfiguration config, boolean initialize) throws BackendException { + final ModifiableConfiguration adjustedConfig = new ModifiableConfiguration(GraphDatabaseConfiguration.ROOT_NS,config.copy(), BasicConfiguration.Restriction.NONE); adjustedConfig.set(GraphDatabaseConfiguration.LOCK_LOCAL_MEDIATOR_GROUP, "tmp"); adjustedConfig.set(GraphDatabaseConfiguration.UNIQUE_INSTANCE_ID, "inst"); - Backend backend = new Backend(adjustedConfig); + final Backend backend = new Backend(adjustedConfig); if (initialize) { backend.initialize(adjustedConfig); } @@ -144,7 +144,7 @@ public void newTx() { public static Map validateConfigOptions(Object... settings) { //Parse settings Preconditions.checkArgument(settings.length%2==0,"Expected even number of settings: %s",settings); - Map options = Maps.newHashMap(); + final Map options = Maps.newHashMap(); for (int i=0;i0) { - Map options = validateConfigOptions(settings); + final Map options = validateConfigOptions(settings); JanusGraphManagement janusGraphManagement = null; - ModifiableConfiguration modifiableConfiguration = new ModifiableConfiguration(GraphDatabaseConfiguration.ROOT_NS,config, BasicConfiguration.Restriction.LOCAL); - for (Map.Entry option : options.entrySet()) { + final ModifiableConfiguration modifiableConfiguration = new ModifiableConfiguration(GraphDatabaseConfiguration.ROOT_NS,config, BasicConfiguration.Restriction.LOCAL); + for (final Map.Entry option : options.entrySet()) { if (option.getKey().option.isLocal()) { modifiableConfiguration.set(option.getKey().option,option.getValue(),option.getKey().umbrella); } else { @@ -206,13 +206,13 @@ public TestConfigOption(ConfigOption option, String... umbrella) { private void closeLogs() { try { - for (LogManager lm : logManagers.values()) lm.close(); + for (final LogManager lm : logManagers.values()) lm.close(); logManagers.clear(); if (logStoreManager!=null) { logStoreManager.close(); logStoreManager=null; } - } catch (BackendException e) { + } catch (final BackendException e) { throw new JanusGraphException(e); } } @@ -221,7 +221,7 @@ public void closeLogManager(String logManagerName) { if (logManagers.containsKey(logManagerName)) { try { logManagers.remove(logManagerName).close(); - } catch (BackendException e) { + } catch (final BackendException e) { throw new JanusGraphException("Could not close log manager " + logManagerName,e); } } @@ -237,28 +237,28 @@ public Log openTxLog() { private Log openLog(String logManagerName, String logName) { try { - ModifiableConfiguration configuration = new ModifiableConfiguration(GraphDatabaseConfiguration.ROOT_NS,config.copy(), BasicConfiguration.Restriction.NONE); + final ModifiableConfiguration configuration = new ModifiableConfiguration(GraphDatabaseConfiguration.ROOT_NS,config.copy(), BasicConfiguration.Restriction.NONE); configuration.set(GraphDatabaseConfiguration.UNIQUE_INSTANCE_ID, "reader"); configuration.set(GraphDatabaseConfiguration.LOG_READ_INTERVAL, Duration.ofMillis(500L), logManagerName); if (logStoreManager==null) { logStoreManager = Backend.getStorageManager(configuration); } - StoreFeatures f = logStoreManager.getFeatures(); - boolean part = f.isDistributed() && f.isKeyOrdered(); + final StoreFeatures f = logStoreManager.getFeatures(); + final boolean part = f.isDistributed() && f.isKeyOrdered(); if (part) { - for (String partitionedLogName : new String[]{USER_LOG,TRANSACTION_LOG,MANAGEMENT_LOG}) + for (final String partitionedLogName : new String[]{USER_LOG,TRANSACTION_LOG,MANAGEMENT_LOG}) configuration.set(KCVSLogManager.LOG_MAX_PARTITIONS,8,partitionedLogName); } assert logStoreManager!=null; if (!logManagers.containsKey(logManagerName)) { //Open log manager - only supports KCVSLog - Configuration logConfig = configuration.restrictTo(logManagerName); + final Configuration logConfig = configuration.restrictTo(logManagerName); Preconditions.checkArgument(logConfig.get(LOG_BACKEND).equals(LOG_BACKEND.getDefaultValue())); logManagers.put(logManagerName,new KCVSLogManager(logStoreManager,logConfig)); } assert logManagers.containsKey(logManagerName); return logManagers.get(logManagerName).openLog(logName); - } catch (BackendException e) { + } catch (final BackendException e) { throw new JanusGraphException("Could not open log: "+ logName,e); } } @@ -268,13 +268,13 @@ private Log openLog(String logManagerName, String logName) { */ public PropertyKey makeVertexIndexedKey(String name, Class dataType) { - PropertyKey key = mgmt.makePropertyKey(name).dataType(dataType).cardinality(Cardinality.SINGLE).make(); + final PropertyKey key = mgmt.makePropertyKey(name).dataType(dataType).cardinality(Cardinality.SINGLE).make(); mgmt.buildIndex(name,Vertex.class).addKey(key).buildCompositeIndex(); return key; } public PropertyKey makeVertexIndexedUniqueKey(String name, Class dataType) { - PropertyKey key = mgmt.makePropertyKey(name).dataType(dataType).cardinality(Cardinality.SINGLE).make(); + final PropertyKey key = mgmt.makePropertyKey(name).dataType(dataType).cardinality(Cardinality.SINGLE).make(); mgmt.buildIndex(name,Vertex.class).addKey(key).unique().buildCompositeIndex(); return key; } @@ -294,7 +294,7 @@ public JanusGraphIndex getExternalIndex(Class clazz, String b else if (JanusGraphVertexProperty.class.isAssignableFrom(clazz)) prefix = "p"; else throw new AssertionError(clazz.toString()); - String indexName = prefix+backingIndex; + final String indexName = prefix+backingIndex; JanusGraphIndex index = mgmt.getGraphIndex(indexName); if (index==null) { index = mgmt.buildIndex(indexName,clazz).buildMixedIndex(backingIndex); @@ -325,7 +325,7 @@ public EdgeLabel makeKeyedEdgeLabel(String name, PropertyKey sort, PropertyKey s public static final int DEFAULT_THREAD_COUNT = 4; public static int getThreadCount() { - String s = System.getProperty("janusgraph.test.threads"); + final String s = System.getProperty("janusgraph.test.threads"); if (null != s) return Integer.valueOf(s); else @@ -381,7 +381,7 @@ public static E getOnlyElement(Iterable traversal, E defaultElement) { public static E getOnlyElement(Iterator traversal, E defaultElement) { if (!traversal.hasNext()) return defaultElement; - E result = traversal.next(); + final E result = traversal.next(); if (traversal.hasNext()) throw new IllegalArgumentException("Traversal contains more than 1 element: " + result + ", " + traversal.next()); return result; } @@ -429,10 +429,10 @@ public static void verifyElementOrder(Iterator elements, Stri Comparable previous = null; int count = 0; while (elements.hasNext()) { - Element element = elements.next(); - Comparable current = element.value(key); + final Element element = elements.next(); + final Comparable current = element.value(key); if (previous != null) { - int cmp = previous.compareTo(current); + final int cmp = previous.compareTo(current); assertTrue(previous + " <> " + current + " @ " + count, order == Order.ASC ? cmp <= 0 : cmp >= 0); } @@ -447,4 +447,13 @@ public static Stream asStream(final Iterator source) { return StreamSupport.stream(iterable.spliterator(),false); } + public JanusGraph getForceIndexGraph() throws BackendException { + final ModifiableConfiguration adjustedConfig = new ModifiableConfiguration(GraphDatabaseConfiguration.ROOT_NS,getConfiguration(), BasicConfiguration.Restriction.NONE); + adjustedConfig.set(GraphDatabaseConfiguration.FORCE_INDEX_USAGE, true); + final WriteConfiguration writeConfig = adjustedConfig.getConfiguration(); + TestGraphConfigs.applyOverrides(writeConfig); + Preconditions.checkNotNull(writeConfig); + return JanusGraphFactory.open(writeConfig); + } + } diff --git a/janusgraph-test/src/main/java/org/janusgraph/graphdb/JanusGraphIndexTest.java b/janusgraph-test/src/main/java/org/janusgraph/graphdb/JanusGraphIndexTest.java index a41bd24d58b..f9d8a3b8fa7 100644 --- a/janusgraph-test/src/main/java/org/janusgraph/graphdb/JanusGraphIndexTest.java +++ b/janusgraph-test/src/main/java/org/janusgraph/graphdb/JanusGraphIndexTest.java @@ -40,10 +40,12 @@ import org.janusgraph.core.schema.SchemaAction; import org.janusgraph.core.schema.SchemaStatus; import org.janusgraph.core.schema.JanusGraphIndex; +import org.janusgraph.core.schema.JanusGraphManagement; import org.janusgraph.core.util.ManagementUtil; import org.janusgraph.diskstorage.Backend; import org.janusgraph.diskstorage.BackendException; import org.janusgraph.diskstorage.configuration.ConfigElement; +import org.janusgraph.diskstorage.configuration.ModifiableConfiguration; import org.janusgraph.diskstorage.configuration.WriteConfiguration; import org.janusgraph.diskstorage.indexing.IndexFeatures; import org.janusgraph.diskstorage.indexing.IndexInformation; @@ -56,14 +58,21 @@ import org.janusgraph.graphdb.internal.ElementCategory; import org.janusgraph.graphdb.internal.Order; import org.janusgraph.graphdb.log.StandardTransactionLogProcessor; +import org.janusgraph.graphdb.query.condition.Not; import org.janusgraph.graphdb.types.ParameterType; import org.janusgraph.graphdb.types.StandardEdgeLabelMaker; import org.janusgraph.testcategory.BrittleTests; import org.janusgraph.testutil.TestGraphConfigs; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.ElementValueComparator; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.junit.Assert; @@ -101,8 +110,14 @@ /** * @author Matthias Broecheler (me@matthiasb.com) */ +@SuppressWarnings({"rawtypes", "unchecked"}) public abstract class JanusGraphIndexTest extends JanusGraphBaseTest { + private static final ElementValueComparator ORDER_AGE_DECR = new ElementValueComparator("age", org.apache.tinkerpop.gremlin.process.traversal.Order.decr); + private static final ElementValueComparator ORDER_AGE_INCR = new ElementValueComparator("age", org.apache.tinkerpop.gremlin.process.traversal.Order.incr); + private static final ElementValueComparator ORDER_LENGTH_DECR = new ElementValueComparator("length", org.apache.tinkerpop.gremlin.process.traversal.Order.decr); + private static final ElementValueComparator ORDER_LENGTH_INCR = new ElementValueComparator("length", org.apache.tinkerpop.gremlin.process.traversal.Order.incr); + public static final String INDEX = GraphOfTheGodsFactory.INDEX_NAME; public static final String VINDEX = "v" + INDEX; public static final String EINDEX = "e" + INDEX; @@ -175,7 +190,7 @@ public void testGraphOfTheGods() { public static void assertGraphOfTheGods(JanusGraph graphOfTheGods) { assertCount(12, graphOfTheGods.query().vertices()); assertCount(3, graphOfTheGods.query().has(LABEL_NAME, "god").vertices()); - JanusGraphVertex h = getOnlyVertex(graphOfTheGods.query().has("name", "hercules")); + final JanusGraphVertex h = getOnlyVertex(graphOfTheGods.query().has("name", "hercules")); assertEquals(30, h.value("age").intValue()); assertEquals("demigod", h.label()); assertCount(5, h.query().direction(Direction.BOTH).edges()); @@ -194,7 +209,7 @@ public void testClearStorage() throws Exception { final Backend backend = getBackend(config, false); assertStorageExists(backend, true); clearGraph(config); - try { backend.close(); } catch (Exception e) { /* Most backends do not support closing after clearing */} + try { backend.close(); } catch (final Exception e) { /* Most backends do not support closing after clearing */} try (final Backend newBackend = getBackend(config, false)) { assertStorageExists(newBackend, false); } @@ -210,7 +225,7 @@ private static void assertStorageExists(Backend backend, boolean exists) throws @Test public void testSimpleUpdate() { - PropertyKey name = makeKey("name", String.class); + final PropertyKey name = makeKey("name", String.class); makeLabel("knows"); mgmt.buildIndex("namev", Vertex.class).addKey(name).buildMixedIndex(INDEX); mgmt.buildIndex("namee", Edge.class).addKey(name).buildMixedIndex(INDEX); @@ -220,11 +235,11 @@ public void testSimpleUpdate() { Edge e = v.addEdge("knows", v, "name", "Hulu Bubab"); assertCount(1, tx.query().has("name", Text.CONTAINS, "marko").vertices()); assertCount(1, tx.query().has("name", Text.CONTAINS, "Hulu").edges()); - for (Vertex u : tx.getVertices()) assertEquals("Marko Rodriguez", u.value("name")); + for (final Vertex u : tx.getVertices()) assertEquals("Marko Rodriguez", u.value("name")); clopen(); assertCount(1, tx.query().has("name", Text.CONTAINS, "marko").vertices()); assertCount(1, tx.query().has("name", Text.CONTAINS, "Hulu").edges()); - for (Vertex u : tx.getVertices()) assertEquals("Marko Rodriguez", u.value("name")); + for (final Vertex u : tx.getVertices()) assertEquals("Marko Rodriguez", u.value("name")); v = getOnlyVertex(tx.query().has("name", Text.CONTAINS, "marko")); v.property(VertexProperty.Cardinality.single, "name", "Marko"); e = getOnlyEdge(v.query().direction(Direction.OUT)); @@ -232,12 +247,12 @@ public void testSimpleUpdate() { assertCount(1, tx.query().has("name", Text.CONTAINS, "marko").vertices()); assertCount(1, tx.query().has("name", Text.CONTAINS, "Rubu").edges()); assertCount(0, tx.query().has("name", Text.CONTAINS, "Hulu").edges()); - for (Vertex u : tx.getVertices()) assertEquals("Marko", u.value("name")); + for (final Vertex u : tx.getVertices()) assertEquals("Marko", u.value("name")); clopen(); assertCount(1, tx.query().has("name", Text.CONTAINS, "marko").vertices()); assertCount(1, tx.query().has("name", Text.CONTAINS, "Rubu").edges()); assertCount(0, tx.query().has("name", Text.CONTAINS, "Hulu").edges()); - for (Vertex u : tx.getVertices()) assertEquals("Marko", u.value("name")); + for (final Vertex u : tx.getVertices()) assertEquals("Marko", u.value("name")); } @Test @@ -245,11 +260,11 @@ public void testListUpdate() { if (!indexFeatures.supportsCardinality(Cardinality.LIST)) { return; } - PropertyKey name = makeKey("name", String.class); + final PropertyKey name = makeKey("name", String.class); if (!indexFeatures.supportsStringMapping(Mapping.TEXTSTRING)) { - + } - PropertyKey alias = mgmt.makePropertyKey("alias") .dataType(String.class).cardinality(Cardinality.LIST).make(); + final PropertyKey alias = mgmt.makePropertyKey("alias") .dataType(String.class).cardinality(Cardinality.LIST).make(); mgmt.buildIndex("namev", Vertex.class).addKey(name).addKey(alias, indexFeatures.supportsStringMapping(Mapping.TEXTSTRING) ?Mapping.TEXTSTRING.asParameter(): Mapping.DEFAULT.asParameter()).buildMixedIndex(INDEX); finishSchema(); JanusGraphVertex v = tx.addVertex("name", "Marko Rodriguez"); @@ -272,14 +287,14 @@ public void testListUpdate() { assertCount(1, tx.query().has("alias", Cmp.EQUAL, "mRodriguez").vertices()); } } - + @Test public void testSetUpdate() { if (!indexFeatures.supportsCardinality(Cardinality.SET)) { return; } - PropertyKey name = makeKey("name", String.class); - PropertyKey alias = mgmt.makePropertyKey("alias").dataType(String.class).cardinality(Cardinality.SET).make(); + final PropertyKey name = makeKey("name", String.class); + final PropertyKey alias = mgmt.makePropertyKey("alias").dataType(String.class).cardinality(Cardinality.SET).make(); mgmt.buildIndex("namev", Vertex.class).addKey(name).addKey(alias, indexFeatures.supportsStringMapping(Mapping.TEXTSTRING) ?Mapping.TEXTSTRING.asParameter(): Mapping.DEFAULT.asParameter()).buildMixedIndex(INDEX); finishSchema(); JanusGraphVertex v = tx.addVertex("name", "Marko Rodriguez"); @@ -307,7 +322,7 @@ public void testSetUpdate() { @Test public void testIndexing() throws InterruptedException { - PropertyKey text = makeKey("text", String.class); + final PropertyKey text = makeKey("text", String.class); createExternalVertexIndex(text, INDEX); createExternalEdgeIndex(text, INDEX); @@ -315,23 +330,23 @@ public void testIndexing() throws InterruptedException { mgmt.addIndexKey(getExternalIndex(Vertex.class,INDEX),name, Parameter.of("mapping", Mapping.TEXT)); mgmt.addIndexKey(getExternalIndex(Edge.class,INDEX),name, Parameter.of("mapping", Mapping.TEXT)); - PropertyKey location = makeKey("location", Geoshape.class); + final PropertyKey location = makeKey("location", Geoshape.class); createExternalVertexIndex(location, INDEX); createExternalEdgeIndex(location, INDEX); - PropertyKey boundary = makeKey("boundary", Geoshape.class); + final PropertyKey boundary = makeKey("boundary", Geoshape.class); mgmt.addIndexKey(getExternalIndex(Vertex.class,INDEX),boundary, Parameter.of("mapping", Mapping.PREFIX_TREE), Parameter.of("index-geo-dist-error-pct", 0.0025)); mgmt.addIndexKey(getExternalIndex(Edge.class,INDEX),boundary, Parameter.of("mapping", Mapping.PREFIX_TREE), Parameter.of("index-geo-dist-error-pct", 0.0025)); - PropertyKey time = makeKey("time", Long.class); + final PropertyKey time = makeKey("time", Long.class); createExternalVertexIndex(time, INDEX); createExternalEdgeIndex(time, INDEX); - PropertyKey category = makeKey("category", Integer.class); + final PropertyKey category = makeKey("category", Integer.class); mgmt.buildIndex("vcategory", Vertex.class).addKey(category).buildCompositeIndex(); mgmt.buildIndex("ecategory", Edge.class).addKey(category).buildCompositeIndex(); - PropertyKey group = makeKey("group", Byte.class); + final PropertyKey group = makeKey("group", Byte.class); createExternalVertexIndex(group, INDEX); createExternalEdgeIndex(group, INDEX); @@ -340,14 +355,14 @@ public void testIndexing() throws InterruptedException { finishSchema(); clopen(); - String[] words = {"world", "aurelius", "janusgraph", "graph"}; - int numCategories = 5; - int numGroups = 10; - double distance, offset; + final String[] words = {"world", "aurelius", "janusgraph", "graph"}; + final int numCategories = 5; + final int numGroups = 10; + double offset; int numV = 100; final int originalNumV = numV; for (int i = 0; i < numV; i++) { - JanusGraphVertex v = tx.addVertex(); + final JanusGraphVertex v = tx.addVertex(); v.property(VertexProperty.Cardinality.single, "uid", i); v.property(VertexProperty.Cardinality.single, "category", i % numCategories); v.property(VertexProperty.Cardinality.single, "group", i % numGroups); @@ -363,7 +378,7 @@ public void testIndexing() throws InterruptedException { v.property(VertexProperty.Cardinality.single, "boundary", Geoshape.polygon(Arrays.asList(new double[][] {{offset-0.1,offset-0.1},{offset+0.1,offset-0.1},{offset+0.1,offset+0.1},{offset-0.1,offset+0.1},{offset-0.1,offset-0.1}}))); } - Edge e = v.addEdge("knows", getVertex("uid", Math.max(0, i - 1))); + final Edge e = v.addEdge("knows", getVertex("uid", Math.max(0, i - 1))); e.property("text", "Vertex " + words[i % words.length]); e.property("name",words[i % words.length]); e.property("time", i); @@ -389,7 +404,7 @@ public void testIndexing() throws InterruptedException { try { checkIndexingCounts(words, numV, originalNumV, true); status = 0; - } catch (AssertionError e) { + } catch (final AssertionError e) { if (retry >= RETRY_COUNT-1) throw e; Thread.sleep(RETRY_INTERVAL); } @@ -397,7 +412,7 @@ public void testIndexing() throws InterruptedException { newTx(); - int numDelete = 12; + final int numDelete = 12; for (int i = numV - numDelete; i < numV; i++) { getVertex("uid", i).remove(); } @@ -415,9 +430,9 @@ private void checkIndexingCounts(String[] words, int numV, int originalNumV, boo //Test ordering if (checkOrder) { - for (String orderKey : new String[]{"time", "category"}) { - for (Order order : Order.values()) { - for (JanusGraphQuery traversal : ImmutableList.of( + for (final String orderKey : new String[]{"time", "category"}) { + for (final Order order : Order.values()) { + for (final JanusGraphQuery traversal : ImmutableList.of( tx.query().has("text", Text.CONTAINS, word).orderBy(orderKey, order.getTP()), tx.query().has("text", Text.CONTAINS, word).orderBy(orderKey, order.getTP()) )) { @@ -445,8 +460,8 @@ private void checkIndexingCounts(String[] words, int numV, int originalNumV, boo assertCount(4, tx.query().has("category", 1).interval("time", 10, 28).edges()); assertCount(5, tx.query().has("time", Cmp.GREATER_THAN_EQUAL, 10).has("time", Cmp.LESS_THAN, 30).has("text", Text.CONTAINS, words[0]).vertices()); - double offset = (19 * 50.0 / originalNumV); - double distance = Geoshape.point(0.0, 0.0).getPoint().distance(Geoshape.point(offset, offset).getPoint()) + 20; + final double offset = (19 * 50.0 / originalNumV); + final double distance = Geoshape.point(0.0, 0.0).getPoint().distance(Geoshape.point(offset, offset).getPoint()) + 20; assertCount(5, tx.query().has("location", Geo.INTERSECT, Geoshape.circle(0.0, 0.0, distance)).has("text", Text.CONTAINS, words[0]).vertices()); assertCount(5, tx.query().has("boundary", Geo.INTERSECT, Geoshape.circle(0.0, 0.0, distance)).has("text", Text.CONTAINS, words[0]).vertices()); @@ -471,16 +486,16 @@ private void checkIndexingCounts(String[] words, int numV, int originalNumV, boo */ @Test public void testBooleanIndexing() { - PropertyKey name = makeKey("visible", Boolean.class); + final PropertyKey name = makeKey("visible", Boolean.class); mgmt.buildIndex("booleanIndex", Vertex.class). addKey(name).buildMixedIndex(INDEX); finishSchema(); clopen(); - JanusGraphVertex v1 = graph.addVertex(); + final JanusGraphVertex v1 = graph.addVertex(); v1.property("visible", true); - JanusGraphVertex v2 = graph.addVertex(); + final JanusGraphVertex v2 = graph.addVertex(); v2.property("visible", false); assertCount(2, graph.vertices()); @@ -503,16 +518,16 @@ public void testBooleanIndexing() { */ @Test public void testDateIndexing() { - PropertyKey name = makeKey("date", Date.class); + final PropertyKey name = makeKey("date", Date.class); mgmt.buildIndex("dateIndex", Vertex.class). addKey(name).buildMixedIndex(INDEX); finishSchema(); clopen(); - JanusGraphVertex v1 = graph.addVertex(); + final JanusGraphVertex v1 = graph.addVertex(); v1.property("date", new Date(1)); - JanusGraphVertex v2 = graph.addVertex(); + final JanusGraphVertex v2 = graph.addVertex(); v2.property("date", new Date(2000)); @@ -540,18 +555,18 @@ public void testDateIndexing() { */ @Test public void testInstantIndexing() { - PropertyKey name = makeKey("instant", Instant.class); + final PropertyKey name = makeKey("instant", Instant.class); mgmt.buildIndex("instantIndex", Vertex.class). addKey(name).buildMixedIndex(INDEX); finishSchema(); clopen(); Instant firstTimestamp = Instant.ofEpochMilli(1); - Instant secondTimestamp = Instant.ofEpochMilli(2000); + final Instant secondTimestamp = Instant.ofEpochMilli(2000); JanusGraphVertex v1 = graph.addVertex(); v1.property("instant", firstTimestamp); - JanusGraphVertex v2 = graph.addVertex(); + final JanusGraphVertex v2 = graph.addVertex(); v2.property("instant", secondTimestamp); testInstant(firstTimestamp, secondTimestamp, v1, v2); @@ -566,7 +581,7 @@ public void testInstantIndexing() { try { assertEquals(v1, getOnlyVertex(graph.query().has("instant", Cmp.EQUAL, firstTimestamp))); Assert.fail("Should have failed to update the index"); - } catch (Exception ignored) { + } catch (final Exception ignored) { } } @@ -596,19 +611,19 @@ private void testInstant(Instant firstTimestamp, Instant secondTimestamp, JanusG */ @Test public void testUUIDIndexing() { - PropertyKey name = makeKey("uid", UUID.class); + final PropertyKey name = makeKey("uid", UUID.class); mgmt.buildIndex("uuidIndex", Vertex.class). addKey(name).buildMixedIndex(INDEX); finishSchema(); clopen(); - UUID uid1 = UUID.randomUUID(); - UUID uid2 = UUID.randomUUID(); + final UUID uid1 = UUID.randomUUID(); + final UUID uid2 = UUID.randomUUID(); - JanusGraphVertex v1 = graph.addVertex(); + final JanusGraphVertex v1 = graph.addVertex(); v1.property("uid", uid1); - JanusGraphVertex v2 = graph.addVertex(); + final JanusGraphVertex v2 = graph.addVertex(); v2.property("uid", uid2); assertCount(2, graph.query().vertices()); @@ -676,19 +691,19 @@ public void testConditionalIndexing() { //Already exists mgmt.buildIndex("index2", Vertex.class).addKey(weight).buildMixedIndex(INDEX); fail(); - } catch (IllegalArgumentException ignored) { + } catch (final IllegalArgumentException ignored) { } try { //Already exists mgmt.buildIndex("index2", Vertex.class).addKey(weight).buildCompositeIndex(); fail(); - } catch (IllegalArgumentException ignored) { + } catch (final IllegalArgumentException ignored) { } try { //Key is already added mgmt.addIndexKey(index2, weight); fail(); - } catch (IllegalArgumentException ignored) { + } catch (final IllegalArgumentException ignored) { } finishSchema(); @@ -722,19 +737,19 @@ public void testConditionalIndexing() { //Already exists mgmt.buildIndex("index2", Vertex.class).addKey(weight).buildMixedIndex(INDEX); fail(); - } catch (IllegalArgumentException ignored) { + } catch (final IllegalArgumentException ignored) { } try { //Already exists mgmt.buildIndex("index2", Vertex.class).addKey(weight).buildCompositeIndex(); fail(); - } catch (IllegalArgumentException ignored) { + } catch (final IllegalArgumentException ignored) { } try { //Key is already added mgmt.addIndexKey(index2, weight); fail(); - } catch (IllegalArgumentException ignored) { + } catch (final IllegalArgumentException ignored) { } @@ -743,14 +758,14 @@ public void testConditionalIndexing() { final int numV = 200; - String[] strings = {"houseboat", "humanoid", "differential", "extraordinary"}; - String[] stringsTwo = new String[strings.length]; + final String[] strings = {"houseboat", "humanoid", "differential", "extraordinary"}; + final String[] stringsTwo = new String[strings.length]; for (int i = 0; i < strings.length; i++) stringsTwo[i] = strings[i] + " " + strings[i]; final int modulo = 5; assert numV % (modulo * strings.length * 2) == 0; for (int i = 0; i < numV; i++) { - JanusGraphVertex v = tx.addVertex(i % 2 == 0 ? "person" : "org"); + final JanusGraphVertex v = tx.addVertex(i % 2 == 0 ? "person" : "org"); v.property("name", strings[i % strings.length]); v.property("text", strings[i % strings.length]); v.property("weight", (i % modulo) + 0.5); @@ -816,27 +831,27 @@ public void testConditionalIndexing() { @Test public void testCompositeAndMixedIndexing() { - PropertyKey name = makeKey("name", String.class); - PropertyKey weight = makeKey("weight", Double.class); - PropertyKey text = makeKey("text", String.class); + final PropertyKey name = makeKey("name", String.class); + final PropertyKey weight = makeKey("weight", Double.class); + final PropertyKey text = makeKey("text", String.class); makeKey("flag", Boolean.class); - JanusGraphIndex composite = mgmt.buildIndex("composite", Vertex.class).addKey(name).addKey(weight).buildCompositeIndex(); - JanusGraphIndex mixed = mgmt.buildIndex("mixed", Vertex.class).addKey(weight) + final JanusGraphIndex composite = mgmt.buildIndex("composite", Vertex.class).addKey(name).addKey(weight).buildCompositeIndex(); + final JanusGraphIndex mixed = mgmt.buildIndex("mixed", Vertex.class).addKey(weight) .addKey(text, getTextMapping()).buildMixedIndex(INDEX); mixed.name(); composite.name(); finishSchema(); final int numV = 100; - String[] strings = {"houseboat", "humanoid", "differential", "extraordinary"}; - String[] stringsTwo = new String[strings.length]; + final String[] strings = {"houseboat", "humanoid", "differential", "extraordinary"}; + final String[] stringsTwo = new String[strings.length]; for (int i = 0; i < strings.length; i++) stringsTwo[i] = strings[i] + " " + strings[i]; final int modulo = 5; final int divisor = modulo * strings.length; for (int i = 0; i < numV; i++) { - JanusGraphVertex v = tx.addVertex(); + final JanusGraphVertex v = tx.addVertex(); v.property("name", strings[i % strings.length]); v.property("text", strings[i % strings.length]); v.property("weight", (i % modulo) + 0.5); @@ -889,15 +904,15 @@ public void testCompositeAndMixedIndexing() { private void setupChainGraph(int numV, String[] strings, boolean sameNameMapping) { clopen(option(INDEX_NAME_MAPPING, INDEX), sameNameMapping); - JanusGraphIndex vindex = getExternalIndex(Vertex.class, INDEX); - JanusGraphIndex eindex = getExternalIndex(Edge.class, INDEX); - JanusGraphIndex pindex = getExternalIndex(JanusGraphVertexProperty.class, INDEX); - PropertyKey name = makeKey("name", String.class); + final JanusGraphIndex vindex = getExternalIndex(Vertex.class, INDEX); + final JanusGraphIndex eindex = getExternalIndex(Edge.class, INDEX); + final JanusGraphIndex pindex = getExternalIndex(JanusGraphVertexProperty.class, INDEX); + final PropertyKey name = makeKey("name", String.class); mgmt.addIndexKey(vindex, name, getStringMapping()); mgmt.addIndexKey(eindex, name, getStringMapping()); mgmt.addIndexKey(pindex, name, getStringMapping(), Parameter.of("mapped-name", "xstr")); - PropertyKey text = makeKey("text", String.class); + final PropertyKey text = makeKey("text", String.class); mgmt.addIndexKey(vindex, text, getTextMapping(), Parameter.of("mapped-name", "xtext")); mgmt.addIndexKey(eindex, text, getTextMapping()); mgmt.addIndexKey(pindex, text, getTextMapping()); @@ -906,7 +921,7 @@ private void setupChainGraph(int numV, String[] strings, boolean sameNameMapping finishSchema(); JanusGraphVertex previous = null; for (int i = 0; i < numV; i++) { - JanusGraphVertex v = graph.addVertex("name", strings[i % strings.length], "text", strings[i % strings.length]); + final JanusGraphVertex v = graph.addVertex("name", strings[i % strings.length], "text", strings[i % strings.length]); v.addEdge("knows", previous == null ? v : previous, "name", strings[i % strings.length], "text", strings[i % strings.length]); v.property("uid", "v" + i, @@ -920,8 +935,8 @@ private void setupChainGraph(int numV, String[] strings, boolean sameNameMapping */ @Test public void testIndexParameters() { - int numV = 1000; - String[] strings = {"Uncle Berry has a farm", "and on his farm he has five ducks", "ducks are beautiful animals", "the sky is very blue today"}; + final int numV = 1000; + final String[] strings = {"Uncle Berry has a farm", "and on his farm he has five ducks", "ducks are beautiful animals", "the sky is very blue today"}; setupChainGraph(numV, strings, false); evaluateQuery(graph.query().has("text", Text.CONTAINS, "ducks"), @@ -1043,8 +1058,8 @@ public void testIndexParameters() { public void testRawQueries() { if (!supportsLuceneStyleQueries()) return; - int numV = 1000; - String[] strings = {"Uncle Berry has a farm", "and on his farm he has five ducks", "ducks are beautiful animals", "the sky is very blue today"}; + final int numV = 1000; + final String[] strings = {"Uncle Berry has a farm", "and on his farm he has five ducks", "ducks are beautiful animals", "the sky is very blue today"}; setupChainGraph(numV, strings, true); clopen(); @@ -1114,7 +1129,7 @@ public void testRawQueriesWithParameters() { Parameter desc_sort_p = null; // ElasticSearch and Solr have different formats for sort parameters - String backend = readConfig.get(INDEX_BACKEND, INDEX); + final String backend = readConfig.get(INDEX_BACKEND, INDEX); switch (backend) { case "elasticsearch": final Map sortAsc = new HashMap<>(); @@ -1140,9 +1155,9 @@ public void testRawQueriesWithParameters() { mgmt.buildIndex("store1", Vertex.class).addKey(field1Key).buildMixedIndex(INDEX); mgmt.commit(); - JanusGraphVertex v1 = tx.addVertex(); - JanusGraphVertex v2 = tx.addVertex(); - JanusGraphVertex v3 = tx.addVertex(); + final JanusGraphVertex v1 = tx.addVertex(); + final JanusGraphVertex v2 = tx.addVertex(); + final JanusGraphVertex v3 = tx.addVertex(); v1.property("field1", "Hello Hello Hello Hello Hello Hello Hello Hello world"); v2.property("field1", "Hello blue and yellow meet green"); @@ -1166,8 +1181,8 @@ public void testRawQueriesWithParameters() { public void testDualMapping() { if (!indexFeatures.supportsStringMapping(Mapping.TEXTSTRING)) return; - PropertyKey name = makeKey("name", String.class); - JanusGraphIndex mixed = mgmt.buildIndex("mixed", Vertex.class).addKey(name, Mapping.TEXTSTRING.asParameter()).buildMixedIndex(INDEX); + final PropertyKey name = makeKey("name", String.class); + final JanusGraphIndex mixed = mgmt.buildIndex("mixed", Vertex.class).addKey(name, Mapping.TEXTSTRING.asParameter()).buildMixedIndex(INDEX); mixed.name(); finishSchema(); @@ -1234,11 +1249,11 @@ public void testIndexReplay() throws Exception { , option(TestMockIndexProvider.INDEX_MOCK_FAILADD, INDEX), true ); - PropertyKey name = mgmt.makePropertyKey("name").dataType(String.class).make(); - PropertyKey age = mgmt.makePropertyKey("age").dataType(Integer.class).make(); + final PropertyKey name = mgmt.makePropertyKey("name").dataType(String.class).make(); + final PropertyKey age = mgmt.makePropertyKey("age").dataType(Integer.class).make(); mgmt.buildIndex("mi", Vertex.class).addKey(name, getTextMapping()).addKey(age).buildMixedIndex(INDEX); finishSchema(); - Vertex vs[] = new JanusGraphVertex[4]; + final Vertex vs[] = new JanusGraphVertex[4]; vs[0] = tx.addVertex("name", "Big Boy Bobson", "age", 55); newTx(); @@ -1263,12 +1278,12 @@ public void testIndexReplay() throws Exception { /* Transaction Recovery */ - TransactionRecovery recovery = JanusGraphFactory.startTransactionRecovery(graph, startTime); + final TransactionRecovery recovery = JanusGraphFactory.startTransactionRecovery(graph, startTime); //wait Thread.sleep(12000L); recovery.shutdown(); - long[] recoveryStats = ((StandardTransactionLogProcessor) recovery).getStatistics(); + final long[] recoveryStats = ((StandardTransactionLogProcessor) recovery).getStatistics(); clopen(); @@ -1294,7 +1309,7 @@ public void testIndexReplay() throws Exception { @Test public void testIndexUpdatesWithoutReindex() throws InterruptedException, ExecutionException { - Object[] settings = new Object[]{option(LOG_SEND_DELAY, MANAGEMENT_LOG), Duration.ofMillis(0), + final Object[] settings = new Object[]{option(LOG_SEND_DELAY, MANAGEMENT_LOG), Duration.ofMillis(0), option(KCVSLog.LOG_READ_LAG_TIME, MANAGEMENT_LOG), Duration.ofMillis(50), option(LOG_READ_INTERVAL, MANAGEMENT_LOG), Duration.ofMillis(250) }; @@ -1307,7 +1322,7 @@ public void testIndexUpdatesWithoutReindex() throws InterruptedException, Execut //Creates types and index only two keys key mgmt.makePropertyKey("time").dataType(Integer.class).make(); - PropertyKey text = mgmt.makePropertyKey("text").dataType(String.class).make(); + final PropertyKey text = mgmt.makePropertyKey("text").dataType(String.class).make(); mgmt.makePropertyKey("height").dataType(Double.class).make(); if (indexFeatures.supportsCardinality(Cardinality.LIST)) { @@ -1341,7 +1356,7 @@ public void testIndexUpdatesWithoutReindex() throws InterruptedException, Execut //Add another key to index ------------------------------------------------------ finishSchema(); - PropertyKey time = mgmt.getPropertyKey("time"); + final PropertyKey time = mgmt.getPropertyKey("time"); mgmt.addIndexKey(mgmt.getGraphIndex("theIndex"), time, getFieldMap(time)); finishSchema(); newTx(); @@ -1386,10 +1401,10 @@ public void testIndexUpdatesWithoutReindex() throws InterruptedException, Execut //Add another key to index ------------------------------------------------------ finishSchema(); - PropertyKey height = mgmt.getPropertyKey("height"); + final PropertyKey height = mgmt.getPropertyKey("height"); mgmt.addIndexKey(mgmt.getGraphIndex("theIndex"), height); if (indexFeatures.supportsCardinality(Cardinality.LIST)) { - PropertyKey phone = mgmt.getPropertyKey("phone"); + final PropertyKey phone = mgmt.getPropertyKey("phone"); mgmt.addIndexKey(mgmt.getGraphIndex("theIndex"), phone, new Parameter("mapping", Mapping.STRING)); } finishSchema(); @@ -1406,7 +1421,7 @@ public void testIndexUpdatesWithoutReindex() throws InterruptedException, Execut finishSchema(); JanusGraphIndex index = mgmt.getGraphIndex("theIndex"); - for (PropertyKey key : index.getFieldKeys()) { + for (final PropertyKey key : index.getFieldKeys()) { assertEquals(SchemaStatus.ENABLED, index.getIndexStatus(key)); } @@ -1466,7 +1481,7 @@ public void testIndexUpdatesWithoutReindex() throws InterruptedException, Execut finishSchema(); index = mgmt.getGraphIndex("theIndex"); - for (PropertyKey key : index.getFieldKeys()) { + for (final PropertyKey key : index.getFieldKeys()) { assertEquals(SchemaStatus.DISABLED, index.getIndexStatus(key)); } @@ -1479,8 +1494,8 @@ public void testIndexUpdatesWithoutReindex() throws InterruptedException, Execut private void addVertex(int time, String text, double height, String[] phones) { newTx(); - JanusGraphVertex v = tx.addVertex("text", text, "time", time, "height", height); - for (String phone : phones) { + final JanusGraphVertex v = tx.addVertex("text", text, "time", time, "height", height); + for (final String phone : phones) { v.property("phone", phone); } @@ -1499,11 +1514,11 @@ public void testVertexTTLWithMixedIndices() throws Exception { return; } - PropertyKey name = makeKey("name", String.class); - PropertyKey time = makeKey("time", Long.class); - PropertyKey text = makeKey("text", String.class); + final PropertyKey name = makeKey("name", String.class); + final PropertyKey time = makeKey("time", Long.class); + final PropertyKey text = makeKey("text", String.class); - VertexLabel event = mgmt.makeVertexLabel("event").setStatic().make(); + final VertexLabel event = mgmt.makeVertexLabel("event").setStatic().make(); final int eventTTLSeconds = (int) TestGraphConfigs.getTTL(TimeUnit.SECONDS); mgmt.setTTL(event, Duration.ofSeconds(eventTTLSeconds)); @@ -1520,12 +1535,12 @@ public void testVertexTTLWithMixedIndices() throws Exception { JanusGraphVertex v1 = tx.addVertex("event"); v1.property(VertexProperty.Cardinality.single, "name", "first event"); v1.property(VertexProperty.Cardinality.single, "text", "this text will help to identify the first event"); - long time1 = System.currentTimeMillis(); + final long time1 = System.currentTimeMillis(); v1.property(VertexProperty.Cardinality.single, "time", time1); JanusGraphVertex v2 = tx.addVertex("event"); v2.property(VertexProperty.Cardinality.single, "name", "second event"); v2.property(VertexProperty.Cardinality.single, "text", "this text won't match"); - long time2 = time1 + 1; + final long time2 = time1 + 1; v2.property(VertexProperty.Cardinality.single, "time", time2); evaluateQuery(tx.query().has("name", "first event").orderBy("time", decr), @@ -1535,8 +1550,8 @@ public void testVertexTTLWithMixedIndices() throws Exception { clopen(); - Object v1Id = v1.id(); - Object v2Id = v2.id(); + final Object v1Id = v1.id(); + final Object v2Id = v2.id(); evaluateQuery(tx.query().has("name", "first event").orderBy("time", decr), ElementCategory.VERTEX, 1, new boolean[]{true, true}, tx.getPropertyKey("time"), Order.DESC, "index1"); @@ -1572,11 +1587,11 @@ public void testEdgeTTLWithMixedIndices() throws Exception { return; } - PropertyKey name = mgmt.makePropertyKey("name").dataType(String.class).make(); - PropertyKey text = mgmt.makePropertyKey("text").dataType(String.class).make(); - PropertyKey time = makeKey("time", Long.class); + final PropertyKey name = mgmt.makePropertyKey("name").dataType(String.class).make(); + final PropertyKey text = mgmt.makePropertyKey("text").dataType(String.class).make(); + final PropertyKey time = makeKey("time", Long.class); - EdgeLabel label = mgmt.makeEdgeLabel("likes").make(); + final EdgeLabel label = mgmt.makeEdgeLabel("likes").make(); final int likesTTLSeconds = (int) TestGraphConfigs.getTTL(TimeUnit.SECONDS); mgmt.setTTL(label, Duration.ofSeconds(likesTTLSeconds)); @@ -1592,16 +1607,16 @@ public void testEdgeTTLWithMixedIndices() throws Exception { JanusGraphVertex v1 = tx.addVertex(), v2 = tx.addVertex(), v3 = tx.addVertex(); Edge e1 = v1.addEdge("likes", v2, "name", "v1 likes v2", "text", "this will help to identify the edge"); - long time1 = System.currentTimeMillis(); + final long time1 = System.currentTimeMillis(); e1.property("time", time1); Edge e2 = v2.addEdge("likes", v3, "name", "v2 likes v3", "text", "this won't match anything"); - long time2 = time1 + 1; + final long time2 = time1 + 1; e2.property("time", time2); tx.commit(); clopen(); - Object e1Id = e1.id(); + final Object e1Id = e1.id(); e2.id(); evaluateQuery(tx.query().has("text", Text.CONTAINS, "help").has(LABEL_NAME, "likes"), @@ -1681,14 +1696,14 @@ public void testDeleteVertexThenModifyProperty() throws BackendException { } @Test - public void testIndexQueryWithScore() { - PropertyKey textKey = mgmt.makePropertyKey("text").dataType(String.class).make(); + public void testIndexQueryWithScore() throws InterruptedException { + final PropertyKey textKey = mgmt.makePropertyKey("text").dataType(String.class).make(); mgmt.buildIndex("store1", Vertex.class).addKey(textKey).buildMixedIndex(INDEX); mgmt.commit(); - JanusGraphVertex v1 = tx.addVertex(); - JanusGraphVertex v2 = tx.addVertex(); - JanusGraphVertex v3 = tx.addVertex(); + final JanusGraphVertex v1 = tx.addVertex(); + final JanusGraphVertex v2 = tx.addVertex(); + final JanusGraphVertex v3 = tx.addVertex(); v1.property("text", "Hello Hello Hello Hello Hello Hello Hello Hello world"); v2.property("text", "Hello abab abab fsdfsd sfdfsd sdffs fsdsdf fdf fsdfsd aera fsad abab abab fsdfsd sfdf"); @@ -1707,13 +1722,13 @@ public void testIndexQueryWithScore() { // this tests a case when there as AND with a single CONTAINS condition inside AND(name:(was here)) // which (in case of Solr) spans multiple conditions such as AND(AND(name:was, name:here)) // so we need to make sure that we don't apply AND twice. - public void testContainsWithMultipleValues() { - PropertyKey name = makeKey("name", String.class); + public void testContainsWithMultipleValues() throws Exception { + final PropertyKey name = makeKey("name", String.class); mgmt.buildIndex("store1", Vertex.class).addKey(name).buildMixedIndex(INDEX); mgmt.commit(); - JanusGraphVertex v1 = tx.addVertex(); + final JanusGraphVertex v1 = tx.addVertex(); v1.property("name", "hercules was here"); tx.commit(); @@ -1745,20 +1760,20 @@ private void testNestedWrites(String initialValue, String updatedValue) throws B final String propName = "foo"; // Write schema and one vertex - PropertyKey prop = makeKey(propName, String.class); + final PropertyKey prop = makeKey(propName, String.class); createExternalVertexIndex(prop, INDEX); finishSchema(); - JanusGraphVertex v = graph.addVertex(); + final JanusGraphVertex v = graph.addVertex(); if (null != initialValue) v.property(VertexProperty.Cardinality.single, propName, initialValue); graph.tx().commit(); - Object id = v.id(); + final Object id = v.id(); // Open two transactions and modify the same vertex - JanusGraphTransaction vertexDeleter = graph.newTransaction(); - JanusGraphTransaction propDeleter = graph.newTransaction(); + final JanusGraphTransaction vertexDeleter = graph.newTransaction(); + final JanusGraphTransaction propDeleter = graph.newTransaction(); getV(vertexDeleter, id).remove(); if (null == updatedValue) @@ -1784,14 +1799,14 @@ private void testNestedWrites(String initialValue, String updatedValue) throws B @Test public void testWidcardQuery() { if (supportsWildcardQuery()) { - PropertyKey p1 = makeKey("p1", String.class); - PropertyKey p2 = makeKey("p2", String.class); + final PropertyKey p1 = makeKey("p1", String.class); + final PropertyKey p2 = makeKey("p2", String.class); mgmt.buildIndex("mixedIndex", Vertex.class).addKey(p1).addKey(p2).buildMixedIndex(INDEX); finishSchema(); clopen(); - JanusGraphVertex v1 = graph.addVertex(); + final JanusGraphVertex v1 = graph.addVertex(); v1.property("p1", "test1"); v1.property("p2", "test2"); @@ -1824,11 +1839,11 @@ public void testSetIndexing() { private void testIndexing(Cardinality cardinality) { if (supportsCollections()) { - PropertyKey stringProperty = mgmt.makePropertyKey("name").dataType(String.class).cardinality(cardinality).make(); - PropertyKey intProperty = mgmt.makePropertyKey("age").dataType(Integer.class).cardinality(cardinality).make(); - PropertyKey longProperty = mgmt.makePropertyKey("long").dataType(Long.class).cardinality(cardinality).make(); - PropertyKey uuidProperty = mgmt.makePropertyKey("uuid").dataType(UUID.class).cardinality(cardinality).make(); - PropertyKey geopointProperty = mgmt.makePropertyKey("geopoint").dataType(Geoshape.class).cardinality(cardinality).make(); + final PropertyKey stringProperty = mgmt.makePropertyKey("name").dataType(String.class).cardinality(cardinality).make(); + final PropertyKey intProperty = mgmt.makePropertyKey("age").dataType(Integer.class).cardinality(cardinality).make(); + final PropertyKey longProperty = mgmt.makePropertyKey("long").dataType(Long.class).cardinality(cardinality).make(); + final PropertyKey uuidProperty = mgmt.makePropertyKey("uuid").dataType(UUID.class).cardinality(cardinality).make(); + final PropertyKey geopointProperty = mgmt.makePropertyKey("geopoint").dataType(Geoshape.class).cardinality(cardinality).make(); mgmt.buildIndex("collectionIndex", Vertex.class).addKey(stringProperty, getStringMapping()).addKey(intProperty).addKey(longProperty).addKey(uuidProperty).addKey(geopointProperty).buildMixedIndex(INDEX); finishSchema(); @@ -1836,19 +1851,19 @@ private void testIndexing(Cardinality cardinality) { testCollection(cardinality, "age", 1, 2); testCollection(cardinality, "long", 1L, 2L); testCollection(cardinality, "geopoint", Geoshape.point(1.0, 1.0), Geoshape.point(2.0, 2.0)); - String backend = readConfig.get(INDEX_BACKEND, INDEX); - // Solr 6 has issues processing UUIDs with Multivalues + final String backend = readConfig.get(INDEX_BACKEND, INDEX); + // Solr 6 has issues processing UUIDs with Multivalues // https://issues.apache.org/jira/browse/SOLR-11264 if (!"solr".equals(backend)) { testCollection(cardinality, "uuid", UUID.randomUUID(), UUID.randomUUID()); } } else { try { - PropertyKey stringProperty = mgmt.makePropertyKey("name").dataType(String.class).cardinality(cardinality).make(); + final PropertyKey stringProperty = mgmt.makePropertyKey("name").dataType(String.class).cardinality(cardinality).make(); //This should throw an exception mgmt.buildIndex("collectionIndex", Vertex.class).addKey(stringProperty, getStringMapping()).buildMixedIndex(INDEX); Assert.fail("Should have thrown an exception"); - } catch (JanusGraphException ignored) { + } catch (final JanusGraphException ignored) { } } @@ -1930,7 +1945,7 @@ private void testCollection(Cardinality cardinality, String property, Object val clopen(); // Flush the index g = graph.traversal(); v1 = g.addV().property(property, value1).property(property, value2).next(); - Vertex v2 = g.addV().property(property, value1).property(property, value2).next(); + g.addV().property(property, value1).property(property, value2).next(); clopen(); // Flush the index g = graph.traversal(); assertEquals(2, g.V().has(property, value1).toList().size()); @@ -1942,9 +1957,9 @@ private void testCollection(Cardinality cardinality, String property, Object val } private void testGeo(int i, int origNumV, int numV) { - double offset = (i * 50.0 / origNumV); - double bufferKm = 20; - double distance = Geoshape.point(0.0, 0.0).getPoint().distance(Geoshape.point(offset, offset).getPoint()) + bufferKm; + final double offset = (i * 50.0 / origNumV); + final double bufferKm = 20; + final double distance = Geoshape.point(0.0, 0.0).getPoint().distance(Geoshape.point(offset, offset).getPoint()) + bufferKm; assertCount(i + 1, tx.query().has("location", Geo.WITHIN, Geoshape.circle(0.0, 0.0, distance)).vertices()); assertCount(i + 1, tx.query().has("location", Geo.WITHIN, Geoshape.circle(0.0, 0.0, distance)).edges()); @@ -1966,10 +1981,10 @@ private void testGeo(int i, int origNumV, int numV) { assertCount(i % 2, tx.query().has("boundary", Geo.CONTAINS, Geoshape.point(-offset, -offset)).edges()); } - double buffer = bufferKm/111.; - double min = -Math.abs(offset); - double max = Math.abs(offset); - Geoshape bufferedBox = Geoshape.box(min-buffer, min-buffer, max+buffer, max+buffer); + final double buffer = bufferKm/111.; + final double min = -Math.abs(offset); + final double max = Math.abs(offset); + final Geoshape bufferedBox = Geoshape.box(min-buffer, min-buffer, max+buffer, max+buffer); assertCount(i + 1, tx.query().has("location", Geo.WITHIN, bufferedBox).vertices()); assertCount(i + 1, tx.query().has("location", Geo.WITHIN, bufferedBox).edges()); assertCount(i + 1, tx.query().has("location", Geo.INTERSECT, bufferedBox).vertices()); @@ -1977,7 +1992,7 @@ private void testGeo(int i, int origNumV, int numV) { assertCount(numV-(i + 1), tx.query().has("location", Geo.DISJOINT, bufferedBox).vertices()); assertCount(numV-(i + 1), tx.query().has("location", Geo.DISJOINT, bufferedBox).edges()); if (i > 0) { - Geoshape exactBox = Geoshape.box(min, min, max, max); + final Geoshape exactBox = Geoshape.box(min, min, max, max); assertCount(i, tx.query().has("boundary", Geo.WITHIN, exactBox).vertices()); assertCount(i, tx.query().has("boundary", Geo.WITHIN, exactBox).edges()); } @@ -1986,10 +2001,10 @@ private void testGeo(int i, int origNumV, int numV) { assertCount(numV-(i + 1), tx.query().has("boundary", Geo.DISJOINT, bufferedBox).vertices()); assertCount(numV-(i + 1), tx.query().has("boundary", Geo.DISJOINT, bufferedBox).edges()); - Geoshape bufferedPoly = Geoshape.polygon(Arrays.asList(new double[][] + final Geoshape bufferedPoly = Geoshape.polygon(Arrays.asList(new double[][] {{min-buffer,min-buffer},{max+buffer,min-buffer},{max+buffer,max+buffer},{min-buffer,max+buffer},{min-buffer,min-buffer}})); if (i > 0) { - Geoshape exactPoly = Geoshape.polygon(Arrays.asList(new double[][] + final Geoshape exactPoly = Geoshape.polygon(Arrays.asList(new double[][] {{min,min},{max,min},{max,max},{min,max},{min,min}})); assertCount(i, tx.query().has("boundary", Geo.WITHIN, exactPoly).vertices()); assertCount(i, tx.query().has("boundary", Geo.WITHIN, exactPoly).edges()); @@ -2002,22 +2017,201 @@ private void testGeo(int i, int origNumV, int numV) { @Test public void shouldAwaitMultipleStatuses() throws InterruptedException, ExecutionException { - PropertyKey key1 = makeKey("key1", String.class); - JanusGraphIndex index = mgmt.buildIndex("randomMixedIndex", Vertex.class).addKey(key1).buildMixedIndex(INDEX); + final PropertyKey key1 = makeKey("key1", String.class); + final JanusGraphIndex index = mgmt.buildIndex("randomMixedIndex", Vertex.class).addKey(key1).buildMixedIndex(INDEX); if (index.getIndexStatus(key1) == SchemaStatus.INSTALLED) { mgmt.updateIndex(mgmt.getGraphIndex("randomMixedIndex"), SchemaAction.REGISTER_INDEX).get(); mgmt.updateIndex(mgmt.getGraphIndex("randomMixedIndex"), SchemaAction.ENABLE_INDEX).get(); } else if (index.getIndexStatus(key1) == SchemaStatus.REGISTERED) { mgmt.updateIndex(mgmt.getGraphIndex("randomMixedIndex"), SchemaAction.ENABLE_INDEX).get(); } - PropertyKey key2 = makeKey("key2", String.class); + final PropertyKey key2 = makeKey("key2", String.class); mgmt.addIndexKey(index, key2); mgmt.commit(); //key1 now has status ENABLED, let's ensure we can watch for REGISTERED and ENABLED try { ManagementSystem.awaitGraphIndexStatus(graph, "randomMixedIndex").status(SchemaStatus.REGISTERED, SchemaStatus.ENABLED).call(); - } catch (Exception e) { + } catch (final Exception e) { Assert.fail("Failed to awaitGraphIndexStatus on multiple statuses."); } } + + @Test + public void testAndForceIndex() throws Exception { + JanusGraph customGraph = null; + try { + customGraph = this.getForceIndexGraph(); + final JanusGraphManagement management = customGraph.openManagement(); + final PropertyKey nameProperty = management.makePropertyKey("name").dataType(String.class).cardinality(Cardinality.SINGLE).make(); + final PropertyKey ageProperty = management.makePropertyKey("age").dataType(Integer.class).cardinality(Cardinality.SINGLE).make(); + management.buildIndex("oridx", Vertex.class).addKey(nameProperty, getStringMapping()).addKey(ageProperty).buildMixedIndex(INDEX); + management.commit(); + customGraph.tx().commit(); + final GraphTraversalSource g = customGraph.traversal(); + g.addV().property("name", "Hiro").property("age", 2).next(); + g.addV().property("name", "Totoro").property("age", 1).next(); + customGraph.tx().commit(); + assertCount(1, g.V().has("name", "Totoro")); + assertCount(1, g.V().has("age", 2)); + assertCount(1, g.V().and(__.has("name", "Hiro"),__.has("age", 2))); + assertCount(0, g.V().and(__.has("name", "Totoro"),__.has("age", 2))); + } finally { + if (customGraph != null) { + JanusGraphFactory.close(customGraph); + } + } + } + + @Test + public void testOrForceIndexUniqueMixedIndex() throws Exception { + JanusGraph customGraph = null; + try { + customGraph = this.getForceIndexGraph(); + final JanusGraphManagement management = customGraph.openManagement(); + final PropertyKey nameProperty = management.makePropertyKey("name").dataType(String.class).cardinality(Cardinality.SINGLE).make(); + final PropertyKey ageProperty = management.makePropertyKey("age").dataType(Integer.class).cardinality(Cardinality.SINGLE).make(); + final PropertyKey lengthProperty = management.makePropertyKey("length").dataType(Integer.class).cardinality(Cardinality.SINGLE).make(); + management.buildIndex("oridx", Vertex.class).addKey(nameProperty, getStringMapping()).addKey(ageProperty).addKey(lengthProperty).buildMixedIndex(INDEX); + management.commit(); + customGraph.tx().commit(); + testOr(customGraph); + } finally { + if (customGraph != null) { + JanusGraphFactory.close(customGraph); + } + } + } + + @Test + public void testOrForceIndexMixedAndCompositeIndex() throws Exception { + JanusGraph customGraph = null; + try { + customGraph = this.getForceIndexGraph(); + final JanusGraphManagement management = customGraph.openManagement(); + final PropertyKey nameProperty = management.makePropertyKey("name").dataType(String.class).cardinality(Cardinality.SINGLE).make(); + final PropertyKey ageProperty = management.makePropertyKey("age").dataType(Integer.class).cardinality(Cardinality.SINGLE).make(); + final PropertyKey lengthProperty = management.makePropertyKey("length").dataType(Integer.class).cardinality(Cardinality.SINGLE).make(); + management.buildIndex("nameidx", Vertex.class).addKey(nameProperty, getStringMapping()).buildMixedIndex(INDEX); + management.buildIndex("ageridx", Vertex.class).addKey(ageProperty).buildCompositeIndex(); + management.buildIndex("lengthidx", Vertex.class).addKey(lengthProperty).buildMixedIndex(INDEX); + management.commit(); + customGraph.tx().commit(); + testOr(customGraph); + } finally { + if (customGraph != null) { + JanusGraphFactory.close(customGraph); + } + } + } + + @Test + public void testOrPartialIndex() { + final PropertyKey nameProperty = mgmt.makePropertyKey("name").dataType(String.class).cardinality(Cardinality.SINGLE).make(); + mgmt.makePropertyKey("age").dataType(Integer.class).cardinality(Cardinality.SINGLE).make(); + final PropertyKey lengthProperty = mgmt.makePropertyKey("length").dataType(Integer.class).cardinality(Cardinality.SINGLE).make(); + mgmt.buildIndex("otheridx", Vertex.class).addKey(nameProperty, getStringMapping()).addKey(lengthProperty).buildMixedIndex(INDEX); + finishSchema(); + clopen(); + testOr(graph); + } + + private void testOr(final Graph aGraph) { + final GraphTraversalSource g = aGraph.traversal(); + final Vertex hiro = g.addV().property("name", "Hiro").property("age", 2).property("length", 90).next(); + final Vertex totoro = g.addV().property("name", "Totoro").property("age", 1).next(); + final Vertex john = g.addV().property("name", "John").property("age", 3).property("length", 110).next(); + final Vertex mike = g.addV().property("name", "Mike").property("age", 4).property("length", 130).next(); + aGraph.tx().commit(); + + assertCount(1, g.V().has("name", "Totoro")); + assertCount(1, g.V().has("age", 2)); + assertCount(1, g.V().or(__.has("name", "Hiro"),__.has("age", 2))); + assertCount(2, g.V().or(__.has("name", "Totoro"),__.has("age", 2))); + assertCount(2, g.V().or(__.has("name", "Totoro").has("age", 1),__.has("age", 2))); + assertCount(2, g.V().or(__.and(__.has("name", "Totoro"), __.has("age", 1)),__.has("age", 2))); + + assertTraversal(g.V().has("length", P.lte(100)).or(__.has("name", "Totoro"),__.has("age", P.gte(2))), hiro); + assertTraversal(g.V().or(__.has("name", "Totoro"),__.has("age", P.gte(2))).has("length", P.lte(100)), hiro); + + assertTraversal(g.V().or(__.has("name", "Totoro"),__.has("age", 2)).order().by(ORDER_AGE_DECR), hiro, totoro); + assertTraversal(g.V().or(__.has("name", "Totoro"),__.has("age", 2)).order().by(ORDER_AGE_INCR), totoro, hiro); + assertTraversal(g.V().or(__.has("name", "Hiro"),__.has("age", 2)).order().by(ORDER_AGE_INCR), hiro); + + assertTraversal(g.V().or(__.has("name", "Totoro"), __.has("length", P.lte(120)).order().by(ORDER_LENGTH_DECR)), totoro, john, hiro); + assertTraversal(g.V().or(__.has("name", "Totoro"), __.has("length", P.lte(120)).order().by(ORDER_LENGTH_INCR)), totoro, hiro, john); + assertTraversal(g.V().or(__.has("name", "John"), __.has("length", P.lte(120)).order().by(ORDER_LENGTH_INCR)), john, hiro); + + assertTraversal(g.V().or(__.has("name", "Totoro"), __.has("length", P.lte(120)).order().by(ORDER_AGE_DECR)), totoro, john, hiro); + assertTraversal(g.V().or(__.has("name", "Totoro"), __.has("length", P.lte(120)).order().by(ORDER_AGE_INCR)), totoro, hiro, john); + + assertTraversal(g.V().or(__.has("name", "Totoro"), __.has("length", P.lte(120)).order().by(ORDER_LENGTH_DECR)).order().by(ORDER_AGE_INCR), totoro, hiro, john); + assertTraversal(g.V().or(__.has("name", "Totoro"), __.has("length", P.lte(120)).order().by(ORDER_LENGTH_INCR)).order().by(ORDER_AGE_DECR), john, hiro, totoro); + + assertTraversal(g.V().or(__.has("name", "Totoro"), __.has("length", P.lte(120))).order().by(ORDER_AGE_INCR).limit(2), totoro, hiro); + assertTraversal(g.V().or(__.has("name", "Totoro"), __.has("length", P.lte(120))).order().by(ORDER_AGE_INCR).range(2, 3), john); + + assertTraversal(g.V().or(__.has("name", "Totoro"), __.has("length", P.lte(130)).order().by(ORDER_LENGTH_INCR).limit(1)), totoro, hiro); + assertTraversal(g.V().or(__.has("name", "Hiro"), __.has("length", P.lte(130)).order().by(ORDER_LENGTH_INCR).limit(1)), hiro); + assertTraversal(g.V().or(__.has("name", "Totoro"), __.has("length", P.lte(130)).order().by(ORDER_LENGTH_INCR).range(1, 2)), totoro, john); + assertTraversal(g.V().or(__.has("name", "Totoro"), __.has("length", P.lte(130)).order().by(ORDER_LENGTH_INCR).range(1, 3)).limit(2), totoro, john); + + assertTraversal(g.V().has("length", P.gte(130).or(P.lt(100))).order().by(ORDER_AGE_INCR), hiro, mike); + assertTraversal(g.V().has("length", P.gte(80).and(P.gte(130).or(P.lt(100)))).order().by(ORDER_AGE_INCR), hiro, mike); + if (indexFeatures.supportNotQueryNormalForm()) { + assertTraversal(g.V().has("length", P.gte(80).and(P.gte(130)).or(P.gte(80).and(P.lt(100)))).order().by(ORDER_AGE_INCR), hiro, mike); + } + + } + + @Test + public void testOrForceIndexPartialIndex() throws Exception { + JanusGraph customGraph = null; + try { + customGraph = this.getForceIndexGraph(); + final JanusGraphManagement management = customGraph.openManagement(); + final PropertyKey stringProperty = management.makePropertyKey("name").dataType(String.class).cardinality(Cardinality.SINGLE).make(); + management.makePropertyKey("age").dataType(Integer.class).cardinality(Cardinality.SINGLE).make(); + management.buildIndex("oridx", Vertex.class).addKey(stringProperty, getStringMapping()).buildMixedIndex(INDEX); + management.commit(); + customGraph.tx().commit(); + final GraphTraversalSource g = customGraph.traversal(); + g.addV().property("name", "Hiro").property("age", 2).next(); + g.addV().property("name", "Totoro").property("age", 1).next(); + customGraph.tx().commit(); + g.V().or(__.has("name", "Totoro"),__.has("age", 2)).hasNext(); + fail("should fail"); + } catch (final JanusGraphException e){ + assertTrue(e.getMessage().contains("Could not find a suitable index to answer graph query and graph scans are disabled")); + } finally { + if (customGraph != null) { + JanusGraphFactory.close(customGraph); + } + } + } + + @Test + public void testOrForceIndexComposite() throws Exception { + JanusGraph customGraph = null; + try { + customGraph = this.getForceIndexGraph(); + final JanusGraphManagement management = customGraph.openManagement(); + management.makePropertyKey("name").dataType(String.class).cardinality(Cardinality.SINGLE).make(); + final PropertyKey ageProperty = management.makePropertyKey("age").dataType(Integer.class).cardinality(Cardinality.SINGLE).make(); + management.buildIndex("ageridx", Vertex.class).addKey(ageProperty).buildCompositeIndex(); + management.commit(); + customGraph.tx().commit(); + final GraphTraversalSource g = customGraph.traversal(); + g.addV().property("name", "Hiro").property("age", 2).next(); + g.addV().property("name", "Totoro").property("age", 1).next(); + customGraph.tx().commit(); + g.V().has("age", P.gte(4).or(P.lt(2))).hasNext(); + fail("should fail"); + } catch (final JanusGraphException e){ + assertTrue(e.getMessage().contains("Could not find a suitable index to answer graph query and graph scans are disabled")); + } finally { + if (customGraph != null) { + JanusGraphFactory.close(customGraph); + } + } + } } diff --git a/janusgraph-test/src/main/java/org/janusgraph/testutil/JanusGraphAssert.java b/janusgraph-test/src/main/java/org/janusgraph/testutil/JanusGraphAssert.java index 653fef0074a..1e35da3041b 100644 --- a/janusgraph-test/src/main/java/org/janusgraph/testutil/JanusGraphAssert.java +++ b/janusgraph-test/src/main/java/org/janusgraph/testutil/JanusGraphAssert.java @@ -18,8 +18,12 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.structure.Element; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + import java.lang.reflect.Array; import java.util.Collection; import java.util.Iterator; @@ -58,6 +62,13 @@ public static void assertNotEmpty(Object object) { org.junit.Assert.assertFalse(isEmpty(object)); } + public static void assertTraversal(GraphTraversal req, E... expectedElements) { + for (final E expectedElement : expectedElements) { + assertEquals(expectedElement, req.next()); + } + assertFalse(req.hasNext()); + } + private static boolean isEmpty(Object obj) { Preconditions.checkArgument(obj != null); if (obj instanceof Traversal) return !((Traversal) obj).hasNext(); diff --git a/janusgraph-test/src/test/java/org/janusgraph/blueprints/process/traversal/strategy/optimization/JanusGraphStepStrategyTest.java b/janusgraph-test/src/test/java/org/janusgraph/blueprints/process/traversal/strategy/optimization/JanusGraphStepStrategyTest.java index b65cf807767..9082a95998a 100644 --- a/janusgraph-test/src/test/java/org/janusgraph/blueprints/process/traversal/strategy/optimization/JanusGraphStepStrategyTest.java +++ b/janusgraph-test/src/test/java/org/janusgraph/blueprints/process/traversal/strategy/optimization/JanusGraphStepStrategyTest.java @@ -19,22 +19,29 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies; import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.filter.OrStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.filter.RangeGlobalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.ElementValueComparator; import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.FilterRankingStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.InlineFilterStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.util.AndP; +import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP; import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies; +import org.apache.tinkerpop.gremlin.process.traversal.util.OrP; import org.apache.tinkerpop.gremlin.structure.T; import org.apache.tinkerpop.gremlin.structure.Vertex; - import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; - +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.janusgraph.StorageSetup; import org.janusgraph.core.JanusGraph; import org.janusgraph.core.schema.JanusGraphManagement; import org.janusgraph.graphdb.database.StandardJanusGraph; +import org.janusgraph.graphdb.predicate.ConnectiveJanusPredicate; +import org.janusgraph.graphdb.query.JanusGraphPredicate.Converter; import org.janusgraph.graphdb.tinkerpop.optimize.HasStepFolder; import org.janusgraph.graphdb.tinkerpop.optimize.JanusGraphStep; import org.janusgraph.graphdb.tinkerpop.optimize.JanusGraphStepStrategy; @@ -81,22 +88,40 @@ public void doTest() { assertEquals(this.optimized, this.original); } + @SuppressWarnings({ "unchecked", "rawtypes" }) private static GraphTraversal.Admin g_V(final Object... hasKeyValues) { final GraphTraversal.Admin traversal = new DefaultGraphTraversal<>(); final JanusGraphStep graphStep = new JanusGraphStep<>(new GraphStep<>(traversal, Vertex.class, true)); - for (int i = 0; i < hasKeyValues.length; i = i + 2) { - if (hasKeyValues[i].equals(T.id)) { + for (int i = 0; i < hasKeyValues.length; i++) { + if(hasKeyValues[i].equals(T.id)) { graphStep.addIds(Arrays.asList(hasKeyValues[i + 1])); + i++; } else if (hasKeyValues[i] instanceof HasStepFolder.OrderEntry) { final HasStepFolder.OrderEntry orderEntry = (HasStepFolder.OrderEntry) hasKeyValues[i]; graphStep.orderBy(orderEntry.key, orderEntry.order); + } else if (hasKeyValues[i] instanceof DefaultGraphTraversal && ((DefaultGraphTraversal) hasKeyValues[i]).getStartStep() instanceof OrStep){ + for (final Traversal.Admin child : ((OrStep) ((DefaultGraphTraversal) hasKeyValues[i]).getStartStep()).getLocalChildren()) { + final JanusGraphStep localGraphStep = ((JanusGraphStep) ((DefaultGraphTraversal) child).getStartStep()); + graphStep.addLocalAll(localGraphStep.getHasContainers()); + localGraphStep.getOrders().forEach(orderEntry -> graphStep.localOrderBy(localGraphStep.getHasContainers(), orderEntry.key, orderEntry.order)); + graphStep.setLocalLimit(localGraphStep.getHasContainers(), localGraphStep.getLowLimit(), localGraphStep.getHighLimit()); + } + } else if (hasKeyValues[i] instanceof DefaultGraphTraversal && ((DefaultGraphTraversal) hasKeyValues[i]).getStartStep() instanceof RangeGlobalStep){ + final RangeGlobalStep range = (RangeGlobalStep) ((DefaultGraphTraversal) hasKeyValues[i]).getStartStep(); + graphStep.setLimit((int) range.getLowRange(), (int) range.getHighRange()); + } else if (i < hasKeyValues.length -1 && hasKeyValues[i + 1] instanceof ConnectiveP) { + final ConnectiveJanusPredicate connectivePredicate = Converter.instanceConnectiveJanusPredicate((ConnectiveP) hasKeyValues[i + 1] ); + graphStep.addHasContainer(new HasContainer((String) hasKeyValues[i], new P<>(connectivePredicate, Converter.convert(((ConnectiveP) hasKeyValues[i + 1]), connectivePredicate)))); + i++; } else { graphStep.addHasContainer(new HasContainer((String) hasKeyValues[i], (P) hasKeyValues[i + 1])); + i++; } } return traversal.addStep(graphStep); } + @Parameterized.Parameters(name = "{0}") public static Iterable generateTestParameters() { final StandardJanusGraph graph = (StandardJanusGraph) StorageSetup.getInMemoryGraph(); @@ -112,7 +137,11 @@ public static Iterable generateTestParameters() { {g.V().out(), g_V().out(), Collections.emptyList()}, {g.V().has("name", "marko").out(), g_V("name", eq("marko")).out(), Collections.emptyList()}, {g.V().has("name", "marko").has("age", gt(31).and(lt(10))).out(), - g_V("name", eq("marko"), "age", gt(31), "age", lt(10)).out(), Collections.emptyList()}, + g_V("name", eq("marko"), "age", new AndP(Arrays.asList(gt(31), lt(10)))).out(), Collections.emptyList()}, + {g.V().has("name", "marko").has("age", gt(31).or(lt(10))).out(), + g_V("name", eq("marko"), "age", new OrP(Arrays.asList(gt(31), lt(10)))).out(), Collections.emptyList()}, + {g.V().has("name", "marko").has("age", gt(31).and(lt(10).or(gt(40)))).out(), + g_V("name", eq("marko"), "age", new OrP(Arrays.asList(gt(31), new AndP(Arrays.asList(lt(10), gt(40)))))).out(), Collections.emptyList()}, {g.V().has("name", "marko").or(has("age"), has("age", gt(32))).has("lang", "java"), g_V("name", eq("marko"), "lang", eq("java")).or(has("age"), has("age", gt(32))), Collections.singletonList(FilterRankingStrategy.instance())}, {g.V().has("name", "marko").as("a").or(has("age"), has("age", gt(32))).has("lang", "java"), @@ -130,7 +159,7 @@ public static Iterable generateTestParameters() { {g.V().has("name", "marko").or(not(has("age")), has("age", gt(32))).has("name", "bob").has("lang", "java"), g_V("name", eq("marko"), "name", eq("bob"), "lang", eq("java")).or(not(filter(properties("age"))), has("age", gt(32))), TraversalStrategies.GlobalCache.getStrategies(JanusGraph.class).toList()}, {g.V().has("name", eq("marko").and(eq("bob").and(eq("stephen")))).out("knows"), - g_V("name", eq("marko"), "name", eq("bob"), "name", eq("stephen")).out("knows"), Collections.emptyList()}, + g_V("name", new AndP(Arrays.asList(eq("marko"), eq("bob"),eq("stephen")))).out("knows"), Collections.emptyList()}, {g.V().hasId(1), g_V(T.id, 1), Collections.emptyList()}, {g.V().hasId(within(1, 2)), g_V(T.id, 1, T.id, 2), Collections.emptyList()}, {g.V().hasId(1).has("name", "marko"), g_V(T.id, 1, "name", eq("marko")), Collections.emptyList()}, @@ -140,6 +169,8 @@ public static Iterable generateTestParameters() { // same as above, different order {g.V().hasLabel("Person").has("lang", "java").order().by("name", Order.decr), g_V("~label", eq("Person"), "lang", eq("java"), new HasStepFolder.OrderEntry("name", Order.decr)), Collections.emptyList()}, + {g.V().hasLabel("Person").has("lang", "java").order().by(new ElementValueComparator("name", org.apache.tinkerpop.gremlin.process.traversal.Order.incr)), + g_V("~label", eq("Person"), "lang", eq("java"), new HasStepFolder.OrderEntry("name", Order.incr)), Collections.emptyList()}, // if multiple order steps are specified in a row, only the last will be folded in because it overrides previous ordering {g.V().hasLabel("Person").has("lang", "java").order().by("lang", Order.incr).order().by("name", Order.decr), g_V("~label", eq("Person"), "lang", eq("java"), new HasStepFolder.OrderEntry("name", Order.decr)), Collections.emptyList()}, @@ -152,6 +183,17 @@ public static Iterable generateTestParameters() { // Per the TinkerGraph reference implementation, multiple hasIds in a row should not be folded // into a single within(ids) lookup {g.V().hasId(1).hasId(2), g_V(T.id, 1).hasId(2), Collections.emptyList()}, + {g.V().has("name", "marko").range(10, 20), g_V("name", eq("marko"), __.range(10, 20)), Collections.emptyList()}, + {g.V().has("name", "marko").or(has("length", lt(160)), has("age", gt(32))).has("lang", "java"), + g_V("name", eq("marko"), "lang", eq("java"),__.or(g_V("length", lt(160)), g_V("age", gt(32)))), Collections.emptyList()}, + {g.V().or(has("length", lt(160)), has("age", gt(32)).range(1, 5)), + g_V(__.or(g_V("length", lt(160)), g_V("age", gt(32), __.range(1, 5)))), Collections.emptyList()}, + {g.V().or(has("length", lt(160)), has("age", gt(32)).range(1, 5)).range(10, 20), + g_V(__.or(g_V("length", lt(160)), g_V("age", gt(32), __.range(1, 5))), __.range(10, 20)), Collections.emptyList()}, + {g.V().or(__.has("name", "marko"), has("lang", "java").order().by("name", Order.decr)), + g_V(__.or(g_V("name", eq("marko")), g_V("lang", eq("java"), new HasStepFolder.OrderEntry("name", Order.decr)))), Collections.emptyList()}, + {g.V().or(__.has("name", "marko"), has("lang", "java").order().by("name", Order.decr)).order().by("lang", Order.incr), + g_V(__.or(g_V("name", eq("marko")), g_V("lang", eq("java"), new HasStepFolder.OrderEntry("name", Order.decr))), new HasStepFolder.OrderEntry("lang", Order.incr)), Collections.emptyList()}, }); } } diff --git a/janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/AndJanusPredicateTest.java b/janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/AndJanusPredicateTest.java new file mode 100644 index 00000000000..d82de2b0879 --- /dev/null +++ b/janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/AndJanusPredicateTest.java @@ -0,0 +1,52 @@ +/* + * 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.janusgraph.graphdb.predicate; + +import java.util.Arrays; +import java.util.List; + +import org.janusgraph.core.attribute.Cmp; +import org.janusgraph.core.attribute.Text; +import org.janusgraph.graphdb.query.JanusGraphPredicate; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author David Clement (david.clement90@laposte.net) + */ +public class AndJanusPredicateTest extends ConnectiveJanusPredicateTest{ + + @Override + ConnectiveJanusPredicate getPredicate(List childPredicates) { + return new AndJanusPredicate(childPredicates); + } + + @Override + ConnectiveJanusPredicate getNegatePredicate(List childPredicates) { + return new OrJanusPredicate(childPredicates); + } + + @Test + public void testIsQNF() { + Assert.assertTrue(getPredicate(Arrays.asList(Text.PREFIX, Cmp.EQUAL)).isQNF()); + Assert.assertTrue(getPredicate(Arrays.asList(Text.PREFIX, Cmp.EQUAL, new OrJanusPredicate(Arrays.asList(Text.PREFIX, Cmp.EQUAL)))).isQNF()); + Assert.assertTrue(getPredicate(Arrays.asList(Text.PREFIX, Cmp.EQUAL, new AndJanusPredicate(Arrays.asList(Text.PREFIX, Cmp.EQUAL)))).isQNF()); + } +} diff --git a/janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/ConnectiveJanusGraphPTest.java b/janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/ConnectiveJanusGraphPTest.java new file mode 100644 index 00000000000..300312cd25a --- /dev/null +++ b/janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/ConnectiveJanusGraphPTest.java @@ -0,0 +1,37 @@ +package org.janusgraph.graphdb.predicate; + +import java.util.Arrays; +import java.util.List; + +import org.janusgraph.core.attribute.Cmp; +import org.janusgraph.core.attribute.Text; +import org.junit.Assert; +import org.junit.Test; + + +public class ConnectiveJanusGraphPTest { + + @Test + public void testToString() { + final AndJanusPredicate biPredicate = new AndJanusPredicate(Arrays.asList(Text.PREFIX, Cmp.EQUAL, new OrJanusPredicate(Arrays.asList(Text.PREFIX, Cmp.EQUAL)))); + final List values = Arrays.asList("john", "jo", Arrays.asList("mike","mi")); + final ConnectiveJanusGraphP predicate = new ConnectiveJanusGraphP(biPredicate, values); + Assert.assertEquals("and(textPrefix(john), =(jo), or(textPrefix(mike), =(mi)))", predicate.toString()); + } + + @Test + public void testToStringOneElement() { + final AndJanusPredicate biPredicate = new AndJanusPredicate(Arrays.asList(Text.PREFIX)); + final List values = Arrays.asList("john"); + final ConnectiveJanusGraphP predicate = new ConnectiveJanusGraphP(biPredicate, values); + Assert.assertEquals("textPrefix(john)", predicate.toString()); + } + + @Test + public void testToStringEmpty() { + final AndJanusPredicate biPredicate = new AndJanusPredicate(); + final List values = Arrays.asList(); + final ConnectiveJanusGraphP predicate = new ConnectiveJanusGraphP(biPredicate, values); + Assert.assertEquals("and()", predicate.toString()); + } +} diff --git a/janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/ConnectiveJanusPredicateTest.java b/janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/ConnectiveJanusPredicateTest.java new file mode 100644 index 00000000000..eec1e9a7e9e --- /dev/null +++ b/janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/ConnectiveJanusPredicateTest.java @@ -0,0 +1,113 @@ +/* + * 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.janusgraph.graphdb.predicate; + +import java.util.Arrays; +import java.util.List; + +import org.janusgraph.core.attribute.Cmp; +import org.janusgraph.core.attribute.Geo; +import org.janusgraph.core.attribute.Geoshape; +import org.janusgraph.core.attribute.Text; +import org.janusgraph.graphdb.query.JanusGraphPredicate; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author David Clement (david.clement90@laposte.net) + */ +public abstract class ConnectiveJanusPredicateTest { + + + abstract ConnectiveJanusPredicate getPredicate(List childPredicates); + + abstract ConnectiveJanusPredicate getNegatePredicate(List childPredicates); + + @Test + public void testIsValidConditionNotAList() { + Assert.assertFalse(getPredicate(Arrays.asList()).isValidCondition(3)); + } + + @Test + public void testIsValidConditionDifferentSize() { + Assert.assertFalse(getPredicate(Arrays.asList()).isValidCondition(Arrays.asList(3))); + } + + @Test + public void testIsValidConditionOk() { + Assert.assertTrue(getPredicate(Arrays.asList(Text.CONTAINS, Cmp.EQUAL, Geo.WITHIN)).isValidCondition(Arrays.asList("john", 3, Geoshape.point(2.0, 4.0)))); + } + + @Test + public void testIsValidConditionKoFirst() { + Assert.assertFalse(getPredicate(Arrays.asList(Text.CONTAINS, Cmp.EQUAL, Geo.WITHIN)).isValidCondition(Arrays.asList(1L, 3, Geoshape.point(2.0, 4.0)))); + } + + @Test + public void testIsValidConditionKo() { + Assert.assertFalse(getPredicate(Arrays.asList(Text.CONTAINS, Cmp.EQUAL, Geo.WITHIN)).isValidCondition(Arrays.asList("john", 3, 1L))); + } + + @Test + public void testIsValidTypeOk() { + Assert.assertTrue(getPredicate(Arrays.asList(Text.CONTAINS, Cmp.EQUAL)).isValidValueType(String.class)); + } + + @Test + public void testIsValidKo() { + Assert.assertFalse(getPredicate(Arrays.asList(Text.CONTAINS, Cmp.EQUAL)).isValidValueType(Integer.class)); + } + + @Test + public void testHasNegationOk() { + Assert.assertTrue(getPredicate(Arrays.asList(Geo.INTERSECT, Cmp.EQUAL)).hasNegation()); + } + + @Test + public void testHasNegationKo() { + Assert.assertFalse(getPredicate(Arrays.asList(Text.CONTAINS, Cmp.EQUAL)).hasNegation()); + } + + @Test + public void testNegate() { + Assert.assertEquals(getNegatePredicate(Arrays.asList(Geo.DISJOINT, Cmp.NOT_EQUAL)), getPredicate(Arrays.asList(Geo.INTERSECT, Cmp.EQUAL)).negate()); + } + + @Test + public void testTestNotAList() { + Assert.assertFalse(getPredicate(Arrays.asList()).test("john","jo")); + } + + @Test + public void testTestDifferentSize() { + Assert.assertFalse(getPredicate(Arrays.asList()).test("john",Arrays.asList("jo"))); + } + + + @Test + public void testTest() { + final ConnectiveJanusPredicate predicate = getPredicate(Arrays.asList(Text.PREFIX, Cmp.EQUAL)); + Assert.assertTrue(predicate.test("john",Arrays.asList("jo", "john"))); + Assert.assertEquals(predicate.isOr(), predicate.test("john",Arrays.asList("jo", "mike"))); + Assert.assertEquals(predicate.isOr(), predicate.test("john",Arrays.asList("mi", "john"))); + Assert.assertFalse(predicate.test("john",Arrays.asList("mi", "mike"))); + } + +} diff --git a/janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/OrJanusPredicateTest.java b/janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/OrJanusPredicateTest.java new file mode 100644 index 00000000000..0f6c06886ba --- /dev/null +++ b/janusgraph-test/src/test/java/org/janusgraph/graphdb/predicate/OrJanusPredicateTest.java @@ -0,0 +1,52 @@ +/* + * 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.janusgraph.graphdb.predicate; + +import java.util.Arrays; +import java.util.List; + +import org.janusgraph.core.attribute.Cmp; +import org.janusgraph.core.attribute.Text; +import org.janusgraph.graphdb.query.JanusGraphPredicate; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author David Clement (david.clement90@laposte.net) + */ +public class OrJanusPredicateTest extends ConnectiveJanusPredicateTest{ + + @Override + ConnectiveJanusPredicate getPredicate(List childPredicates) { + return new OrJanusPredicate(childPredicates); + } + + @Override + ConnectiveJanusPredicate getNegatePredicate(List childPredicates) { + return new AndJanusPredicate(childPredicates); + } + + @Test + public void testIsQNF() { + Assert.assertTrue(getPredicate(Arrays.asList(Text.PREFIX, Cmp.EQUAL)).isQNF()); + Assert.assertTrue(getPredicate(Arrays.asList(Text.PREFIX, Cmp.EQUAL, new OrJanusPredicate(Arrays.asList(Text.PREFIX, Cmp.EQUAL)))).isQNF()); + Assert.assertFalse(getPredicate(Arrays.asList(Text.PREFIX, Cmp.EQUAL, new AndJanusPredicate(Arrays.asList(Text.PREFIX, Cmp.EQUAL)))).isQNF()); + } +}