View Javadoc

    1   /**
    2    * Copyright (c) 2002-2011 "Neo Technology,"
    3    * Network Engine for Objects in Lund AB [http://neotechnology.com]
    4    *
    5    * This file is part of Neo4j.
    6    *
    7    * Neo4j is free software: you can redistribute it and/or modify
    8    * it under the terms of the GNU General Public License as published by
    9    * the Free Software Foundation, either version 3 of the License, or
    10   * (at your option) any later version.
    11   *
    12   * This program is distributed in the hope that it will be useful,
    13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15   * GNU General Public License for more details.
    16   *
    17   * You should have received a copy of the GNU General Public License
    18   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    19   */
    20  package org.neo4j.server.rest.web;
    21
    22  import static org.hamcrest.Matchers.hasKey;
    23  import static org.junit.Assert.assertEquals;
    24  import static org.junit.Assert.assertFalse;
    25  import static org.junit.Assert.assertNotNull;
    26  import static org.junit.Assert.assertNull;
    27  import static org.junit.Assert.assertThat;
    28  import static org.junit.Assert.assertTrue;
    29  import static org.junit.Assert.fail;
    30  import static org.neo4j.helpers.collection.MapUtil.map;
    31  import static org.neo4j.server.rest.repr.RepresentationTestBase.serialize;
    32
    33  import java.io.File;
    34  import java.io.IOException;
    35  import java.util.Arrays;
    36  import java.util.Collection;
    37  import java.util.Collections;
    38  import java.util.HashMap;
    39  import java.util.List;
    40  import java.util.Map;
    41
    42  import org.apache.commons.io.FileUtils;
    43  import org.junit.After;
    44  import org.junit.Before;
    45  import org.junit.Test;
    46  import org.neo4j.graphdb.DynamicRelationshipType;
    47  import org.neo4j.graphdb.Node;
    48  import org.neo4j.graphdb.NotFoundException;
    49  import org.neo4j.graphdb.PropertyContainer;
    50  import org.neo4j.graphdb.Relationship;
    51  import org.neo4j.graphdb.Transaction;
    52  import org.neo4j.helpers.collection.MapUtil;
    53  import org.neo4j.server.ServerTestUtils;
    54  import org.neo4j.server.database.Database;
    55  import org.neo4j.server.database.DatabaseBlockedException;
    56  import org.neo4j.server.rest.domain.EndNodeNotFoundException;
    57  import org.neo4j.server.rest.domain.GraphDbHelper;
    58  import org.neo4j.server.rest.domain.StartNodeNotFoundException;
    59  import org.neo4j.server.rest.domain.StartNodeSameAsEndNodeException;
    60  import org.neo4j.server.rest.domain.TraverserReturnType;
    61  import org.neo4j.server.rest.repr.BadInputException;
    62  import org.neo4j.server.rest.repr.ListRepresentation;
    63  import org.neo4j.server.rest.repr.NodeRepresentation;
    64  import org.neo4j.server.rest.repr.NodeRepresentationTest;
    65  import org.neo4j.server.rest.repr.RelationshipRepresentationTest;
    66  import org.neo4j.server.rest.web.DatabaseActions.RelationshipDirection;
    67
    68  public class DatabaseActionsTest
    69  {
    70      private DatabaseActions actions;
    71      private GraphDbHelper graphdbHelper;
    72      private Database database;
    73      private String databasePath;
    74
    75      @Before
    76      public void clearDb() throws IOException
    77      {
    78          databasePath = ServerTestUtils.createTempDir().getAbsolutePath();
    79          database = new Database( ServerTestUtils.EMBEDDED_GRAPH_DATABASE_FACTORY, databasePath );
    80
    81          graphdbHelper = new GraphDbHelper( database );
    82          this.actions = new DatabaseActions( database );
    83      }
    84
    85      @After
    86      public void shutdownDatabase() throws IOException
    87      {
    88          this.database.shutdown();
    89          FileUtils.forceDelete( new File( databasePath ) );
    90      }
    91
    92      private long createNode( Map<String, Object> properties ) throws DatabaseBlockedException
    93      {
    94
    95          long nodeId;
    96          Transaction tx = database.graph.beginTx();
    97          try
    98          {
    99              Node node = database.graph.createNode();
    100             for ( Map.Entry<String, Object> entry : properties.entrySet() )
    101             {
    102                 node.setProperty( entry.getKey(), entry.getValue() );
    103             }
    104             nodeId = node.getId();
    105             tx.success();
    106         } finally
    107         {
    108             tx.finish();
    109         }
    110         return nodeId;
    111     }
    112
    113
    114     @Test
    115     public void createdNodeShouldBeInDatabase() throws Exception
    116     {
    117         NodeRepresentation noderep = actions.createNode( Collections.<String, Object>emptyMap() );
    118
    119         Transaction tx = database.graph.beginTx();
    120         try
    121         {
    122             assertNotNull( database.graph.getNodeById( noderep.getId() ) );
    123         } finally
    124         {
    125             tx.finish();
    126         }
    127     }
    128
    129     @Test
    130     public void nodeInDatabaseShouldBeRetreivable() throws DatabaseBlockedException,
    131             NodeNotFoundException
    132     {
    133         long nodeId = new GraphDbHelper( database ).createNode();
    134         assertNotNull( actions.getNode( nodeId ) );
    135     }
    136
    137     @Test
    138     public void shouldBeAbleToStorePropertiesInAnExistingNode() throws DatabaseBlockedException,
    139             PropertyValueException, NodeNotFoundException
    140     {
    141         long nodeId = graphdbHelper.createNode();
    142         Map<String, Object> properties = new HashMap<String, Object>();
    143         properties.put( "foo", "bar" );
    144         properties.put( "baz", 17 );
    145         actions.setAllNodeProperties( nodeId, properties );
    146
    147         Transaction tx = database.graph.beginTx();
    148         try
    149         {
    150             Node node = database.graph.getNodeById( nodeId );
    151             assertHasProperties( node, properties );
    152         } finally
    153         {
    154             tx.finish();
    155         }
    156     }
    157
    158
    159     @Test(expected = PropertyValueException.class)
    160     public void shouldFailOnTryingToStoreMixedArraysAsAProperty() throws Exception
    161     {
    162         long nodeId = graphdbHelper.createNode();
    163         Map<String, Object> properties = new HashMap<String, Object>();
    164         Object[] dodgyArray = new Object[3];
    165         dodgyArray[0] = 0;
    166         dodgyArray[1] = 1;
    167         dodgyArray[2] = "two";
    168         properties.put( "foo", dodgyArray);
    169
    170         actions.setAllNodeProperties( nodeId, properties );
    171     }
    172
    173     @Test
    174     public void shouldOverwriteExistingProperties() throws DatabaseBlockedException,
    175             PropertyValueException, NodeNotFoundException
    176     {
    177
    178         long nodeId;
    179         Transaction tx = database.graph.beginTx();
    180         try
    181         {
    182             Node node = database.graph.createNode();
    183             node.setProperty( "remove me", "trash" );
    184             nodeId = node.getId();
    185             tx.success();
    186         } finally
    187         {
    188             tx.finish();
    189         }
    190         Map<String, Object> properties = new HashMap<String, Object>();
    191         properties.put( "foo", "bar" );
    192         properties.put( "baz", 17 );
    193         actions.setAllNodeProperties( nodeId, properties );
    194         tx = database.graph.beginTx();
    195         try
    196         {
    197             Node node = database.graph.getNodeById( nodeId );
    198             assertHasProperties( node, properties );
    199             assertNull( node.getProperty( "remove me", null ) );
    200         } finally
    201         {
    202             tx.finish();
    203         }
    204     }
    205
    206     @Test
    207     public void shouldBeAbleToGetPropertiesOnNode() throws DatabaseBlockedException,
    208             NodeNotFoundException
    209     {
    210
    211         long nodeId;
    212         Map<String, Object> properties = new HashMap<String, Object>();
    213         properties.put( "foo", "bar" );
    214         properties.put( "neo", "Thomas A. Anderson" );
    215         properties.put( "number", 15L );
    216         Transaction tx = database.graph.beginTx();
    217         try
    218         {
    219             Node node = database.graph.createNode();
    220             for ( Map.Entry<String, Object> entry : properties.entrySet() )
    221             {
    222                 node.setProperty( entry.getKey(), entry.getValue() );
    223             }
    224             nodeId = node.getId();
    225             tx.success();
    226         } finally
    227         {
    228             tx.finish();
    229         }
    230
    231         Map<String, Object> readProperties = serialize( actions.getAllNodeProperties( nodeId ) );
    232         assertEquals( properties, readProperties );
    233     }
    234
    235     @Test
    236     public void shouldRemoveNodeWithNoRelationsFromDBOnDelete() throws DatabaseBlockedException,
    237             NodeNotFoundException, OperationFailureException
    238     {
    239         long nodeId;
    240         Transaction tx = database.graph.beginTx();
    241         try
    242         {
    243             Node node = database.graph.createNode();
    244             nodeId = node.getId();
    245             tx.success();
    246         } finally
    247         {
    248             tx.finish();
    249         }
    250
    251         int nodeCount = graphdbHelper.getNumberOfNodes();
    252         actions.deleteNode( nodeId );
    253         assertEquals( nodeCount - 1, graphdbHelper.getNumberOfNodes() );
    254     }
    255
    256     @Test
    257     public void shouldBeAbleToSetPropertyOnNode() throws DatabaseBlockedException,
    258             PropertyValueException, NodeNotFoundException
    259     {
    260         long nodeId = createNode( Collections.<String, Object>emptyMap() );
    261         String key = "foo";
    262         Object value = "bar";
    263         actions.setNodeProperty( nodeId, key, value );
    264         assertEquals( Collections.singletonMap( key, value ), graphdbHelper.getNodeProperties( nodeId ) );
    265     }
    266
    267     @Test
    268     public void shouldBeAbleToGetPropertyOnNode() throws DatabaseBlockedException,
    269             NodeNotFoundException, NoSuchPropertyException, BadInputException
    270     {
    271         String key = "foo";
    272         Object value = "bar";
    273         long nodeId = createNode( Collections.singletonMap( key, value ) );
    274         assertEquals( value, serialize( actions.getNodeProperty( nodeId, key ) ) );
    275     }
    276
    277     @Test
    278     public void shouldBeAbleToRemoveNodeProperties() throws DatabaseBlockedException,
    279             NodeNotFoundException
    280     {
    281         Map<String, Object> properties = new HashMap<String, Object>();
    282         properties.put( "foo", "bar" );
    283         properties.put( "number", 15 );
    284         long nodeId = createNode( properties );
    285         actions.removeAllNodeProperties( nodeId );
    286
    287         Transaction tx = database.graph.beginTx();
    288         try
    289         {
    290             Node node = database.graph.getNodeById( nodeId );
    291             assertEquals( false, node.getPropertyKeys().iterator().hasNext() );
    292             tx.success();
    293         } finally
    294         {
    295             tx.finish();
    296         }
    297     }
    298
    299     @Test
    300     public void shouldStoreRelationshipsBetweenTwoExistingNodes() throws Exception
    301     {
    302         int relationshipCount = graphdbHelper.getNumberOfRelationships();
    303         actions.createRelationship( graphdbHelper.createNode(), graphdbHelper.createNode(),
    304                 "LOVES", Collections.<String, Object>emptyMap() );
    305         assertEquals( relationshipCount + 1, graphdbHelper.getNumberOfRelationships() );
    306     }
    307
    308     @Test
    309     public void shouldStoreSuppliedPropertiesWhenCreatingRelationship() throws Exception
    310     {
    311         Map<String, Object> properties = new HashMap<String, Object>();
    312         properties.put( "string", "value" );
    313         properties.put( "integer", 17 );
    314         long relId = actions.createRelationship( graphdbHelper.createNode(),
    315                 graphdbHelper.createNode(), "LOVES", properties ).getId();
    316
    317         Transaction tx = database.graph.beginTx();
    318         try
    319         {
    320             Relationship rel = database.graph.getRelationshipById( relId );
    321             for ( String key : rel.getPropertyKeys() )
    322             {
    323                 assertTrue( "extra property stored", properties.containsKey( key ) );
    324             }
    325             for ( Map.Entry<String, Object> entry : properties.entrySet() )
    326             {
    327                 assertEquals( entry.getValue(), rel.getProperty( entry.getKey() ) );
    328             }
    329         } finally
    330         {
    331             tx.finish();
    332         }
    333     }
    334
    335     @Test
    336     public void shouldNotCreateRelationshipBetweenNonExistentNodes() throws Exception
    337     {
    338         long nodeId = graphdbHelper.createNode();
    339         Map<String, Object> properties = Collections.<String, Object>emptyMap();
    340         try
    341         {
    342             actions.createRelationship( nodeId, nodeId * 1000, "Loves", properties );
    343             fail();
    344         } catch ( EndNodeNotFoundException e )
    345         {
    346             // ok
    347         }
    348         try
    349         {
    350             actions.createRelationship( nodeId * 1000, nodeId, "Loves", properties );
    351             fail();
    352         } catch ( StartNodeNotFoundException e )
    353         {
    354             // ok
    355         }
    356     }
    357
    358     @Test
    359     public void shouldNotCreateRelationshipWithSameStartAsEndNode() throws Exception
    360     {
    361         long nodeId = graphdbHelper.createNode();
    362         Map<String, Object> properties = Collections.<String, Object>emptyMap();
    363         try
    364         {
    365             actions.createRelationship( nodeId, nodeId, "Loves", properties );
    366             fail();
    367         } catch ( StartNodeSameAsEndNodeException e )
    368         {
    369             // ok
    370         }
    371     }
    372
    373     @Test
    374     public void shouldBeAbleToRemoveNodeProperty() throws DatabaseBlockedException,
    375             NodeNotFoundException, NoSuchPropertyException
    376     {
    377         Map<String, Object> properties = new HashMap<String, Object>();
    378         properties.put( "foo", "bar" );
    379         properties.put( "number", 15 );
    380         long nodeId = createNode( properties );
    381         actions.removeNodeProperty( nodeId, "foo" );
    382
    383         Transaction tx = database.graph.beginTx();
    384         try
    385         {
    386             Node node = database.graph.getNodeById( nodeId );
    387             assertEquals( 15, node.getProperty( "number" ) );
    388             assertEquals( false, node.hasProperty( "foo" ) );
    389             tx.success();
    390         } finally
    391         {
    392             tx.finish();
    393         }
    394     }
    395
    396     @Test
    397     public void shouldReturnTrueIfNodePropertyRemoved() throws DatabaseBlockedException,
    398             NodeNotFoundException, NoSuchPropertyException
    399     {
    400         Map<String, Object> properties = new HashMap<String, Object>();
    401         properties.put( "foo", "bar" );
    402         properties.put( "number", 15 );
    403         long nodeId = createNode( properties );
    404         actions.removeNodeProperty( nodeId, "foo" );
    405     }
    406
    407     @Test( expected = NoSuchPropertyException.class )
    408     public void shouldReturnFalseIfNodePropertyNotRemoved() throws DatabaseBlockedException,
    409             NodeNotFoundException, NoSuchPropertyException
    410     {
    411         Map<String, Object> properties = new HashMap<String, Object>();
    412         properties.put( "foo", "bar" );
    413         properties.put( "number", 15 );
    414         long nodeId = createNode( properties );
    415         actions.removeNodeProperty( nodeId, "baz" );
    416     }
    417
    418     @Test
    419     public void shouldBeAbleToRetrieveARelationship() throws DatabaseBlockedException,
    420             RelationshipNotFoundException
    421     {
    422         long relationship = graphdbHelper.createRelationship( "ENJOYED" );
    423         assertNotNull( actions.getRelationship( relationship ) );
    424     }
    425
    426     @Test
    427     public void shouldBeAbleToGetPropertiesOnRelationship() throws DatabaseBlockedException,
    428             RelationshipNotFoundException
    429     {
    430
    431         long relationshipId;
    432         Map<String, Object> properties = new HashMap<String, Object>();
    433         properties.put( "foo", "bar" );
    434         properties.put( "neo", "Thomas A. Anderson" );
    435         properties.put( "number", 15L );
    436         Transaction tx = database.graph.beginTx();
    437         try
    438         {
    439             Node startNode = database.graph.createNode();
    440             Node endNode = database.graph.createNode();
    441             Relationship relationship = startNode.createRelationshipTo( endNode, DynamicRelationshipType.withName( "knows" ) );
    442             for ( Map.Entry<String, Object> entry : properties.entrySet() )
    443             {
    444                 relationship.setProperty( entry.getKey(), entry.getValue() );
    445             }
    446             relationshipId = relationship.getId();
    447             tx.success();
    448         } finally
    449         {
    450             tx.finish();
    451         }
    452
    453         Map<String, Object> readProperties = serialize( actions.getAllRelationshipProperties( relationshipId ) );
    454         assertEquals( properties, readProperties );
    455     }
    456
    457     @Test
    458     public void shouldBeAbleToRetrieveASinglePropertyFromARelationship()
    459             throws DatabaseBlockedException, NoSuchPropertyException, RelationshipNotFoundException, BadInputException
    460     {
    461         Map<String, Object> properties = new HashMap<String, Object>();
    462         properties.put( "foo", "bar" );
    463         properties.put( "neo", "Thomas A. Anderson" );
    464         properties.put( "number", 15L );
    465
    466         long relationshipId = graphdbHelper.createRelationship( "LOVES" );
    467         graphdbHelper.setRelationshipProperties( relationshipId, properties );
    468
    469         Object relationshipProperty = serialize( actions.getRelationshipProperty( relationshipId, "foo" ) );
    470         assertEquals( "bar", relationshipProperty );
    471     }
    472
    473     @Test
    474     public void shouldBeAbleToDeleteARelationship() throws DatabaseBlockedException,
    475             RelationshipNotFoundException
    476     {
    477         long relationshipId = graphdbHelper.createRelationship( "LOVES" );
    478
    479         actions.deleteRelationship( relationshipId );
    480         try
    481         {
    482             graphdbHelper.getRelationship( relationshipId );
    483             fail();
    484         } catch ( NotFoundException e )
    485         {
    486         }
    487     }
    488
    489     @Test
    490     public void shouldBeAbleToRetrieveRelationshipsFromNode() throws DatabaseBlockedException,
    491             NodeNotFoundException
    492     {
    493         long nodeId = graphdbHelper.createNode();
    494         graphdbHelper.createRelationship( "LIKES", nodeId, graphdbHelper.createNode() );
    495         graphdbHelper.createRelationship( "LIKES", graphdbHelper.createNode(), nodeId );
    496         graphdbHelper.createRelationship( "HATES", nodeId, graphdbHelper.createNode() );
    497
    498         verifyRelReps( 3, actions.getNodeRelationships( nodeId, RelationshipDirection.all,
    499                 Collections.<String>emptyList() ) );
    500         verifyRelReps( 1, actions.getNodeRelationships( nodeId, RelationshipDirection.in,
    501                 Collections.<String>emptyList() ) );
    502         verifyRelReps( 2, actions.getNodeRelationships( nodeId, RelationshipDirection.out,
    503                 Collections.<String>emptyList() ) );
    504
    505         verifyRelReps( 3, actions.getNodeRelationships( nodeId, RelationshipDirection.all,
    506                 Arrays.asList( "LIKES", "HATES" ) ) );
    507         verifyRelReps( 1, actions.getNodeRelationships( nodeId, RelationshipDirection.in,
    508                 Arrays.asList( "LIKES", "HATES" ) ) );
    509         verifyRelReps( 2, actions.getNodeRelationships( nodeId, RelationshipDirection.out,
    510                 Arrays.asList( "LIKES", "HATES" ) ) );
    511
    512         verifyRelReps( 2, actions.getNodeRelationships( nodeId, RelationshipDirection.all,
    513                 Arrays.asList( "LIKES" ) ) );
    514         verifyRelReps( 1, actions.getNodeRelationships( nodeId, RelationshipDirection.in,
    515                 Arrays.asList( "LIKES" ) ) );
    516         verifyRelReps( 1, actions.getNodeRelationships( nodeId, RelationshipDirection.out,
    517                 Arrays.asList( "LIKES" ) ) );
    518
    519         verifyRelReps( 1, actions.getNodeRelationships( nodeId, RelationshipDirection.all,
    520                 Arrays.asList( "HATES" ) ) );
    521         verifyRelReps( 0, actions.getNodeRelationships( nodeId, RelationshipDirection.in,
    522                 Arrays.asList( "HATES" ) ) );
    523         verifyRelReps( 1, actions.getNodeRelationships( nodeId, RelationshipDirection.out,
    524                 Arrays.asList( "HATES" ) ) );
    525     }
    526
    527     @Test
    528     public void shouldNotGetAnyRelationshipsWhenRetrievingFromNodeWithoutRelationships()
    529             throws DatabaseBlockedException, NodeNotFoundException
    530     {
    531         long nodeId = graphdbHelper.createNode();
    532
    533         verifyRelReps( 0, actions.getNodeRelationships( nodeId, RelationshipDirection.all,
    534                 Collections.<String>emptyList() ) );
    535         verifyRelReps( 0, actions.getNodeRelationships( nodeId, RelationshipDirection.in,
    536                 Collections.<String>emptyList() ) );
    537         verifyRelReps( 0, actions.getNodeRelationships( nodeId, RelationshipDirection.out,
    538                 Collections.<String>emptyList() ) );
    539     }
    540
    541     @Test
    542     public void shouldBeAbleToSetRelationshipProperties() throws DatabaseBlockedException,
    543             PropertyValueException, RelationshipNotFoundException
    544     {
    545         long relationshipId = graphdbHelper.createRelationship( "KNOWS" );
    546         Map<String, Object> properties = new HashMap<String, Object>();
    547         properties.put( "foo", "bar" );
    548         properties.put( "number", 10 );
    549         actions.setAllRelationshipProperties( relationshipId, properties );
    550         assertEquals( properties, graphdbHelper.getRelationshipProperties( relationshipId ) );
    551     }
    552
    553     @Test
    554     public void shouldBeAbleToSetRelationshipProperty() throws DatabaseBlockedException,
    555             PropertyValueException, RelationshipNotFoundException
    556     {
    557         long relationshipId = graphdbHelper.createRelationship( "KNOWS" );
    558         String key = "foo";
    559         Object value = "bar";
    560         actions.setRelationshipProperty( relationshipId, key, value );
    561         assertEquals( Collections.singletonMap( key, value ), graphdbHelper.getRelationshipProperties( relationshipId ) );
    562     }
    563
    564     @Test
    565     public void shouldRemoveRelationProperties() throws DatabaseBlockedException,
    566             RelationshipNotFoundException
    567     {
    568         long relId = graphdbHelper.createRelationship( "PAIR-PROGRAMS_WITH" );
    569         Map<String, Object> map = new HashMap<String, Object>();
    570         map.put( "foo", "bar" );
    571         map.put( "baz", 22 );
    572         graphdbHelper.setRelationshipProperties( relId, map );
    573
    574         actions.removeAllRelationshipProperties( relId );
    575
    576         assertTrue( graphdbHelper.getRelationshipProperties( relId ).isEmpty() );
    577     }
    578
    579     @Test
    580     public void shouldRemoveRelationshipProperty() throws DatabaseBlockedException,
    581             RelationshipNotFoundException, NoSuchPropertyException
    582     {
    583         long relId = graphdbHelper.createRelationship( "PAIR-PROGRAMS_WITH" );
    584         Map<String, Object> map = new HashMap<String, Object>();
    585         map.put( "foo", "bar" );
    586         map.put( "baz", 22 );
    587         graphdbHelper.setRelationshipProperties( relId, map );
    588
    589         actions.removeRelationshipProperty( relId, "foo" );
    590         assertEquals( 1, graphdbHelper.getRelationshipProperties( relId ).size() );
    591     }
    592
    593     @SuppressWarnings( "unchecked" )
    594     private void verifyRelReps( int expectedSize, ListRepresentation repr )
    595     {
    596         List<Object> relreps = serialize( repr );
    597         assertEquals( expectedSize, relreps.size() );
    598         for ( Object relrep : relreps )
    599         {
    600             RelationshipRepresentationTest.verifySerialisation( (Map<String, Object>)relrep );
    601         }
    602     }
    603
    604     private void assertHasProperties( PropertyContainer container, Map<String, Object> properties )
    605     {
    606         for ( Map.Entry<String, Object> entry : properties.entrySet() )
    607         {
    608             assertEquals( entry.getValue(), container.getProperty( entry.getKey() ) );
    609         }
    610     }
    611
    612     @Test
    613     public void shouldBeAbleToIndexNode() throws DatabaseBlockedException
    614     {
    615         String key = "mykey";
    616         String value = "myvalue";
    617         long nodeId = graphdbHelper.createNode();
    618         String indexName = "node";
    619
    620         actions.createNodeIndex( MapUtil.map( "name", indexName ) );
    621
    622         assertFalse( serialize( actions.getIndexedNodesByExactMatch( indexName, key, value ) ).iterator().hasNext() );
    623         actions.addToNodeIndex( indexName, key, value, nodeId );
    624         assertEquals( Arrays.asList( nodeId ), graphdbHelper.getIndexedNodes( indexName, key, value ) );
    625     }
    626
    627     @Test
    628     public void shouldBeAbleToFulltextIndex() throws DatabaseBlockedException
    629     {
    630         String key = "key";
    631         String value = "the value with spaces";
    632         long nodeId = graphdbHelper.createNode();
    633         String indexName = "fulltext-node";
    634         graphdbHelper.createNodeFullTextIndex( indexName );
    635         assertFalse( serialize( actions.getIndexedNodesByExactMatch( indexName, key, value ) ).iterator().hasNext() );
    636         actions.addToNodeIndex( indexName, key, value, nodeId );
    637         assertEquals( Arrays.asList( nodeId ), graphdbHelper.getIndexedNodes( indexName, key, value ) );
    638         assertEquals( Arrays.asList( nodeId ), graphdbHelper.getIndexedNodes( indexName, key, "the value with spaces" ) );
    639         assertEquals( Arrays.asList( nodeId ), graphdbHelper.queryIndexedNodes( indexName, key, "the" ) );
    640         assertEquals( Arrays.asList( nodeId ), graphdbHelper.queryIndexedNodes( indexName, key, "value" ) );
    641         assertEquals( Arrays.asList( nodeId ), graphdbHelper.queryIndexedNodes( indexName, key, "with" ) );
    642         assertEquals( Arrays.asList( nodeId ), graphdbHelper.queryIndexedNodes( indexName, key, "spaces" ) );
    643         assertEquals( Arrays.asList( nodeId ), graphdbHelper.queryIndexedNodes( indexName, key, "*spaces*" ) );
    644         assertTrue( graphdbHelper.getIndexedNodes( indexName, key, "nohit" ).isEmpty() );
    645     }
    646
    647     @Test
    648     public void shouldBeAbleToGetReferenceNode() throws DatabaseBlockedException,
    649             NodeNotFoundException
    650     {
    651         NodeRepresentation rep = actions.getReferenceNode();
    652         actions.getNode( rep.getId() );
    653     }
    654
    655     @Test
    656     public void shouldGetExtendedNodeRepresentationsWhenGettingFromIndex() throws DatabaseBlockedException
    657     {
    658         String key = "mykey3";
    659         String value = "value";
    660
    661         long nodeId = graphdbHelper.createNode();
    662         String indexName = "node";
    663         graphdbHelper.addNodeToIndex( indexName, key, value, nodeId );
    664         int counter = 0;
    665         for ( Object rep : serialize( actions.getIndexedNodesByExactMatch( indexName, key, value ) ) )
    666         {
    667             Map<String, Object> serialized = (Map<String, Object>)rep;
    668             NodeRepresentationTest.verifySerialisation( serialized );
    669             assertNotNull( serialized.get( "indexed" ) );
    670             counter++;
    671         }
    672         assertEquals( 1, counter );
    673     }
    674
    675     @Test
    676     public void shouldBeAbleToRemoveNodeFromIndex() throws DatabaseBlockedException
    677     {
    678         String key = "mykey2";
    679         String value = "myvalue";
    680         String value2 = "myvalue2";
    681         String indexName = "node";
    682         long nodeId = graphdbHelper.createNode();
    683         actions.addToNodeIndex( indexName, key, value, nodeId );
    684         actions.addToNodeIndex( indexName, key, value2, nodeId );
    685         assertEquals( 1, graphdbHelper.getIndexedNodes( indexName, key, value ).size() );
    686         assertEquals( 1, graphdbHelper.getIndexedNodes( indexName, key, value2 ).size() );
    687         actions.removeFromNodeIndex( indexName, key, value, nodeId );
    688         assertEquals( 0, graphdbHelper.getIndexedNodes( indexName, key, value ).size() );
    689         assertEquals( 1, graphdbHelper.getIndexedNodes( indexName, key, value2 ).size() );
    690         actions.removeFromNodeIndex( indexName, key, value2, nodeId );
    691         assertEquals( 0, graphdbHelper.getIndexedNodes( indexName, key, value ).size() );
    692         assertEquals( 0, graphdbHelper.getIndexedNodes( indexName, key, value2 ).size() );
    693     }
    694
    695     @Test
    696     public void shouldBeAbleToRemoveNodeFromIndexWithoutKeyValue() throws DatabaseBlockedException
    697     {
    698         String key1 = "kvkey1";
    699         String key2 = "kvkey2";
    700         String value = "myvalue";
    701         String value2 = "myvalue2";
    702         String indexName = "node";
    703         long nodeId = graphdbHelper.createNode();
    704         actions.addToNodeIndex( indexName, key1, value, nodeId );
    705         actions.addToNodeIndex( indexName, key1, value2, nodeId );
    706         actions.addToNodeIndex( indexName, key2, value, nodeId );
    707         actions.addToNodeIndex( indexName, key2, value2, nodeId );
    708         assertEquals( 1, graphdbHelper.getIndexedNodes( indexName, key1, value ).size() );
    709         assertEquals( 1, graphdbHelper.getIndexedNodes( indexName, key1, value2 ).size() );
    710         assertEquals( 1, graphdbHelper.getIndexedNodes( indexName, key2, value ).size() );
    711         assertEquals( 1, graphdbHelper.getIndexedNodes( indexName, key2, value2 ).size() );
    712         actions.removeFromNodeIndexNoValue( indexName, key1, nodeId );
    713         assertEquals( 0, graphdbHelper.getIndexedNodes( indexName, key1, value ).size() );
    714         assertEquals( 0, graphdbHelper.getIndexedNodes( indexName, key1, value2 ).size() );
    715         assertEquals( 1, graphdbHelper.getIndexedNodes( indexName, key2, value ).size() );
    716         assertEquals( 1, graphdbHelper.getIndexedNodes( indexName, key2, value2 ).size() );
    717         actions.removeFromNodeIndexNoKeyValue( indexName, nodeId );
    718         assertEquals( 0, graphdbHelper.getIndexedNodes( indexName, key1, value ).size() );
    719         assertEquals( 0, graphdbHelper.getIndexedNodes( indexName, key1, value2 ).size() );
    720         assertEquals( 0, graphdbHelper.getIndexedNodes( indexName, key2, value ).size() );
    721         assertEquals( 0, graphdbHelper.getIndexedNodes( indexName, key2, value2 ).size() );
    722     }
    723
    724     private long createBasicTraversableGraph() throws DatabaseBlockedException
    725     {
    726         // (Root)
    727         // / \
    728         // (Mattias) (Johan)
    729         // / / \
    730         // (Emil) (Peter) (Tobias)
    731
    732         long startNode = graphdbHelper.createNode( MapUtil.map( "name", "Root" ) );
    733         long child1_l1 = graphdbHelper.createNode( MapUtil.map( "name", "Mattias" ) );
    734         graphdbHelper.createRelationship( "knows", startNode, child1_l1 );
    735         long child2_l1 = graphdbHelper.createNode( MapUtil.map( "name", "Johan" ) );
    736         graphdbHelper.createRelationship( "knows", startNode, child2_l1 );
    737         long child1_l2 = graphdbHelper.createNode( MapUtil.map( "name", "Emil" ) );
    738         graphdbHelper.createRelationship( "knows", child2_l1, child1_l2 );
    739         long child1_l3 = graphdbHelper.createNode( MapUtil.map( "name", "Peter" ) );
    740         graphdbHelper.createRelationship( "knows", child1_l2, child1_l3 );
    741         long child2_l3 = graphdbHelper.createNode( MapUtil.map( "name", "Tobias" ) );
    742         graphdbHelper.createRelationship( "loves", child1_l2, child2_l3 );
    743         return startNode;
    744     }
    745
    746     private long[] createMoreComplexGraph() throws DatabaseBlockedException
    747     {
    748         // (a)
    749         // / \
    750         // v v
    751         // (b)<---(c) (d)-->(e)
    752         // \ / \ / /
    753         // v v v v /
    754         // (f)--->(g)<----
    755
    756         long a = graphdbHelper.createNode();
    757         long b = graphdbHelper.createNode();
    758         long c = graphdbHelper.createNode();
    759         long d = graphdbHelper.createNode();
    760         long e = graphdbHelper.createNode();
    761         long f = graphdbHelper.createNode();
    762         long g = graphdbHelper.createNode();
    763         graphdbHelper.createRelationship( "to", a, c );
    764         graphdbHelper.createRelationship( "to", a, d );
    765         graphdbHelper.createRelationship( "to", c, b );
    766         graphdbHelper.createRelationship( "to", d, e );
    767         graphdbHelper.createRelationship( "to", b, f );
    768         graphdbHelper.createRelationship( "to", c, f );
    769         graphdbHelper.createRelationship( "to", f, g );
    770         graphdbHelper.createRelationship( "to", d, g );
    771         graphdbHelper.createRelationship( "to", e, g );
    772         graphdbHelper.createRelationship( "to", c, g );
    773         return new long[]{a, g};
    774     }
    775
    776     private void createRelationshipWithProperties( long start, long end, Map<String, Object> properties )
    777     {
    778         long rel = graphdbHelper.createRelationship( "to", start, end );
    779         graphdbHelper.setRelationshipProperties( rel, properties );
    780     }
    781
    782     private long[] createDijkstraGraph( boolean includeOnes ) throws DatabaseBlockedException
    783     {
    784         /* Layout:
    785          *                       (y)
    786          *                        ^
    787          *                        [2]  _____[1]___
    788          *                          \ v           |
    789          * (start)--[1]->(a)--[9]-->(x)<-        (e)--[2]->(f)
    790          *                |         ^ ^^  \       ^
    791          *               [1]  ---[7][5][4] -[3]  [1]
    792          *                v  /       | /      \  /
    793          *               (b)--[1]-->(c)--[1]->(d)
    794          */
    795
    796         Map<String, Object> costOneProperties = includeOnes ? map( "cost", (double) 1 ) : map();
    797         long start = graphdbHelper.createNode();
    798         long a = graphdbHelper.createNode();
    799         long b = graphdbHelper.createNode();
    800         long c = graphdbHelper.createNode();
    801         long d = graphdbHelper.createNode();
    802         long e = graphdbHelper.createNode();
    803         long f = graphdbHelper.createNode();
    804         long x = graphdbHelper.createNode();
    805         long y = graphdbHelper.createNode();
    806
    807         createRelationshipWithProperties( start, a, costOneProperties );
    808         createRelationshipWithProperties( a, x, map( "cost", (double) 9 ) );
    809         createRelationshipWithProperties( a, b, costOneProperties );
    810         createRelationshipWithProperties( b, x, map( "cost", (double) 7 ) );
    811         createRelationshipWithProperties( b, c, costOneProperties );
    812         createRelationshipWithProperties( c, x, map( "cost", (double) 5 ) );
    813         createRelationshipWithProperties( c, x, map( "cost", (double) 4 ) );
    814         createRelationshipWithProperties( c, d, costOneProperties );
    815         createRelationshipWithProperties( d, x, map( "cost", (double) 3 ) );
    816         createRelationshipWithProperties( d, e, costOneProperties );
    817         createRelationshipWithProperties( e, x, costOneProperties );
    818         createRelationshipWithProperties( e, f, map( "cost", (double) 2 ) );
    819         createRelationshipWithProperties( x, y, map( "cost", (double) 2 ) );
    820         return new long[] { start, x };
    821     }
    822
    823     @Test
    824     public void shouldBeAbleToTraverseWithDefaultParameters() throws DatabaseBlockedException
    825     {
    826         long startNode = createBasicTraversableGraph();
    827         List<Object> hits = serialize( actions.traverse( startNode, new HashMap<String, Object>(),
    828                 TraverserReturnType.node ) );
    829         assertEquals( 2, hits.size() );
    830     }
    831
    832     @Test
    833     public void shouldBeAbleToTraverseDepthTwo() throws DatabaseBlockedException
    834     {
    835         long startNode = createBasicTraversableGraph();
    836         List<Object> hits = serialize( actions.traverse( startNode, MapUtil.map(
    837                 "max depth", 2 ), TraverserReturnType.node ) );
    838         assertEquals( 3, hits.size() );
    839     }
    840
    841     @Test
    842     public void shouldBeAbleToTraverseEverything() throws DatabaseBlockedException
    843     {
    844         long startNode = createBasicTraversableGraph();
    845         List<Object> hits = serialize( actions.traverse( startNode, MapUtil.map(
    846                 "return filter", MapUtil.map( "language", "javascript", "body", "true;" ),
    847                 "max depth", 10 ), TraverserReturnType.node ) );
    848         assertEquals( 6, hits.size() );
    849         hits = serialize( actions.traverse( startNode, MapUtil.map( "return filter", MapUtil.map(
    850                 "language", "builtin", "name", "all" ), "max depth", 10 ), TraverserReturnType.node ) );
    851         assertEquals( 6, hits.size() );
    852     }
    853
    854     @Test
    855     public void shouldBeAbleToUseCustomReturnFilter() throws DatabaseBlockedException
    856     {
    857         long startNode = createBasicTraversableGraph();
    858         List<Object> hits = serialize( actions.traverse( startNode, MapUtil.map( "prune evaluator",
    859                 MapUtil.map( "language", "builtin", "name", "none" ),
    860                 "return filter", MapUtil.map( "language", "javascript", "body", "position.endNode().getProperty( 'name' ).contains( 'o' )" ) ),
    861                 TraverserReturnType.node ) );
    862         assertEquals( 3, hits.size() );
    863     }
    864
    865     @Test
    866     public void shouldBeAbleToTraverseWithMaxDepthAndPruneEvaluatorCombined() throws DatabaseBlockedException
    867     {
    868         long startNode = createBasicTraversableGraph();
    869         List<Object> hits = serialize( actions.traverse( startNode, MapUtil.map( "max depth", 2,
    870                 "prune evaluator", MapUtil.map( "language", "javascript", "body",
    871                 "position.endNode().getProperty('name').equals('Emil')" ) ),
    872                 TraverserReturnType.node ) );
    873         assertEquals( 3, hits.size() );
    874         hits = serialize( actions.traverse( startNode, MapUtil.map( "max depth", 1,
    875                 "prune evaluator", MapUtil.map( "language", "javascript", "body",
    876                 "position.endNode().getProperty('name').equals('Emil')" ) ),
    877                 TraverserReturnType.node ) );
    878         assertEquals( 2, hits.size() );
    879     }
    880
    881     @Test
    882     public void shouldBeAbleToGetRelationshipsIfSpecified() throws DatabaseBlockedException
    883     {
    884         long startNode = createBasicTraversableGraph();
    885         ListRepresentation traverse = actions.traverse( startNode, new HashMap<String, Object>(),
    886                 TraverserReturnType.relationship );
    887         List<Object> hits = serialize( traverse );
    888         for ( Object hit : hits )
    889         {
    890             RelationshipRepresentationTest.verifySerialisation( (Map<String, Object>)hit );
    891         }
    892     }
    893
    894     @Test
    895     public void shouldBeAbleToGetPathsIfSpecified() throws DatabaseBlockedException
    896     {
    897         long startNode = createBasicTraversableGraph();
    898         List<Object> hits = serialize( actions.traverse( startNode, new HashMap<String, Object>(),
    899                 TraverserReturnType.path ) );
    900
    901         for ( Object hit : hits )
    902         {
    903             Map<String, Object> map = (Map<String, Object>)hit;
    904             assertThat( map, hasKey( "start" ) );
    905             assertThat( map, hasKey( "end" ) );
    906             assertThat( map, hasKey( "length" ) );
    907         }
    908     }
    909     @Test
    910     public void shouldBeAbleToGetFullPathsIfSpecified() throws
    911             DatabaseBlockedException
    912     {
    913         long startNode = createBasicTraversableGraph();
    914         List<Object> hits = serialize( actions.traverse( startNode, new HashMap<String, Object>(),
    915                 TraverserReturnType.fullpath ) );
    916
    917         for ( Object hit : hits )
    918         {
    919             Map<String, Object> map = (Map<String, Object>)hit;
    920             Collection<Object> relationships= (Collection<Object>) map.get("relationships");
    921             for (Object relationship : relationships)
    922             {
    923                 RelationshipRepresentationTest.verifySerialisation(
    924                         (Map<String, Object>)relationship );
    925             }
    926             Collection<Object> nodes = (Collection<Object>) map.get("nodes");
    927             for (Object node : nodes)
    928             {
    929                 NodeRepresentationTest.verifySerialisation(
    930                         (Map<String, Object>)node );
    931             }
    932             assertThat( map, hasKey( "start" ) );
    933             assertThat( map, hasKey( "end" ) );
    934             assertThat( map, hasKey( "length" ) );
    935         }
    936     }
    937
    938     @Test
    939     public void shouldBeAbleToGetShortestPaths() throws Exception
    940     {
    941         long[] nodes = createMoreComplexGraph();
    942
    943         // /paths
    944         List<Object> result = serialize( actions.findPaths( nodes[ 0 ], nodes[ 1 ], MapUtil.map(
    945                 "max depth", 2, "algorithm", "shortestPath", "relationships", MapUtil.map( "type",
    946                 "to", "direction", "out" ) ) ) );
    947         assertPaths( 2, nodes, 2, result );
    948
    949         // /path
    950         Map<String, Object> path = serialize( actions.findSinglePath( nodes[ 0 ], nodes[ 1 ],
    951                 MapUtil.map( "max depth", 2, "algorithm", "shortestPath", "relationships",
    952                         MapUtil.map( "type", "to", "direction", "out" ) ) ) );
    953         assertPaths( 1, nodes, 2, Arrays.<Object>asList( path ) );
    954
    955         // /path {single: false} (has no effect)
    956         path = serialize( actions.findSinglePath( nodes[ 0 ], nodes[ 1 ], MapUtil.map( "max depth", 2,
    957                 "algorithm", "shortestPath", "relationships", MapUtil.map( "type", "to",
    958                 "direction", "out" ), "single", false ) ) );
    959         assertPaths( 1, nodes, 2, Arrays.<Object>asList( path ) );
    960     }
    961
    962     @Test
    963     public void shouldBeAbleToGetPathsUsingDijkstra() throws Exception
    964     {
    965         long[] nodes = createDijkstraGraph( true );
    966
    967         // /paths
    968         List<Object> result = serialize( actions.findPaths( nodes[ 0 ], nodes[ 1 ], map(
    969                 "algorithm", "dijkstra", "cost property", "cost", "relationships", map( "type",
    970                 "to", "direction", "out" ) ) ) );
    971         assertPaths( 1, nodes, 6, result );
    972
    973         // /path
    974         Map<String, Object> path = serialize( actions.findSinglePath( nodes[ 0 ], nodes[ 1 ],
    975                 map( "algorithm", "dijkstra", "cost property", "cost", "relationships",
    976                         map( "type", "to", "direction", "out" ) ) ) );
    977         assertPaths( 1, nodes, 6, Arrays.<Object>asList( path ) );
    978         assertEquals( 6.0d, path.get( "weight" ) );
    979     }
    980
    981     @Test
    982     public void shouldBeAbleToGetPathsUsingDijkstraWithDefaults() throws Exception
    983     {
    984         long[] nodes = createDijkstraGraph( false );
    985
    986         // /paths
    987         List<Object> result = serialize( actions.findPaths( nodes[ 0 ], nodes[ 1 ], map(
    988                 "algorithm", "dijkstra", "cost property", "cost", "default cost", 1,
    989                 "relationships", map( "type", "to", "direction", "out" ) ) ) );
    990         assertPaths( 1, nodes, 6, result );
    991
    992         // /path
    993         Map<String, Object> path = serialize( actions.findSinglePath( nodes[ 0 ], nodes[ 1 ],
    994                 map( "algorithm", "dijkstra", "cost property", "cost", "default cost", 1,
    995                         "relationships", map( "type", "to", "direction", "out" ) ) ) );
    996         assertPaths( 1, nodes, 6, Arrays.<Object>asList( path ) );
    997         assertEquals( 6.0d, path.get( "weight" ) );
    998     }
    999
    1000     @Test( expected = NotFoundException.class )
    1001     public void shouldHandleNoFoundPathsCorrectly()
    1002     {
    1003         long[] nodes = createMoreComplexGraph();
    1004         serialize( actions.findSinglePath( nodes[ 0 ], nodes[ 1 ], map( "max depth", 2,
    1005                 "algorithm", "shortestPath", "relationships", map( "type", "to",
    1006                 "direction", "in" ), "single", false ) ) );
    1007     }
    1008
    1009     private void assertPaths( int numPaths, long[] nodes, int length, List<Object> result )
    1010     {
    1011         assertEquals( numPaths, result.size() );
    1012         for ( Object path : result )
    1013         {
    1014             Map<String, Object> serialized = (Map<String, Object>)path;
    1015             assertTrue( serialized.get( "start" ).toString().endsWith( "/" + nodes[ 0 ] ) );
    1016             assertTrue( serialized.get( "end" ).toString().endsWith( "/" + nodes[ 1 ] ) );
    1017             assertEquals( length, serialized.get( "length" ) );
    1018         }
    1019     }
    1020 }