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 java.lang.reflect.Array;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  
30  import org.neo4j.graphalgo.CommonEvaluators;
31  import org.neo4j.graphalgo.CostEvaluator;
32  import org.neo4j.graphalgo.GraphAlgoFactory;
33  import org.neo4j.graphalgo.PathFinder;
34  import org.neo4j.graphalgo.WeightedPath;
35  import org.neo4j.graphdb.Direction;
36  import org.neo4j.graphdb.DynamicRelationshipType;
37  import org.neo4j.graphdb.Expander;
38  import org.neo4j.graphdb.Node;
39  import org.neo4j.graphdb.NotFoundException;
40  import org.neo4j.graphdb.Path;
41  import org.neo4j.graphdb.PropertyContainer;
42  import org.neo4j.graphdb.Relationship;
43  import org.neo4j.graphdb.RelationshipExpander;
44  import org.neo4j.graphdb.Transaction;
45  import org.neo4j.graphdb.TransactionFailureException;
46  import org.neo4j.graphdb.index.Index;
47  import org.neo4j.graphdb.index.IndexHits;
48  import org.neo4j.graphdb.index.RelationshipIndex;
49  import org.neo4j.graphdb.traversal.TraversalDescription;
50  import org.neo4j.helpers.collection.IterableWrapper;
51  import org.neo4j.kernel.AbstractGraphDatabase;
52  import org.neo4j.kernel.Traversal;
53  import org.neo4j.server.database.Database;
54  import org.neo4j.server.database.DatabaseBlockedException;
55  import org.neo4j.server.rest.domain.EndNodeNotFoundException;
56  import org.neo4j.server.rest.domain.RelationshipExpanderBuilder;
57  import org.neo4j.server.rest.domain.StartNodeNotFoundException;
58  import org.neo4j.server.rest.domain.StartNodeSameAsEndNodeException;
59  import org.neo4j.server.rest.domain.TraversalDescriptionBuilder;
60  import org.neo4j.server.rest.domain.TraverserReturnType;
61  import org.neo4j.server.rest.repr.DatabaseRepresentation;
62  import org.neo4j.server.rest.repr.IndexRepresentation;
63  import org.neo4j.server.rest.repr.IndexedEntityRepresentation;
64  import org.neo4j.server.rest.repr.ListRepresentation;
65  import org.neo4j.server.rest.repr.NodeIndexRepresentation;
66  import org.neo4j.server.rest.repr.NodeIndexRootRepresentation;
67  import org.neo4j.server.rest.repr.NodeRepresentation;
68  import org.neo4j.server.rest.repr.PathRepresentation;
69  import org.neo4j.server.rest.repr.PropertiesRepresentation;
70  import org.neo4j.server.rest.repr.RelationshipIndexRepresentation;
71  import org.neo4j.server.rest.repr.RelationshipIndexRootRepresentation;
72  import org.neo4j.server.rest.repr.RelationshipRepresentation;
73  import org.neo4j.server.rest.repr.Representation;
74  import org.neo4j.server.rest.repr.RepresentationType;
75  import org.neo4j.server.rest.repr.WeightedPathRepresentation;
76  
77  // TODO: move this to another package. domain?
78  public class DatabaseActions
79  {
80      private final AbstractGraphDatabase graphDb;
81  
82      public DatabaseActions( Database database )
83      {
84          this.graphDb = database.graph;
85      }
86  
87      private Node node( long id ) throws NodeNotFoundException
88      {
89          try
90          {
91              return graphDb.getNodeById( id );
92          } catch ( NotFoundException e )
93          {
94              throw new NodeNotFoundException();
95          }
96      }
97  
98      private Relationship relationship( long id ) throws RelationshipNotFoundException
99      {
100         try
101         {
102             return graphDb.getRelationshipById( id );
103         } catch ( NotFoundException e )
104         {
105             throw new RelationshipNotFoundException();
106         }
107     }
108 
109     private <T extends PropertyContainer> T set( T entity, Map<String, Object> properties )
110             throws PropertyValueException
111     {
112         if ( properties != null )
113         {
114             for ( Map.Entry<String, Object> property : properties.entrySet() )
115             {
116                 try
117                 {
118                     entity.setProperty( property.getKey(), property( property.getValue() ) );
119                 } catch ( IllegalArgumentException ex )
120                 {
121                     throw new PropertyValueException( property.getKey(), property.getValue() );
122                 }
123             }
124         }
125         return entity;
126     }
127 
128     private Object property( Object value )
129     {
130         if ( value instanceof Collection<?> )
131         {
132             Collection<?> collection = (Collection<?>) value;
133             Object[] array = null;
134             Iterator<?> objects = collection.iterator();
135             for ( int i = 0; objects.hasNext(); i++ )
136             {
137                 Object object = objects.next();
138                 if ( array == null )
139                 {
140                     array = (Object[]) Array.newInstance( object.getClass(), collection.size() );
141                 }
142                 array[i] = object;
143             }
144             return array;
145         }
146         else
147         {
148             return value;
149         }
150     }
151 
152     private <T extends PropertyContainer> T clear( T entity )
153     {
154         for ( String key : entity.getPropertyKeys() )
155         {
156             entity.removeProperty( key );
157         }
158         return entity;
159     }
160 
161     // API
162 
163     public DatabaseRepresentation root()
164     {
165         return new DatabaseRepresentation( graphDb );
166     }
167 
168     // Nodes
169 
170     public NodeRepresentation createNode( Map<String, Object> properties )
171             throws PropertyValueException
172     {
173         final NodeRepresentation result;
174         Transaction tx = graphDb.beginTx();
175         try
176         {
177             result = new NodeRepresentation( set( graphDb.createNode(), properties ) );
178             tx.success();
179         } finally
180         {
181             tx.finish();
182         }
183         return result;
184     }
185 
186     public NodeRepresentation getNode( long nodeId ) throws NodeNotFoundException
187     {
188         return new NodeRepresentation( node( nodeId ) );
189     }
190 
191     public void deleteNode( long nodeId ) throws NodeNotFoundException, OperationFailureException
192     {
193         Node node = node( nodeId );
194         Transaction tx = graphDb.beginTx();
195         try
196         {
197             node.delete();
198             tx.success();
199         } finally
200         {
201             try
202             {
203                 tx.finish();
204             } catch ( TransactionFailureException e )
205             {
206                 throw new OperationFailureException();
207             }
208         }
209     }
210 
211     public NodeRepresentation getReferenceNode()
212     {
213         return new NodeRepresentation( graphDb.getReferenceNode() );
214     }
215 
216     // Node properties
217 
218     public Representation getNodeProperty( long nodeId, String key ) throws NodeNotFoundException,
219             NoSuchPropertyException
220     {
221         Node node = node( nodeId );
222         try
223         {
224             return PropertiesRepresentation.value( node.getProperty( key ) );
225         } catch ( NotFoundException e )
226         {
227             throw new NoSuchPropertyException( node, key );
228         }
229     }
230 
231     public void setNodeProperty( long nodeId, String key, Object value )
232             throws PropertyValueException, NodeNotFoundException
233     {
234         Node node = node( nodeId );
235         value = property( value );
236         Transaction tx = graphDb.beginTx();
237         try
238         {
239             node.setProperty( key, value );
240             tx.success();
241         } catch ( IllegalArgumentException e )
242         {
243             throw new PropertyValueException( key, value );
244         } finally
245         {
246             tx.finish();
247         }
248     }
249 
250     public void removeNodeProperty( long nodeId, String key ) throws NodeNotFoundException,
251             NoSuchPropertyException
252     {
253         Node node = node( nodeId );
254         Transaction tx = graphDb.beginTx();
255         try
256         {
257             if ( node.removeProperty( key ) == null )
258             {
259                 throw new NoSuchPropertyException( node, key );
260             }
261             tx.success();
262         } finally
263         {
264             tx.finish();
265         }
266     }
267 
268     public PropertiesRepresentation getAllNodeProperties( long nodeId )
269             throws NodeNotFoundException
270     {
271         return new PropertiesRepresentation( node( nodeId ) );
272     }
273 
274     public void setAllNodeProperties( long nodeId, Map<String, Object> properties )
275             throws PropertyValueException, NodeNotFoundException
276     {
277         Node node = node( nodeId );
278         Transaction tx = graphDb.beginTx();
279         try
280         {
281             set( clear( node ), properties );
282             tx.success();
283         } finally
284         {
285             tx.finish();
286         }
287     }
288 
289     public void removeAllNodeProperties( long nodeId ) throws NodeNotFoundException
290     {
291         Node node = node( nodeId );
292         Transaction tx = graphDb.beginTx();
293         try
294         {
295             clear( node );
296             tx.success();
297         } finally
298         {
299             tx.finish();
300         }
301     }
302 
303     public String[] getNodeIndexNames()
304     {
305         return graphDb.index().nodeIndexNames();
306     }
307 
308     public String[] getRelationshipIndexNames()
309     {
310         return graphDb.index().relationshipIndexNames();
311     }
312 
313     public IndexRepresentation createNodeIndex( Map<String, Object> indexSpecification )
314     {
315         final String indexName = (String) indexSpecification.get( "name" );
316         if ( indexSpecification.containsKey( "config" ) )
317         {
318 
319             @SuppressWarnings( "unchecked" )
320             Map<String, String> config = (Map<String, String>) indexSpecification.get( "config" );
321             graphDb.index().forNodes( indexName, config );
322 
323             return new NodeIndexRepresentation( indexName, config );
324         }
325 
326         graphDb.index().forNodes( indexName );
327         return new NodeIndexRepresentation( indexName, Collections.<String, String>emptyMap() );
328     }
329 
330 
331     public IndexRepresentation createRelationshipIndex( Map<String, Object> indexSpecification )
332     {
333         final String indexName = (String) indexSpecification.get( "name" );
334         if ( indexSpecification.containsKey( "config" ) )
335         {
336 
337             @SuppressWarnings( "unchecked" )
338             Map<String, String> config = (Map<String, String>) indexSpecification.get( "config" );
339             graphDb.index().forRelationships( indexName, config );
340 
341             return new RelationshipIndexRepresentation( indexName, config );
342         }
343 
344         graphDb.index().forRelationships( indexName );
345         return new RelationshipIndexRepresentation( indexName, Collections.<String, String>emptyMap() );
346     }
347 
348     public boolean nodeIsIndexed( String indexName, String key, Object value, long nodeId ) throws DatabaseBlockedException
349     {
350 
351         Index<Node> index = graphDb.index().forNodes( indexName );
352         Transaction tx = graphDb.beginTx();
353         try
354         {
355             Node expectedNode = graphDb.getNodeById( nodeId );
356             IndexHits<Node> hits = index.get( key, value );
357             boolean contains = iterableContains( hits, expectedNode );
358             tx.success();
359             return contains;
360         } finally
361         {
362             tx.finish();
363         }
364     }
365 
366     public boolean relationshipIsIndexed( String indexName, String key, Object value, long relationshipId ) throws DatabaseBlockedException
367     {
368 
369         Index<Relationship> index = graphDb.index().forRelationships( indexName );
370         Transaction tx = graphDb.beginTx();
371         try
372         {
373             Relationship expectedNode = graphDb.getRelationshipById( relationshipId );
374             IndexHits<Relationship> hits = index.get( key, value );
375             boolean contains = iterableContains( hits, expectedNode );
376             tx.success();
377             return contains;
378         } finally
379         {
380             tx.finish();
381         }
382     }
383 
384     private <T> boolean iterableContains( Iterable<T> iterable, T expectedElement )
385     {
386         for ( T possibleMatch : iterable )
387         {
388             if ( possibleMatch.equals( expectedElement ) )
389                 return true;
390         }
391         return false;
392     }
393 
394     // Relationships
395 
396     public enum RelationshipDirection
397     {
398         all( Direction.BOTH ),
399         in( Direction.INCOMING ),
400         out( Direction.OUTGOING );
401         final Direction internal;
402 
403         private RelationshipDirection( Direction internal )
404         {
405             this.internal = internal;
406         }
407     }
408 
409     public RelationshipRepresentation createRelationship( long startNodeId, long endNodeId,
410                                                           String type,
411                                                           Map<String, Object> properties ) throws StartNodeNotFoundException,
412             EndNodeNotFoundException, StartNodeSameAsEndNodeException, PropertyValueException
413     {
414         if ( startNodeId == endNodeId )
415         {
416             throw new StartNodeSameAsEndNodeException();
417         }
418         Node start, end;
419         try
420         {
421             start = node( startNodeId );
422         } catch ( NodeNotFoundException e )
423         {
424             throw new StartNodeNotFoundException();
425         }
426         try
427         {
428             end = node( endNodeId );
429         } catch ( NodeNotFoundException e )
430         {
431             throw new EndNodeNotFoundException();
432         }
433         final RelationshipRepresentation result;
434         Transaction tx = graphDb.beginTx();
435         try
436         {
437             result = new RelationshipRepresentation( set( start.createRelationshipTo( end,
438                     DynamicRelationshipType.withName( type ) ), properties ) );
439             tx.success();
440         } finally
441         {
442             tx.finish();
443         }
444         return result;
445     }
446 
447     public RelationshipRepresentation getRelationship( long relationshipId )
448             throws RelationshipNotFoundException
449     {
450         return new RelationshipRepresentation( relationship( relationshipId ) );
451     }
452 
453     public void deleteRelationship( long relationshipId ) throws RelationshipNotFoundException
454     {
455         Relationship relationship = relationship( relationshipId );
456         Transaction tx = graphDb.beginTx();
457         try
458         {
459             relationship.delete();
460             tx.success();
461         } finally
462         {
463             tx.finish();
464         }
465     }
466 
467     public ListRepresentation getNodeRelationships( long nodeId, RelationshipDirection direction,
468                                                     Collection<String> types ) throws NodeNotFoundException
469     {
470         Node node = node( nodeId );
471         Expander expander;
472         if ( types.isEmpty() )
473         {
474             expander = Traversal.expanderForAllTypes( direction.internal );
475         }
476         else
477         {
478             expander = Traversal.emptyExpander();
479             for ( String type : types )
480             {
481                 expander = expander.add( DynamicRelationshipType.withName( type ),
482                         direction.internal );
483             }
484         }
485         return RelationshipRepresentation.list( expander.expand( node ) );
486     }
487 
488     // Relationship properties
489 
490     public PropertiesRepresentation getAllRelationshipProperties( long relationshipId )
491             throws RelationshipNotFoundException
492     {
493         return new PropertiesRepresentation( relationship( relationshipId ) );
494     }
495 
496     public Representation getRelationshipProperty( long relationshipId, String key )
497             throws NoSuchPropertyException, RelationshipNotFoundException
498     {
499         Relationship relationship = relationship( relationshipId );
500         try
501         {
502             return PropertiesRepresentation.value( relationship.getProperty( key ) );
503         } catch ( NotFoundException e )
504         {
505             throw new NoSuchPropertyException( relationship, key );
506         }
507     }
508 
509     public void setAllRelationshipProperties( long relationshipId, Map<String, Object> properties )
510             throws PropertyValueException, RelationshipNotFoundException
511     {
512         Relationship relationship = relationship( relationshipId );
513         Transaction tx = graphDb.beginTx();
514         try
515         {
516             set( clear( relationship ), properties );
517             tx.success();
518         } finally
519         {
520             tx.finish();
521         }
522     }
523 
524     public void setRelationshipProperty( long relationshipId, String key, Object value )
525             throws PropertyValueException, RelationshipNotFoundException
526     {
527         Relationship relationship = relationship( relationshipId );
528         value = property( value );
529         Transaction tx = graphDb.beginTx();
530         try
531         {
532             relationship.setProperty( key, value );
533             tx.success();
534         } catch ( IllegalArgumentException e )
535         {
536             throw new PropertyValueException( key, value );
537         } finally
538         {
539             tx.finish();
540         }
541     }
542 
543     public void removeAllRelationshipProperties( long relationshipId )
544             throws RelationshipNotFoundException
545     {
546         Relationship relationship = relationship( relationshipId );
547         Transaction tx = graphDb.beginTx();
548         try
549         {
550             clear( relationship );
551             tx.success();
552         } finally
553         {
554             tx.finish();
555         }
556     }
557 
558     public void removeRelationshipProperty( long relationshipId, String key )
559             throws RelationshipNotFoundException, NoSuchPropertyException
560     {
561         Relationship relationship = relationship( relationshipId );
562         Transaction tx = graphDb.beginTx();
563         try
564         {
565             if ( relationship.removeProperty( key ) == null )
566             {
567                 throw new NoSuchPropertyException( relationship, key );
568             }
569             tx.success();
570         } finally
571         {
572             tx.finish();
573         }
574     }
575 
576     // Index
577 
578     public enum IndexType
579     {
580         node( "index" )
581                 {
582                 },
583         relationship( "index" )
584                 {
585                 };
586         private final String pathPrefix;
587 
588         private IndexType( String pathPrefix )
589         {
590             this.pathPrefix = pathPrefix;
591         }
592 
593         @SuppressWarnings("boxing")
594         String path( String indexName, String key, String value, long id )
595         {
596             return String.format( "%s/%s/%s/%s/%s", pathPrefix, indexName, key, value, id );
597         }
598     }
599 
600     public Representation nodeIndexRoot()
601     {
602         return new NodeIndexRootRepresentation( graphDb.index() );
603     }
604 
605     public Representation relationshipIndexRoot()
606     {
607         return new RelationshipIndexRootRepresentation( graphDb.index() );
608     }
609 
610     public IndexedEntityRepresentation addToRelationshipIndex( String indexName, String key,
611                                                                String value, long relationshipId )
612     {
613         Transaction tx = graphDb.beginTx();
614         try
615         {
616             Relationship relationship = graphDb.getRelationshipById( relationshipId );
617             Index<Relationship> index = graphDb.index().forRelationships( indexName );
618             index.add( relationship, key, value );
619             tx.success();
620             return new IndexedEntityRepresentation( relationship, key, value, new RelationshipIndexRepresentation( indexName, Collections.<String, String>emptyMap() ) );
621         } finally
622         {
623             tx.finish();
624         }
625     }
626 
627     public IndexedEntityRepresentation addToNodeIndex( String indexName, String key,
628                                                        String value, long nodeId )
629     {
630         Transaction tx = graphDb.beginTx();
631         try
632         {
633             Node node = graphDb.getNodeById( nodeId );
634             Index<Node> index = graphDb.index().forNodes( indexName );
635             index.add( node, key, value );
636             tx.success();
637             return new IndexedEntityRepresentation( node, key, value, new NodeIndexRepresentation( indexName, Collections.<String, String>emptyMap() ) );
638         } finally
639         {
640             tx.finish();
641         }
642     }
643 
644     public void removeFromNodeIndex( String indexName, String key, String value, long id )
645     {
646         Index<Node> index = graphDb.index().forNodes( indexName );
647         Transaction tx = graphDb.beginTx();
648         try
649         {
650             index.remove( graphDb.getNodeById( id ), key, value );
651             tx.success();
652         } finally
653         {
654             tx.finish();
655         }
656     }
657 
658     public void removeFromNodeIndexNoValue( String indexName, String key, long id )
659     {
660         Index<Node> index = graphDb.index().forNodes( indexName );
661         Transaction tx = graphDb.beginTx();
662         try
663         {
664             index.remove( graphDb.getNodeById( id ), key );
665             tx.success();
666         } finally
667         {
668             tx.finish();
669         }
670     }
671     
672     public void removeFromNodeIndexNoKeyValue( String indexName, long id )
673     {
674         Index<Node> index = graphDb.index().forNodes( indexName );
675         Transaction tx = graphDb.beginTx();
676         try
677         {
678             index.remove( graphDb.getNodeById( id ) );
679             tx.success();
680         } finally
681         {
682             tx.finish();
683         }
684     }
685     
686     public void removeFromRelationshipIndex( String indexName, String key, String value, long id )
687     {
688         RelationshipIndex index = graphDb.index().forRelationships( indexName );
689         Transaction tx = graphDb.beginTx();
690         try
691         {
692             index.remove( graphDb.getRelationshipById( id ), key, value );
693             tx.success();
694         } finally
695         {
696             tx.finish();
697         }
698     }
699 
700     public void removeFromRelationshipIndexNoValue( String indexName, String key, long id )
701     {
702         RelationshipIndex index = graphDb.index().forRelationships( indexName );
703         Transaction tx = graphDb.beginTx();
704         try
705         {
706             index.remove( graphDb.getRelationshipById( id ), key );
707             tx.success();
708         } finally
709         {
710             tx.finish();
711         }
712     }
713     
714     public void removeFromRelationshipIndexNoKeyValue( String indexName, long id )
715     {
716         RelationshipIndex index = graphDb.index().forRelationships( indexName );
717         Transaction tx = graphDb.beginTx();
718         try
719         {
720             index.remove( graphDb.getRelationshipById( id ) );
721             tx.success();
722         } finally
723         {
724             tx.finish();
725         }
726     }
727     
728     public IndexedEntityRepresentation getIndexedNode( String indexName,
729                                                        String key, String value, long id )
730     {
731         if ( !nodeIsIndexed( indexName, key, value, id ) )
732             throw new NotFoundException();
733         Node node = graphDb.getNodeById( id );
734         return new IndexedEntityRepresentation( node, key, value, new NodeIndexRepresentation( indexName, Collections.<String, String>emptyMap() ) );
735     }
736 
737     public IndexedEntityRepresentation getIndexedRelationship( String indexName,
738                                                                String key, String value, long id )
739     {
740         if ( !relationshipIsIndexed( indexName, key, value, id ) )
741             throw new NotFoundException();
742         Relationship node = graphDb.getRelationshipById( id );
743         return new IndexedEntityRepresentation( node, key, value, new RelationshipIndexRepresentation( indexName, Collections.<String, String>emptyMap() ) );
744     }
745 
746 
747     public ListRepresentation getIndexedNodesByExactMatch( String indexName, String key,
748                                                String value )
749     {
750         if ( !graphDb.index().existsForNodes( indexName ) )
751             throw new NotFoundException();
752         Index<Node> index = graphDb.index().forNodes( indexName );
753         List<IndexedEntityRepresentation> representations = new ArrayList<IndexedEntityRepresentation>();
754 
755         Transaction tx = graphDb.beginTx();
756         try
757         {
758             IndexRepresentation indexRepresentation = new NodeIndexRepresentation( indexName );
759             for ( Node node : index.get( key, value ) )
760             {
761                 representations.add( new IndexedEntityRepresentation( node, key, value, indexRepresentation ) );
762             }
763             tx.success();
764             return new ListRepresentation( RepresentationType.NODE, representations );
765         } finally
766         {
767             tx.finish();
768         }
769     }
770 
771     public ListRepresentation getIndexedNodesByQuery( String indexName, String key,
772                                                String query )
773     {
774         if ( !graphDb.index().existsForNodes( indexName ) )
775             throw new NotFoundException();
776         Index<Node> index = graphDb.index().forNodes( indexName );
777         List<Representation> representations = new ArrayList<Representation>();
778 
779         Transaction tx = graphDb.beginTx();
780         try
781         {
782             for ( Node node : index.query( key, query ) )
783             {
784                 representations.add( new NodeRepresentation( node ));
785             }
786             tx.success();
787             return new ListRepresentation( RepresentationType.NODE, representations );
788         } finally
789         {
790             tx.finish();
791         }
792     }
793 
794 
795     public ListRepresentation getIndexedRelationships( String indexName, String key,
796                                                        String value )
797     {
798         if ( !graphDb.index().existsForRelationships( indexName ) )
799             throw new NotFoundException();
800         List<IndexedEntityRepresentation> representations = new ArrayList<IndexedEntityRepresentation>();
801         Index<Relationship> index = graphDb.index().forRelationships( indexName );
802 
803         Transaction tx = graphDb.beginTx();
804         try
805         {
806             IndexRepresentation indexRepresentation = new RelationshipIndexRepresentation( indexName );
807             for ( Relationship node : index.get( key, value ) )
808             {
809                 representations.add( new IndexedEntityRepresentation( node, key, value, indexRepresentation ) );
810             }
811             tx.success();
812             return new ListRepresentation( RepresentationType.RELATIONSHIP, representations );
813         } finally
814         {
815             tx.finish();
816         }
817     }
818 
819     public ListRepresentation getIndexedRelationshipsByQuery( String indexName, String key,
820                                                        String query )
821     {
822         if ( !graphDb.index().existsForRelationships( indexName ) )
823             throw new NotFoundException();
824         List<Representation> representations = new ArrayList<Representation>();
825         Index<Relationship> index = graphDb.index().forRelationships( indexName );
826 
827         Transaction tx = graphDb.beginTx();
828         try
829         {
830             for ( Relationship rel : index.query( key, query ) )
831             {
832                 representations.add( new RelationshipRepresentation( rel ));
833             }
834             tx.success();
835             return new ListRepresentation( RepresentationType.RELATIONSHIP, representations );
836         } finally
837         {
838             tx.finish();
839         }
840     }
841 
842     // Traversal
843 
844     public ListRepresentation traverse( long startNode, Map<String, Object> description,
845                                         TraverserReturnType returnType )
846     {
847         Node node = graphDb.getNodeById( startNode );
848 
849         List<Representation> result = new ArrayList<Representation>();
850 
851         TraversalDescription traversalDescription = TraversalDescriptionBuilder.from( description );
852         for ( Path position : traversalDescription.traverse( node ) )
853         {
854             result.add( returnType.toRepresentation( position ) );
855         }
856 
857         return new ListRepresentation( returnType.repType, result );
858     }
859 
860     @SuppressWarnings( "rawtypes" )
861     public PathRepresentation findSinglePath( long startId, long endId,
862                                               Map<String, Object> map )
863     {
864         FindParams findParams = new FindParams( startId, endId, map ).invoke();
865         PathFinder finder = findParams.getFinder();
866         Node startNode = findParams.getStartNode();
867         Node endNode = findParams.getEndNode();
868 
869         Path path = finder.findSinglePath( startNode, endNode );
870         if ( path == null )
871         {
872             throw new NotFoundException();
873         }
874         return findParams.pathRepresentationOf( path );
875     }
876 
877     @SuppressWarnings( { "rawtypes", "unchecked" } )
878     public ListRepresentation findPaths( long startId, long endId,
879                                          Map<String, Object> map )
880     {
881         final FindParams findParams = new FindParams( startId, endId, map ).invoke();
882         PathFinder finder = findParams.getFinder();
883         Node startNode = findParams.getStartNode();
884         Node endNode = findParams.getEndNode();
885 
886         Iterable paths = finder.findAllPaths( startNode, endNode );
887 
888         IterableWrapper<PathRepresentation, Path> pathRepresentations = new IterableWrapper<PathRepresentation, Path>( paths )
889         {
890             @Override
891             protected PathRepresentation underlyingObjectToObject( Path path )
892             {
893                 return findParams.pathRepresentationOf( path );
894             }
895         };
896 
897         return new ListRepresentation( RepresentationType.PATH, pathRepresentations );
898     }
899 
900     private class FindParams
901     {
902         private final long startId;
903         private final long endId;
904         private final Map<String, Object> map;
905         private Node startNode;
906         private Node endNode;
907         private PathFinder<? extends Path> finder;
908         @SuppressWarnings( "rawtypes" )
909         private PathRepresentationCreator representationCreator = PATH_REPRESENTATION_CREATOR;
910 
911         public FindParams( final long startId, final long endId, final Map<String, Object> map )
912         {
913             this.startId = startId;
914             this.endId = endId;
915             this.map = map;
916         }
917 
918         public Node getStartNode()
919         {
920             return startNode;
921         }
922 
923         public Node getEndNode()
924         {
925             return endNode;
926         }
927 
928         public PathFinder<? extends Path> getFinder()
929         {
930             return finder;
931         }
932         
933         @SuppressWarnings( "unchecked" )
934         public PathRepresentation<? extends Path> pathRepresentationOf( Path path )
935         {
936             return representationCreator.from( path );
937         }
938 
939         public FindParams invoke()
940         {
941             startNode = graphDb.getNodeById( startId );
942             endNode = graphDb.getNodeById( endId );
943 
944             Integer maxDepthObj = (Integer) map.get( "max depth" );
945             int maxDepth = (maxDepthObj != null) ? maxDepthObj : 1;
946 
947             RelationshipExpander expander = RelationshipExpanderBuilder.describeRelationships( map );
948 
949             String algorithm = (String) map.get( "algorithm" );
950             algorithm = (algorithm != null) ? algorithm : "shortestPath";
951 
952             finder = getAlgorithm( algorithm, expander, maxDepth );
953             return this;
954         }
955 
956         private PathFinder<? extends Path> getAlgorithm( String algorithm, RelationshipExpander expander, int maxDepth )
957         {
958             if ( algorithm.equals( "shortestPath" ) )
959             {
960                 return GraphAlgoFactory.shortestPath( expander, maxDepth );
961             }
962             else if ( algorithm.equals( "allSimplePaths" ) )
963             {
964                 return GraphAlgoFactory.allSimplePaths( expander, maxDepth );
965             }
966             else if ( algorithm.equals( "allPaths" ) )
967             {
968                 return GraphAlgoFactory.allPaths( expander, maxDepth );
969             }
970             else if ( algorithm.equals( "dijkstra" ) )
971             {
972                 String costProperty = (String) map.get( "cost property" );
973                 Number defaultCost = (Number) map.get( "default cost" );
974                 CostEvaluator<Double> costEvaluator = defaultCost == null ?
975                         CommonEvaluators.doubleCostEvaluator( costProperty ) :
976                         CommonEvaluators.doubleCostEvaluator( costProperty, defaultCost.doubleValue() );
977                 representationCreator = WEIGHTED_PATH_REPRESENTATION_CREATOR;
978                 return GraphAlgoFactory.dijkstra( expander, costEvaluator );
979             }
980 
981             throw new RuntimeException( "Failed to find matching algorithm" );
982         }
983     }
984     
985     private interface PathRepresentationCreator<T extends Path>
986     {
987         PathRepresentation<T> from( T path );
988     }
989     
990     private static final PathRepresentationCreator<Path> PATH_REPRESENTATION_CREATOR = new PathRepresentationCreator<Path>()
991     {
992         @Override
993         public PathRepresentation<Path> from( Path path )
994         {
995             return new PathRepresentation<Path>( path );
996         }
997     };
998     
999     private static final PathRepresentationCreator<WeightedPath> WEIGHTED_PATH_REPRESENTATION_CREATOR =
1000             new PathRepresentationCreator<WeightedPath>()
1001     {
1002         @Override
1003         public PathRepresentation<WeightedPath> from( WeightedPath path )
1004         {
1005             return new WeightedPathRepresentation( path );
1006         }
1007     };
1008 }