Biblioteca Java - Blame information for rev 18
Subversion Repositories:
(root)/Frameworks and Technologies/Neo4J Samples/Neo4JTutorial/src/main/java/com/linkscreens/graphsin/repository/PersonNode.java
Rev | Author | Line No. | Line |
---|---|---|---|
18 | mihai | 1 | /** |
2 | * Licensed to Neo Technology under one or more contributor | ||
3 | * license agreements. See the NOTICE file distributed with | ||
4 | * this work for additional information regarding copyright | ||
5 | * ownership. Neo Technology licenses this file to you under | ||
6 | * the Apache License, Version 2.0 (the "License"); you may | ||
7 | * not use this file except in compliance with the License. | ||
8 | * You may obtain a copy of the License at | ||
9 | * | ||
10 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | * | ||
12 | * Unless required by applicable law or agreed to in writing, | ||
13 | * software distributed under the License is distributed on an | ||
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | * KIND, either express or implied. See the License for the | ||
16 | * specific language governing permissions and limitations | ||
17 | * under the License. | ||
18 | */ | ||
19 | package com.linkscreens.graphsin.repository; | ||
20 | |||
21 | import java.util.ArrayList; | ||
22 | import java.util.Collections; | ||
23 | import java.util.Comparator; | ||
24 | import java.util.Date; | ||
25 | import java.util.HashSet; | ||
26 | import java.util.Iterator; | ||
27 | |||
28 | import org.neo4j.graphalgo.GraphAlgoFactory; | ||
29 | import org.neo4j.graphalgo.PathFinder; | ||
30 | import org.neo4j.graphdb.Direction; | ||
31 | import org.neo4j.graphdb.GraphDatabaseService; | ||
32 | import org.neo4j.graphdb.Node; | ||
33 | import org.neo4j.graphdb.Path; | ||
34 | import org.neo4j.graphdb.Relationship; | ||
35 | import org.neo4j.graphdb.traversal.Evaluators; | ||
36 | import org.neo4j.graphdb.traversal.TraversalDescription; | ||
37 | import org.neo4j.graphdb.traversal.Traverser; | ||
38 | import org.neo4j.helpers.collection.IterableWrapper; | ||
39 | import org.neo4j.helpers.collection.IteratorUtil; | ||
40 | import org.neo4j.graphdb.traversal.Uniqueness; | ||
41 | |||
42 | import com.linkscreens.graphsin.network.FriendsStatusUpdateIterator; | ||
43 | |||
44 | import static org.neo4j.graphdb.Direction.BOTH; | ||
45 | import static org.neo4j.graphdb.PathExpanders.forTypeAndDirection; | ||
46 | |||
47 | public class PersonNode | ||
48 | { | ||
49 | public static final String EMAIL = "email"; | ||
50 | |||
51 | // START SNIPPET: the-node | ||
52 | private final Node underlyingNode; | ||
53 | |||
54 | public PersonNode( Node personNode ) | ||
55 | { | ||
56 | this.underlyingNode = personNode; | ||
57 | } | ||
58 | |||
59 | public Node getUnderlyingNode() | ||
60 | { | ||
61 | return underlyingNode; | ||
62 | } | ||
63 | |||
64 | // END SNIPPET: the-node | ||
65 | |||
66 | // START SNIPPET: delegate-to-the-node | ||
67 | public String getName() | ||
68 | { | ||
69 | return (String)underlyingNode.getProperty( EMAIL ); | ||
70 | } | ||
71 | |||
72 | // END SNIPPET: delegate-to-the-node | ||
73 | |||
74 | |||
75 | // START SNIPPET: override | ||
76 | @Override | ||
77 | public int hashCode() | ||
78 | { | ||
79 | return underlyingNode.hashCode(); | ||
80 | } | ||
81 | |||
82 | @Override | ||
83 | public boolean equals( Object o ) | ||
84 | { | ||
85 | return o instanceof PersonNode && | ||
86 | underlyingNode.equals( ( (PersonNode)o ).getUnderlyingNode() ); | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public String toString() | ||
91 | { | ||
92 | return "Person[" + getName() + "]"; | ||
93 | } | ||
94 | |||
95 | // END SNIPPET: override | ||
96 | |||
97 | public void addFriend( PersonNode otherPerson ) | ||
98 | { | ||
99 | if ( !this.equals( otherPerson ) ) | ||
100 | { | ||
101 | Relationship friendRel = getFriendRelationshipTo( otherPerson ); | ||
102 | if ( friendRel == null ) | ||
103 | { | ||
104 | underlyingNode.createRelationshipTo( otherPerson.getUnderlyingNode(), RelTypes.FRIEND ); | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | public int getNrOfFriends() | ||
110 | { | ||
111 | return IteratorUtil.count( getFriends() ); | ||
112 | } | ||
113 | |||
114 | public Iterable<PersonNode> getFriends() | ||
115 | { | ||
116 | return getFriendsByDepth( 1 ); | ||
117 | } | ||
118 | |||
119 | public void removeFriend( PersonNode otherPerson ) | ||
120 | { | ||
121 | if ( !this.equals( otherPerson ) ) | ||
122 | { | ||
123 | Relationship friendRel = getFriendRelationshipTo( otherPerson ); | ||
124 | if ( friendRel != null ) | ||
125 | { | ||
126 | friendRel.delete(); | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | |||
131 | public Iterable<PersonNode> getFriendsOfFriends() | ||
132 | { | ||
133 | return getFriendsByDepth( 2 ); | ||
134 | } | ||
135 | |||
136 | public Iterable<PersonNode> getShortestPathTo( PersonNode otherPerson, | ||
137 | int maxDepth ) | ||
138 | { | ||
139 | // use graph algo to calculate a shortest path | ||
140 | PathFinder<Path> finder = GraphAlgoFactory.shortestPath( | ||
141 | forTypeAndDirection(RelTypes.FRIEND, BOTH ), maxDepth ); | ||
142 | |||
143 | Path path = finder.findSinglePath( underlyingNode, | ||
144 | otherPerson.getUnderlyingNode() ); | ||
145 | return createPersonsFromNodes( path ); | ||
146 | } | ||
147 | |||
148 | public Iterable<PersonNode> getFriendRecommendation( | ||
149 | int numberOfFriendsToReturn ) | ||
150 | { | ||
151 | HashSet<PersonNode> friends = new HashSet<>(); | ||
152 | IteratorUtil.addToCollection( getFriends(), friends ); | ||
153 | |||
154 | HashSet<PersonNode> friendsOfFriends = new HashSet<>(); | ||
155 | IteratorUtil.addToCollection( getFriendsOfFriends(), friendsOfFriends ); | ||
156 | |||
157 | friendsOfFriends.removeAll( friends ); | ||
158 | |||
159 | ArrayList<RankedPerson> rankedFriends = new ArrayList<>(); | ||
160 | for ( PersonNode friend : friendsOfFriends ) | ||
161 | { | ||
162 | int rank = getNumberOfPathsToPerson( friend ); | ||
163 | rankedFriends.add( new RankedPerson( friend, rank ) ); | ||
164 | } | ||
165 | |||
166 | Collections.sort( rankedFriends, new RankedComparer() ); | ||
167 | trimTo( rankedFriends, numberOfFriendsToReturn ); | ||
168 | |||
169 | return onlyFriend( rankedFriends ); | ||
170 | } | ||
171 | |||
172 | // Status update management | ||
173 | |||
174 | public Iterable<StatusUpdateNode> getStatus() | ||
175 | { | ||
176 | Relationship firstStatus = underlyingNode.getSingleRelationship( | ||
177 | RelTypes.STATUS, Direction.OUTGOING ); | ||
178 | if ( firstStatus == null ) | ||
179 | { | ||
180 | return Collections.emptyList(); | ||
181 | } | ||
182 | |||
183 | // START SNIPPET: getStatusTraversal | ||
184 | TraversalDescription traversal = graphDb().traversalDescription() | ||
185 | .depthFirst() | ||
186 | .relationships( RelTypes.NEXT ); | ||
187 | // END SNIPPET: getStatusTraversal | ||
188 | |||
189 | |||
190 | return new IterableWrapper<StatusUpdateNode, Path>( | ||
191 | traversal.traverse( firstStatus.getEndNode() ) ) | ||
192 | { | ||
193 | @Override | ||
194 | protected StatusUpdateNode underlyingObjectToObject( Path path ) | ||
195 | { | ||
196 | return new StatusUpdateNode( path.endNode() ); | ||
197 | } | ||
198 | }; | ||
199 | } | ||
200 | |||
201 | public Iterator<StatusUpdateNode> friendStatuses() | ||
202 | { | ||
203 | return new FriendsStatusUpdateIterator( this ); | ||
204 | } | ||
205 | |||
206 | public void addStatus( String text ) | ||
207 | { | ||
208 | StatusUpdateNode oldStatus; | ||
209 | if ( getStatus().iterator().hasNext() ) | ||
210 | { | ||
211 | oldStatus = getStatus().iterator().next(); | ||
212 | } else | ||
213 | { | ||
214 | oldStatus = null; | ||
215 | } | ||
216 | |||
217 | Node newStatus = createNewStatusNode( text ); | ||
218 | |||
219 | if ( oldStatus != null ) | ||
220 | { | ||
221 | underlyingNode.getSingleRelationship( RelTypes.STATUS, Direction.OUTGOING ).delete(); | ||
222 | newStatus.createRelationshipTo( oldStatus.getUnderlyingNode(), RelTypes.NEXT ); | ||
223 | } | ||
224 | |||
225 | underlyingNode.createRelationshipTo( newStatus, RelTypes.STATUS ); | ||
226 | } | ||
227 | |||
228 | private GraphDatabaseService graphDb() | ||
229 | { | ||
230 | return underlyingNode.getGraphDatabase(); | ||
231 | } | ||
232 | |||
233 | private Node createNewStatusNode( String text ) | ||
234 | { | ||
235 | Node newStatus = graphDb().createNode(); | ||
236 | newStatus.setProperty( StatusUpdateNode.TEXT, text ); | ||
237 | newStatus.setProperty( StatusUpdateNode.DATE, new Date().getTime() ); | ||
238 | return newStatus; | ||
239 | } | ||
240 | |||
241 | private final class RankedPerson | ||
242 | { | ||
243 | final PersonNode person; | ||
244 | |||
245 | final int rank; | ||
246 | |||
247 | private RankedPerson( PersonNode person, int rank ) | ||
248 | { | ||
249 | |||
250 | this.person = person; | ||
251 | this.rank = rank; | ||
252 | } | ||
253 | |||
254 | public PersonNode getPerson() | ||
255 | { | ||
256 | return person; | ||
257 | } | ||
258 | public int getRank() | ||
259 | { | ||
260 | return rank; | ||
261 | } | ||
262 | |||
263 | } | ||
264 | |||
265 | private class RankedComparer implements Comparator<RankedPerson> | ||
266 | { | ||
267 | @Override | ||
268 | public int compare( RankedPerson a, RankedPerson b ) | ||
269 | { | ||
270 | return b.getRank() - a.getRank(); | ||
271 | } | ||
272 | |||
273 | } | ||
274 | |||
275 | private void trimTo( ArrayList<RankedPerson> rankedFriends, | ||
276 | int numberOfFriendsToReturn ) | ||
277 | { | ||
278 | while ( rankedFriends.size() > numberOfFriendsToReturn ) | ||
279 | { | ||
280 | rankedFriends.remove( rankedFriends.size() - 1 ); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | private Iterable<PersonNode> onlyFriend( Iterable<RankedPerson> rankedFriends ) | ||
285 | { | ||
286 | ArrayList<PersonNode> retVal = new ArrayList<>(); | ||
287 | for ( RankedPerson person : rankedFriends ) | ||
288 | { | ||
289 | retVal.add( person.getPerson() ); | ||
290 | } | ||
291 | return retVal; | ||
292 | } | ||
293 | |||
294 | private Relationship getFriendRelationshipTo( PersonNode otherPerson ) | ||
295 | { | ||
296 | Node otherNode = otherPerson.getUnderlyingNode(); | ||
297 | for ( Relationship rel : underlyingNode.getRelationships( RelTypes.FRIEND ) ) | ||
298 | { | ||
299 | if ( rel.getOtherNode( underlyingNode ).equals( otherNode ) ) | ||
300 | { | ||
301 | return rel; | ||
302 | } | ||
303 | } | ||
304 | return null; | ||
305 | } | ||
306 | |||
307 | private Iterable<PersonNode> getFriendsByDepth( int depth ) | ||
308 | { | ||
309 | // return all my friends and their friends using new traversal API | ||
310 | TraversalDescription travDesc = graphDb().traversalDescription() | ||
311 | .breadthFirst() | ||
312 | .relationships( RelTypes.FRIEND ) | ||
313 | .uniqueness( Uniqueness.NODE_GLOBAL ) | ||
314 | .evaluator( Evaluators.toDepth( depth ) ) | ||
315 | .evaluator( Evaluators.excludeStartPosition() ); | ||
316 | |||
317 | return createPersonsFromPath( travDesc.traverse( underlyingNode ) ); | ||
318 | } | ||
319 | |||
320 | private IterableWrapper<PersonNode, Path> createPersonsFromPath( | ||
321 | Traverser iterableToWrap ) | ||
322 | { | ||
323 | return new IterableWrapper<PersonNode, Path>( iterableToWrap ) | ||
324 | { | ||
325 | @Override | ||
326 | protected PersonNode underlyingObjectToObject( Path path ) | ||
327 | { | ||
328 | return new PersonNode( path.endNode() ); | ||
329 | } | ||
330 | }; | ||
331 | } | ||
332 | |||
333 | private int getNumberOfPathsToPerson( PersonNode otherPerson ) | ||
334 | { | ||
335 | PathFinder<Path> finder = GraphAlgoFactory.allPaths( forTypeAndDirection( RelTypes.FRIEND, BOTH ), 2 ); | ||
336 | Iterable<Path> paths = finder.findAllPaths( getUnderlyingNode(), otherPerson.getUnderlyingNode() ); | ||
337 | return IteratorUtil.count( paths ); | ||
338 | } | ||
339 | |||
340 | private Iterable<PersonNode> createPersonsFromNodes( final Path path ) | ||
341 | { | ||
342 | return new IterableWrapper<PersonNode, Node>( path.nodes() ) | ||
343 | { | ||
344 | @Override | ||
345 | protected PersonNode underlyingObjectToObject( Node node ) | ||
346 | { | ||
347 | return new PersonNode( node ); | ||
348 | } | ||
349 | }; | ||
350 | } | ||
351 | |||
352 | /////////////////////////////// | ||
353 | |||
354 | /** | ||
355 | * Create a relationship between person and album | ||
356 | * @param album | ||
357 | */ | ||
358 | |||
359 | private IterableWrapper<AlbumNode, Path> createAlbumsFromPath( | ||
360 | Traverser iterableToWrap ) | ||
361 | { | ||
362 | return new IterableWrapper<AlbumNode, Path>( iterableToWrap ) | ||
363 | { | ||
364 | @Override | ||
365 | protected AlbumNode underlyingObjectToObject( Path path ) | ||
366 | { | ||
367 | return new AlbumNode( path.endNode() ); | ||
368 | } | ||
369 | }; | ||
370 | } | ||
371 | |||
372 | |||
373 | public void addAlbum(AlbumNode album){ | ||
374 | underlyingNode.createRelationshipTo( album.getUnderlyingNode(), RelTypes.HAS_ALBUM ); | ||
375 | } | ||
376 | |||
377 | public Iterable<AlbumNode> getAlbums(){ | ||
378 | TraversalDescription travDesc = graphDb().traversalDescription() | ||
379 | .breadthFirst() | ||
380 | .relationships( RelTypes.HAS_ALBUM ) | ||
381 | .uniqueness( Uniqueness.NODE_GLOBAL ) | ||
382 | .evaluator( Evaluators.toDepth( 1 ) ) | ||
383 | .evaluator( Evaluators.excludeStartPosition() ); | ||
384 | |||
385 | return createAlbumsFromPath( travDesc.traverse( underlyingNode ) ); | ||
386 | |||
387 | } | ||
388 | |||
389 | } |