gt:         assertEquals( node2, hits.next() );
context: /**
   * Copyright (c) 2002-2014 "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 <http://www.gnu.org/licenses/>.
   */
  package org.neo4j.index.impl.lucene;
  
  import org.apache.lucene.index.Term;
  import org.apache.lucene.queryparser.classic.QueryParser;
  import org.apache.lucene.search.BooleanClause.Occur;
  import org.apache.lucene.search.BooleanQuery;
  import org.apache.lucene.search.NumericRangeQuery;
  import org.apache.lucene.search.Sort;
  import org.apache.lucene.search.TermQuery;
  import org.apache.lucene.search.similarities.DefaultSimilarity;
  import org.junit.Ignore;
  import org.junit.Test;
  import org.neo4j.graphdb.Direction;
  import org.neo4j.graphdb.DynamicRelationshipType;
  import org.neo4j.graphdb.Node;
  import org.neo4j.graphdb.NotFoundException;
  import org.neo4j.graphdb.PropertyContainer;
  import org.neo4j.graphdb.Relationship;
  import org.neo4j.graphdb.RelationshipType;
  import org.neo4j.graphdb.index.Index;
  import org.neo4j.graphdb.index.IndexHits;
  import org.neo4j.graphdb.index.IndexManager;
  import org.neo4j.graphdb.index.RelationshipIndex;
  import org.neo4j.graphdb.index.UniqueFactory;
  import org.neo4j.helpers.collection.IteratorUtil;
  import org.neo4j.helpers.collection.MapUtil;
  import org.neo4j.index.lucene.QueryContext;
  import org.neo4j.index.lucene.ValueContext;
  import org.neo4j.kernel.InternalAbstractGraphDatabase;
  import org.neo4j.test.ImpermanentGraphDatabase;
  
  import java.io.IOException;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.concurrent.Future;
  import java.util.concurrent.atomic.AtomicInteger;
  
  import static org.apache.lucene.search.NumericRangeQuery.newIntRange;
  import static org.hamcrest.core.Is.is;
  import static org.hamcrest.core.IsNull.nullValue;
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertNotNull;
  import static org.junit.Assert.assertNull;
  import static org.junit.Assert.assertThat;
  import static org.junit.Assert.assertTrue;
  import static org.junit.Assert.fail;
  import static org.neo4j.helpers.collection.MapUtil.stringMap;
  import static org.neo4j.index.Neo4jTestCase.assertContains;
  import static org.neo4j.index.Neo4jTestCase.assertContainsInOrder;
  import static org.neo4j.index.impl.lucene.Contains.contains;
  import static org.neo4j.index.impl.lucene.IsEmpty.isEmpty;
  import static org.neo4j.index.lucene.QueryContext.numericRange;
  import static org.neo4j.index.lucene.ValueContext.numeric;
  
  public class TestLuceneIndex extends AbstractLuceneIndexTest
  {
      @SuppressWarnings( "unchecked" )
      private <T extends PropertyContainer> void makeSureAdditionsCanBeRead(
              Index<T> index, EntityCreator<T> entityCreator )
      {
          String key = "name";
          String value = "Mattias";
          assertThat( index.get( key, value ).getSingle(), is( nullValue() ) );
          assertThat( index.get( key, value ), isEmpty() );
  
          assertThat( index.query( key, "*" ), isEmpty() );
  
          T entity1 = entityCreator.create();
          T entity2 = entityCreator.create();
          index.add( entity1, key, value );
          for ( int i = 0; i < 2; i++ )
          {
              assertThat( index.get( key, value ), contains( entity1 ) );
              assertThat( index.query( key, "*" ), contains( entity1 ) );
              assertThat( index.get( key, value ), contains( entity1 ) );
  
              restartTx();
          }
  
          index.add( entity2, key, value );
          assertThat( index.get( key, value ), contains( entity1, entity2 ) );
  
          restartTx();
          assertThat( index.get( key, value ), contains( entity1, entity2 ) );
          index.delete();
      }
  
      @Test
      public void makeSureYouGetLatestTxModificationsInQueryByDefault()
      {
          Index<Node> index = nodeIndex( LuceneIndexImplementation.FULLTEXT_CONFIG );
          Node node = graphDb.createNode();
          index.add( node, "key", "value" );
          assertThat( index.query( "key:value" ), contains( node ) );
      }
  
      @Test
      public void makeSureLuceneIndexesReportAsWriteable()
      {
          Index<Node> index = nodeIndex( LuceneIndexImplementation.FULLTEXT_CONFIG );
          Node node = graphDb.createNode();
          index.add( node, "key", "value" );
          assertTrue( index.isWriteable() );
      }
  
      @Test
      public void testStartupInExistingDirectory() {
          InternalAbstractGraphDatabase graphDatabase = new ImpermanentGraphDatabase();
          Index<Node> index = graphDatabase.index().forNodes("nodes");
          assertNotNull(index);
      }
  
      @Test
      public void makeSureAdditionsCanBeReadNodeExact()
      {
          makeSureAdditionsCanBeRead( nodeIndex( LuceneIndexImplementation.EXACT_CONFIG ),
                  NODE_CREATOR );
      }
  
      @Test
      public void makeSureAdditionsCanBeReadNodeFulltext()
      {
          makeSureAdditionsCanBeRead( nodeIndex( 
                  LuceneIndexImplementation.FULLTEXT_CONFIG ), NODE_CREATOR );
      }
  
      @Test
      public void makeSureAdditionsCanBeReadRelationshipExact()
      {
          makeSureAdditionsCanBeRead( relationshipIndex( 
                  LuceneIndexImplementation.EXACT_CONFIG ), RELATIONSHIP_CREATOR );
      }
  
      @Test
      public void makeSureAdditionsCanBeReadRelationshipFulltext()
      {
          makeSureAdditionsCanBeRead( relationshipIndex( 
                  LuceneIndexImplementation.FULLTEXT_CONFIG ), RELATIONSHIP_CREATOR );
      }
  
      @Test
      public void makeSureAdditionsCanBeRemovedInSameTx()
      {
          makeSureAdditionsCanBeRemoved( false );
      }
  
      @Test
      @Ignore
      public void makeSureYouCanAskIfAnIndexExistsOrNot()
      {
          String name = currentIndexName();
          assertFalse( graphDb.index().existsForNodes( name ) );
          graphDb.index().forNodes( name );
          assertTrue( graphDb.index().existsForNodes( name ) );
  
          assertFalse( graphDb.index().existsForRelationships( name ) );
          graphDb.index().forRelationships( name );
          assertTrue( graphDb.index().existsForRelationships( name ) );
      }
  
      private void makeSureAdditionsCanBeRemoved( boolean restartTx )
      {
          Index<Node> index = nodeIndex( LuceneIndexImplementation.EXACT_CONFIG );
          String key = "name";
          String value = "Mattias";
          assertNull( index.get( key, value ).getSingle() );
          Node node = graphDb.createNode();
          index.add( node, key, value );
          if ( restartTx )
          {
              restartTx();
          }
          assertEquals( node, index.get( key, value ).getSingle() );
          index.remove( node, key, value );
          assertNull( index.get( key, value ).getSingle() );
          restartTx();
          assertNull( index.get( key, value ).getSingle() );
          node.delete();
          index.delete();
      }
  
      @Test
      public void makeSureAdditionsCanBeRemoved()
      {
          makeSureAdditionsCanBeRemoved( true );
      }
  
      private void makeSureSomeAdditionsCanBeRemoved( boolean restartTx )
      {
          Index<Node> index = nodeIndex( LuceneIndexImplementation.EXACT_CONFIG );
          String key1 = "name";
          String key2 = "title";
          String value1 = "Mattias";
          assertNull( index.get( key1, value1 ).getSingle() );
          assertNull( index.get( key2, value1 ).getSingle() );
          Node node = graphDb.createNode();
          Node node2 = graphDb.createNode();
          index.add( node, key1, value1 );
          index.add( node, key2, value1 );
          index.add( node2, key1, value1 );
          if ( restartTx )
          {
              restartTx();
          }
          index.remove( node, key1, value1 );
          index.remove( node, key2, value1 );
          assertEquals( node2, index.get( key1, value1 ).getSingle() );
          assertNull( index.get( key2, value1 ).getSingle() );
          assertEquals( node2, index.get( key1, value1 ).getSingle() );
          assertNull( index.get( key2, value1 ).getSingle() );
          node.delete();
          index.delete();
      }
  
      @Test
      public void makeSureSomeAdditionsCanBeRemovedInSameTx()
      {
          makeSureSomeAdditionsCanBeRemoved( false );
      }
  
      @Test
      public void makeSureSomeAdditionsCanBeRemoved()
      {
          makeSureSomeAdditionsCanBeRemoved( true );
      }
  
      @Test
      public void makeSureThereCanBeMoreThanOneValueForAKeyAndEntity()
      {
          makeSureThereCanBeMoreThanOneValueForAKeyAndEntity( false );
      }
  
      @Test
      public void makeSureThereCanBeMoreThanOneValueForAKeyAndEntitySameTx()
      {
          makeSureThereCanBeMoreThanOneValueForAKeyAndEntity( true );
      }
  
      private void makeSureThereCanBeMoreThanOneValueForAKeyAndEntity(
              boolean restartTx )
      {
          Index<Node> index = nodeIndex( LuceneIndexImplementation.EXACT_CONFIG );
          String key = "name";
          String value1 = "Lucene";
          String value2 = "Index";
          String value3 = "Rules";
          assertThat( index.query( key, "*" ), isEmpty() );
          Node node = graphDb.createNode();
          index.add( node, key, value1 );
          index.add( node, key, value2 );
          if ( restartTx )
          {
              restartTx();
          }
          index.add( node, key, value3 );
          for ( int i = 0; i < 2; i++ )
          {
              assertThat( index.get( key, value1 ), contains( node ) );
              assertThat( index.get( key, value2 ), contains( node ) );
              assertThat( index.get( key, value3 ), contains( node ) );
              assertThat( index.get( key, "whatever" ), isEmpty() );
              restartTx();
          }
          index.delete();
      }
  
      @Test
      public void shouldNotGetLatestTxModificationsWhenChoosingSpeedQueries()
      {
          Index<Node> index = nodeIndex( LuceneIndexImplementation.EXACT_CONFIG );
          Node node = graphDb.createNode();
          index.add( node, "key", "value" );
          QueryContext queryContext = new QueryContext( "value" ).tradeCorrectnessForSpeed();
          assertThat( index.query( "key", queryContext ), isEmpty() );
          assertThat( index.query( "key", "value" ), contains( node ) );
      }
  
      @Test
      public void makeSureArrayValuesAreSupported()
      {
          Index<Node> index = nodeIndex( LuceneIndexImplementation.EXACT_CONFIG );
          String key = "name";
          String value1 = "Lucene";
          String value2 = "Index";
          String value3 = "Rules";
          assertThat( index.query( key, "*" ), isEmpty() );
          Node node = graphDb.createNode();
          index.add( node, key, new String[]{value1, value2, value3} );
          for ( int i = 0; i < 2; i++ )
          {
              assertThat( index.get( key, value1 ), contains( node ) );
              assertThat( index.get( key, value2 ), contains( node ) );
              assertThat( index.get( key, value3 ), contains( node ) );
              assertThat( index.get( key, "whatever" ), isEmpty() );
              restartTx();
          }
  
          index.remove( node, key, new String[]{value2, value3} );
  
          for ( int i = 0; i < 2; i++ )
          {
              assertThat( index.get( key, value1 ), contains( node ) );
              assertThat( index.get( key, value2 ), isEmpty() );
              assertThat( index.get( key, value3 ), isEmpty() );
              restartTx();
          }
          index.delete();
      }
  
      @Test
      public void makeSureWildcardQueriesCanBeAsked()
      {
          Index<Node> index = nodeIndex( LuceneIndexImplementation.EXACT_CONFIG );
          String key = "name";
          String value1 = "neo4j";
          String value2 = "nescafe";
          Node node1 = graphDb.createNode();
          Node node2 = graphDb.createNode();
          index.add( node1, key, value1 );
          index.add( node2, key, value2 );
  
          for ( int i = 0; i < 2; i++ )
          {
              assertThat( index.query( key, "neo*" ), contains( node1 ) );
              assertThat( index.query( key, "n?o4j" ), contains( node1 ) );
              assertThat( index.query( key, "ne*" ), contains( node1, node2 ) );
              assertThat( index.query( key + ":neo4j" ), contains( node1 ) );
              assertThat( index.query( key + ":neo*" ), contains( node1 ) );
              assertThat( index.query( key + ":n?o4j" ), contains( node1 ) );
              assertThat( index.query( key + ":ne*" ), contains( node1, node2 ) );
  
              restartTx();
          }
          index.delete();
      }
  
      @Test
      public void makeSureCompositeQueriesCanBeAsked()
      {
          Index<Node> index = nodeIndex( LuceneIndexImplementation.EXACT_CONFIG );
          Node neo = graphDb.createNode();
          Node trinity = graphDb.createNode();
          index.add( neo, "username", "neo@matrix" );
          index.add( neo, "sex", "male" );
          index.add( trinity, "username", "trinity@matrix" );
          index.add( trinity, "sex", "female" );
  
          for ( int i = 0; i < 2; i++ )
          {
              assertThat( index.query( "username:*@matrix AND sex:male" ), contains( neo ) );
              assertThat( index.query( new QueryContext( "username:*@matrix sex:male" ).defaultOperator( QueryParser.Operator.AND ) ), contains( neo ) );
              assertThat( index.query( "username:*@matrix OR sex:male" ), contains( neo, trinity ) );
              assertThat( index.query( new QueryContext( "username:*@matrix sex:male" ).defaultOperator( QueryParser.Operator.OR ) ), contains( neo, trinity ) );
  
              restartTx();
          }
          index.delete();
      }
  
      @SuppressWarnings( "unchecked" )
      private <T extends PropertyContainer> void doSomeRandomUseCaseTestingWithExactIndex(
              Index<T> index, EntityCreator<T> creator )
      {
          String name = "name";
          String mattias = "Mattias Persson";
          String title = "title";
          String hacker = "Hacker";
  
          assertThat( index.get( name, mattias ), isEmpty() );
  
          T entity1 = creator.create();
          T entity2 = creator.create();
  
          assertNull( index.get( name, mattias ).getSingle() );
          index.add( entity1, name, mattias );
          assertThat( index.get( name, mattias ), contains( entity1 ) );
  
          assertContains( index.query( name, "\"" + mattias + "\"" ), entity1 );
          assertContains( index.query( "name:\"" + mattias + "\"" ), entity1 );
  
          assertEquals( entity1, index.get( name, mattias ).getSingle() );
  
          assertContains( index.query( "name", "Mattias*" ), entity1 );
  
          commitTx();
          assertThat( index.get( name, mattias ), contains( entity1 ) );
          assertThat( index.query( name, "\"" + mattias + "\"" ), contains( entity1 ) );
          assertThat( index.query( "name:\"" + mattias + "\"" ), contains( entity1 ) );
          assertEquals( entity1, index.get( name, mattias ).getSingle() );
          assertThat( index.query( "name", "Mattias*" ), contains( entity1 ) );
  
          beginTx();
          index.add( entity2, title, hacker );
          index.add( entity1, title, hacker );
          assertThat( index.get( name, mattias ), contains( entity1 ) );
          assertThat( index.get( title, hacker ), contains( entity1, entity2 ) );
  
          assertContains( index.query( "name:\"" + mattias + "\" OR title:\"" +
                  hacker + "\"" ), entity1, entity2 );
  
          commitTx();
          assertThat( index.get( name, mattias ), contains( entity1 ) );
          assertThat( index.get( title, hacker ), contains( entity1, entity2 ) );
          assertThat( index.query( "name:\"" + mattias + "\" OR title:\"" + hacker + "\"" ), contains( entity1, entity2 ) );
          assertThat( index.query( "name:\"" + mattias + "\" AND title:\"" +
                  hacker + "\"" ), contains( entity1 ) );
  
          beginTx();
          index.remove( entity2, title, hacker );
          assertThat( index.get( name, mattias ), contains( entity1 ) );
          assertThat( index.get( title, hacker ), contains( entity1 ) );
  
          assertContains( index.query( "name:\"" + mattias + "\" OR title:\"" +
                  hacker + "\"" ), entity1 );
  
          commitTx();
          assertThat( index.get( name, mattias ), contains( entity1 ) );
          assertThat( index.get( title, hacker ), contains( entity1 ) );
          assertThat( index.query( "name:\"" + mattias + "\" OR title:\"" +
                  hacker + "\"" ), contains( entity1 ) );
  
          beginTx();
          index.remove( entity1, title, hacker );
          index.remove( entity1, name, mattias );
          index.delete();
          commitTx();
      }
  
      @Test
      public void doSomeRandomUseCaseTestingWithExactNodeIndex()
      {
          doSomeRandomUseCaseTestingWithExactIndex( nodeIndex( 
                  LuceneIndexImplementation.EXACT_CONFIG ), NODE_CREATOR );
      }
  
      @Test
      public void doSomeRandomUseCaseTestingWithExactRelationshipIndex()
      {
          doSomeRandomUseCaseTestingWithExactIndex( relationshipIndex( 
                  LuceneIndexImplementation.EXACT_CONFIG ), RELATIONSHIP_CREATOR );
      }
  
      @SuppressWarnings( "unchecked" )
      private <T extends PropertyContainer> void doSomeRandomTestingWithFulltextIndex(
              Index<T> index,
              EntityCreator<T> creator )
      {
          T entity1 = creator.create();
          T entity2 = creator.create();
  
          String key = "name";
          index.add( entity1, key, "The quick brown fox" );
          index.add( entity2, key, "brown fox jumped over" );
  
          for ( int i = 0; i < 2; i++ )
          {
              assertThat( index.get( key, "The quick brown fox" ), contains( entity1 ) );
              assertThat( index.get( key, "brown fox jumped over" ), contains( entity2 ) );
              assertThat( index.query( key, "quick" ), contains( entity1 ) );
              assertThat( index.query( key, "brown" ), contains( entity1, entity2 ) );
              assertThat( index.query( key, "quick OR jumped" ), contains( entity1, entity2 ) );
              assertThat( index.query( key, "brown AND fox" ), contains( entity1, entity2 ) );
  
              restartTx();
          }
  
          index.delete();
      }
  
      @Test
      public void doSomeRandomTestingWithNodeFulltextInde()
      {
          doSomeRandomTestingWithFulltextIndex( nodeIndex( 
                  LuceneIndexImplementation.FULLTEXT_CONFIG ), NODE_CREATOR );
      }
  
      @Test
      public void doSomeRandomTestingWithRelationshipFulltextInde()
      {
          doSomeRandomTestingWithFulltextIndex( relationshipIndex( 
                  LuceneIndexImplementation.FULLTEXT_CONFIG ), RELATIONSHIP_CREATOR );
      }
  
      @Test
      public void testNodeLocalRelationshipIndex()
      {
          RelationshipIndex index = relationshipIndex( 
                  LuceneIndexImplementation.EXACT_CONFIG );
  
          RelationshipType type = DynamicRelationshipType.withName( "YO" );
          Node startNode = graphDb.createNode();
          Node endNode1 = graphDb.createNode();
          Node endNode2 = graphDb.createNode();
          Relationship rel1 = startNode.createRelationshipTo( endNode1, type );
          Relationship rel2 = startNode.createRelationshipTo( endNode2, type );
          index.add( rel1, "name", "something" );
          index.add( rel2, "name", "something" );
  
          for ( int i = 0; i < 2; i++ )
          {
              assertThat( index.query( "name:something" ), contains( rel1, rel2 ) );
              assertThat( index.query( "name:something", null, endNode1 ), contains( rel1 ) );
              assertThat( index.query( "name:something", startNode, endNode2 ), contains( rel2 ) );
              assertThat( index.query( null, startNode, endNode1 ), contains( rel1 ) );
              assertThat( index.get( "name", "something", null, endNode1 ), contains( rel1 ) );
              assertThat( index.get( "name", "something", startNode, endNode2 ), contains( rel2 ) );
              assertThat( index.get( null, null, startNode, endNode1 ), contains( rel1 ) );
  
              restartTx();
          }
  
          rel2.delete();
          rel1.delete();
          startNode.delete();
          endNode1.delete();
          endNode2.delete();
          index.delete();
      }
  
      @Test
      public void testSortByRelevance()
      {
          Index<Node> index = nodeIndex( LuceneIndexImplementation.EXACT_CONFIG );
  
          Node node1 = graphDb.createNode();
          Node node2 = graphDb.createNode();
          Node node3 = graphDb.createNode();
          index.add( node1, "name", "something" );
          index.add( node2, "name", "something" );
          index.add( node2, "foo", "yes" );
          index.add( node3, "name", "something" );
          index.add( node3, "foo", "yes" );
          index.add( node3, "bar", "yes" );
          restartTx();
  
          IndexHits<Node> hits = index.query(
                  new QueryContext( "+name:something foo:yes bar:yes" ).sort( Sort.RELEVANCE ) );
          assertEquals( node3, hits.next() );
  