From 650835cbef906f15e2b4410cc70b61fa1ac9b88b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Finn=C3=A9?= Date: Fri, 4 May 2018 20:07:28 +0200 Subject: [PATCH] Adds IndexCapability#limitations Where additional flags can be raised about limitations in a particular index implementation. Such flags can be used to create e.g. notifications. --- .../neo4j/kernel/api/exceptions/Status.java | 2 + .../internal/kernel/api/IndexCapability.java | 10 ++++ .../internal/kernel/api/IndexLimitation.java | 28 +++++++++ .../impl/notification/NotificationCode.java | 6 +- .../index/schema/StringIndexProvider.java | 9 +++ .../impl/newapi/UnionIndexCapability.java | 19 +++++++ .../impl/newapi/UnionIndexCapabilityTest.java | 57 ++++++++++++++++++- 7 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/IndexLimitation.java diff --git a/community/common/src/main/java/org/neo4j/kernel/api/exceptions/Status.java b/community/common/src/main/java/org/neo4j/kernel/api/exceptions/Status.java index b0a13e808e922..0580d4293894b 100644 --- a/community/common/src/main/java/org/neo4j/kernel/api/exceptions/Status.java +++ b/community/common/src/main/java/org/neo4j/kernel/api/exceptions/Status.java @@ -249,6 +249,8 @@ enum Statement implements Status "The database was unable to plan a hinted join." ), NoApplicableIndexWarning( ClientNotification, "Adding a schema index may speed up this query." ), + SuboptimalIndexForWildcardQuery( ClientNotification, + "Index cannot execute wildcard query efficiently" ), UnboundedVariableLengthPatternWarning( ClientNotification, "The provided pattern is unbounded, consider adding an upper limit to the number of node hops." ), ExhaustiveShortestPathWarning( ClientNotification, diff --git a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/IndexCapability.java b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/IndexCapability.java index 06473d3bc96ea..cdea24cdc51dc 100644 --- a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/IndexCapability.java +++ b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/IndexCapability.java @@ -32,6 +32,7 @@ public interface IndexCapability { IndexOrder[] ORDER_ASC = {IndexOrder.ASCENDING}; IndexOrder[] ORDER_NONE = new IndexOrder[0]; + IndexLimitation[] LIMITIATION_NONE = new IndexLimitation[0]; /** * What possible orderings is this index capable to provide for a query on given combination of {@link ValueCategory}. @@ -59,6 +60,15 @@ public interface IndexCapability */ IndexValueCapability valueCapability( ValueCategory... valueCategories ); + /** + * @return an array of limitations that this index has. It could be anything that planning could look at and + * either try to avoid or issue warning for. + */ + default IndexLimitation[] limitations() + { + return LIMITIATION_NONE; + } + default boolean singleWildcard( ValueCategory[] valueCategories ) { return valueCategories.length == 1 && valueCategories[0] == ValueCategory.UNKNOWN; diff --git a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/IndexLimitation.java b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/IndexLimitation.java new file mode 100644 index 0000000000000..dac83a754f488 --- /dev/null +++ b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/IndexLimitation.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.internal.kernel.api; + +public enum IndexLimitation +{ + /** + * Highlights that CONTAINS and ENDS WITH isn't supported efficiently. + */ + SLOW_CONTAINS; +} diff --git a/community/kernel/src/main/java/org/neo4j/graphdb/impl/notification/NotificationCode.java b/community/kernel/src/main/java/org/neo4j/graphdb/impl/notification/NotificationCode.java index 3ecdbe90aba17..9c0f7a4db7548 100644 --- a/community/kernel/src/main/java/org/neo4j/graphdb/impl/notification/NotificationCode.java +++ b/community/kernel/src/main/java/org/neo4j/graphdb/impl/notification/NotificationCode.java @@ -193,7 +193,11 @@ public enum NotificationCode EXPERIMENTAL_FEATURE( SeverityLevel.WARNING, Status.Statement.ExperimentalFeature, - "You are using an experimental feature" ); + "You are using an experimental feature" ), + SUBOPTIMAL_INDEX_FOR_WILDCARD_QUERY( + SeverityLevel.INFORMATION, + Status.Statement.SuboptimalIndexForWildcardQuery, + "Index cannot execute wildcard query efficiently" ); private final Status status; private final String description; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringIndexProvider.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringIndexProvider.java index a0c19522e9576..d68e5e4e14019 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringIndexProvider.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/StringIndexProvider.java @@ -26,6 +26,7 @@ import org.neo4j.index.internal.gbptree.Layout; import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector; import org.neo4j.internal.kernel.api.IndexCapability; +import org.neo4j.internal.kernel.api.IndexLimitation; import org.neo4j.internal.kernel.api.IndexOrder; import org.neo4j.internal.kernel.api.IndexValueCapability; import org.neo4j.io.fs.FileSystemAbstraction; @@ -90,6 +91,8 @@ public IndexCapability getCapability( SchemaIndexDescriptor schemaIndexDescripto */ private static class StringIndexCapability implements IndexCapability { + private final IndexLimitation[] limitations = {IndexLimitation.SLOW_CONTAINS}; + @Override public IndexOrder[] orderCapability( ValueCategory... valueCategories ) { @@ -114,6 +117,12 @@ public IndexValueCapability valueCapability( ValueCategory... valueCategories ) return IndexValueCapability.NO; } + @Override + public IndexLimitation[] limitations() + { + return limitations; + } + private boolean support( ValueCategory[] valueCategories ) { return valueCategories.length == 1 && valueCategories[0] == ValueCategory.TEXT; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/UnionIndexCapability.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/UnionIndexCapability.java index ff554dd718717..16cfd1db763de 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/UnionIndexCapability.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/UnionIndexCapability.java @@ -24,6 +24,7 @@ import java.util.Set; import org.neo4j.internal.kernel.api.IndexCapability; +import org.neo4j.internal.kernel.api.IndexLimitation; import org.neo4j.internal.kernel.api.IndexOrder; import org.neo4j.internal.kernel.api.IndexValueCapability; import org.neo4j.values.storable.ValueCategory; @@ -35,10 +36,12 @@ public class UnionIndexCapability implements IndexCapability { private final IndexCapability[] capabilities; + private final IndexLimitation[] limitationsUnion; public UnionIndexCapability( IndexCapability... capabilities ) { this.capabilities = capabilities; + this.limitationsUnion = limitationsUnion( capabilities ); } @Override @@ -66,4 +69,20 @@ public IndexValueCapability valueCapability( ValueCategory... valueCategories ) } return currentBest; } + + @Override + public IndexLimitation[] limitations() + { + return limitationsUnion; + } + + private IndexLimitation[] limitationsUnion( IndexCapability[] capabilities ) + { + HashSet union = new HashSet<>(); + for ( IndexCapability capability : capabilities ) + { + union.addAll( Arrays.asList( capability.limitations() ) ); + } + return union.toArray( new IndexLimitation[union.size()] ); + } } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/UnionIndexCapabilityTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/UnionIndexCapabilityTest.java index e6af1feb1fa53..71e326e489ec8 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/UnionIndexCapabilityTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/UnionIndexCapabilityTest.java @@ -21,8 +21,11 @@ import org.junit.Test; +import java.util.Collections; + import org.neo4j.helpers.ArrayUtil; import org.neo4j.internal.kernel.api.IndexCapability; +import org.neo4j.internal.kernel.api.IndexLimitation; import org.neo4j.internal.kernel.api.IndexOrder; import org.neo4j.internal.kernel.api.IndexValueCapability; import org.neo4j.values.storable.ValueCategory; @@ -32,6 +35,8 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.neo4j.helpers.ArrayUtil.array; +import static org.neo4j.helpers.collection.Iterators.asSet; public class UnionIndexCapabilityTest { @@ -117,6 +122,47 @@ public void shouldCreateUnionOfValueCapability() assertValueCapability( union, IndexValueCapability.YES ); } + @Test + public void shouldCreateUnionOfIndexLimitations() + { + UnionIndexCapability union; + + // given + union = unionOfIndexLimitations( IndexCapability.LIMITIATION_NONE, IndexCapability.LIMITIATION_NONE ); + + // then + assertEquals( Collections.emptySet(), asSet( union.limitations() ) ); + + // given + union = unionOfIndexLimitations( IndexCapability.LIMITIATION_NONE, array( IndexLimitation.SLOW_CONTAINS ) ); + + // then + assertEquals( asSet( IndexLimitation.SLOW_CONTAINS ), asSet( union.limitations() ) ); + + // given + union = unionOfIndexLimitations( array( IndexLimitation.SLOW_CONTAINS ), array( IndexLimitation.SLOW_CONTAINS ) ); + + // then + assertEquals( asSet( IndexLimitation.SLOW_CONTAINS ), asSet( union.limitations() ) ); + } + + private UnionIndexCapability unionOfIndexLimitations( IndexLimitation[]... limitiations ) + { + IndexCapability[] capabilities = new IndexCapability[limitiations.length]; + for ( int i = 0; i < limitiations.length; i++ ) + { + capabilities[i] = capabilityWithIndexLimitations( limitiations[i] ); + } + return new UnionIndexCapability( capabilities ); + } + + private IndexCapability capabilityWithIndexLimitations( IndexLimitation[] limitations ) + { + IndexCapability mock = mockedIndexCapability(); + when( mock.limitations() ).thenReturn( limitations ); + return mock; + } + private UnionIndexCapability unionOfValueCapabilities( IndexValueCapability... valueCapabilities ) { IndexCapability[] capabilities = new IndexCapability[valueCapabilities.length]; @@ -139,14 +185,21 @@ private UnionIndexCapability unionOfOrderCapabilities( IndexOrder[]... indexOrde private IndexCapability capabilityWithValue( IndexValueCapability valueCapability ) { - IndexCapability mock = mock( IndexCapability.class ); + IndexCapability mock = mockedIndexCapability(); when( mock.valueCapability( any() ) ).thenReturn( valueCapability ); return mock; } - private IndexCapability capabilityWithOrder( IndexOrder[] indexOrder ) + private IndexCapability mockedIndexCapability() { IndexCapability mock = mock( IndexCapability.class ); + when( mock.limitations() ).thenReturn( IndexCapability.LIMITIATION_NONE ); + return mock; + } + + private IndexCapability capabilityWithOrder( IndexOrder[] indexOrder ) + { + IndexCapability mock = mockedIndexCapability(); when( mock.orderCapability( any() ) ).thenReturn( indexOrder ); return mock; }