diff --git a/community/kernel/src/main/java/org/neo4j/kernel/builtinprocs/BuiltInProcedures.java b/community/kernel/src/main/java/org/neo4j/kernel/builtinprocs/BuiltInProcedures.java index deaecddacb88b..5e299336f420d 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/builtinprocs/BuiltInProcedures.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/builtinprocs/BuiltInProcedures.java @@ -23,6 +23,7 @@ import java.util.Comparator; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Spliterator; import java.util.Spliterators; import java.util.concurrent.TimeUnit; @@ -231,7 +232,7 @@ public Stream nodeManualIndexSeek( @Name( "indexName" ) String legac @Description( "Search nodes from manual index. Replaces `START n=node:nodes('key:foo*')`" ) @Procedure( name = "db.nodeManualIndexSearch", mode = READ ) - public Stream nodeManualIndexSearch( @Name( "indexName" ) String manualIndexName, + public Stream nodeManualIndexSearch( @Name( "indexName" ) String manualIndexName, @Name( "query" ) Object query ) throws ProcedureException { @@ -239,7 +240,7 @@ public Stream nodeManualIndexSearch( @Name( "indexName" ) String man { ReadOperations readOperations = statement.readOperations(); LegacyIndexHits hits = readOperations.nodeLegacyIndexQuery( manualIndexName, query ); - return toStream( hits, ( id ) -> new NodeResult( graphDatabaseAPI.getNodeById( id ) ) ); + return toWeightedNodeResultStream( hits ); } catch ( LegacyIndexNotFoundKernelException e ) { @@ -270,7 +271,7 @@ public Stream relationshipManualIndexSeek( @Name( "indexName @Description( "Search relationship from manual index. Replaces `START r=relationship:relIndex('key:foo*')`" ) @Procedure( name = "db.relationshipManualIndexSearch", mode = READ ) - public Stream relationshipManualIndexSearch( @Name( "indexName" ) String manualIndexName, + public Stream relationshipManualIndexSearch( @Name( "indexName" ) String manualIndexName, @Name( "query" ) Object query ) throws ProcedureException { @@ -278,7 +279,7 @@ public Stream relationshipManualIndexSearch( @Name( "indexNa { ReadOperations readOperations = statement.readOperations(); LegacyIndexHits hits = readOperations.relationshipLegacyIndexQuery( manualIndexName, query, -1, -1 ); - return toStream( hits, ( id ) -> new RelationshipResult( graphDatabaseAPI.getRelationshipById( id ) ) ); + return toWeightedRelationshipResultStream( hits ); } catch ( LegacyIndexNotFoundKernelException e ) { @@ -306,13 +307,13 @@ public Stream nodeAutoIndexSeek( @Name( "key" ) String key, @Name( " @Description( "Search nodes from automatic index. Replaces `START n=node:node_auto_index('key:foo*')`" ) @Procedure( name = "db.nodeAutoIndexSearch", mode = READ ) - public Stream nodeAutoIndexSearch( @Name( "query" ) Object query ) throws ProcedureException + public Stream nodeAutoIndexSearch( @Name( "query" ) Object query ) throws ProcedureException { try ( Statement statement = tx.acquireStatement() ) { ReadOperations readOperations = statement.readOperations(); LegacyIndexHits hits = readOperations.nodeLegacyIndexQuery( "node_auto_index", query ); - return toStream( hits, ( id ) -> new NodeResult( graphDatabaseAPI.getNodeById( id ) ) ); + return toWeightedNodeResultStream( hits ); } catch ( LegacyIndexNotFoundKernelException e ) { @@ -340,13 +341,13 @@ public Stream relationshipAutoIndexSeek( @Name( "key" ) Stri @Description( "Search relationship from automatic index. Replaces `START r=relationship:relationship_auto_index('key:foo*')`" ) @Procedure( name = "db.relationshipAutoIndexSearch", mode = READ ) - public Stream relationshipAutoIndexSearch( @Name( "query" ) Object query ) throws ProcedureException + public Stream relationshipAutoIndexSearch( @Name( "query" ) Object query ) throws ProcedureException { try ( Statement statement = tx.acquireStatement() ) { ReadOperations readOperations = statement.readOperations(); LegacyIndexHits hits = readOperations.relationshipLegacyIndexQuery( "relationship_auto_index", query, -1, -1 ); - return toStream( hits, ( id ) -> new RelationshipResult( graphDatabaseAPI.getRelationshipById( id ) ) ); + return toWeightedRelationshipResultStream( hits ); } catch ( LegacyIndexNotFoundKernelException e ) { @@ -355,92 +356,129 @@ public Stream relationshipAutoIndexSearch( @Name( "query" ) } } + @Description( "Get or create a node manual index - YIELD type,name,config" ) + @Procedure( name = "db.nodeManualIndex", mode = WRITE ) + public Stream nodeManualIndex( @Name( "indexName" ) String legacyIndexName ) + { + IndexManager mgr = graphDatabaseAPI.index(); + Index index = mgr.forNodes( legacyIndexName ); + return Stream.of( new LegacyIndexInfo( "NODE", legacyIndexName, mgr.getConfiguration( index ) ) ); + } + + @Description( "Get or create a relationship manual index - YIELD type,name,config" ) + @Procedure( name = "db.relationshipManualIndex", mode = WRITE ) + public Stream relationshipManualIndex( @Name( "indexName" ) String legacyIndexName ) + { + IndexManager mgr = graphDatabaseAPI.index(); + Index index = mgr.forRelationships( legacyIndexName ); + return Stream.of( new LegacyIndexInfo( "RELATIONSHIP", legacyIndexName, mgr.getConfiguration( index ) ) ); + } + @Description( "Check if a node manual index exists" ) @Procedure( name = "db.nodeManualIndexExists", mode = READ ) public Stream nodeManualIndexExists( @Name( "indexName" ) String legacyIndexName ) { - BooleanResult result = new BooleanResult( graphDatabaseAPI.index().existsForNodes( legacyIndexName ) ); - ArrayList arrayResult = new ArrayList<>(); - arrayResult.add( result ); - return arrayResult.stream(); + return Stream.of( new BooleanResult( graphDatabaseAPI.index().existsForNodes( legacyIndexName ) ) ); } @Description( "Check if a relationship manual index exists" ) @Procedure( "db.relationshipManualIndexExists" ) public Stream relationshipManualIndexExists( @Name( "indexName" ) String legacyIndexName ) { - BooleanResult result = new BooleanResult( graphDatabaseAPI.index().existsForRelationships( legacyIndexName ) ); - ArrayList arrayResult = new ArrayList<>(); - arrayResult.add( result ); - return arrayResult.stream(); + return Stream.of( new BooleanResult( graphDatabaseAPI.index().existsForRelationships( legacyIndexName ) ) ); } - @Description( "Removes an manual index" ) + @Description("List all manual indexes - YIELD type,name,config") + @Procedure( name = "db.manualIndexes", mode = READ ) + public Stream list() + { + IndexManager mgr = graphDatabaseAPI.index(); + List indexInfos = new ArrayList<>( 100 ); + for ( String name : mgr.nodeIndexNames() ) + { + Index index = mgr.forNodes( name ); + indexInfos.add( new LegacyIndexInfo( "NODE", name, mgr.getConfiguration( index ) ) ); + } + for ( String name : mgr.relationshipIndexNames() ) + { + RelationshipIndex index = mgr.forRelationships( name ); + indexInfos.add( new LegacyIndexInfo( "RELATIONSHIP", name, mgr.getConfiguration( index ) ) ); + } + return indexInfos.stream(); + } + + @Description( "Remove a manual index - YIELD type,name,config" ) @Procedure( name = "db.manualIndexDrop", mode = WRITE ) - public Stream manualIndexDrop( @Name( "indexName" ) String legacyIndexName ) + public Stream manualIndexDrop( @Name( "indexName" ) String legacyIndexName ) { IndexManager mgr = graphDatabaseAPI.index(); - List results = new ArrayList<>( 2 ); + List results = new ArrayList<>( 2 ); if ( mgr.existsForNodes( legacyIndexName ) ) { Index index = mgr.forNodes( legacyIndexName ); - results.add( new BooleanResult( true ) ); + results.add( new LegacyIndexInfo( "NODE", legacyIndexName, mgr.getConfiguration( index ) ) ); index.delete(); } if ( mgr.existsForRelationships( legacyIndexName ) ) { RelationshipIndex index = mgr.forRelationships( legacyIndexName ); - results.add( new BooleanResult( true ) ); + results.add( new LegacyIndexInfo( "RELATIONSHIP", legacyIndexName, mgr.getConfiguration( index ) ) ); index.delete(); } - if ( results.isEmpty() ) - { - results.add( new BooleanResult( false ) ); - } return results.stream(); } - @Description( "Add a node to a manual index" ) + @Description( "Add a node to a manual index based on a specified key and value" ) @Procedure( name = "db.nodeManualIndexAdd", mode = WRITE ) public Stream nodeManualIndexAdd( @Name( "indexName" ) String legacyIndexName, @Name( "node" ) Node node, @Name( "key" ) String key, @Name( "value" ) Object value ) { graphDatabaseAPI.index().forNodes( legacyIndexName ).add( node, key, value ); - List results = new ArrayList<>( 1 ); - results.add( new BooleanResult( true ) ); - return results.stream(); + // Failures will be expressed as exceptions before the return + return Stream.of( new BooleanResult( true ) ); } - @Description( "Add a relationship to a manual index" ) + @Description( "Add a relationship to a manual index based on a specified key and value" ) @Procedure( name = "db.relationshipManualIndexAdd", mode = WRITE ) public Stream relationshipManualIndexAdd( @Name( "indexName" ) String legacyIndexName, @Name( "relationship" ) Relationship relationship, @Name( "key" ) String key, @Name( "value" ) Object value ) { graphDatabaseAPI.index().forRelationships( legacyIndexName ).add( relationship, key, value ); - List results = new ArrayList<>( 1 ); - results.add( new BooleanResult( true ) ); - return results.stream(); + // Failures will be expressed as exceptions before the return + return Stream.of( new BooleanResult( true ) ); } - @Description( "Remove a node for a manual index" ) + @Description( "Remove a node from a manual index with an optional key" ) @Procedure( name = "db.nodeManualIndexRemove", mode = WRITE ) public Stream nodeManualIndexRemove( @Name( "indexName" ) String legacyIndexName, @Name( "node" ) Node node, @Name( "key" ) String key ) { - graphDatabaseAPI.index().forNodes( legacyIndexName ).remove( node, key ); - List results = new ArrayList<>( 1 ); - results.add( new BooleanResult( true ) ); - return results.stream(); + if ( key.equals( Name.DEFAULT_VALUE ) ) + { + graphDatabaseAPI.index().forNodes( legacyIndexName ).remove( node ); + } + else + { + graphDatabaseAPI.index().forNodes( legacyIndexName ).remove( node, key ); + } + // Failures will be expressed as exceptions before the return + return Stream.of( new BooleanResult( true ) ); } - @Description( "Remove a relationship for a manual index" ) + @Description( "Remove a relationship from a manual index with an optional key" ) @Procedure( name = "db.relationshipManualIndexRemove", mode = WRITE ) public Stream relationshipManualIndexRemove( @Name( "indexName" ) String legacyIndexName, @Name( "relationship" ) Relationship relationship, @Name( "key" ) String key ) { - graphDatabaseAPI.index().forRelationships( legacyIndexName ).remove( relationship, key ); - List results = new ArrayList<>( 1 ); - results.add( new BooleanResult( true ) ); - return results.stream(); + if ( key.equals( Name.DEFAULT_VALUE ) ) + { + graphDatabaseAPI.index().forRelationships( legacyIndexName ).remove( relationship ); + } + else + { + graphDatabaseAPI.index().forRelationships( legacyIndexName ).remove( relationship, key ); + } + // Failures will be expressed as exceptions before the return + return Stream.of( new BooleanResult( true ) ); } private Stream toStream( PrimitiveLongResourceIterator iterator, Function mapper ) @@ -463,6 +501,46 @@ public T next() return StreamSupport.stream( Spliterators.spliteratorUnknownSize( it, Spliterator.ORDERED ), false ); } + private Stream toWeightedNodeResultStream( LegacyIndexHits iterator ) + { + Iterator it = new Iterator() + { + @Override + public boolean hasNext() + { + return iterator.hasNext(); + } + + @Override + public WeightedNodeResult next() + { + return new WeightedNodeResult( graphDatabaseAPI.getNodeById( iterator.next() ), iterator.currentScore() ); + } + }; + + return StreamSupport.stream( Spliterators.spliteratorUnknownSize( it, Spliterator.ORDERED ), false ); + } + + private Stream toWeightedRelationshipResultStream( LegacyIndexHits iterator ) + { + Iterator it = new Iterator() + { + @Override + public boolean hasNext() + { + return iterator.hasNext(); + } + + @Override + public WeightedRelationshipResult next() + { + return new WeightedRelationshipResult( graphDatabaseAPI.getRelationshipById( iterator.next() ), iterator.currentScore() ); + } + }; + + return StreamSupport.stream( Spliterators.spliteratorUnknownSize( it, Spliterator.ORDERED ), false ); + } + private IndexProcedures indexProcedures() { return new IndexProcedures( tx, resolver.resolveDependency( IndexingService.class ) ); @@ -527,6 +605,18 @@ private IndexResult( String description, String state, String type ) } } + public static class LegacyIndexInfo { + public final String type; + public final String name; + public final Map config; + + public LegacyIndexInfo(String type, String name, Map config) { + this.type = type; + this.name = name; + this.config = config; + } + } + @SuppressWarnings( "unused" ) public class ConstraintResult { @@ -549,6 +639,32 @@ public NodeResult( Node node ) public final Node node; } + @SuppressWarnings( "unused" ) + public class WeightedNodeResult + { + public final Node node; + public final double weight; + + public WeightedNodeResult( Node node, double weight ) + { + this.node = node; + this.weight = weight; + } + } + + @SuppressWarnings( "unused" ) + public class WeightedRelationshipResult + { + public final Relationship relationship; + public final double weight; + + public WeightedRelationshipResult( Relationship relationship, double weight ) + { + this.relationship = relationship; + this.weight = weight; + } + } + @SuppressWarnings( "unused" ) public class RelationshipResult { diff --git a/community/kernel/src/test/java/org/neo4j/kernel/builtinprocs/BuiltInProceduresTest.java b/community/kernel/src/test/java/org/neo4j/kernel/builtinprocs/BuiltInProceduresTest.java index f1e4e760073c2..a71c840bc7e7d 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/builtinprocs/BuiltInProceduresTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/builtinprocs/BuiltInProceduresTest.java @@ -209,18 +209,66 @@ public void shouldListCorrectBuiltinProcedures() throws Throwable "db.schema() :: (nodes :: LIST? OF NODE?, relationships :: LIST? OF RELATIONSHIP?)", "Show the schema of the data." ), record( "db.nodeManualIndexSearch", - "db.nodeManualIndexSearch(indexName :: STRING?, query :: ANY?) :: (node :: NODE?)", + "db.nodeManualIndexSearch(indexName :: STRING?, query :: ANY?) :: (node :: NODE?, weight :: FLOAT?)", "Search nodes from manual index. Replaces `START n=node:nodes('key:foo*')`"), record( "db.nodeManualIndexSeek", "db.nodeManualIndexSeek(indexName :: STRING?, key :: STRING?, value :: ANY?) :: (node :: NODE?)", "Get node from manual index. Replaces `START n=node:nodes(key = 'A')`"), record( "db.relationshipManualIndexSearch", - "db.relationshipManualIndexSearch(indexName :: STRING?, query :: ANY?) :: (relationship :: RELATIONSHIP?)", + "db.relationshipManualIndexSearch(indexName :: STRING?, query :: ANY?) :: (relationship :: RELATIONSHIP?, weight :: FLOAT?)", "Search relationship from manual index. Replaces `START r=relationship:relIndex('key:foo*')`"), record( "db.relationshipManualIndexSeek", "db.relationshipManualIndexSeek(indexName :: STRING?, key :: STRING?, value :: ANY?) :: " + "(relationship :: RELATIONSHIP?)", "Get relationship from manual index. Replaces `START r=relationship:relIndex(key = 'A')`"), + record( "db.nodeAutoIndexSearch", + "db.nodeAutoIndexSearch(query :: ANY?) :: (node :: NODE?, weight :: FLOAT?)", + "Search nodes from automatic index. Replaces `START n=node:node_auto_index('key:foo*')`"), + record( "db.nodeAutoIndexSeek", + "db.nodeAutoIndexSeek(key :: STRING?, value :: ANY?) :: (node :: NODE?)", + "Get node from automatic index. Replaces `START n=node:node_auto_index(key = 'A')`"), + record( "db.relationshipAutoIndexSearch", + "db.relationshipAutoIndexSearch(query :: ANY?) :: (relationship :: RELATIONSHIP?, weight :: FLOAT?)", + "Search relationship from automatic index. Replaces `START r=relationship:relationship_auto_index('key:foo*')`"), + record( "db.relationshipAutoIndexSeek", + "db.relationshipAutoIndexSeek(key :: STRING?, value :: ANY?) :: " + + "(relationship :: RELATIONSHIP?)", + "Get relationship from automatic index. Replaces `START r=relationship:relationship_auto_index(key = 'A')`"), + record( "db.nodeManualIndexAdd", + "db.nodeManualIndexAdd(indexName :: STRING?, node :: NODE?, key :: STRING?, value :: ANY?) :: (success :: BOOLEAN?)", + "Add a node to a manual index based on a specified key and value"), + record( "db.relationshipManualIndexAdd", + "db.relationshipManualIndexAdd(indexName :: STRING?, relationship :: RELATIONSHIP?, key :: STRING?, value :: ANY?) :: " + + "(success :: BOOLEAN?)", + "Add a relationship to a manual index based on a specified key and value"), + record( "db.nodeManualIndexRemove", + "db.nodeManualIndexRemove(indexName :: STRING?, node :: NODE?, key :: STRING?) :: (success :: BOOLEAN?)", + "Remove a node from a manual index with an optional key"), + record( "db.relationshipManualIndexRemove", + "db.relationshipManualIndexRemove(indexName :: STRING?, relationship :: RELATIONSHIP?, key :: STRING?) :: " + + "(success :: BOOLEAN?)", + "Remove a relationship from a manual index with an optional key"), + record( "db.manualIndexDrop", + "db.manualIndexDrop(indexName :: STRING?) :: " + + "(type :: STRING?, name :: STRING?, config :: MAP?)", + "Remove a manual index - YIELD type,name,config"), + record( "db.nodeManualIndex", + "db.nodeManualIndex(indexName :: STRING?) :: " + + "(type :: STRING?, name :: STRING?, config :: MAP?)", + "Get or create a node manual index - YIELD type,name,config"), + record( "db.relationshipManualIndex", + "db.relationshipManualIndex(indexName :: STRING?) :: " + + "(type :: STRING?, name :: STRING?, config :: MAP?)", + "Get or create a relationship manual index - YIELD type,name,config"), + record( "db.nodeManualIndexExists", + "db.nodeManualIndexExists(indexName :: STRING?) :: (success :: BOOLEAN?)", + "Check if a node manual index exists"), + record( "db.relationshipManualIndexExists", + "db.relationshipManualIndexExists(indexName :: STRING?) :: (success :: BOOLEAN?)", + "Check if a relationship manual index exists"), + record( "db.manualIndexes", + "db.manualIndexes() :: (type :: STRING?, name :: STRING?, config :: MAP?)", + "List all manual indexes - YIELD type,name,config"), record( "dbms.components", "dbms.components() :: (name :: STRING?, versions :: LIST? OF STRING?, edition :: STRING?)", "List DBMS components and their versions." ), diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/integrationtest/BuiltInProceduresIT.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/integrationtest/BuiltInProceduresIT.java index d7947474fa992..ed916c5d70b4c 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/integrationtest/BuiltInProceduresIT.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/integrationtest/BuiltInProceduresIT.java @@ -165,7 +165,7 @@ public void listProcedures() throws Throwable "Create a RelationshipType" } ), equalTo( new Object[]{"db.nodeManualIndexSearch", - "db.nodeManualIndexSearch(indexName :: STRING?, query :: ANY?) :: (node :: NODE?)", + "db.nodeManualIndexSearch(indexName :: STRING?, query :: ANY?) :: (node :: NODE?, weight :: FLOAT?)", "Search nodes from manual index. Replaces `START n=node:nodes('key:foo*')`" } ), equalTo( new Object[]{"db.nodeManualIndexSeek", @@ -175,9 +175,58 @@ public void listProcedures() throws Throwable } ), equalTo( new Object[]{"db.relationshipManualIndexSearch", "db.relationshipManualIndexSearch(indexName :: STRING?, query :: ANY?) :: (relationship :: " + - "RELATIONSHIP?)", + "RELATIONSHIP?, weight :: FLOAT?)", "Search relationship from manual index. Replaces `START r=relationship:relIndex('key:foo*')`" } ), + equalTo( new Object[]{ "db.nodeAutoIndexSearch", + "db.nodeAutoIndexSearch(query :: ANY?) :: (node :: NODE?, weight :: FLOAT?)", + "Search nodes from automatic index. Replaces `START n=node:node_auto_index('key:foo*')`"} ), + equalTo( new Object[]{ "db.nodeAutoIndexSeek", + "db.nodeAutoIndexSeek(key :: STRING?, value :: ANY?) :: (node :: NODE?)", + "Get node from automatic index. Replaces `START n=node:node_auto_index(key = 'A')`"} ), + equalTo( new Object[]{ "db.relationshipAutoIndexSearch", + "db.relationshipAutoIndexSearch(query :: ANY?) :: (relationship :: RELATIONSHIP?, weight :: FLOAT?)", + "Search relationship from automatic index. Replaces `START r=relationship:relationship_auto_index('key:foo*')`"} ), + equalTo( new Object[]{ "db.relationshipAutoIndexSeek", + "db.relationshipAutoIndexSeek(key :: STRING?, value :: ANY?) :: " + + "(relationship :: RELATIONSHIP?)", + "Get relationship from automatic index. Replaces `START r=relationship:relationship_auto_index(key = 'A')`"} ), + equalTo( new Object[]{ "db.nodeManualIndexAdd", + "db.nodeManualIndexAdd(indexName :: STRING?, node :: NODE?, key :: STRING?, value :: ANY?) :: (success :: BOOLEAN?)", + "Add a node to a manual index based on a specified key and value"} ), + equalTo( new Object[]{ "db.relationshipManualIndexAdd", + "db.relationshipManualIndexAdd(indexName :: STRING?, relationship :: RELATIONSHIP?, key :: STRING?, value :: ANY?) :: " + + "(success :: BOOLEAN?)", + "Add a relationship to a manual index based on a specified key and value"} ), + equalTo( new Object[]{ "db.nodeManualIndexRemove", + "db.nodeManualIndexRemove(indexName :: STRING?, node :: NODE?, key :: STRING?) :: (success :: BOOLEAN?)", + "Remove a node from a manual index with an optional key"} ), + equalTo( new Object[]{ "db.relationshipManualIndexRemove", + "db.relationshipManualIndexRemove(indexName :: STRING?, relationship :: RELATIONSHIP?, key :: STRING?) :: " + + "(success :: BOOLEAN?)", + "Remove a relationship from a manual index with an optional key"} ), + equalTo( new Object[]{ "db.manualIndexDrop", + "db.manualIndexDrop(indexName :: STRING?) :: " + + "(type :: STRING?, name :: STRING?, config :: MAP?)", + "Remove a manual index - YIELD type,name,config"} ), + equalTo( new Object[]{ "db.nodeManualIndex", + "db.nodeManualIndex(indexName :: STRING?) :: " + + "(type :: STRING?, name :: STRING?, config :: MAP?)", + "Get or create a node manual index - YIELD type,name,config"} ), + equalTo( new Object[]{ "db.relationshipManualIndex", + "db.relationshipManualIndex(indexName :: STRING?) :: " + + "(type :: STRING?, name :: STRING?, config :: MAP?)", + "Get or create a relationship manual index - YIELD type,name,config"} ), + equalTo( new Object[]{ "db.nodeManualIndexExists", + "db.nodeManualIndexExists(indexName :: STRING?) :: (success :: BOOLEAN?)", + "Check if a node manual index exists"} ), + equalTo( new Object[]{ "db.relationshipManualIndexExists", + "db.relationshipManualIndexExists(indexName :: STRING?) :: (success :: BOOLEAN?)", + "Check if a relationship manual index exists"} ), + equalTo( new Object[]{ "db.manualIndexes", + "db.manualIndexes() :: (type :: STRING?, name :: STRING?, config :: MAP?)", + "List all manual indexes - YIELD type,name,config"} ), + equalTo( new Object[]{"db.relationshipManualIndexSeek", "db.relationshipManualIndexSeek(indexName :: STRING?, key :: STRING?, value :: ANY?) :: " + "(relationship :: RELATIONSHIP?)", diff --git a/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/ManualIndexProcsIT.scala b/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/ManualIndexProcsIT.scala index 85a3ebd4ee439..d1d5ffdbff44d 100644 --- a/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/ManualIndexProcsIT.scala +++ b/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/ManualIndexProcsIT.scala @@ -228,10 +228,10 @@ class ManualIndexProcsIT extends ExecutionEngineFunSuite { test("Maunal relationships index should exist") { val a = createNode(Map("name" -> "Neo")) val b = createNode() - val rel = relate(a, b, "weight" -> 12) + val rel = relate(a, b, "distance" -> 12) val addResult = execute( - """MATCH (n)-[r]-(m) WHERE n.name = 'Neo' WITH r CALL db.relationshipManualIndexAdd('relIndex', r, 'weight', 12) YIELD success as s RETURN s""" + """MATCH (n)-[r]-(m) WHERE n.name = 'Neo' WITH r CALL db.relationshipManualIndexAdd('relIndex', r, 'distance', 12) YIELD success as s RETURN s""" .stripMargin).toList addResult should be(List(Map("s" -> true))) @@ -240,7 +240,6 @@ class ManualIndexProcsIT extends ExecutionEngineFunSuite { """CALL db.relationshipManualIndexExists('relIndex') |YIELD success AS s RETURN s""".stripMargin).toList - result should equal(List(Map("s" -> true))) } @@ -249,34 +248,34 @@ class ManualIndexProcsIT extends ExecutionEngineFunSuite { val node = createNode(Map("name" -> "Neo")) // When adding a node to the index - val addResult = execute( - """MATCH (n) WITH n CALL db.nodeManualIndexAdd('usernames', n, 'name', 'Neo') YIELD success as s RETURN s""" - .stripMargin).toList + graph.inTx { + graph.index().forNodes("usernames").add(node, "name", "Neo"); + } - // Then the index should exist + // Then the index should be possible to drop val result = execute("CALL db.manualIndexDrop('usernames')").toList - result should be(List(Map("success" -> true))) + result should be(List(Map("name" -> "usernames", "type" -> "NODE", "config" -> Map("provider" -> "lucene", "type" -> "exact")))) } test("Should be able to drop relationship index") { + // Given a relationship with property val a = createNode(Map("name" -> "Neo")) val b = createNode() - val rel = relate(a, b, "weight" -> 12) - - val addResult = execute( - """MATCH (n)-[r]-(m) WHERE n.name = 'Neo' WITH r CALL db.relationshipManualIndexAdd('relIndex', r, 'weight', 12) YIELD success as s RETURN s""" - .stripMargin).toList + val rel = relate(a, b, "distance" -> 12) - val result = execute( - """CALL db.manualIndexDrop('relIndex') - |YIELD success AS s RETURN s""".stripMargin).toList + // When adding the relationship to an index + graph.inTx { + graph.index().forRelationships("relIndex").add(rel, "distance", 12); + } + // Then the index should be possible to drop + val result = execute("CALL db.manualIndexDrop('relIndex')").toList - result should equal(List(Map("s" -> true))) + result should be(List(Map("name" -> "relIndex", "type" -> "RELATIONSHIP", "config" -> Map("provider" -> "lucene", "type" -> "exact")))) } - test("Should able to remove a node from manual index") { + test("Should able to add and remove a node from manual index") { val node = createNode(Map("name" -> "Neo")) val addResult = execute( @@ -301,33 +300,94 @@ class ManualIndexProcsIT extends ExecutionEngineFunSuite { } - test("Should able to remove a relationship from manual index") { + test("Should able to add and remove a relationship from manual index") { val a = createNode(Map("name" -> "Neo")) val b = createNode() - val rel = relate(a, b, "weight" -> 12) + val rel = relate(a, b, "distance" -> 12) val addResult = execute( - """MATCH (n)-[r]-(m) WHERE n.name = 'Neo' WITH r CALL db.relationshipManualIndexAdd('relIndex', r, 'weight', 12) YIELD success as s RETURN s""" + """MATCH (n)-[r]-(m) WHERE n.name = 'Neo' WITH r CALL db.relationshipManualIndexAdd('relIndex', r, 'distance', 12) YIELD success as s RETURN s""" .stripMargin).toList addResult should be(List(Map("s" -> true))) - val seekResult = execute("CALL db.relationshipManualIndexSeek('relIndex', 'weight', '12') YIELD relationship AS r ").toList + val seekResult = execute("CALL db.relationshipManualIndexSeek('relIndex', 'distance', '12') YIELD relationship AS r ").toList seekResult should equal(List(Map("r" -> rel))) val result = execute( - """MATCH (n)-[r]-(m) WHERE n.name = 'Neo' WITH r CALL db.relationshipManualIndexRemove('relIndex', r, 'weight') YIELD success as s RETURN s""" + """MATCH (n)-[r]-(m) WHERE n.name = 'Neo' WITH r CALL db.relationshipManualIndexRemove('relIndex', r, 'distance') YIELD success as s RETURN s""" .stripMargin).toList result should equal(List(Map("s" -> true))) - val emptyResult = execute("CALL db.relationshipManualIndexSeek('relIndex', 'weight', '12') YIELD relationship AS r ").toList + val emptyResult = execute("CALL db.relationshipManualIndexSeek('relIndex', 'distance', '12') YIELD relationship AS r ").toList emptyResult should equal(List.empty) } + test("should be able to get or create a node index") { + //Given the node index does not exist + graph.inTx { + graph.index().existsForNodes("usernames") should be(false) + } + + //When calling nodeManualIndex + graph.execute("CALL db.nodeManualIndex('usernames')") + + //Then the index should exist + graph.inTx { + graph.index().existsForNodes("usernames") should be(true) + } + } + + test("should be able to get or create a relationship index") { + //Given the relationship index does not exist + graph.inTx { + graph.index().existsForRelationships("relIndex") should be(false) + } + + //When calling nodeManualIndex + graph.execute("CALL db.relationshipManualIndex('relIndex')") + + //Then the index should exist + graph.inTx { + graph.index().existsForRelationships("relIndex") should be(true) + } + } + + test("should be able to list manual and automatic indexes") { + //Given the node and relationship indexes do not exist + graph.inTx { + graph.index.nodeIndexNames().length should be(0) + } + + //When creating indexes both manually and automatically + graph.execute("CALL db.nodeManualIndex('manual1')") + graph.execute("CALL db.relationshipManualIndex('manual2')") + graph.execute("CREATE (n) WITH n CALL db.nodeManualIndexAdd('usernames',n,'username','Neo') YIELD success RETURN success") + graph.execute("CREATE (n), (m), (n)-[r:KNOWS]->(m) WITH r CALL db.relationshipManualIndexAdd('relIndex',r,'distance',42) YIELD success RETURN success") + graph.execute("CREATE (n {email:'joe@soap.net'})") + graph.execute("CREATE (n), (m), (n)-[r:KNOWS {weight:42}]->(m)") + + //Then the indexes should all exist + graph.inTx { + graph.index.nodeIndexNames().toSet should be(Set("manual1", "usernames", "node_auto_index")) + graph.index.relationshipIndexNames().toSet should be(Set("manual2", "relIndex", "relationship_auto_index")) + } + + //And have the right types + val result = execute("CALL db.manualIndexes").toSet + result should be(Set( + Map("name" -> "manual1", "type" -> "NODE", "config" -> Map("provider" -> "lucene", "type" -> "exact")), + Map("name" -> "manual2", "type" -> "RELATIONSHIP", "config" -> Map("provider" -> "lucene", "type" -> "exact")), + Map("name" -> "usernames", "type" -> "NODE", "config" -> Map("provider" -> "lucene", "type" -> "exact")), + Map("name" -> "relIndex", "type" -> "RELATIONSHIP", "config" -> Map("provider" -> "lucene", "type" -> "exact")), + Map("name" -> "node_auto_index", "type" -> "NODE", "config" -> Map("provider" -> "lucene", "type" -> "exact")), + Map("name" -> "relationship_auto_index", "type" -> "RELATIONSHIP", "config" -> Map("provider" -> "lucene", "type" -> "exact")) + )) + } }