gqlite 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/Cargo.toml +4 -3
- data/ext/gqlitedb/Cargo.toml +7 -4
- data/ext/gqlitedb/src/aggregators/arithmetic.rs +1 -0
- data/ext/gqlitedb/src/compiler/expression_analyser.rs +2 -0
- data/ext/gqlitedb/src/compiler.rs +31 -19
- data/ext/gqlitedb/src/connection.rs +42 -7
- data/ext/gqlitedb/src/error.rs +3 -0
- data/ext/gqlitedb/src/functions/containers.rs +1 -1
- data/ext/gqlitedb/src/functions/path.rs +1 -1
- data/ext/gqlitedb/src/functions/scalar.rs +23 -0
- data/ext/gqlitedb/src/functions.rs +8 -0
- data/ext/gqlitedb/src/interpreter/evaluators.rs +10 -10
- data/ext/gqlitedb/src/interpreter/instructions.rs +3 -3
- data/ext/gqlitedb/src/lib.rs +1 -3
- data/ext/gqlitedb/src/parser/ast.rs +1 -0
- data/ext/gqlitedb/src/parser/gql.pest +3 -1
- data/ext/gqlitedb/src/parser/parser_impl.rs +8 -0
- data/ext/gqlitedb/src/prelude.rs +3 -0
- data/ext/gqlitedb/src/store/{pgql.rs → pgrx.rs} +2 -0
- data/ext/gqlitedb/src/store/postgres.rs +0 -0
- data/ext/gqlitedb/src/store/sqlbase/sqlmetadata.rs +117 -0
- data/ext/gqlitedb/src/store/sqlbase/sqlqueries.rs +62 -0
- data/ext/gqlitedb/src/store/sqlbase/sqlstore.rs +55 -0
- data/ext/gqlitedb/src/store/sqlbase/sqlvalue.rs +189 -0
- data/ext/gqlitedb/src/store/sqlbase.rs +456 -0
- data/ext/gqlitedb/src/store/sqlite.rs +271 -573
- data/ext/gqlitedb/src/store.rs +7 -5
- data/ext/gqlitedb/src/tests/templates/programs.rs +10 -10
- data/ext/gqlitedb/src/utils.rs +25 -0
- data/ext/gqlitedb/src/value/compare.rs +6 -0
- data/ext/gqlitedb/src/value.rs +18 -2
- data/ext/gqlitedb/templates/sql/sqlite/edge_select.sql +18 -18
- data/ext/gqlitedb/templates/sql/sqlite/edge_update.sql +3 -3
- data/ext/gqlitedb/templates/sql/sqlite/node_select.sql +6 -6
- data/ext/gqlitedb/templates/sql/sqlite/node_update.sql +3 -3
- data/ext/gqliterb/src/lib.rs +30 -2
- data/ext/graphcore/Cargo.toml +3 -2
- data/ext/graphcore/src/error.rs +2 -0
- data/ext/graphcore/src/lib.rs +2 -1
- data/ext/graphcore/src/prelude.rs +1 -1
- data/ext/graphcore/src/table.rs +1 -1
- data/ext/graphcore/src/timestamp.rs +104 -0
- data/ext/graphcore/src/value.rs +106 -23
- metadata +10 -7
- data/ext/gqlitedb/gqlite_bench_data/README.MD +0 -6
- data/ext/gqlitedb/gqlite_bench_data/scripts/generate_smaller_pokec.rb +0 -85
- data/ext/gqlitedb/gqlite_bench_data/scripts/to_efficient_pokec.rb +0 -34
- data/ext/graphcore/release.toml +0 -1
data/ext/graphcore/src/value.rs
CHANGED
|
@@ -8,6 +8,7 @@ use std::{
|
|
|
8
8
|
mod value_map;
|
|
9
9
|
|
|
10
10
|
pub(crate) use crate::prelude::*;
|
|
11
|
+
use crate::TimeStamp;
|
|
11
12
|
|
|
12
13
|
pub use value_map::ValueMap;
|
|
13
14
|
|
|
@@ -33,6 +34,8 @@ pub enum Value
|
|
|
33
34
|
Float(f64),
|
|
34
35
|
/// String value.
|
|
35
36
|
String(String),
|
|
37
|
+
/// Timestamp value
|
|
38
|
+
TimeStamp(timestamp::TimeStamp),
|
|
36
39
|
/// Array of values.
|
|
37
40
|
Array(Vec<Value>),
|
|
38
41
|
/// Unordered map of values.
|
|
@@ -97,6 +100,7 @@ impl Hash for Value
|
|
|
97
100
|
bits.hash(state);
|
|
98
101
|
}
|
|
99
102
|
Value::String(s) => s.hash(state),
|
|
103
|
+
Value::TimeStamp(ts) => ts.hash(state),
|
|
100
104
|
Value::Array(a) => a.hash(state),
|
|
101
105
|
Value::Map(m) => m.hash(state),
|
|
102
106
|
Value::Node(n) => n.hash(state),
|
|
@@ -118,6 +122,7 @@ impl Add for Value
|
|
|
118
122
|
| Value::Node(..)
|
|
119
123
|
| Value::Edge(..)
|
|
120
124
|
| Value::Map(..)
|
|
125
|
+
| Value::TimeStamp(..)
|
|
121
126
|
| Value::Path(..) => Err(Error::InvalidBinaryOperands),
|
|
122
127
|
Value::Null => Ok(Value::Null),
|
|
123
128
|
Self::Array(lhs) => match rhs
|
|
@@ -171,6 +176,7 @@ macro_rules! impl_mdsr {
|
|
|
171
176
|
Value::Boolean(..)
|
|
172
177
|
| Value::Key(..)
|
|
173
178
|
| Value::String(..)
|
|
179
|
+
| Value::TimeStamp(..)
|
|
174
180
|
| Value::Node(..)
|
|
175
181
|
| Value::Edge(..)
|
|
176
182
|
| Value::Array(..)
|
|
@@ -212,6 +218,7 @@ impl Value
|
|
|
212
218
|
Value::Boolean(..)
|
|
213
219
|
| Value::Key(..)
|
|
214
220
|
| Value::String(..)
|
|
221
|
+
| Value::TimeStamp(..)
|
|
215
222
|
| Value::Node(..)
|
|
216
223
|
| Value::Edge(..)
|
|
217
224
|
| Value::Array(..)
|
|
@@ -255,6 +262,7 @@ impl Neg for Value
|
|
|
255
262
|
| Value::String(..)
|
|
256
263
|
| Value::Node(..)
|
|
257
264
|
| Value::Edge(..)
|
|
265
|
+
| Value::TimeStamp(..)
|
|
258
266
|
| Value::Array(..)
|
|
259
267
|
| Value::Map(..)
|
|
260
268
|
| Value::Path(..) => Err(Error::InvalidNegationOperands),
|
|
@@ -274,6 +282,7 @@ impl std::fmt::Display for Value
|
|
|
274
282
|
Value::Integer(i) => write!(f, "{}", i),
|
|
275
283
|
Value::Float(fl) => write!(f, "{}", fl),
|
|
276
284
|
Value::String(s) => write!(f, "{}", s),
|
|
285
|
+
Value::TimeStamp(t) => write!(f, "{}", t),
|
|
277
286
|
Value::Array(v) => write!(f, "[{}]", v.iter().map(|x| x.to_string()).join(", ")),
|
|
278
287
|
Value::Map(o) => write!(f, "{}", o),
|
|
279
288
|
Value::Node(n) => write!(f, "{}", n),
|
|
@@ -298,7 +307,42 @@ impl ValueTryIntoRef<Value> for Value
|
|
|
298
307
|
}
|
|
299
308
|
}
|
|
300
309
|
|
|
301
|
-
|
|
310
|
+
impl<T> From<Option<T>> for Value
|
|
311
|
+
where
|
|
312
|
+
Value: From<T>,
|
|
313
|
+
{
|
|
314
|
+
fn from(value: Option<T>) -> Self
|
|
315
|
+
{
|
|
316
|
+
match value
|
|
317
|
+
{
|
|
318
|
+
Some(value) => value.into(),
|
|
319
|
+
None => Value::Null,
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
macro_rules! impl_from_value {
|
|
325
|
+
($type:ty, $vn:tt, try) => {
|
|
326
|
+
impl_from_value!($type, $vn);
|
|
327
|
+
impl TryFrom<Value> for $type
|
|
328
|
+
{
|
|
329
|
+
type Error = Error;
|
|
330
|
+
fn try_from(value: Value) -> Result<$type, Self::Error>
|
|
331
|
+
{
|
|
332
|
+
match value
|
|
333
|
+
{
|
|
334
|
+
Value::$vn(v) => Ok(v),
|
|
335
|
+
_ => Err(
|
|
336
|
+
Error::InvalidValueCast {
|
|
337
|
+
value: Box::new(value),
|
|
338
|
+
typename: stringify!($type),
|
|
339
|
+
}
|
|
340
|
+
.into(),
|
|
341
|
+
),
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
};
|
|
302
346
|
($type:ty, $vn:tt) => {
|
|
303
347
|
impl From<$type> for Value
|
|
304
348
|
{
|
|
@@ -315,25 +359,38 @@ macro_rules! impl_to_value {
|
|
|
315
359
|
Value::Array(v.into_iter().map(|v| v.into()).collect())
|
|
316
360
|
}
|
|
317
361
|
}
|
|
318
|
-
impl
|
|
362
|
+
impl TryFrom<&Value> for $type
|
|
319
363
|
{
|
|
320
364
|
type Error = Error;
|
|
321
|
-
fn
|
|
365
|
+
fn try_from(value: &Value) -> Result<$type, Self::Error>
|
|
322
366
|
{
|
|
323
|
-
|
|
367
|
+
<$type>::try_from(value.to_owned())
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
impl TryFrom<Value> for Option<$type>
|
|
371
|
+
{
|
|
372
|
+
type Error = Error;
|
|
373
|
+
fn try_from(value: Value) -> Result<Self>
|
|
374
|
+
{
|
|
375
|
+
match value
|
|
324
376
|
{
|
|
325
|
-
Value
|
|
326
|
-
_ =>
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
.into(),
|
|
332
|
-
),
|
|
377
|
+
Value::Null => Ok(None),
|
|
378
|
+
_ =>
|
|
379
|
+
{
|
|
380
|
+
let t: $type = value.try_into()?;
|
|
381
|
+
Ok(Some(t))
|
|
382
|
+
}
|
|
333
383
|
}
|
|
334
384
|
}
|
|
335
385
|
}
|
|
336
|
-
|
|
386
|
+
impl TryFrom<&Value> for Option<$type>
|
|
387
|
+
{
|
|
388
|
+
type Error = Error;
|
|
389
|
+
fn try_from(value: &Value) -> Result<Option<$type>, Self::Error>
|
|
390
|
+
{
|
|
391
|
+
<Option<$type>>::try_from(value.to_owned())
|
|
392
|
+
}
|
|
393
|
+
}
|
|
337
394
|
impl ValueTryIntoRef<$type> for Value
|
|
338
395
|
{
|
|
339
396
|
fn try_into_ref(&self) -> Result<&$type, Error>
|
|
@@ -354,16 +411,42 @@ macro_rules! impl_to_value {
|
|
|
354
411
|
};
|
|
355
412
|
}
|
|
356
413
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
414
|
+
impl_from_value!(graph::Key, Key, try);
|
|
415
|
+
impl_from_value!(bool, Boolean, try);
|
|
416
|
+
impl_from_value!(i64, Integer, try);
|
|
417
|
+
impl_from_value!(f64, Float, try);
|
|
418
|
+
impl_from_value!(String, String, try);
|
|
419
|
+
impl_from_value!(TimeStamp, TimeStamp);
|
|
420
|
+
impl_from_value!(graph::Node, Node, try);
|
|
421
|
+
impl_from_value!(graph::Edge, Edge, try);
|
|
422
|
+
impl_from_value!(graph::SinglePath, Path, try);
|
|
423
|
+
impl_from_value!(Vec<Value>, Array, try);
|
|
424
|
+
impl_from_value!(ValueMap, Map, try);
|
|
425
|
+
|
|
426
|
+
impl TryFrom<Value> for TimeStamp
|
|
427
|
+
{
|
|
428
|
+
type Error = Error;
|
|
429
|
+
fn try_from(value: Value) -> Result<TimeStamp, Self::Error>
|
|
430
|
+
{
|
|
431
|
+
match value
|
|
432
|
+
{
|
|
433
|
+
Value::String(s) => Ok(TimeStamp::parse(&s)?),
|
|
434
|
+
Value::TimeStamp(v) => Ok(v),
|
|
435
|
+
_ => Err(Error::InvalidValueCast {
|
|
436
|
+
value: Box::new(value),
|
|
437
|
+
typename: stringify!($type),
|
|
438
|
+
}),
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
impl From<()> for Value
|
|
444
|
+
{
|
|
445
|
+
fn from(_: ()) -> Self
|
|
446
|
+
{
|
|
447
|
+
Self::Null
|
|
448
|
+
}
|
|
449
|
+
}
|
|
367
450
|
|
|
368
451
|
impl From<&str> for Value
|
|
369
452
|
{
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gqlite
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Cyrille Berger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-11-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rb_sys
|
|
@@ -69,9 +69,6 @@ files:
|
|
|
69
69
|
- ext/gqlitedb/benches/common/pokec.rs
|
|
70
70
|
- ext/gqlitedb/benches/pokec_divan.rs
|
|
71
71
|
- ext/gqlitedb/benches/pokec_iai.rs
|
|
72
|
-
- ext/gqlitedb/gqlite_bench_data/README.MD
|
|
73
|
-
- ext/gqlitedb/gqlite_bench_data/scripts/generate_smaller_pokec.rb
|
|
74
|
-
- ext/gqlitedb/gqlite_bench_data/scripts/to_efficient_pokec.rb
|
|
75
72
|
- ext/gqlitedb/release.toml
|
|
76
73
|
- ext/gqlitedb/src/aggregators.rs
|
|
77
74
|
- ext/gqlitedb/src/aggregators/arithmetic.rs
|
|
@@ -106,8 +103,14 @@ files:
|
|
|
106
103
|
- ext/gqlitedb/src/prelude.rs
|
|
107
104
|
- ext/gqlitedb/src/query_result.rs
|
|
108
105
|
- ext/gqlitedb/src/store.rs
|
|
109
|
-
- ext/gqlitedb/src/store/
|
|
106
|
+
- ext/gqlitedb/src/store/pgrx.rs
|
|
107
|
+
- ext/gqlitedb/src/store/postgres.rs
|
|
110
108
|
- ext/gqlitedb/src/store/redb.rs
|
|
109
|
+
- ext/gqlitedb/src/store/sqlbase.rs
|
|
110
|
+
- ext/gqlitedb/src/store/sqlbase/sqlmetadata.rs
|
|
111
|
+
- ext/gqlitedb/src/store/sqlbase/sqlqueries.rs
|
|
112
|
+
- ext/gqlitedb/src/store/sqlbase/sqlstore.rs
|
|
113
|
+
- ext/gqlitedb/src/store/sqlbase/sqlvalue.rs
|
|
111
114
|
- ext/gqlitedb/src/store/sqlite.rs
|
|
112
115
|
- ext/gqlitedb/src/tests.rs
|
|
113
116
|
- ext/gqlitedb/src/tests/compiler.rs
|
|
@@ -148,13 +151,13 @@ files:
|
|
|
148
151
|
- ext/gqliterb/src/lib.rs
|
|
149
152
|
- ext/graphcore/Cargo.toml
|
|
150
153
|
- ext/graphcore/README.MD
|
|
151
|
-
- ext/graphcore/release.toml
|
|
152
154
|
- ext/graphcore/src/error.rs
|
|
153
155
|
- ext/graphcore/src/graph.rs
|
|
154
156
|
- ext/graphcore/src/lib.rs
|
|
155
157
|
- ext/graphcore/src/prelude.rs
|
|
156
158
|
- ext/graphcore/src/serialize_with.rs
|
|
157
159
|
- ext/graphcore/src/table.rs
|
|
160
|
+
- ext/graphcore/src/timestamp.rs
|
|
158
161
|
- ext/graphcore/src/value.rs
|
|
159
162
|
- ext/graphcore/src/value/value_map.rs
|
|
160
163
|
- lib/gqlite.rb
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
 GQLite Benchnark Data
|
|
2
|
-
==============================================
|
|
3
|
-
|
|
4
|
-
This repository includes data used to benchmark GQLite.
|
|
5
|
-
|
|
6
|
-
* `pokec_tiny_nodes.cypher` is a subset of the [pokec mini](https://github.com/memgraph/memgraph/tree/master/tests/mgbench#pokec) dataset ([original](https://snap.stanford.edu/data/soc-Pokec.html)).
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# Script that reduces the pokec dataset, to reduce it to a subset of users.
|
|
2
|
-
# It also reduce the initial set of queries into a single one.
|
|
3
|
-
#
|
|
4
|
-
# Use as:
|
|
5
|
-
# ```bash
|
|
6
|
-
# ruby reduce_pokec.rb size input.cypher > output.cypher
|
|
7
|
-
# ```
|
|
8
|
-
|
|
9
|
-
require 'set'
|
|
10
|
-
|
|
11
|
-
size = ARGV[0] or abort("Usage: ruby #{$0} micro/tiny input.cypher")
|
|
12
|
-
input_file = ARGV[1] or abort("Usage: ruby #{$0} micro/tiny input.cypher")
|
|
13
|
-
|
|
14
|
-
if size == "tiny"
|
|
15
|
-
initial = 5
|
|
16
|
-
increments = 2
|
|
17
|
-
elsif size == "micro"
|
|
18
|
-
initial = 1
|
|
19
|
-
increments = 1
|
|
20
|
-
else
|
|
21
|
-
abort("Unknown size '#{size}', should be 'tiny' or 'micro'")
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
user_lines = {}
|
|
25
|
-
all_edges = []
|
|
26
|
-
edges_by_user = Hash.new { |h, k| h[k] = [] }
|
|
27
|
-
selected_edges = []
|
|
28
|
-
seen_edges = Set.new
|
|
29
|
-
selected_users = Set.new
|
|
30
|
-
|
|
31
|
-
def edge_key(id1, id2)
|
|
32
|
-
[id1, id2].sort.join('-')
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
File.foreach(input_file) do |line|
|
|
36
|
-
if line =~ /CREATE\s+\(:User\s*(\{.*id:\s*(\d+)[^}]*\})\)/i
|
|
37
|
-
user_lines[$2.to_i] = $1
|
|
38
|
-
|
|
39
|
-
elsif line =~ /MATCH\s+\(n:User\s*\{id:\s*(\d+)\}\),\s*\(m:User\s*\{id:\s*(\d+)\}\).*CREATE.*Friend/i
|
|
40
|
-
id1, id2 = $1.to_i, $2.to_i
|
|
41
|
-
edge = { line: line, ids: [id1, id2] }
|
|
42
|
-
all_edges << edge
|
|
43
|
-
edges_by_user[id1] << edge
|
|
44
|
-
edges_by_user[id2] << edge
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Step 1: Select initial edges
|
|
49
|
-
initial_edges = all_edges.take(initial)
|
|
50
|
-
initial_edges.each do |edge|
|
|
51
|
-
k = edge_key(*edge[:ids])
|
|
52
|
-
next if seen_edges.include?(k)
|
|
53
|
-
|
|
54
|
-
seen_edges << k
|
|
55
|
-
selected_edges << edge
|
|
56
|
-
selected_users.merge(edge[:ids])
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Step 2: Find all other edges connected to selected users
|
|
60
|
-
for i in 0...increments
|
|
61
|
-
for uid in selected_users.clone()
|
|
62
|
-
edges_by_user[uid].each do |edge|
|
|
63
|
-
k = edge_key(*edge[:ids])
|
|
64
|
-
next if seen_edges.include?(k)
|
|
65
|
-
|
|
66
|
-
seen_edges << k
|
|
67
|
-
selected_edges << edge
|
|
68
|
-
|
|
69
|
-
# Add any new user ID to the queue
|
|
70
|
-
selected_users.merge(edge[:ids])
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
puts "CREATE"
|
|
76
|
-
# Output user CREATEs
|
|
77
|
-
selected_users.each do |id|
|
|
78
|
-
puts " (user_#{id}:User #{user_lines[id]})," if user_lines.key?(id)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Output edges
|
|
82
|
-
selected_edges.each do |edge|
|
|
83
|
-
ids = edge[:ids]
|
|
84
|
-
puts " (user_#{ids[0]})-[:Friend]->(user_#{ids[1]}),"
|
|
85
|
-
end
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# Script that transform the pokec dataset into a more efficient query.
|
|
2
|
-
#
|
|
3
|
-
# Use as:
|
|
4
|
-
# ```bash
|
|
5
|
-
# ruby to_efficient_pokec.rb input.cypher > output.cypher
|
|
6
|
-
# ```
|
|
7
|
-
|
|
8
|
-
require 'set'
|
|
9
|
-
|
|
10
|
-
input_file = ARGV[0] or abort("Usage: ruby #{$0} input.cypher")
|
|
11
|
-
|
|
12
|
-
user_lines = []
|
|
13
|
-
edge_lines = []
|
|
14
|
-
|
|
15
|
-
File.foreach(input_file) do |line|
|
|
16
|
-
if line =~ /CREATE\s+\(:User\s*(\{.*id:\s*(\d+)[^}]*\})\)/i
|
|
17
|
-
user_lines.append({ id: $2.to_i, data: $1})
|
|
18
|
-
|
|
19
|
-
elsif line =~ /MATCH\s+\(n:User\s*\{id:\s*(\d+)\}\),\s*\(m:User\s*\{id:\s*(\d+)\}\).*CREATE.*Friend/i
|
|
20
|
-
id1, id2 = $1.to_i, $2.to_i
|
|
21
|
-
edge_lines.append({ id1: id1, id2: id2 })
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
puts "CREATE"
|
|
26
|
-
# Output user CREATEs
|
|
27
|
-
user_lines.each do |user|
|
|
28
|
-
puts " (user_#{user[:id]}:User #{user[:data]}),"
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Output edges
|
|
32
|
-
edge_lines.each do |edge|
|
|
33
|
-
puts " (user_#{edge[:id1]})-[:Friend]->(user_#{edge[:id2]}),"
|
|
34
|
-
end
|
data/ext/graphcore/release.toml
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
tag = false
|