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 }