diff --git a/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextFactory.java b/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextFactory.java index 221ec1b9f402..d855d3dfafc0 100644 --- a/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextFactory.java +++ b/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextFactory.java @@ -70,8 +70,13 @@ public FulltextFactory( FileSystemAbstraction fileSystem, File storeDir, Analyze public void createFulltextIndex( String identifier, FulltextProvider.FulltextIndexType type, List properties, FulltextProvider provider ) throws IOException { + // First delete any existing index, since we don't know if whatever it might contain actually matches our + // current configuration: + File indexRootFolder = new File( indexDir, identifier ); + fileSystem.deleteRecursively( indexRootFolder ); + LuceneIndexStorageBuilder storageBuilder = LuceneIndexStorageBuilder.create(); - storageBuilder.withFileSystem( fileSystem ).withIndexFolder( new File( indexDir, identifier ) ); + storageBuilder.withFileSystem( fileSystem ).withIndexFolder( indexRootFolder ); provider.register( new LuceneFulltext( storageBuilder.build(), partitionFactory, properties, analyzer, identifier, type ) ); } } diff --git a/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/integrations/bloom/BloomIT.java b/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/integrations/bloom/BloomIT.java index f14490f7d50d..2f8f51c8708c 100644 --- a/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/integrations/bloom/BloomIT.java +++ b/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/integrations/bloom/BloomIT.java @@ -164,6 +164,116 @@ public void shouldPopulateIndexWithExistingDataOnIndexCreate() throws Exception assertFalse( result.hasNext() ); } + @Test + public void startupPopulationShouldNotCauseDuplicates() throws Exception + { + GraphDatabaseBuilder builder = factory.newEmbeddedDatabaseBuilder( testDirectory.graphDbDir() ); + builder.setConfig( LoadableBloomFulltextConfig.bloom_indexed_properties, "prop" ); + + db = builder.newGraphDatabase(); + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode(); + nodeId = node.getId(); + node.setProperty( "prop", "Jyllingevej" ); + tx.success(); + } + + // Verify it's indexed exactly once + db.execute( "CALL db.fulltext.bloomAwaitPopulation" ).close(); + Result result = db.execute( String.format( NODES, "Jyllingevej" ) ); + assertTrue( result.hasNext() ); + assertEquals( nodeId, result.next().get( ENTITYID ) ); + assertFalse( result.hasNext() ); + + db.shutdown(); + db = builder.newGraphDatabase(); + + // Verify it's STILL indexed exactly once + db.execute( "CALL db.fulltext.bloomAwaitPopulation" ).close(); + result = db.execute( String.format( NODES, "Jyllingevej" ) ); + assertTrue( result.hasNext() ); + assertEquals( nodeId, result.next().get( ENTITYID ) ); + assertFalse( result.hasNext() ); + } + + @Test + public void staleDataFromEntityDeleteShouldNotBeAccessibleAfterConfigurationChange() throws Exception + { + GraphDatabaseBuilder builder = factory.newEmbeddedDatabaseBuilder( testDirectory.graphDbDir() ); + builder.setConfig( LoadableBloomFulltextConfig.bloom_indexed_properties, "prop" ); + + db = builder.newGraphDatabase(); + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode(); + nodeId = node.getId(); + node.setProperty( "prop", "Esplanaden" ); + tx.success(); + } + + db.shutdown(); + builder.setConfig( LoadableBloomFulltextConfig.bloom_indexed_properties, "not-prop" ); + db = builder.newGraphDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + // This should no longer be indexed + db.getNodeById( nodeId ).delete(); + tx.success(); + } + + db.shutdown(); + builder.setConfig( LoadableBloomFulltextConfig.bloom_indexed_properties, "prop" ); + db = builder.newGraphDatabase(); + + // Verify that the node is no longer indexed + db.execute( "CALL db.fulltext.bloomAwaitPopulation" ).close(); + Result result = db.execute( String.format( NODES, "Esplanaden" ) ); + assertFalse( result.hasNext() ); + result.close(); + } + + @Test + public void staleDataFromPropertyRemovalShouldNotBeAccessibleAfterConfigurationChange() throws Exception + { + GraphDatabaseBuilder builder = factory.newEmbeddedDatabaseBuilder( testDirectory.graphDbDir() ); + builder.setConfig( LoadableBloomFulltextConfig.bloom_indexed_properties, "prop" ); + + db = builder.newGraphDatabase(); + long nodeId; + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode(); + nodeId = node.getId(); + node.setProperty( "prop", "Esplanaden" ); + tx.success(); + } + + db.shutdown(); + builder.setConfig( LoadableBloomFulltextConfig.bloom_indexed_properties, "not-prop" ); + db = builder.newGraphDatabase(); + + try ( Transaction tx = db.beginTx() ) + { + // This should no longer be indexed + db.getNodeById( nodeId ).removeProperty( "prop" ); + tx.success(); + } + + db.shutdown(); + builder.setConfig( LoadableBloomFulltextConfig.bloom_indexed_properties, "prop" ); + db = builder.newGraphDatabase(); + + // Verify that the node is no longer indexed + db.execute( "CALL db.fulltext.bloomAwaitPopulation" ).close(); + Result result = db.execute( String.format( NODES, "Esplanaden" ) ); + assertFalse( result.hasNext() ); + result.close(); + } + @Test public void shouldNotBeAbleToStartWithoutConfiguringProperties() throws Exception {