/** * Copyright (c) 2002-2011 "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 Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.neo4j.index.impl.lucene; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.index.Index; import org.neo4j.helpers.collection.MapUtil; import org.neo4j.index.Neo4jTestCase; import org.neo4j.kernel.AbstractGraphDatabase; import org.neo4j.kernel.EmbeddedGraphDatabase; import java.io.File; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.neo4j.index.Neo4jTestCase.assertContains; import static org.neo4j.index.impl.lucene.Contains.contains; import static org.neo4j.index.impl.lucene.HasThrownException.hasThrownException; public class TestIndexDeletion { private static final String INDEX_NAME = "index"; private static GraphDatabaseService graphDb; private Index<Node> index; private Transaction tx; private String key; private Node node; private String value; private List<WorkThread> workers; @BeforeClass public static void setUpStuff() { String storeDir = "target/var/freshindex"; Neo4jTestCase.deleteFileOrDirectory( new File( storeDir ) ); graphDb = new EmbeddedGraphDatabase( storeDir, MapUtil.stringMap( "index", "lucene" ) ); } @AfterClass public static void tearDownStuff() { graphDb.shutdown(); } @After public void commitTx() { finishTx( true ); for ( WorkThread worker : workers ) { worker.rollback(); worker.die(); } } public void rollbackTx() { finishTx( false ); } public void finishTx( boolean success ) { if ( tx != null ) { if ( success ) { tx.success(); } tx.finish(); tx = null; } } @Before public void createInitialData() { beginTx(); index = graphDb.index().forNodes( INDEX_NAME ); index.delete(); restartTx(); index = graphDb.index().forNodes( INDEX_NAME ); key = "key"; value = "my own value"; node = graphDb.createNode(); index.add( node, key, value ); workers = new ArrayList<WorkThread>(); } public void beginTx() { if ( tx == null ) { tx = graphDb.beginTx(); } } void restartTx() { finishTx( true ); beginTx(); } @Test public void shouldBeAbleToDeleteAndRecreateIndex() { restartTx(); assertContains( index.query( key, "own" ) ); index.delete(); restartTx(); Index<Node> recreatedIndex = graphDb.index().forNodes( INDEX_NAME, LuceneIndexProvider.FULLTEXT_CONFIG ); assertNull( recreatedIndex.get( key, value ).getSingle() ); recreatedIndex.add( node, key, value ); restartTx(); assertContains( recreatedIndex.query( key, "own" ), node ); recreatedIndex.delete(); } @Test public void shouldNotBeDeletedWhenDeletionRolledBack() { restartTx(); index.delete(); rollbackTx(); index.get( key, value ); } @Test( expected = IllegalStateException.class ) public void shouldThrowIllegalStateForActionsAfterDeletedOnIndex() { restartTx(); index.delete(); restartTx(); index.query( key, "own" ); } @Test( expected = IllegalStateException.class ) public void shouldThrowIllegalStateForActionsAfterDeletedOnIndex2() { restartTx(); index.delete(); restartTx(); index.add( node, key, value ); } @Test( expected = IllegalStateException.class ) public void shouldThrowIllegalStateForActionsAfterDeletedOnIndex3() { restartTx(); index.delete(); index.query( key, "own" ); } @Test( expected = IllegalStateException.class ) public void shouldThrowIllegalStateForActionsAfterDeletedOnIndex4() { restartTx(); index.delete(); Index<Node> newIndex = graphDb.index().forNodes( INDEX_NAME ); newIndex.query( key, "own" ); } @Test public void deleteInOneTxShouldNotAffectTheOther() throws InterruptedException { index.delete(); WorkThread firstTx = createWorker(); firstTx.createNodeAndIndexBy( key, "another value" ); firstTx.commit(); } @Test public void deleteAndCommitShouldBePublishedToOtherTransaction2() throws InterruptedException { WorkThread firstTx = createWorker(); WorkThread secondTx = createWorker(); firstTx.beginTransaction(); secondTx.beginTransaction(); firstTx.createNodeAndIndexBy( key, "some value" ); secondTx.createNodeAndIndexBy( key, "some other value" ); firstTx.deleteIndex(); firstTx.commit(); secondTx.queryIndex( key, "some other value" ); assertThat( secondTx, hasThrownException() ); secondTx.rollback(); // Since $Before will start a tx, add a value and keep tx open and workers will // delete the index so this test will fail in @After if we don't rollback this tx rollbackTx(); } @Test public void indexDeletesShouldNotByVisibleUntilCommit() { commitTx(); WorkThread firstTx = createWorker(); WorkThread secondTx = createWorker(); firstTx.beginTransaction(); firstTx.removeFromIndex( key, value ); assertThat( secondTx.queryIndex( key, value ), contains( node ) ); firstTx.rollback(); } @Test public void indexDeleteShouldDeleteDirectory() { String otherIndexName = "other-index"; File pathToLuceneIndex = new File( ((AbstractGraphDatabase)graphDb).getStoreDir() + "/index/lucene/node/" + INDEX_NAME ); File pathToOtherLuceneIndex = new File( ((AbstractGraphDatabase)graphDb).getStoreDir() + "/index/lucene/node/" + otherIndexName ); Index<Node> otherIndex = graphDb.index().forNodes( otherIndexName ); Node node = graphDb.createNode(); otherIndex.add( node, "someKey", "someValue" ); assertFalse( pathToLuceneIndex.exists() ); assertFalse( pathToOtherLuceneIndex.exists() ); restartTx(); // Here "index" and "other-index" indexes should exist assertTrue( pathToLuceneIndex.exists() ); assertTrue( pathToOtherLuceneIndex.exists() ); index.delete(); assertTrue( pathToLuceneIndex.exists() ); assertTrue( pathToOtherLuceneIndex.exists() ); restartTx(); // Here only "other-index" should exist assertFalse( pathToLuceneIndex.exists() ); assertTrue( pathToOtherLuceneIndex.exists() ); } private WorkThread createWorker() { WorkThread workThread = new WorkThread( index, graphDb ); workers.add( workThread ); return workThread; } }