1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.neo4j.examples.socnet;
20
21 import org.neo4j.graphalgo.GraphAlgoFactory;
22 import org.neo4j.graphalgo.PathFinder;
23 import org.neo4j.graphdb.*;
24 import org.neo4j.graphdb.traversal.*;
25 import org.neo4j.graphdb.traversal.Traverser;
26 import org.neo4j.helpers.collection.IterableWrapper;
27 import org.neo4j.helpers.collection.IteratorUtil;
28 import org.neo4j.kernel.Traversal;
29 import org.neo4j.kernel.Uniqueness;
30
31 import java.util.*;
32
33 import static org.neo4j.examples.socnet.RelTypes.STATUS;
34 import static org.neo4j.examples.socnet.RelTypes.FRIEND;
35 import static org.neo4j.examples.socnet.RelTypes.NEXT;
36
37 public class Person
38 {
39 static final String NAME = "name";
40
41
42 private final Node underlyingNode;
43
44 Person( Node personNode )
45 {
46 this.underlyingNode = personNode;
47 }
48
49 protected Node getUnderlyingNode()
50 {
51 return underlyingNode;
52 }
53
54
55
56
57 public String getName()
58 {
59 return (String)underlyingNode.getProperty( NAME );
60 }
61
62
63
64
65 @Override
66 public int hashCode()
67 {
68 return underlyingNode.hashCode();
69 }
70
71 @Override
72 public boolean equals( Object o )
73 {
74 return o instanceof Person &&
75 underlyingNode.equals( ( (Person)o ).getUnderlyingNode() );
76 }
77
78 @Override
79 public String toString()
80 {
81 return "Person[" + getName() + "]";
82 }
83
84
85
86 public void addFriend( Person otherPerson )
87 {
88 Transaction tx = underlyingNode.getGraphDatabase().beginTx();
89 try
90 {
91 if ( !this.equals( otherPerson ) )
92 {
93 Relationship friendRel = getFriendRelationshipTo( otherPerson );
94 if ( friendRel == null )
95 {
96 underlyingNode.createRelationshipTo( otherPerson.getUnderlyingNode(), FRIEND );
97 }
98 tx.success();
99 }
100 }
101 finally
102 {
103 tx.finish();
104 }
105 }
106
107 public int getNrOfFriends()
108 {
109 return IteratorUtil.count( getFriends() );
110 }
111
112 public Iterable<Person> getFriends()
113 {
114 return getFriendsByDepth( 1 );
115 }
116
117 public void removeFriend( Person otherPerson )
118 {
119 Transaction tx = underlyingNode.getGraphDatabase().beginTx();
120 try
121 {
122 if ( !this.equals( otherPerson ) )
123 {
124 Relationship friendRel = getFriendRelationshipTo( otherPerson );
125 if ( friendRel != null )
126 {
127 friendRel.delete();
128 }
129 tx.success();
130 }
131 }
132 finally
133 {
134 tx.finish();
135 }
136 }
137
138 public Iterable<Person> getFriendsOfFriends()
139 {
140 return getFriendsByDepth( 2 );
141 }
142
143 public Iterable<Person> getShortestPathTo( Person otherPerson,
144 int maxDepth )
145 {
146
147 PathFinder<Path> finder = GraphAlgoFactory.shortestPath(
148 Traversal.expanderForTypes( FRIEND, Direction.BOTH ), maxDepth );
149
150 Path path = finder.findSinglePath( underlyingNode,
151 otherPerson.getUnderlyingNode() );
152 return createPersonsFromNodes( path );
153 }
154
155 public Iterable<Person> getFriendRecommendation(
156 int numberOfFriendsToReturn )
157 {
158 HashSet<Person> friends = new HashSet<Person>();
159 IteratorUtil.addToCollection( getFriends(), friends );
160
161 HashSet<Person> friendsOfFriends = new HashSet<Person>();
162 IteratorUtil.addToCollection( getFriendsOfFriends(), friendsOfFriends );
163
164 friendsOfFriends.removeAll( friends );
165
166 ArrayList<RankedPerson> rankedFriends = new ArrayList<RankedPerson>();
167 for ( Person friend : friendsOfFriends )
168 {
169 int rank = getNumberOfPathsToPerson( friend );
170 rankedFriends.add( new RankedPerson( friend, rank ) );
171 }
172
173 Collections.sort( rankedFriends, new RankedComparer() );
174 trimTo( rankedFriends, numberOfFriendsToReturn );
175
176 return onlyFriend( rankedFriends );
177 }
178
179 public Iterable<StatusUpdate> getStatus()
180 {
181 Relationship firstStatus = underlyingNode.getSingleRelationship(
182 STATUS, Direction.OUTGOING );
183 if ( firstStatus == null )
184 {
185 return Collections.emptyList();
186 }
187
188
189 TraversalDescription traversal = Traversal.description().
190 depthFirst().
191 relationships( NEXT ).
192 filter( Traversal.returnAll() );
193
194
195
196 return new IterableWrapper<StatusUpdate, Path>(
197 traversal.traverse( firstStatus.getEndNode() ) )
198 {
199 @Override
200 protected StatusUpdate underlyingObjectToObject( Path path )
201 {
202 return new StatusUpdate( path.endNode() );
203 }
204 };
205 }
206
207 public Iterator<StatusUpdate> friendStatuses()
208 {
209 return new FriendsStatusUpdateIterator( this );
210 }
211
212 public void addStatus( String text )
213 {
214 Transaction tx = graphDb().beginTx();
215 try
216 {
217 StatusUpdate oldStatus;
218 if ( getStatus().iterator().hasNext() )
219 {
220 oldStatus = getStatus().iterator().next();
221 } else
222 {
223 oldStatus = null;
224 }
225
226 Node newStatus = createNewStatusNode( text );
227
228 if ( oldStatus != null )
229 {
230 underlyingNode.getSingleRelationship( RelTypes.STATUS, Direction.OUTGOING ).delete();
231 newStatus.createRelationshipTo( oldStatus.getUnderlyingNode(), RelTypes.NEXT );
232 }
233
234 underlyingNode.createRelationshipTo( newStatus, RelTypes.STATUS );
235 tx.success();
236 }
237 finally
238 {
239 tx.finish();
240 }
241 }
242
243 private GraphDatabaseService graphDb()
244 {
245 return underlyingNode.getGraphDatabase();
246 }
247
248 private Node createNewStatusNode( String text )
249 {
250 Node newStatus = graphDb().createNode();
251 newStatus.setProperty( StatusUpdate.TEXT, text );
252 newStatus.setProperty( StatusUpdate.DATE, new Date().getTime() );
253 return newStatus;
254 }
255
256 private final class RankedPerson
257 {
258 final Person person;
259
260 final int rank;
261
262 private RankedPerson( Person person, int rank )
263 {
264
265 this.person = person;
266 this.rank = rank;
267 }
268
269 public Person getPerson()
270 {
271 return person;
272 }
273 public int getRank()
274 {
275 return rank;
276 }
277
278 }
279
280 private class RankedComparer implements Comparator<RankedPerson>
281 {
282 public int compare( RankedPerson a, RankedPerson b )
283 {
284 return b.getRank() - a.getRank();
285 }
286
287 }
288
289 private void trimTo( ArrayList<RankedPerson> rankedFriends,
290 int numberOfFriendsToReturn )
291 {
292 while ( rankedFriends.size() > numberOfFriendsToReturn )
293 {
294 rankedFriends.remove( rankedFriends.size() - 1 );
295 }
296 }
297
298 private Iterable<Person> onlyFriend( Iterable<RankedPerson> rankedFriends )
299 {
300 ArrayList<Person> retVal = new ArrayList<Person>();
301 for ( RankedPerson person : rankedFriends )
302 {
303 retVal.add( person.getPerson() );
304 }
305 return retVal;
306 }
307
308 private Relationship getFriendRelationshipTo( Person otherPerson )
309 {
310 Node otherNode = otherPerson.getUnderlyingNode();
311 for ( Relationship rel : underlyingNode.getRelationships( FRIEND ) )
312 {
313 if ( rel.getOtherNode( underlyingNode ).equals( otherNode ) )
314 {
315 return rel;
316 }
317 }
318 return null;
319 }
320
321 private Iterable<Person> getFriendsByDepth( int depth )
322 {
323
324 TraversalDescription travDesc = Traversal.description()
325 .breadthFirst()
326 .relationships( FRIEND )
327 .uniqueness( Uniqueness.NODE_GLOBAL )
328 .prune( Traversal.pruneAfterDepth( depth ) )
329 .filter( Traversal.returnAllButStartNode() );
330
331 return createPersonsFromPath( travDesc.traverse( underlyingNode ) );
332 }
333
334 private IterableWrapper<Person, Path> createPersonsFromPath(
335 Traverser iterableToWrap )
336 {
337 return new IterableWrapper<Person, Path>( iterableToWrap )
338 {
339 @Override
340 protected Person underlyingObjectToObject( Path path )
341 {
342 return new Person( path.endNode() );
343 }
344 };
345 }
346
347 private int getNumberOfPathsToPerson( Person otherPerson )
348 {
349 PathFinder<Path> finder = GraphAlgoFactory.allPaths( Traversal.expanderForTypes( FRIEND, Direction.BOTH ), 2 );
350 Iterable<Path> paths = finder.findAllPaths( getUnderlyingNode(), otherPerson.getUnderlyingNode() );
351 return IteratorUtil.count( paths );
352 }
353
354 private Iterable<Person> createPersonsFromNodes( final Path path )
355 {
356 return new IterableWrapper<Person, Node>( path.nodes() )
357 {
358 @Override
359 protected Person underlyingObjectToObject( Node node )
360 {
361 return new Person( node );
362 }
363 };
364 }
365
366 }