rgraphum 0.0.1.alpha
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +26 -0
- data/GLOSSARIES.md +108 -0
- data/GREMLIN.md +1398 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +136 -0
- data/Rakefile +16 -0
- data/bin/.irbrc +41 -0
- data/bin/rgraphum_console +61 -0
- data/bin/rgraphum_runner +57 -0
- data/examples/ba_model/make.rb +19 -0
- data/examples/ba_model/make_dummy_twitter_rt_data.rb +0 -0
- data/examples/basic/check_modularity.rb +27 -0
- data/examples/basic/make_graph.rb +12 -0
- data/examples/parser/dot.rb +28 -0
- data/examples/sis_model/lifegame.rb +161 -0
- data/graph_struct.jpg +0 -0
- data/lib/rgraphum/analyzer/linear_regression.rb +31 -0
- data/lib/rgraphum/analyzer/meme_tracker.rb +296 -0
- data/lib/rgraphum/analyzer/twitter/rt_at_mark.rb +45 -0
- data/lib/rgraphum/analyzer.rb +8 -0
- data/lib/rgraphum/cluster.rb +67 -0
- data/lib/rgraphum/communities.rb +65 -0
- data/lib/rgraphum/community.rb +86 -0
- data/lib/rgraphum/cosine_similarity_matrix.rb +40 -0
- data/lib/rgraphum/edge.rb +194 -0
- data/lib/rgraphum/edges.rb +161 -0
- data/lib/rgraphum/ext/cosine_similarity_matrix.rb +79 -0
- data/lib/rgraphum/ext/linear_regression.rb +22 -0
- data/lib/rgraphum/ext/tf_idf.rb +52 -0
- data/lib/rgraphum/graph/gremlin.rb +193 -0
- data/lib/rgraphum/graph/math/clustering_coefficient.rb +53 -0
- data/lib/rgraphum/graph/math/community_detection.rb +141 -0
- data/lib/rgraphum/graph/math/degree_distribution.rb +50 -0
- data/lib/rgraphum/graph/math/dijkstra.rb +331 -0
- data/lib/rgraphum/graph/math.rb +45 -0
- data/lib/rgraphum/graph.rb +267 -0
- data/lib/rgraphum/importer.rb +97 -0
- data/lib/rgraphum/marshal.rb +26 -0
- data/lib/rgraphum/motifs.rb +8 -0
- data/lib/rgraphum/parsers/flare.rb +42 -0
- data/lib/rgraphum/parsers/gephi.rb +193 -0
- data/lib/rgraphum/parsers/graphviz.rb +78 -0
- data/lib/rgraphum/parsers/miserables.rb +54 -0
- data/lib/rgraphum/parsers.rb +32 -0
- data/lib/rgraphum/path.rb +37 -0
- data/lib/rgraphum/query.rb +130 -0
- data/lib/rgraphum/rgraphum_array.rb +159 -0
- data/lib/rgraphum/rgraphum_array_dividers.rb +43 -0
- data/lib/rgraphum/rgraphum_random.rb +5 -0
- data/lib/rgraphum/simulator/ba_model.rb +140 -0
- data/lib/rgraphum/simulator/sir_model.rb +178 -0
- data/lib/rgraphum/simulator/sis_model.rb +158 -0
- data/lib/rgraphum/simulator.rb +29 -0
- data/lib/rgraphum/statistic/power_law.rb +9 -0
- data/lib/rgraphum/t.rb +12 -0
- data/lib/rgraphum/tf_idf.rb +27 -0
- data/lib/rgraphum/version.rb +3 -0
- data/lib/rgraphum/vertex.rb +354 -0
- data/lib/rgraphum/vertices.rb +97 -0
- data/lib/rgraphum.rb +38 -0
- data/performance/add-vertices-edges.rb +20 -0
- data/performance/add-vertices.rb +12 -0
- data/performance/build-graph.rb +19 -0
- data/performance/delete-graph.rb +24 -0
- data/performance/delete-vertices.rb +25 -0
- data/performance/refer-graph.rb +23 -0
- data/rgraphum.gemspec +30 -0
- data/test/lib/rgraphum/analyzer/linear_regression_test.rb +20 -0
- data/test/lib/rgraphum/analyzer/meme_tracker_test.rb +383 -0
- data/test/lib/rgraphum/analyzer/twitter/rt_at_mark_test.rb +120 -0
- data/test/lib/rgraphum/array_test.rb +95 -0
- data/test/lib/rgraphum/bubble_test.rb +7 -0
- data/test/lib/rgraphum/communities_test.rb +53 -0
- data/test/lib/rgraphum/cosine_similarity_test.rb +18 -0
- data/test/lib/rgraphum/edge_test.rb +89 -0
- data/test/lib/rgraphum/edges_test.rb +178 -0
- data/test/lib/rgraphum/graph_builder_test.rb +64 -0
- data/test/lib/rgraphum/graph_dup_test.rb +199 -0
- data/test/lib/rgraphum/graph_plus_test.rb +80 -0
- data/test/lib/rgraphum/graph_test.rb +512 -0
- data/test/lib/rgraphum/gremlin_test.rb +145 -0
- data/test/lib/rgraphum/importers/idg_json_edges.json +20 -0
- data/test/lib/rgraphum/importers/idg_json_test.rb +207 -0
- data/test/lib/rgraphum/importers/idg_json_vertices.json +46 -0
- data/test/lib/rgraphum/math/average_distance_matrix_test.rb +142 -0
- data/test/lib/rgraphum/math/clustering_coefficient_test.rb +219 -0
- data/test/lib/rgraphum/math/community_test.rb +78 -0
- data/test/lib/rgraphum/math/degree_distribution_test.rb +40 -0
- data/test/lib/rgraphum/math/dijkstra_test.rb +146 -0
- data/test/lib/rgraphum/math/modularity_test.rb +154 -0
- data/test/lib/rgraphum/math/quick_average_distance_matrix_test.rb +84 -0
- data/test/lib/rgraphum/path_test.rb +44 -0
- data/test/lib/rgraphum/query/enumerable_test.rb +42 -0
- data/test/lib/rgraphum/query/where_operators_test.rb +75 -0
- data/test/lib/rgraphum/query/where_test.rb +59 -0
- data/test/lib/rgraphum/simulator/ba_model_test.rb +75 -0
- data/test/lib/rgraphum/simulator/sir_model_test.rb +513 -0
- data/test/lib/rgraphum/simulator/sis_model_test.rb +478 -0
- data/test/lib/rgraphum/simulator_test.rb +22 -0
- data/test/lib/rgraphum/tf_idf_test.rb +30 -0
- data/test/lib/rgraphum/vertex_test.rb +50 -0
- data/test/lib/rgraphum/vertices_test.rb +180 -0
- data/test/test_helper.rb +98 -0
- data/tmp/.gitkeep +0 -0
- metadata +254 -0
data/GREMLIN.md
ADDED
@@ -0,0 +1,1398 @@
|
|
1
|
+
# Gremlin
|
2
|
+
|
3
|
+
|
4
|
+
## Transform
|
5
|
+
|
6
|
+
Transform steps take an object and emit a transformation of it.
|
7
|
+
|
8
|
+
### _
|
9
|
+
|
10
|
+
Identity turns an arbitrary object into a "pipeline".
|
11
|
+
|
12
|
+
gremlin> x = [1,2,3]
|
13
|
+
==>1
|
14
|
+
==>2
|
15
|
+
==>3
|
16
|
+
gremlin> x._().transform{it+1}
|
17
|
+
==>2
|
18
|
+
==>3
|
19
|
+
==>4
|
20
|
+
gremlin> x = g.E.has('weight', T.gt, 0.5f).toList()
|
21
|
+
==>e[10][4-created->5]
|
22
|
+
==>e[8][1-knows->4]
|
23
|
+
gremlin> x.inV
|
24
|
+
==>[StartPipe, InPipe]
|
25
|
+
==>[StartPipe, InPipe]
|
26
|
+
gremlin> x._().inV
|
27
|
+
==>v[5]
|
28
|
+
==>v[4]
|
29
|
+
|
30
|
+
### both
|
31
|
+
|
32
|
+
Get both adjacent vertices of the vertex, the in and the out.
|
33
|
+
|
34
|
+
gremlin> v = g.v(4)
|
35
|
+
==>v[4]
|
36
|
+
gremlin> v.both
|
37
|
+
==>v[1]
|
38
|
+
==>v[5]
|
39
|
+
==>v[3]
|
40
|
+
gremlin> v.both('knows')
|
41
|
+
==>v[1]
|
42
|
+
gremlin> v.both('knows', 'created')
|
43
|
+
==>v[1]
|
44
|
+
==>v[5]
|
45
|
+
==>v[3]
|
46
|
+
|
47
|
+
### bothE
|
48
|
+
|
49
|
+
Get both incoming and outgoing edges of the vertex.
|
50
|
+
|
51
|
+
gremlin> v = g.v(4)
|
52
|
+
==>v[4]
|
53
|
+
gremlin> v.bothE
|
54
|
+
==>e[8][1-knows->4]
|
55
|
+
==>e[10][4-created->5]
|
56
|
+
==>e[11][4-created->3]
|
57
|
+
gremlin> v.bothE('knows')
|
58
|
+
==>e[8][1-knows->4]
|
59
|
+
gremlin> v.bothE('knows', 'created')
|
60
|
+
==>e[8][1-knows->4]
|
61
|
+
==>e[10][4-created->5]
|
62
|
+
==>e[11][4-created->3]
|
63
|
+
|
64
|
+
### bothV
|
65
|
+
|
66
|
+
Get both incoming and outgoing vertices of the edge.
|
67
|
+
|
68
|
+
gremlin> e = g.e(12)
|
69
|
+
==>e[12][6-created->3]
|
70
|
+
gremlin> e.outV
|
71
|
+
==>v[6]
|
72
|
+
gremlin> e.inV
|
73
|
+
==>v[3]
|
74
|
+
gremlin> e.bothV
|
75
|
+
==>v[6]
|
76
|
+
==>v[3]
|
77
|
+
|
78
|
+
### cap
|
79
|
+
|
80
|
+
Gets the side-effect of the pipe prior. In other words, it emits the value of the previous step and not the values that flow through it.
|
81
|
+
|
82
|
+
gremlin> g.V('lang', 'java').in('created').name.groupCount
|
83
|
+
==>marko
|
84
|
+
==>josh
|
85
|
+
==>peter
|
86
|
+
==>josh
|
87
|
+
gremlin> g.V('lang', 'java').in('created').name.groupCount.cap
|
88
|
+
==>{marko=1, peter=1, josh=2}
|
89
|
+
|
90
|
+
### E
|
91
|
+
|
92
|
+
The edge iterator for the graph. Utilize this to iterate through all the edges in the graph. Use with care on large graphs.
|
93
|
+
|
94
|
+
gremlin> g.E
|
95
|
+
==>e[10][4-created->5]
|
96
|
+
==>e[7][1-knows->2]
|
97
|
+
==>e[9][1-created->3]
|
98
|
+
==>e[8][1-knows->4]
|
99
|
+
==>e[11][4-created->3]
|
100
|
+
==>e[12][6-created->3]
|
101
|
+
gremlin> g.E.weight
|
102
|
+
==>1.0
|
103
|
+
==>0.5
|
104
|
+
==>0.4
|
105
|
+
==>1.0
|
106
|
+
==>0.4
|
107
|
+
==>0.2
|
108
|
+
|
109
|
+
### gather
|
110
|
+
|
111
|
+
Collect all objects up to that step and process the gathered list with the provided closure.
|
112
|
+
|
113
|
+
gremlin> g.v(1).out
|
114
|
+
==>v[2]
|
115
|
+
==>v[4]
|
116
|
+
==>v[3]
|
117
|
+
gremlin> g.v(1).out.gather
|
118
|
+
==>[v[2], v[4], v[3]]
|
119
|
+
gremlin> g.v(1).out.gather{it.size()}
|
120
|
+
==>3
|
121
|
+
|
122
|
+
See Also: scatter
|
123
|
+
|
124
|
+
### id
|
125
|
+
|
126
|
+
Gets the unique identifier of the element.
|
127
|
+
|
128
|
+
gremlin> v = g.V("name", "marko").next()
|
129
|
+
==>v[1]
|
130
|
+
gremlin> v.id
|
131
|
+
==>1
|
132
|
+
gremlin> g.v(1).id
|
133
|
+
==>1
|
134
|
+
|
135
|
+
### in
|
136
|
+
|
137
|
+
Gets the adjacent vertices to the vertex.
|
138
|
+
|
139
|
+
gremlin> v = g.v(4)
|
140
|
+
==>v[4]
|
141
|
+
gremlin> v.inE.outV
|
142
|
+
==>v[1]
|
143
|
+
gremlin> v.in
|
144
|
+
==>v[1]
|
145
|
+
gremlin> v = g.v(3)
|
146
|
+
==>v[3]
|
147
|
+
gremlin> v.in("created")
|
148
|
+
==>v[1]
|
149
|
+
==>v[4]
|
150
|
+
==>v[6]
|
151
|
+
gremlin> v.inE("created").outV
|
152
|
+
==>v[1]
|
153
|
+
==>v[4]
|
154
|
+
==>v[6]
|
155
|
+
|
156
|
+
### inE
|
157
|
+
|
158
|
+
Gets the incoming edges of the vertex.
|
159
|
+
|
160
|
+
gremlin> v = g.v(4)
|
161
|
+
==>v[4]
|
162
|
+
gremlin> v.inE.outV
|
163
|
+
==>v[1]
|
164
|
+
gremlin> v.in
|
165
|
+
==>v[1]
|
166
|
+
gremlin> v = g.v(3)
|
167
|
+
==>v[3]
|
168
|
+
gremlin> v.in("created")
|
169
|
+
==>v[1]
|
170
|
+
==>v[4]
|
171
|
+
==>v[6]
|
172
|
+
gremlin> v.inE("created").outV
|
173
|
+
==>v[1]
|
174
|
+
==>v[4]
|
175
|
+
==>v[6]
|
176
|
+
|
177
|
+
### inV
|
178
|
+
|
179
|
+
Get both incoming head vertex of the edge.
|
180
|
+
|
181
|
+
gremlin> e = g.e(12)
|
182
|
+
==>e[12][6-created->3]
|
183
|
+
gremlin> e.outV
|
184
|
+
==>v[6]
|
185
|
+
gremlin> e.inV
|
186
|
+
==>v[3]
|
187
|
+
gremlin> e.bothV
|
188
|
+
==>v[6]
|
189
|
+
==>v[3]
|
190
|
+
|
191
|
+
### key
|
192
|
+
|
193
|
+
Get the property value of an element. The property value can be obtained by simply appending the name to the end of the element or by referencing it as a Groovy map element with square brackets.
|
194
|
+
For best performance, drop down to the Blueprints API and use getProperty(key).
|
195
|
+
|
196
|
+
gremlin> v = g.v(3)
|
197
|
+
==>v[3]
|
198
|
+
gremlin> v.name
|
199
|
+
==>lop
|
200
|
+
gremlin> v['name']
|
201
|
+
==>lop
|
202
|
+
gremlin> x = 'name'
|
203
|
+
==>name
|
204
|
+
gremlin> v[x]
|
205
|
+
==>lop
|
206
|
+
gremlin> v.getProperty('name')
|
207
|
+
==>lop
|
208
|
+
|
209
|
+
### label
|
210
|
+
|
211
|
+
Gets the label of an edge.
|
212
|
+
|
213
|
+
gremlin> g.v(6).outE.label
|
214
|
+
==>created
|
215
|
+
gremlin> g.v(1).outE.filter{it.label=='created'}
|
216
|
+
==>e[9][1-created->3]
|
217
|
+
|
218
|
+
// a more efficient approach to use of label
|
219
|
+
gremlin> g.v(1).outE.has('label','created')
|
220
|
+
==>e[9][1-created->3]
|
221
|
+
|
222
|
+
### linkBoth[In/Out]
|
223
|
+
|
224
|
+
An element-centric mutation that takes every incoming vertex and creates an edge to the provided vertex. It can be used with both a Vertex object or a named step.
|
225
|
+
|
226
|
+
gremlin> marko = g.v(1)
|
227
|
+
==>v[1]
|
228
|
+
gremlin> g.V.except([marko]).linkBoth('connected',marko)
|
229
|
+
==>v[3]
|
230
|
+
==>v[2]
|
231
|
+
==>v[6]
|
232
|
+
==>v[5]
|
233
|
+
==>v[4]
|
234
|
+
gremlin> marko.outE('connected')
|
235
|
+
==>e[2][1-connected->2]
|
236
|
+
==>e[0][1-connected->3]
|
237
|
+
==>e[6][1-connected->5]
|
238
|
+
==>e[4][1-connected->6]
|
239
|
+
==>e[14][1-connected->4]
|
240
|
+
gremlin> g.V.except([marko]).outE('connected')
|
241
|
+
==>e[1][3-connected->1]
|
242
|
+
==>e[3][2-connected->1]
|
243
|
+
==>e[5][6-connected->1]
|
244
|
+
==>e[13][5-connected->1]
|
245
|
+
==>e[15][4-connected->1]
|
246
|
+
gremlin> g.v(1).as('x').out('created').in('created').except([g.v(1)]).linkBoth('cocreator','x')
|
247
|
+
==>v[4]
|
248
|
+
==>v[6]
|
249
|
+
gremlin> g.E.has('label','cocreator')
|
250
|
+
==>e[3][6-cocreator->1]
|
251
|
+
==>e[2][1-cocreator->6]
|
252
|
+
==>e[1][4-cocreator->1]
|
253
|
+
==>e[0][1-cocreator->4]
|
254
|
+
|
255
|
+
### map
|
256
|
+
|
257
|
+
Gets the property map of the graph element.
|
258
|
+
|
259
|
+
gremlin> g.v(1).map
|
260
|
+
==>{name=marko, age=29}
|
261
|
+
gremlin> g.v(1).map()
|
262
|
+
==>name=marko
|
263
|
+
==>age=29
|
264
|
+
|
265
|
+
### memoize
|
266
|
+
|
267
|
+
Remembers a particular mapping from input to output. Long or expensive expressions with no side effects can use this step to remember a mapping, which helps reduce load when previously processed objects are passed into it.
|
268
|
+
|
269
|
+
For situations where memoization may consume large amounts of RAM, consider using an embedded key-value store like JDBM or some other persistent Map implementation.
|
270
|
+
|
271
|
+
gremlin> g.V.out.out.memoize(1).name
|
272
|
+
==>ripple
|
273
|
+
==>lop
|
274
|
+
gremlin> g.V.out.as('here').out.memoize('here').name
|
275
|
+
==>ripple
|
276
|
+
==>lop
|
277
|
+
gremlin> m = [:]
|
278
|
+
gremlin> g.V.out.out.memoize(1,m).name
|
279
|
+
==>ripple
|
280
|
+
==>lop
|
281
|
+
|
282
|
+
### order
|
283
|
+
|
284
|
+
Order the items in the stream according to the closure if provided. If no closure is provided, then a default sort order is used.
|
285
|
+
|
286
|
+
gremlin> g.V.name.order
|
287
|
+
==>josh
|
288
|
+
==>lop
|
289
|
+
==>marko
|
290
|
+
==>peter
|
291
|
+
==>ripple
|
292
|
+
==>vadas
|
293
|
+
gremlin> g.V.name.order{it.b <=> it.a}
|
294
|
+
==>vadas
|
295
|
+
==>ripple
|
296
|
+
==>peter
|
297
|
+
==>marko
|
298
|
+
==>lop
|
299
|
+
==>josh
|
300
|
+
gremlin> g.V.order{it.b.name <=> it.a.name}.out('knows')
|
301
|
+
==>v[2]
|
302
|
+
==>v[4]
|
303
|
+
|
304
|
+
### orderMap
|
305
|
+
|
306
|
+
For every incoming map, sort with supplied closure or T.decr or T.incr and emit keys.
|
307
|
+
|
308
|
+
gremlin> g.V.both.groupCount.cap.next()
|
309
|
+
==>v[3]=3
|
310
|
+
==>v[2]=1
|
311
|
+
==>v[1]=3
|
312
|
+
==>v[6]=1
|
313
|
+
==>v[5]=1
|
314
|
+
==>v[4]=3
|
315
|
+
gremlin> g.V.both.groupCount.cap.orderMap(T.decr)
|
316
|
+
==>v[3]
|
317
|
+
==>v[1]
|
318
|
+
==>v[4]
|
319
|
+
==>v[2]
|
320
|
+
==>v[6]
|
321
|
+
==>v[5]
|
322
|
+
gremlin> g.V.both.groupCount.cap.orderMap(T.decr)[0..1]
|
323
|
+
==>v[3]
|
324
|
+
==>v[1]
|
325
|
+
gremlin> g.V.both.groupCount.cap.orderMap(T.decr)[0..1].name
|
326
|
+
==>lop
|
327
|
+
==>marko
|
328
|
+
|
329
|
+
### out
|
330
|
+
|
331
|
+
Gets the out adjacent vertices to the vertex.
|
332
|
+
|
333
|
+
gremlin> v = g.v(1)
|
334
|
+
==>v[1]
|
335
|
+
gremlin> v.outE.inV
|
336
|
+
==>v[2]
|
337
|
+
==>v[4]
|
338
|
+
==>v[3]
|
339
|
+
gremlin> v.out
|
340
|
+
==>v[2]
|
341
|
+
==>v[4]
|
342
|
+
==>v[3]
|
343
|
+
gremlin> v.outE('knows').inV
|
344
|
+
==>v[2]
|
345
|
+
==>v[4]
|
346
|
+
gremlin> v.out('knows')
|
347
|
+
==>v[2]
|
348
|
+
==>v[4]
|
349
|
+
|
350
|
+
### outE
|
351
|
+
|
352
|
+
Gets the outgoing edges to the vertex.
|
353
|
+
|
354
|
+
gremlin> v = g.v(1)
|
355
|
+
==>v[1]
|
356
|
+
gremlin> v.outE.inV
|
357
|
+
==>v[2]
|
358
|
+
==>v[4]
|
359
|
+
==>v[3]
|
360
|
+
gremlin> v.out
|
361
|
+
==>v[2]
|
362
|
+
==>v[4]
|
363
|
+
==>v[3]
|
364
|
+
gremlin> v.outE('knows').inV
|
365
|
+
==>v[2]
|
366
|
+
==>v[4]
|
367
|
+
gremlin> v.out('knows')
|
368
|
+
==>v[2]
|
369
|
+
==>v[4]
|
370
|
+
|
371
|
+
### outV
|
372
|
+
|
373
|
+
Get both outgoing tail vertex of the edge.
|
374
|
+
|
375
|
+
gremlin> e = g.e(12)
|
376
|
+
==>e[12][6-created->3]
|
377
|
+
gremlin> e.outV
|
378
|
+
==>v[6]
|
379
|
+
gremlin> e.inV
|
380
|
+
==>v[3]
|
381
|
+
gremlin> e.bothV
|
382
|
+
==>v[6]
|
383
|
+
==>v[3]
|
384
|
+
|
385
|
+
### path
|
386
|
+
|
387
|
+
Gets the path through the pipeline up to this point, where closures are post-processing for each object in the path. If the path step is provided closures then, in a round robin fashion, the closures are evaluated over each object of the path and that post-processed path is returned.
|
388
|
+
|
389
|
+
gremlin> g.v(1).out.path
|
390
|
+
==>[v[1], v[2]]
|
391
|
+
==>[v[1], v[4]]
|
392
|
+
==>[v[1], v[3]]
|
393
|
+
gremlin> g.v(1).out.path{it.id}
|
394
|
+
==>[1, 2]
|
395
|
+
==>[1, 4]
|
396
|
+
==>[1, 3]
|
397
|
+
gremlin> g.v(1).out.path{it.id}{it.name}
|
398
|
+
==>[1, vadas]
|
399
|
+
==>[1, josh]
|
400
|
+
==>[1, lop]
|
401
|
+
gremlin> g.v(1).outE.inV.name.path
|
402
|
+
==>[v[1], e[7][1-knows->2], v[2], vadas]
|
403
|
+
==>[v[1], e[8][1-knows->4], v[4], josh]
|
404
|
+
==>[v[1], e[9][1-created->3], v[3], lop]
|
405
|
+
|
406
|
+
### scatter
|
407
|
+
|
408
|
+
Unroll all objects in the iterable at that step. Gather/Scatter is good for breadth-first traversals where the gather closure filters out unwanted elements at the current radius.
|
409
|
+
|
410
|
+
gremlin> g.v(1).out
|
411
|
+
==>v[2]
|
412
|
+
==>v[4]
|
413
|
+
==>v[3]
|
414
|
+
gremlin> g.v(1).out.gather{it[1..2]}
|
415
|
+
==>[v[4], v[3]]
|
416
|
+
gremlin> g.v(1).out.gather{it[1..2]}.scatter
|
417
|
+
==>v[4]
|
418
|
+
==>v[3]
|
419
|
+
|
420
|
+
See Also: gather
|
421
|
+
|
422
|
+
### select
|
423
|
+
|
424
|
+
Select the named steps to emit after select with post-processing closures.
|
425
|
+
|
426
|
+
gremlin> g.v(1).as('x').out('knows').as('y').select
|
427
|
+
==>[x:v[1], y:v[2]]
|
428
|
+
==>[x:v[1], y:v[4]]
|
429
|
+
gremlin> g.v(1).as('x').out('knows').as('y').select(["y"])
|
430
|
+
==>[y:v[2]]
|
431
|
+
==>[y:v[4]]
|
432
|
+
gremlin> g.v(1).as('x').out('knows').as('y').select(["y"]){it.name}
|
433
|
+
==>[y:vadas]
|
434
|
+
==>[y:josh]
|
435
|
+
gremlin> g.v(1).as('x').out('knows').as('y').select{it.id}{it.name}
|
436
|
+
==>[x:1, y:vadas]
|
437
|
+
==>[x:1, y:josh]
|
438
|
+
|
439
|
+
### shuffle
|
440
|
+
|
441
|
+
Collect all objects up to that step into a list and randomize their order.
|
442
|
+
|
443
|
+
gremlin> g.v(1).out.shuffle
|
444
|
+
==>v[2]
|
445
|
+
==>v[3]
|
446
|
+
==>v[4]
|
447
|
+
gremlin> g.v(1).out.shuffle
|
448
|
+
==>v[3]
|
449
|
+
==>v[2]
|
450
|
+
==>v[4]
|
451
|
+
|
452
|
+
See Also: random
|
453
|
+
|
454
|
+
### transform
|
455
|
+
|
456
|
+
Transform emits the result of a closure.
|
457
|
+
|
458
|
+
gremlin> g.E.has('weight', T.gt, 0.5f).outV.age
|
459
|
+
==>32
|
460
|
+
==>29
|
461
|
+
gremlin> g.E.has('weight', T.gt, 0.5f).outV.age.transform{it+2}
|
462
|
+
==>34
|
463
|
+
==>31
|
464
|
+
gremlin> g.E.has('weight', T.gt, 0.5f).outV.transform{[it.id,it.age]}
|
465
|
+
==>[4, 32]
|
466
|
+
==>[1, 29]
|
467
|
+
gremlin> g.E.has('weight', T.gt, 0.5f).outV.transform{[id:it.id,age:it.age]}
|
468
|
+
==>{id=4, age=32}
|
469
|
+
==>{id=1, age=29}
|
470
|
+
|
471
|
+
### V
|
472
|
+
|
473
|
+
The vertex iterator for the graph. Utilize this to iterate through all the vertices in the graph. Use with care on large graphs unless used in combination with a key index lookup.
|
474
|
+
|
475
|
+
gremlin> g.V
|
476
|
+
==>v[3]
|
477
|
+
==>v[2]
|
478
|
+
==>v[1]
|
479
|
+
==>v[6]
|
480
|
+
==>v[5]
|
481
|
+
==>v[4]
|
482
|
+
gremlin> g.V("name", "marko")
|
483
|
+
==>v[1]
|
484
|
+
gremlin> g.V("name", "marko").name
|
485
|
+
==>marko
|
486
|
+
|
487
|
+
|
488
|
+
## Filter
|
489
|
+
|
490
|
+
Filter steps decide whether to allow an object to pass to the next step or not.
|
491
|
+
|
492
|
+
### [i]
|
493
|
+
|
494
|
+
A index filter that emits the particular indexed object.
|
495
|
+
|
496
|
+
gremlin> g.V[0].name
|
497
|
+
==>lop
|
498
|
+
|
499
|
+
### [i..j]
|
500
|
+
|
501
|
+
A range filter that emits the objects within a range.
|
502
|
+
|
503
|
+
gremlin> g.V[0..2].name
|
504
|
+
==>lop
|
505
|
+
==>vadas
|
506
|
+
==>marko
|
507
|
+
gremlin> g.V[0..<2].name
|
508
|
+
==>lop
|
509
|
+
==>vadas
|
510
|
+
|
511
|
+
### and
|
512
|
+
|
513
|
+
Takes a collection of pipes and emits incoming objects that are true for all of the pipes.
|
514
|
+
|
515
|
+
gremlin> g.v(1).outE.and(_().has('weight', T.gt, 0.4f), _().has('weight', T.lt, 0.8f))
|
516
|
+
==>e[7][1-knows->2]
|
517
|
+
gremlin> g.V.and(_().both("knows"), _().both("created"))
|
518
|
+
==>v[1]
|
519
|
+
==>v[4]
|
520
|
+
|
521
|
+
### back
|
522
|
+
|
523
|
+
Go back to the results from n-steps ago or go back to the results of a named step.
|
524
|
+
|
525
|
+
gremlin> g.V.out('knows').has('age', T.gt, 30).back(2).age
|
526
|
+
==>29
|
527
|
+
gremlin> g.V.as('x').outE('knows').inV.has('age', T.gt, 30).back('x').age
|
528
|
+
==>29
|
529
|
+
|
530
|
+
### dedup
|
531
|
+
|
532
|
+
Emit only incoming objects that have not been seen before with an optional closure being the object to check on.
|
533
|
+
|
534
|
+
gremlin> g.v(1).out.in
|
535
|
+
==>v[1]
|
536
|
+
==>v[1]
|
537
|
+
==>v[1]
|
538
|
+
==>v[4]
|
539
|
+
==>v[6]
|
540
|
+
gremlin> g.v(1).out.in.dedup()
|
541
|
+
==>v[1]
|
542
|
+
==>v[4]
|
543
|
+
==>v[6]
|
544
|
+
|
545
|
+
### except
|
546
|
+
|
547
|
+
Emit everything to pass except what is in the supplied collection or in the results of a named step.
|
548
|
+
|
549
|
+
gremlin> x = [g.v(1), g.v(2), g.v(3)]
|
550
|
+
==>v[1]
|
551
|
+
==>v[2]
|
552
|
+
==>v[3]
|
553
|
+
gremlin> g.V.except(x)
|
554
|
+
==>v[6]
|
555
|
+
==>v[5]
|
556
|
+
==>v[4]
|
557
|
+
gremlin> x = []
|
558
|
+
gremlin> g.v(1).out.aggregate(x).out.except(x)
|
559
|
+
==>v[5]
|
560
|
+
gremlin> g.V.has('age',T.lt,30).as('x').out('created').in('created').except('x')
|
561
|
+
==>v[4]
|
562
|
+
==>v[6]
|
563
|
+
|
564
|
+
See Also: retain
|
565
|
+
|
566
|
+
### filter
|
567
|
+
|
568
|
+
Decide whether to allow an object to pass. Return true from the closure to allow an object to pass.
|
569
|
+
|
570
|
+
gremlin> g.V.filter{it.age > 29}.name
|
571
|
+
==>peter
|
572
|
+
==>josh
|
573
|
+
|
574
|
+
### has
|
575
|
+
|
576
|
+
Allows an element if it has a particular property. Utilizes several options for comparisons through T:
|
577
|
+
|
578
|
+
T.gt - greater than
|
579
|
+
T.gte - greater than or equal to
|
580
|
+
T.eq - equal to
|
581
|
+
T.neq - not equal to
|
582
|
+
T.lte - less than or equal to
|
583
|
+
T.lt - less than
|
584
|
+
|
585
|
+
It is worth noting that the syntax of has is similar to g.V("name", "marko"), which has the difference of being a key index lookup and as such will perform faster. In contrast, this line, g.V.has("name", "marko"), will iterate over all vertices checking the name property of each vertex for a match and will be significantly slower than the key index approach.
|
586
|
+
|
587
|
+
gremlin> g.V.has("name", "marko").name
|
588
|
+
==>marko
|
589
|
+
gremlin> g.v(1).outE.has("weight", T.gte, 0.5f).weight
|
590
|
+
==>0.5
|
591
|
+
==>1.0
|
592
|
+
gremlin> g.V.has("age", null).name
|
593
|
+
==>lop
|
594
|
+
==>ripple
|
595
|
+
|
596
|
+
See Also: hasNot
|
597
|
+
|
598
|
+
### hasNot
|
599
|
+
|
600
|
+
Allows an element if it does not have a particular property. Utilizes several options for comparisons on through T:
|
601
|
+
|
602
|
+
T.gt - greater than
|
603
|
+
T.gte - greater than or equal to
|
604
|
+
T.eq - equal to
|
605
|
+
T.neq - not equal to
|
606
|
+
T.lte - less than or equal to
|
607
|
+
T.lt - less than
|
608
|
+
|
609
|
+
gremlin> g.v(1).outE.hasNot("weight", T.eq, 0.5f).weight
|
610
|
+
==>1.0
|
611
|
+
==>0.4
|
612
|
+
gremlin> g.V.hasNot("age", null).name
|
613
|
+
==>vadas
|
614
|
+
==>marko
|
615
|
+
==>peter
|
616
|
+
==>josh
|
617
|
+
|
618
|
+
### interval
|
619
|
+
|
620
|
+
Allow elements to pass that have their property in the provided start and end interval.
|
621
|
+
|
622
|
+
gremlin> g.E.interval("weight", 0.3f, 0.9f).weight
|
623
|
+
==>0.5
|
624
|
+
==>0.4
|
625
|
+
==>0.4
|
626
|
+
|
627
|
+
See Also: has
|
628
|
+
|
629
|
+
### or
|
630
|
+
|
631
|
+
Takes a collection of pipes and emits incoming objects that are true for any of the pipes.
|
632
|
+
|
633
|
+
gremlin> g.v(1).outE.or(_().has('id', T.eq, "9"), _().has('weight', T.lt, 0.6f))
|
634
|
+
==>e[7][1-knows->2]
|
635
|
+
==>e[9][1-created->3]
|
636
|
+
|
637
|
+
### random
|
638
|
+
|
639
|
+
Emits the incoming object if biased coin toss is heads.
|
640
|
+
|
641
|
+
gremlin> g.V.random(0.5)
|
642
|
+
==>v[3]
|
643
|
+
==>v[1]
|
644
|
+
==>v[6]
|
645
|
+
gremlin> g.V.random(0.5)
|
646
|
+
==>v[2]
|
647
|
+
==>v[5]
|
648
|
+
==>v[4]
|
649
|
+
|
650
|
+
### retain
|
651
|
+
|
652
|
+
Allow everything to pass except what is not in the supplied collection or in the results of a named step.
|
653
|
+
|
654
|
+
gremlin> x = [g.v(1), g.v(2), g.v(3)]
|
655
|
+
==>v[1]
|
656
|
+
==>v[2]
|
657
|
+
==>v[3]
|
658
|
+
gremlin> g.V.retain(x)
|
659
|
+
==>v[3]
|
660
|
+
==>v[2]
|
661
|
+
==>v[1]
|
662
|
+
gremlin> x = []
|
663
|
+
gremlin> g.v(1).out.aggregate(x).out.retain(x)
|
664
|
+
==>v[3]
|
665
|
+
gremlin> g.V.as('x').both.both.both.retain('x')
|
666
|
+
==>v[3]
|
667
|
+
==>v[3]
|
668
|
+
==>v[1]
|
669
|
+
==>v[1]
|
670
|
+
==>v[4]
|
671
|
+
==>v[4]
|
672
|
+
|
673
|
+
See Also: except
|
674
|
+
|
675
|
+
### simplePath
|
676
|
+
|
677
|
+
Emit the object only if the current path has no repeated elements.
|
678
|
+
|
679
|
+
gremlin> g.v(1).out.in
|
680
|
+
==>v[1]
|
681
|
+
==>v[1]
|
682
|
+
==>v[1]
|
683
|
+
==>v[4]
|
684
|
+
==>v[6]
|
685
|
+
gremlin> g.v(1).out.in.simplePath
|
686
|
+
==>v[4]
|
687
|
+
==>v[6]
|
688
|
+
|
689
|
+
|
690
|
+
## Side Effect
|
691
|
+
|
692
|
+
Side Effect steps pass the object, but yield some kind of side effect while doing so.
|
693
|
+
|
694
|
+
### aggregate
|
695
|
+
|
696
|
+
Emits input, but adds input in collection, where provided closure processes input prior to insertion (greedy). In being "greedy", 'aggregate' will exhaust all the items that come to it from previous steps before emitting the next element.
|
697
|
+
|
698
|
+
gremlin> x = []
|
699
|
+
gremlin> g.v(1).out.aggregate(x).next()
|
700
|
+
==>v[2]
|
701
|
+
gremlin> x
|
702
|
+
==>v[2]
|
703
|
+
==>v[4]
|
704
|
+
==>v[3]
|
705
|
+
|
706
|
+
See Also: store fill
|
707
|
+
|
708
|
+
### as
|
709
|
+
|
710
|
+
Emits input, but names the previous step.
|
711
|
+
|
712
|
+
gremlin> g.V.out('knows').has('age', T.gt, 30).back(2).age
|
713
|
+
==>29
|
714
|
+
gremlin> g.V.as('x').outE('knows').inV.has('age', T.gt, 30).back('x').age
|
715
|
+
==>29
|
716
|
+
|
717
|
+
### groupBy
|
718
|
+
|
719
|
+
Emits input, but groups input after processing it by provided key-closure and value-closure. It is also possible to supply an optional reduce-closure.
|
720
|
+
|
721
|
+
gremlin> g.V.groupBy{it}{it.out}.cap
|
722
|
+
==>{v[3]=[], v[2]=[], v[1]=[v[2], v[4], v[3]], v[6]=[v[3]], v[5]=[], v[4]=[v[5], v[3]]}
|
723
|
+
gremlin> g.V.groupBy{it}{it.out}{it.size()}.cap
|
724
|
+
==>{v[3]=0, v[2]=0, v[1]=3, v[6]=1, v[5]=0, v[4]=2}
|
725
|
+
gremlin> m = [:]
|
726
|
+
gremlin> g.V.groupBy(m){it}{it.out}.iterate();null;
|
727
|
+
==>null
|
728
|
+
gremlin> m
|
729
|
+
==>v[3]=[]
|
730
|
+
==>v[2]=[]
|
731
|
+
==>v[1]=[v[2], v[4], v[3]]
|
732
|
+
==>v[6]=[v[3]]
|
733
|
+
==>v[5]=[]
|
734
|
+
==>v[4]=[v[5], v[3]]
|
735
|
+
gremlin> g.V.out.groupBy{it.name}{it.in}{it.unique().findAll{i -> i.age > 30}.name}.cap
|
736
|
+
==>{lop=[josh, peter], ripple=[josh], josh=[], vadas=[]}
|
737
|
+
|
738
|
+
See Also: groupCount
|
739
|
+
|
740
|
+
### groupCount
|
741
|
+
|
742
|
+
Emits input, but updates a map for each input, where closures provides generic map update.
|
743
|
+
|
744
|
+
gremlin> g.V.out.groupCount(m)
|
745
|
+
==>v[2]
|
746
|
+
==>v[4]
|
747
|
+
==>v[3]
|
748
|
+
==>v[3]
|
749
|
+
==>v[5]
|
750
|
+
==>v[3]
|
751
|
+
gremlin> m
|
752
|
+
==>v[2]=1
|
753
|
+
==>v[4]=1
|
754
|
+
==>v[3]=3
|
755
|
+
==>v[5]=1
|
756
|
+
gremlin> g.v(1).out.groupCount(m){it}{it.b+1.0}.out.groupCount(m){it}{it.b+0.5}
|
757
|
+
==>v[5]
|
758
|
+
==>v[3]
|
759
|
+
gremlin> m
|
760
|
+
==>v[2]=1.0
|
761
|
+
==>v[4]=1.0
|
762
|
+
==>v[5]=0.5
|
763
|
+
==>v[3]=1.5
|
764
|
+
|
765
|
+
See Also: groupBy
|
766
|
+
|
767
|
+
### optional
|
768
|
+
|
769
|
+
Behaves similar to back except that it does not filter. It will go down a particular path and back up to where it left off. As such, its useful for yielding a sideeffect down a particular branch.
|
770
|
+
|
771
|
+
gremlin> g.V.out('knows').has('age', T.gt, 30).back(2)
|
772
|
+
==>v[1]
|
773
|
+
gremlin> g.V.out('knows').has('age', T.gt, 30).optional(2)
|
774
|
+
==>v[3]
|
775
|
+
==>v[2]
|
776
|
+
==>v[1]
|
777
|
+
==>v[6]
|
778
|
+
==>v[5]
|
779
|
+
==>v[4]
|
780
|
+
gremlin> g.V.as('x').outE('knows').inV.has('age', T.gt, 30).back('x')
|
781
|
+
==>v[1]
|
782
|
+
gremlin> g.V.as('x').outE('knows').inV.has('age', T.gt, 30).optional('x')
|
783
|
+
==>v[3]
|
784
|
+
==>v[2]
|
785
|
+
==>v[1]
|
786
|
+
==>v[6]
|
787
|
+
==>v[5]
|
788
|
+
==>v[4]
|
789
|
+
|
790
|
+
See Also: back
|
791
|
+
|
792
|
+
### sideEffect
|
793
|
+
|
794
|
+
Emits input, but calls a side effect closure on each input.
|
795
|
+
|
796
|
+
gremlin> youngest = Integer.MAX_VALUE
|
797
|
+
==>2147483647
|
798
|
+
gremlin> g.V.hasNot('age', null).sideEffect{youngest=youngest>it.age?it.age:youngest}
|
799
|
+
==>v[2]
|
800
|
+
==>v[1]
|
801
|
+
==>v[6]
|
802
|
+
==>v[4]
|
803
|
+
gremlin> youngest
|
804
|
+
==>27
|
805
|
+
|
806
|
+
### store
|
807
|
+
|
808
|
+
Emits input, but adds input to collection, where provided closure processes input prior to insertion (lazy). In being "lazy", 'store' will keep element as they are being requested.
|
809
|
+
|
810
|
+
gremlin> x = []
|
811
|
+
gremlin> g.v(1).out.store(x).next()
|
812
|
+
==>v[2]
|
813
|
+
gremlin> x
|
814
|
+
==>v[2]
|
815
|
+
|
816
|
+
See Also: aggregate fill
|
817
|
+
|
818
|
+
### table
|
819
|
+
|
820
|
+
Emits input, but stores row of as values (constrained by column names if provided) in a table. Accepts an optional set of closures that are applied in round-robin fashion to each column of the table.
|
821
|
+
|
822
|
+
gremlin> t = new Table()
|
823
|
+
gremlin> g.V.name.as('name').back(1).age.as('age').table(t)
|
824
|
+
==>null
|
825
|
+
==>27
|
826
|
+
==>29
|
827
|
+
==>35
|
828
|
+
==>null
|
829
|
+
==>32
|
830
|
+
gremlin> t
|
831
|
+
==>[name:lop, age:null]
|
832
|
+
==>[name:vadas, age:27]
|
833
|
+
==>[name:marko, age:29]
|
834
|
+
==>[name:peter, age:35]
|
835
|
+
==>[name:ripple, age:null]
|
836
|
+
==>[name:josh, age:32]
|
837
|
+
gremlin> t = new Table()
|
838
|
+
gremlin> g.V.hasNot('age', null).name.as('name').back(1).age.as('age').table(t){it}{it>30 ? 'over thirty' : 'under thirty'}
|
839
|
+
==>27
|
840
|
+
==>29
|
841
|
+
==>35
|
842
|
+
==>32
|
843
|
+
gremlin> t
|
844
|
+
==>[name:vadas, age:under thirty]
|
845
|
+
==>[name:marko, age:under thirty]
|
846
|
+
==>[name:peter, age:over thirty]
|
847
|
+
==>[name:josh, age:over thirty]
|
848
|
+
gremlin> t.get(0,'name')
|
849
|
+
==>vadas
|
850
|
+
|
851
|
+
### tree
|
852
|
+
|
853
|
+
Emit input, but stores the tree formed by the traversal as a map. Accepts an optional set of closures to be applied in round-robin fashion over each level of the tree.
|
854
|
+
|
855
|
+
gremlin> g.v(1).out.out.tree.cap
|
856
|
+
==>{v[1]={v[4]={v[3]={}, v[5]={}}}}
|
857
|
+
gremlin> g.v(1).out.out.tree{it.name}.cap
|
858
|
+
==>{marko={josh={lop={}, ripple={}}}}
|
859
|
+
gremlin> g.v(1).out.out.tree{it.name}{"child1:" + it.name}{"child2:" + it.name}.cap
|
860
|
+
==>{marko={child1:josh={child2:lop={}, child2:ripple={}}}}
|
861
|
+
gremlin> t = new Tree()
|
862
|
+
gremlin> g.v(1).out.out.tree(t){it.name}{"child1:" + it.name}{"child2:" + it.name}
|
863
|
+
==>v[5]
|
864
|
+
==>v[3]
|
865
|
+
gremlin> t.get('marko')
|
866
|
+
==>child1:josh={child2:lop={}, child2:ripple={}}
|
867
|
+
|
868
|
+
|
869
|
+
## Branch
|
870
|
+
|
871
|
+
Branch steps decide which step to take.
|
872
|
+
|
873
|
+
### copySplit
|
874
|
+
|
875
|
+
Copies incoming object to internal pipes.
|
876
|
+
|
877
|
+
gremlin> g.v(1).out('knows').copySplit(_().out('created').name, _().age).fairMerge
|
878
|
+
==>ripple
|
879
|
+
==>27
|
880
|
+
==>lop
|
881
|
+
==>32
|
882
|
+
gremlin> g.v(1).out('knows').copySplit(_().out('created').name, _().age).exhaustMerge
|
883
|
+
==>ripple
|
884
|
+
==>lop
|
885
|
+
==>27
|
886
|
+
==>32
|
887
|
+
|
888
|
+
See Also: exhaustMerge fairMerge
|
889
|
+
|
890
|
+
### exhaustMerge
|
891
|
+
|
892
|
+
Used in combination with a copySplit, merging the parallel traversals by exhaustively getting the objects of the first, then the second, etc.
|
893
|
+
|
894
|
+
gremlin> g.v(1).out('knows').copySplit(_().out('created').name, _().age).exhaustMerge
|
895
|
+
==>ripple
|
896
|
+
==>lop
|
897
|
+
==>27
|
898
|
+
==>32
|
899
|
+
|
900
|
+
See Also: copySplit fairMerge
|
901
|
+
|
902
|
+
### fairMerge
|
903
|
+
|
904
|
+
Used in combination with a copySplit, merging the parallel traversals in a round-robin fashion.
|
905
|
+
|
906
|
+
gremlin> g.v(1).out('knows').copySplit(_().out('created').name, _().age).fairMerge
|
907
|
+
==>ripple
|
908
|
+
==>27
|
909
|
+
==>lop
|
910
|
+
==>32
|
911
|
+
|
912
|
+
See Also: copySplit exaustMerge
|
913
|
+
|
914
|
+
### ifThenElse
|
915
|
+
|
916
|
+
Allows for if-then-else conditional logic.
|
917
|
+
|
918
|
+
gremlin> g.v(1).out.ifThenElse{it.name=='josh'}{it.age}{it.name}
|
919
|
+
==>vadas
|
920
|
+
==>32
|
921
|
+
==>lop
|
922
|
+
|
923
|
+
### loop
|
924
|
+
|
925
|
+
Loop over a particular set of steps in the pipeline. The first argument is either the number of steps back in the pipeline to go or a named step. The second argument is a while closure evaluating the current object. The it component of the loop step closure has three properties that are accessible. These properties can be used to reason about when to break out of the loop.
|
926
|
+
|
927
|
+
it.object: the current object of the traverser.
|
928
|
+
it.path: the current path of the traverser.
|
929
|
+
it.loops: the number of times the traverser has looped through the loop section.
|
930
|
+
|
931
|
+
The final argument is known as the "emit" closure. This boolean-based closure will determine wether the current object in the loop structure is emitted or not. As such, it is possible to emit intermediate objects, not simply those at the end of the loop.
|
932
|
+
|
933
|
+
gremlin> g.v(1).out.out
|
934
|
+
==>v[5]
|
935
|
+
==>v[3]
|
936
|
+
gremlin> g.v(1).out.loop(1){it.loops<3}
|
937
|
+
==>v[5]
|
938
|
+
==>v[3]
|
939
|
+
gremlin> g.v(1).out.loop(1){it.loops<3}{it.object.name=='josh'}
|
940
|
+
==>v[4]
|
941
|
+
|
942
|
+
|
943
|
+
## Methods
|
944
|
+
|
945
|
+
Methods represent functions that make it faster and easier to work with Blueprints and Pipes APIs. It is important to keep in mind that the full Java API and Groovy API are accessible from Gremlin.
|
946
|
+
|
947
|
+
### Element.keys
|
948
|
+
|
949
|
+
Get the property keys of an element.
|
950
|
+
|
951
|
+
gremlin> g.v(1).keys()
|
952
|
+
==>name
|
953
|
+
==>age
|
954
|
+
|
955
|
+
### Element.remove
|
956
|
+
|
957
|
+
Remove an element from the graph.
|
958
|
+
|
959
|
+
gremlin> g.E.weight
|
960
|
+
==>1.0
|
961
|
+
==>0.5
|
962
|
+
==>0.4
|
963
|
+
==>1.0
|
964
|
+
==>0.4
|
965
|
+
==>0.2
|
966
|
+
gremlin> g.E.has("weight",T.lt,0.5f).remove()
|
967
|
+
==>null
|
968
|
+
gremlin> g.E.weight
|
969
|
+
==>1.0
|
970
|
+
==>0.5
|
971
|
+
==>1.0
|
972
|
+
|
973
|
+
### Element.values
|
974
|
+
|
975
|
+
Gets the property values of an element.
|
976
|
+
|
977
|
+
gremlin> g.v(1).values()
|
978
|
+
==>marko
|
979
|
+
==>29
|
980
|
+
|
981
|
+
### Graph.addEdge
|
982
|
+
|
983
|
+
Adds an edge to the graph. Note that most graph implementations ignore the identifier supplied to addEdge.
|
984
|
+
|
985
|
+
gremlin> g = new TinkerGraph()
|
986
|
+
==>tinkergraph[vertices:0 edges:0]
|
987
|
+
gremlin> v1 = g.addVertex(100)
|
988
|
+
==>v[100]
|
989
|
+
gremlin> v2 = g.addVertex(200)
|
990
|
+
==>v[200]
|
991
|
+
gremlin> g.addEdge(v1,v2,'friend')
|
992
|
+
==>e[0][100-friend->200]
|
993
|
+
gremlin> g.addEdge(1000,v1,v2,'buddy')
|
994
|
+
==>e[1000][100-buddy->200]
|
995
|
+
gremlin> g.addEdge(null,v1,v2,'pal',[weight:0.75f])
|
996
|
+
==>e[1][100-pal->200]
|
997
|
+
|
998
|
+
### Graph.addVertex
|
999
|
+
|
1000
|
+
Adds a vertex to the graph. Note that most graph implementations ignore the identifier supplied to addVertex.
|
1001
|
+
|
1002
|
+
gremlin> g = new TinkerGraph()
|
1003
|
+
==>tinkergraph[vertices:0 edges:0]
|
1004
|
+
gremlin> g.addVertex()
|
1005
|
+
==>v[0]
|
1006
|
+
gremlin> g.addVertex(100)
|
1007
|
+
==>v[100]
|
1008
|
+
gremlin> g.addVertex(null,[name:"stephen"])
|
1009
|
+
==>v[1]
|
1010
|
+
|
1011
|
+
### Graph.e
|
1012
|
+
|
1013
|
+
Get an edge or set of edges by providing one or more edge identifiers. The identifiers must be the identifiers assigned by the underlying graph implementation.
|
1014
|
+
|
1015
|
+
gremlin> g.e(10)
|
1016
|
+
==>e[10][4-created->5]
|
1017
|
+
gremlin> g.e(10,11,12)
|
1018
|
+
==>e[10][4-created->5]
|
1019
|
+
==>e[11][4-created->3]
|
1020
|
+
==>e[12][6-created->3]
|
1021
|
+
gremlin> ids = [10,11,12]
|
1022
|
+
==>10
|
1023
|
+
==>11
|
1024
|
+
==>12
|
1025
|
+
gremlin> g.e(ids.toArray())
|
1026
|
+
==>e[10][4-created->5]
|
1027
|
+
==>e[11][4-created->3]
|
1028
|
+
==>e[12][6-created->3]
|
1029
|
+
|
1030
|
+
See Also: Graph.v
|
1031
|
+
|
1032
|
+
### Graph.idx(String)
|
1033
|
+
|
1034
|
+
Get an manual index by its name.
|
1035
|
+
|
1036
|
+
gremlin> g.createIndex("my-index", Vertex.class)
|
1037
|
+
==>index[my-index:Vertex]
|
1038
|
+
gremlin> myIdx = g.idx("my-index").put("name", "marko", g.v(1))
|
1039
|
+
==>null
|
1040
|
+
gremlin> myIdx.getIndexName()
|
1041
|
+
==>my-index
|
1042
|
+
|
1043
|
+
See Also: Index[Map.Entry]
|
1044
|
+
|
1045
|
+
### Graph.load
|
1046
|
+
|
1047
|
+
Load a file from one of several standard formats such as GraphML, GML, or GraphSON.
|
1048
|
+
|
1049
|
+
gremlin> g = new TinkerGraph()
|
1050
|
+
==>tinkergraph[vertices:0 edges:0]
|
1051
|
+
gremlin> g.loadGraphML('data/graph-example-1.xml')
|
1052
|
+
==>null
|
1053
|
+
gremlin> g.V
|
1054
|
+
==>v[3]
|
1055
|
+
==>v[2]
|
1056
|
+
==>v[1]
|
1057
|
+
==>v[6]
|
1058
|
+
==>v[5]
|
1059
|
+
==>v[4]
|
1060
|
+
|
1061
|
+
See Also: Graph.save
|
1062
|
+
|
1063
|
+
### Graph.removeEdge
|
1064
|
+
|
1065
|
+
Remove an edge.
|
1066
|
+
|
1067
|
+
gremlin> g = new TinkerGraph()
|
1068
|
+
==>tinkergraph[vertices:0 edges:0]
|
1069
|
+
gremlin> v1 = g.addVertex()
|
1070
|
+
==>v[100]
|
1071
|
+
gremlin> v2 = g.addVertex()
|
1072
|
+
==>v[200]
|
1073
|
+
gremlin> g.addEdge(v1,v2,'friend')
|
1074
|
+
==>e[0][100-friend->200]
|
1075
|
+
gremlin> g.removeEdge(g.e(0))
|
1076
|
+
==>null
|
1077
|
+
|
1078
|
+
### Graph.removeVertex
|
1079
|
+
|
1080
|
+
Remove a vertex.
|
1081
|
+
|
1082
|
+
gremlin> g.addVertex()
|
1083
|
+
==>v[128]
|
1084
|
+
gremlin> g.removeVertex(g.v(128))
|
1085
|
+
==>null
|
1086
|
+
|
1087
|
+
### Graph.save
|
1088
|
+
|
1089
|
+
Save a graph to file given one of several standard formats such as GraphML, GML, or GraphSON.
|
1090
|
+
|
1091
|
+
gremlin> g.saveGraphML('data/graph.xml')
|
1092
|
+
==>null
|
1093
|
+
|
1094
|
+
See Also: Graph.load
|
1095
|
+
|
1096
|
+
### Graph.v
|
1097
|
+
|
1098
|
+
Get a vertex or set of vertices by providing one or more vertex identifiers. The identifiers must be the identifiers assigned by the underlying graph implementation.
|
1099
|
+
|
1100
|
+
gremlin> g.v(1)
|
1101
|
+
==>v[1]
|
1102
|
+
gremlin> g.v(1,2,3)
|
1103
|
+
==>v[1]
|
1104
|
+
==>v[2]
|
1105
|
+
==>v[3]
|
1106
|
+
gremlin> ids = [1,2,3]
|
1107
|
+
==>1
|
1108
|
+
==>2
|
1109
|
+
==>3
|
1110
|
+
gremlin> g.v(ids.toArray())
|
1111
|
+
==>v[1]
|
1112
|
+
==>v[2]
|
1113
|
+
==>v[3]
|
1114
|
+
|
1115
|
+
See Also: Graph.e
|
1116
|
+
|
1117
|
+
### Index[Map.Entry]
|
1118
|
+
|
1119
|
+
Look up a value in an index.
|
1120
|
+
|
1121
|
+
gremlin> g.createIndex("my-index", Vertex.class)
|
1122
|
+
==>index[my-index:Vertex]
|
1123
|
+
gremlin> g.idx("my-index").put("name", "marko", g.v(1))
|
1124
|
+
==>null
|
1125
|
+
gremlin> g.idx("my-index")[[name:"marko"]]
|
1126
|
+
==>v[1]
|
1127
|
+
|
1128
|
+
See Also: Graph.idx(String)
|
1129
|
+
|
1130
|
+
### Pipe.fill
|
1131
|
+
|
1132
|
+
Takes all the results in the pipeline and puts them into the provided collection.
|
1133
|
+
|
1134
|
+
gremlin> m = []
|
1135
|
+
gremlin> g.v(1).out.fill(m)
|
1136
|
+
==>v[2]
|
1137
|
+
==>v[4]
|
1138
|
+
==>v[3]
|
1139
|
+
gremlin> m
|
1140
|
+
==>v[2]
|
1141
|
+
==>v[4]
|
1142
|
+
==>v[3]
|
1143
|
+
|
1144
|
+
See Also: aggregate store
|
1145
|
+
|
1146
|
+
### Pipe.iterate
|
1147
|
+
|
1148
|
+
Calls Pipe.next for all objects in the pipe. This is an important notion to follow when considering the behavior of the Gremlin Console. The Gremlin Console iterates through the pipeline automatically and outputs the results. Outside of the Gremlin Console or if more than one statement is present on a single line of the Gremlin Console, iterating the pipe must be done manually. Read more about this topic in the Gremlin Wiki Troubleshooting Page.
|
1149
|
+
|
1150
|
+
There are some important things to note in the example below. Had the the first line of Gremlin been executed separately, as opposed to being placed on the same line separated by a semi-colon, the names of all the vertices would have changed because the Gremlin Console would have automatically iterated the pipe and processed the side-effects.
|
1151
|
+
|
1152
|
+
gremlin> g.V.sideEffect{it.name="same-again"};g.V.name
|
1153
|
+
==>lop
|
1154
|
+
==>vadas
|
1155
|
+
==>marko
|
1156
|
+
==>peter
|
1157
|
+
==>ripple
|
1158
|
+
==>josh
|
1159
|
+
gremlin> g.V.sideEffect{it.name="same"}.iterate();g.V.name
|
1160
|
+
==>same
|
1161
|
+
==>same
|
1162
|
+
==>same
|
1163
|
+
==>same
|
1164
|
+
==>same
|
1165
|
+
==>same
|
1166
|
+
|
1167
|
+
See Also: Pipe.next
|
1168
|
+
|
1169
|
+
### Pipe.next
|
1170
|
+
|
1171
|
+
Gets the next object in the pipe or the next n objects. This is an important notion to follow when considering the behavior of the Gremlin Console. The Gremlin Console iterates through the pipeline automatically and outputs the results. Outside of the Gremlin Console or if more than one statement is present on a single line of the Gremlin Console, iterating the pipe must be done manually. Read more about this topic in the Gremlin Wiki Troubleshooting Page.
|
1172
|
+
|
1173
|
+
There are some important things to note in the example below. Had the the first line of Gremlin been executed separately, as opposed to being placed on the same line separated by a semi-colon, the name of the vertex would have changed because the Gremlin Console would have automatically iterated the pipe and processed the side-effect.
|
1174
|
+
|
1175
|
+
gremlin> g.v(1).sideEffect{it.name="same"};g.v(1).name
|
1176
|
+
==>marko
|
1177
|
+
gremlin> g.v(1).sideEffect{it.name="same"}.next();g.v(1).name
|
1178
|
+
==>same
|
1179
|
+
gremlin> g.V.sideEffect{it.name="same-again"}.next(3);g.V.name
|
1180
|
+
==>same-again
|
1181
|
+
==>same-again
|
1182
|
+
==>same-again
|
1183
|
+
==>peter
|
1184
|
+
==>ripple
|
1185
|
+
==>josh
|
1186
|
+
|
1187
|
+
See Also: Pipe.iterate
|
1188
|
+
|
1189
|
+
|
1190
|
+
## Recipes
|
1191
|
+
|
1192
|
+
Recipes are common patterns that are seen in using Gremlin.
|
1193
|
+
Duplicate Edges
|
1194
|
+
|
1195
|
+
Strictly speaking, you cannot have duplicated egdes with the same id. This example finds edges with same outV/inV/label properties.
|
1196
|
+
|
1197
|
+
gremlin> g = TinkerGraphFactory.createTinkerGraph()
|
1198
|
+
==>tinkergraph[vertices:6 edges:6]
|
1199
|
+
gremlin> g.v(1).outE('created')
|
1200
|
+
==>e[9][1-created->3]
|
1201
|
+
gremlin> g.addEdge(null, g.v(1), g.v(3), "created", g.e(9).map()) // see note
|
1202
|
+
==>e[0][1-created->3]
|
1203
|
+
gremlin> g.v(1).outE('created')
|
1204
|
+
==>e[0][1-created->3]
|
1205
|
+
==>e[9][1-created->3]
|
1206
|
+
gremlin> ElementHelper.haveEqualProperties(g.e(9), g.e(0))
|
1207
|
+
==>true
|
1208
|
+
gremlin> e = g.e(9)
|
1209
|
+
==>e[9][1-created->3]
|
1210
|
+
gremlin> e.outV.outE(e.label).filter{ElementHelper.haveEqualProperties(e,it)}.as('e').inV.filter{it==e.inV.next()}.back('e').except([e])
|
1211
|
+
==>e[0][1-created->3]
|
1212
|
+
|
1213
|
+
### Hiding Console Output
|
1214
|
+
|
1215
|
+
The Gremlin Console automatically iterates the pipe and outputs the results to the console. In some cases, this can lead to lots of screen output that isn't terribly useful. To suppress the output, consider the following:
|
1216
|
+
|
1217
|
+
gremlin> g.V.sideEffect{it.name='changed'}
|
1218
|
+
==>v[3]
|
1219
|
+
==>v[2]
|
1220
|
+
==>v[1]
|
1221
|
+
==>v[6]
|
1222
|
+
==>v[5]
|
1223
|
+
==>v[4]
|
1224
|
+
gremlin> g.V.sideEffect{it.name='changed-again'}.iterate()
|
1225
|
+
==>null
|
1226
|
+
gremlin> g.V.name
|
1227
|
+
==>changed-again
|
1228
|
+
==>changed-again
|
1229
|
+
==>changed-again
|
1230
|
+
==>changed-again
|
1231
|
+
==>changed-again
|
1232
|
+
==>changed-again
|
1233
|
+
gremlin> t = g.v(1).out.tree.cap.next()
|
1234
|
+
==>v[1]={v[3]={}, v[2]={}, v[4]={}}
|
1235
|
+
gremlin> t
|
1236
|
+
==>v[1]={v[3]={}, v[2]={}, v[4]={}}
|
1237
|
+
gremlin> s = g.v(1).out.tree.cap.next();null
|
1238
|
+
==>null
|
1239
|
+
gremlin> s
|
1240
|
+
==>v[1]={v[3]={}, v[2]={}, v[4]={}}
|
1241
|
+
|
1242
|
+
See Also: Pipe.iterate Pipe.next
|
1243
|
+
|
1244
|
+
### Paging Results
|
1245
|
+
|
1246
|
+
It is sometimes desireable to not return an entire results set. Results can be paged or limited as follows:
|
1247
|
+
|
1248
|
+
gremlin> g.V.has("age", T.gte, 25)
|
1249
|
+
==>v[2]
|
1250
|
+
==>v[1]
|
1251
|
+
==>v[6]
|
1252
|
+
==>v[4]
|
1253
|
+
gremlin> g.V.has("age", T.gte, 29)
|
1254
|
+
==>v[1]
|
1255
|
+
==>v[6]
|
1256
|
+
==>v[4]
|
1257
|
+
gremlin> g.V.has("age", T.gte, 29)[0..1]
|
1258
|
+
==>v[1]
|
1259
|
+
==>v[6]
|
1260
|
+
gremlin> g.V.has("age", T.gte, 29)[0..<1]
|
1261
|
+
==>v[1]
|
1262
|
+
gremlin> g.V.has("age", T.gte, 29)[1..2]
|
1263
|
+
==>v[6]
|
1264
|
+
==>v[4]
|
1265
|
+
|
1266
|
+
### Reading From a File
|
1267
|
+
|
1268
|
+
Reading data from an edge file formatted as CSV is easy to do with Gremlin.
|
1269
|
+
|
1270
|
+
gremlin> g = new TinkerGraph()
|
1271
|
+
==>tinkergraph[vertices:0 edges:0]
|
1272
|
+
gremlin> vs=[] as Set;new
|
1273
|
+
File("edges.txt").eachLine{l->p=l.split(",");vs<<p[0];vs<<p[1];}
|
1274
|
+
==>1
|
1275
|
+
==>2
|
1276
|
+
==>3
|
1277
|
+
==>4
|
1278
|
+
gremlin> vs.each{v->g.addVertex(v)}
|
1279
|
+
==>1
|
1280
|
+
==>2
|
1281
|
+
==>3
|
1282
|
+
==>4
|
1283
|
+
gremlin> new File("edges.txt").eachLine{l->p=l.split(",");g.addEdge(g.getVertex(p[0]),g.getVertex(p[1]),'friend')}
|
1284
|
+
gremlin> g.E
|
1285
|
+
==>e[3][1-friend->4]
|
1286
|
+
==>e[2][3-friend->4]
|
1287
|
+
==>e[1][2-friend->3]
|
1288
|
+
==>e[0][1-friend->2]
|
1289
|
+
|
1290
|
+
### Sampling
|
1291
|
+
|
1292
|
+
It is sometimes useful to grab a random sample of the items in a collection. That can be done to some degree with the random step, but getting an explicit number of items is not supported using that step.
|
1293
|
+
|
1294
|
+
gremlin> g.v(1).out.shuffle
|
1295
|
+
==>v[2]
|
1296
|
+
==>v[3]
|
1297
|
+
==>v[4]
|
1298
|
+
gremlin> g.v(1).out.shuffle
|
1299
|
+
==>v[3]
|
1300
|
+
==>v[2]
|
1301
|
+
==>v[4]
|
1302
|
+
gremlin> g.v(1).out.random(0.5)
|
1303
|
+
==>v[2]
|
1304
|
+
gremlin> g.v(1).out.random(0.5)
|
1305
|
+
==>v[4]
|
1306
|
+
==>v[3]
|
1307
|
+
|
1308
|
+
### Shortest Path
|
1309
|
+
|
1310
|
+
Finding the shortest path between two vertices can be accomplished with a loop. The following example shows the shortest path between vertex 1 and vertex 5 and if such path cannot be found in five steps, break out of the computation.
|
1311
|
+
|
1312
|
+
gremlin> g.v(1).out.loop(1){it.object.id != "5" && it.loops < 6}.path
|
1313
|
+
==>[v[1], v[4], v[5]]
|
1314
|
+
|
1315
|
+
In the event there are multiple paths to 5, all paths will be output and the shortest path will need to be selected. The following example adds some edges to the toy graph to demonstrate a path length distribution:
|
1316
|
+
|
1317
|
+
gremlin> g.addEdge(g.v(3), g.v(5), 'created')
|
1318
|
+
==>e[0][3-created->5]
|
1319
|
+
gremlin> g.addEdge(g.v(1), g.v(5), 'created')
|
1320
|
+
==>e[1][1-created->5]
|
1321
|
+
gremlin> g.v(1).out.loop(1){it.object.id!="5" && it.loops < 6}.path{it.name}
|
1322
|
+
==>[marko, ripple]
|
1323
|
+
==>[marko, josh, ripple]
|
1324
|
+
==>[marko, lop, ripple]
|
1325
|
+
==>[marko, josh, lop, ripple]
|
1326
|
+
gremlin> g.v(1).out.loop(1){it.object.id!="5" && it.loops < 6}.path{it.name}.groupBy{it.size()}{it}.cap.next()
|
1327
|
+
==>2=[[marko, ripple]]
|
1328
|
+
==>3=[[marko, josh, ripple], [marko, lop, ripple]]
|
1329
|
+
==>4=[[marko, josh, lop, ripple]]
|
1330
|
+
|
1331
|
+
Starting with a new "toy" TinkerGraph, calculating the "cost" of the path (in this case utilizing the weight stored on the edges), can be accomplished with:
|
1332
|
+
|
1333
|
+
gremlin> g = TinkerGraphFactory.createTinkerGraph()
|
1334
|
+
==>tinkergraph[vertices:6 edges:6]
|
1335
|
+
gremlin> g.v(1).outE.inV.loop(2){it.object.id!="3" && it.loops < 6}.path.transform{[it.findAll{it instanceof Edge}.sum{it.weight}, it]}
|
1336
|
+
==>[0.4, [v[1], e[9][1-created->3], v[3]]]
|
1337
|
+
==>[1.4000000059604645, [v[1], e[8][1-knows->4], v[4], e[11][4-created->3], v[3]]]
|
1338
|
+
|
1339
|
+
Evaluating a shortest path between two vertices in both directions (in and out edges) can be expensive. Consider this example:
|
1340
|
+
|
1341
|
+
gremlin> g = new TinkerGraph()
|
1342
|
+
==>tinkergraph[vertices:0 edges:0]
|
1343
|
+
gremlin>
|
1344
|
+
gremlin> root = g.addVertex()
|
1345
|
+
==>v[0]
|
1346
|
+
gremlin>
|
1347
|
+
gremlin> (1..10).each { outer ->
|
1348
|
+
gremlin> parent = root
|
1349
|
+
gremlin> (1..10).each { inner ->
|
1350
|
+
gremlin> child = g.addVertex()
|
1351
|
+
gremlin> g.addEdge(parent, child, 'to')
|
1352
|
+
gremlin> parent = child
|
1353
|
+
gremlin> }
|
1354
|
+
gremlin> }; null
|
1355
|
+
==>null
|
1356
|
+
|
1357
|
+
Given the sample graph generated above, the following uses the same pattern shown above for finding the shortest path, but evaulates both incoming and outgoing edges (prior examples only evaluated outgoing edges).
|
1358
|
+
|
1359
|
+
gremlin> target = '99'; c = 0; root.both().sideEffect{c++}.loop(2){it.object.id != target && it.loops <= 10}.has('id',target).path().iterate(); c
|
1360
|
+
==>557670
|
1361
|
+
|
1362
|
+
The above example shows that 557,670 vertices were touched in the above traversal. Adapting this traversal slightly to use the store/except pattern makes it far more efficient, touching only 100 vertices) as shown below:
|
1363
|
+
|
1364
|
+
gremlin> s = [root] as Set
|
1365
|
+
==>v[0]
|
1366
|
+
gremlin> target = '99'; c = 0; root.both().except(s).store(s).sideEffect{c++}.loop(4){it.object.id != target && it.loops <= 10}.has('id',target).path().iterate(); c
|
1367
|
+
==>100
|
1368
|
+
|
1369
|
+
### Subgraphing
|
1370
|
+
|
1371
|
+
Extracting a portion of a graph out of another graph by side-effecting out graph elements to another graph. Using a TinkerGraph as the host for the subgraph is best (as memory allows), since TinkerGraph will preserve element identifiers after the extractions.
|
1372
|
+
|
1373
|
+
// the goal is to extract the "knows" subgraph
|
1374
|
+
gremlin> g.E.has('label','knows')
|
1375
|
+
==>e[7][1-knows->2]
|
1376
|
+
==>e[8][1-knows->4]
|
1377
|
+
|
1378
|
+
// this is the target subgraph
|
1379
|
+
gremlin> sg = new TinkerGraph()
|
1380
|
+
==>tinkergraph[vertices:0 edges:0]
|
1381
|
+
|
1382
|
+
// define a "Get Or Create" function for vertices...use ElementHelper to copy properties
|
1383
|
+
gremlin> def goc(v,g){nv=g.getVertex(v.id);if(nv==null){nv=g.addVertex(v.id,ElementHelper.getProperties(v))};nv}
|
1384
|
+
==>true
|
1385
|
+
|
1386
|
+
// generate the subgraph by side-effecting graph elements into new graph
|
1387
|
+
gremlin> g.E.has('label','knows').sideEffect{sg.addEdge(it.id,goc(it.outV.next(),sg),goc(it.inV.next(),sg),it.label,ElementHelper.getProperties(it))}.iterate()
|
1388
|
+
==>null
|
1389
|
+
gremlin> sg.E
|
1390
|
+
==>e[7][1-knows->2]
|
1391
|
+
==>e[8][1-knows->4]
|
1392
|
+
|
1393
|
+
### Writing To File
|
1394
|
+
|
1395
|
+
TinkerPop supports a number of different graph file formats, like GraphML, GML, and GraphSON, but sometimes a custom format or just a simple edge list is desireable. The following code shows how to open a file and side-effect out a comma-separated file of in and out vertices for each edge in the graph.
|
1396
|
+
|
1397
|
+
gremlin> new File("/tmp/edge-set.txt").withWriter{f -> g.E.sideEffect{f << "${it.outV.id.next()},${it.inV.id.next()}\r\n"}.iterate()}
|
1398
|
+
==>null
|