/* * Copyright (c) 2002-2009 "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.lucene; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.junit.Ignore; import org.junit.Test; import org.neo4j.graphdb.Node; import org.neo4j.index.IndexHits; import org.neo4j.index.IndexService; public class TestLuceneFulltextIndexService extends TestLuceneIndexingService { @Override protected IndexService instantiateIndex() { return new LuceneFulltextIndexService( graphDb() ); } @Override @Ignore public void testCaching() { // Do nothing } @Override @Ignore public void testGetNodesBug() { // Do nothing } @Override @Ignore public void testRemoveAllWithCache() { // Do nothing } @Test public void testSimpleFulltext() { Node node1 = graphDb().createNode(); String value1 = "A value with spaces in it which the fulltext " + "index should tokenize"; String value2 = "Another value with spaces in it"; String key = "some_property"; assertTrue( !index().getNodes( key, value1 ).iterator().hasNext() ); assertTrue( !index().getNodes( key, value2 ).iterator().hasNext() ); index().index( node1, key, value1 ); Iterator<Node> itr = index().getNodes( key, "fulltext" ).iterator(); assertEquals( node1, itr.next() ); assertTrue( !itr.hasNext() ); index().removeIndex( node1, key, value1 ); assertTrue( !index().getNodes( key, value1 ).iterator().hasNext() ); index().index( node1, key, value1 ); Node node2 = graphDb().createNode(); index().index( node2, key, value1 ); restartTx(); IndexHits<Node> hits = index().getNodes( key, "tokenize" ); itr = hits.iterator(); assertTrue( itr.next() != null ); assertTrue( itr.next() != null ); assertTrue( !itr.hasNext() ); assertTrue( !itr.hasNext() ); assertEquals( 2, hits.size() ); index().removeIndex( node1, key, value1 ); index().removeIndex( node2, key, value1 ); assertTrue( !index().getNodes( key, value1 ).iterator().hasNext() ); itr = index().getNodes( key, value1 ).iterator(); assertTrue( !itr.hasNext() ); restartTx(); node1.delete(); node2.delete(); } @Test public void testSpecific() throws Exception { Node andy = graphDb().createNode(); Node larry = graphDb().createNode(); String key = "atest"; index().index( andy, key, "Andy Wachowski" ); index().index( larry, key, "Larry Wachowski" ); assertCollection( asCollection( index().getNodes( key, "andy wachowski" ) ), andy ); assertCollection( asCollection( index().getNodes( key, "Andy Wachowski\t " ) ), andy ); assertCollection( asCollection( index().getNodes( key, "wachowski larry" ) ), larry ); assertCollection( asCollection( index().getNodes( key, "andy" ) ), andy ); assertCollection( asCollection( index().getNodes( key, "Andy" ) ), andy ); assertCollection( asCollection( index().getNodes( key, "larry" ) ), larry ); assertCollection( asCollection( index().getNodes( key, "andy larry" ) ) ); assertCollection( asCollection( index().getNodes( key, "wachowski" ) ), andy, larry ); assertCollection( asCollection( index().getNodes( key, "wachow*" ) ) ); andy.delete(); larry.delete(); } @Test public void testAnotherChangeValueBug() throws Exception { Node andy = graphDb().createNode(); Node larry = graphDb().createNode(); andy.setProperty( "name", "Andy Wachowski" ); // Deliberately set Larry's name wrong larry.setProperty( "name", "Andy Wachowski" ); index().index( andy, "name", andy.getProperty( "name" ) ); index().index( larry, "name", larry.getProperty( "name" ) ); assertCollection( asCollection( index().getNodes( "name", "wachowski" ) ), andy, larry ); // Correct Larry's name index().removeIndex( larry, "name", larry.getProperty( "name" ) ); larry.setProperty( "name", "Larry Wachowski" ); index().index( larry, "name", larry.getProperty( "name" ) ); assertCollection( asCollection( index().getNodes( "name", "wachowski" ) ), andy, larry ); } @Test public void testBreakLazyIteratorAfterRemove() throws Exception { int oldLazyThreshold = ( ( LuceneFulltextIndexService ) index() ).getLazySearchResultThreshold(); int lazyThreshold = 5; ( ( LuceneFulltextIndexService ) index() ).setLazySearchResultThreshold( lazyThreshold ); List<Node> nodes = new ArrayList<Node>(); String key = "lazykey"; String value = "value"; for ( int i = 0; i < lazyThreshold + 1; i++ ) { Node node = graphDb().createNode(); nodes.add( node ); index().index( node, key, value ); } restartTx(); // Assert they're all there IndexHits<Node> hits = index().getNodes( key, value ); assertCollection( asCollection( hits ), nodes.toArray( new Node[ 0 ] ) ); assertEquals( nodes.size(), hits.size() ); // Search again, but don't iterate the result. then remove one node // and see how it goes (w/o committing). hits = index().getNodes( key, value ); Node anyNode = nodes.get( nodes.size() - 1 ); index().removeIndex( anyNode, key, value ); assertCollection( asCollection( hits ), nodes.toArray( new Node[ 0 ] ) ); assertEquals( nodes.size(), hits.size() ); restartTx( false ); // do it again, but this time commit the removal hits = index().getNodes( key, value ); index().removeIndex( anyNode, key, value ); Node anyOtherNode = nodes.get( nodes.size() - 2 ); anyOtherNode.delete(); restartTx(); // We don't know exactly how lucene does here... if the removedIndex // will cause any trouble (if it has been cached in lucene). // So just see if we get any exceptions. for ( Node hit : hits ) { } ( ( LuceneFulltextIndexService ) index() ).setLazySearchResultThreshold( oldLazyThreshold ); } @Test public void testFulltextRemoveAll() throws Exception { Node node1 = graphDb().createNode(); Node node2 = graphDb().createNode(); String key = "removeall"; index().index( node1, key, "value1" ); index().index( node1, key, "value2" ); index().index( node2, key, "value1" ); index().index( node2, key, "value2" ); assertCollection( asCollection( index().getNodes( key, "value1" ) ), node1, node2 ); index().removeIndex( node1, key ); assertCollection( asCollection( index().getNodes( key, "value1" ) ), node2 ); assertCollection( asCollection( index().getNodes( key, "value2" ) ), node2 ); index().removeIndex( node2, key ); node2.delete(); node1.delete(); } private LuceneFulltextIndexService fulltextIndex() { return (LuceneFulltextIndexService) index(); } @Test public void testExactMatching() { Node node1 = graphDb().createNode(); Node node2 = graphDb().createNode(); String key = "exact"; index().index( node1, key, "neo4j is great" ); index().index( node2, key, "lucene is great" ); assertCollection( index().getNodes( key, "great" ), node1, node2 ); assertCollection( fulltextIndex().getNodesExactMatch( key, "great" ) ); assertCollection( fulltextIndex().getNodesExactMatch( key, "neo4j is great" ), node1 ); assertCollection( fulltextIndex().getNodesExactMatch( key, "lucene is great" ), node2 ); restartTx(); assertCollection( index().getNodes( key, "great" ), node1, node2 ); assertCollection( fulltextIndex().getNodesExactMatch( key, "great" ) ); assertCollection( fulltextIndex().getNodesExactMatch( key, "neo4j is great" ), node1 ); assertCollection( fulltextIndex().getNodesExactMatch( key, "lucene is great" ), node2 ); assertNull( fulltextIndex().getSingleNodeExactMatch( key, "great" ) ); assertEquals( node1, fulltextIndex().getSingleNodeExactMatch( key, "neo4j is great" ) ); assertEquals( node2, fulltextIndex().getSingleNodeExactMatch( key, "lucene is great" ) ); index().removeIndex( key ); node2.delete(); node1.delete(); } /* * This test is just here to get performance numbers on different scenarios: * o Do many gets where the transaction is restarted between each get * o Do many gets, all in the same transaction * o Do many gets, none of them in a transaction */ @Ignore @Test public void testKjsdk() { Node node = graphDb().createNode(); String key = "perf"; for ( int i = 0; i < 100000; i++ ) { index().index( node, key, i ); if ( i % 10000 == 0 ) { restartTx(); } } restartTx(); int times = 20; int count = 10000; for ( int i = 0; i < times; i++ ) { long t = System.currentTimeMillis(); for ( int ii = 0; ii < count; ii++ ) { index().getSingleNode( key, 1000 ); restartTx(); } long total = System.currentTimeMillis() - t; System.out.println( "total:" + total ); } for ( int i = 0; i < times; i++ ) { long t = System.currentTimeMillis(); for ( int ii = 0; ii < count; ii++ ) { index().getSingleNode( key, 1000 ); } long totalSameTx = System.currentTimeMillis() - t; System.out.println( "total same tx:" + totalSameTx ); } finishTx( true ); for ( int i = 0; i < times; i++ ) { long t = System.currentTimeMillis(); for ( int ii = 0; ii < count; ii++ ) { index().getSingleNode( key, 1000 ); } long totalNoTx = System.currentTimeMillis() - t; System.out.println( "total no tx:" + totalNoTx ); } } }