lex-neo4j 0.1.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 +7 -0
- data/.github/CODEOWNERS +7 -0
- data/.github/dependabot.yml +18 -0
- data/.github/workflows/ci.yml +34 -0
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/.rspec_status +80 -0
- data/.rubocop.yml +8 -0
- data/CHANGELOG.md +16 -0
- data/CLAUDE.md +33 -0
- data/Gemfile +13 -0
- data/LICENSE +21 -0
- data/README.md +91 -0
- data/lex-neo4j.gemspec +37 -0
- data/lib/legion/extensions/neo4j/client.rb +44 -0
- data/lib/legion/extensions/neo4j/errors.rb +10 -0
- data/lib/legion/extensions/neo4j/helpers/client.rb +34 -0
- data/lib/legion/extensions/neo4j/runners/admin.rb +79 -0
- data/lib/legion/extensions/neo4j/runners/cypher.rb +50 -0
- data/lib/legion/extensions/neo4j/runners/graph_data_science.rb +88 -0
- data/lib/legion/extensions/neo4j/runners/indexes.rb +87 -0
- data/lib/legion/extensions/neo4j/runners/nodes.rb +86 -0
- data/lib/legion/extensions/neo4j/runners/relationships.rb +104 -0
- data/lib/legion/extensions/neo4j/runners/transactions.rb +56 -0
- data/lib/legion/extensions/neo4j/version.rb +9 -0
- data/lib/legion/extensions/neo4j.rb +21 -0
- metadata +184 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7ee32c6b5641413d91c9465511f127d1fdbdd5b937f3a35807a42d707cefef58
|
|
4
|
+
data.tar.gz: e3b9301354201cb3ac20b8da8cb58bf8166951591642bf03d582626739891232
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 278f30ccfc2d078dbc3160032fbda03cdde627a56184e6fc89d9001dc9846dd3b5f46c77e719fc1cf0deeb327ced7fc079af31c853d7f6eae6beac96d1e261d9
|
|
7
|
+
data.tar.gz: 16b8ab739eb37c008137c8c1a62e740b847f50711ffe80f9aeaf29e51593cb03a183a08b1e3d40c535eac73f244daca4106d2590b8b9cd034ff8f2d59485fc1d
|
data/.github/CODEOWNERS
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: bundler
|
|
4
|
+
directory: /
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
day: monday
|
|
8
|
+
open-pull-requests-limit: 5
|
|
9
|
+
labels:
|
|
10
|
+
- "type:dependencies"
|
|
11
|
+
- package-ecosystem: github-actions
|
|
12
|
+
directory: /
|
|
13
|
+
schedule:
|
|
14
|
+
interval: weekly
|
|
15
|
+
day: monday
|
|
16
|
+
open-pull-requests-limit: 5
|
|
17
|
+
labels:
|
|
18
|
+
- "type:dependencies"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
schedule:
|
|
7
|
+
- cron: '0 9 * * 1'
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
ci:
|
|
11
|
+
uses: LegionIO/.github/.github/workflows/ci.yml@main
|
|
12
|
+
|
|
13
|
+
excluded-files:
|
|
14
|
+
uses: LegionIO/.github/.github/workflows/excluded-files.yml@main
|
|
15
|
+
|
|
16
|
+
security:
|
|
17
|
+
uses: LegionIO/.github/.github/workflows/security-scan.yml@main
|
|
18
|
+
|
|
19
|
+
version-changelog:
|
|
20
|
+
uses: LegionIO/.github/.github/workflows/version-changelog.yml@main
|
|
21
|
+
|
|
22
|
+
dependency-review:
|
|
23
|
+
uses: LegionIO/.github/.github/workflows/dependency-review.yml@main
|
|
24
|
+
|
|
25
|
+
stale:
|
|
26
|
+
if: github.event_name == 'schedule'
|
|
27
|
+
uses: LegionIO/.github/.github/workflows/stale.yml@main
|
|
28
|
+
|
|
29
|
+
release:
|
|
30
|
+
needs: [ci, excluded-files]
|
|
31
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
32
|
+
uses: LegionIO/.github/.github/workflows/release.yml@main
|
|
33
|
+
secrets:
|
|
34
|
+
rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
|
data/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Gemfile.lock
|
data/.rspec
ADDED
data/.rspec_status
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
example_id | status | run_time |
|
|
2
|
+
------------------------------------------------------------------------ | ------ | --------------- |
|
|
3
|
+
./spec/legion/extensions/neo4j/client_spec.rb[1:1] | passed | 0.00615 seconds |
|
|
4
|
+
./spec/legion/extensions/neo4j/client_spec.rb[1:2] | passed | 0.00109 seconds |
|
|
5
|
+
./spec/legion/extensions/neo4j/client_spec.rb[1:3] | passed | 0.00054 seconds |
|
|
6
|
+
./spec/legion/extensions/neo4j/client_spec.rb[1:4:1] | passed | 0.00014 seconds |
|
|
7
|
+
./spec/legion/extensions/neo4j/runners/admin_spec.rb[1:1:1] | passed | 0.00078 seconds |
|
|
8
|
+
./spec/legion/extensions/neo4j/runners/admin_spec.rb[1:2:1] | passed | 0.00061 seconds |
|
|
9
|
+
./spec/legion/extensions/neo4j/runners/admin_spec.rb[1:3:1] | passed | 0.00062 seconds |
|
|
10
|
+
./spec/legion/extensions/neo4j/runners/admin_spec.rb[1:3:2] | passed | 0.00013 seconds |
|
|
11
|
+
./spec/legion/extensions/neo4j/runners/admin_spec.rb[1:4:1] | passed | 0.0001 seconds |
|
|
12
|
+
./spec/legion/extensions/neo4j/runners/admin_spec.rb[1:5:1] | passed | 0.00012 seconds |
|
|
13
|
+
./spec/legion/extensions/neo4j/runners/admin_spec.rb[1:6:1] | passed | 0.00012 seconds |
|
|
14
|
+
./spec/legion/extensions/neo4j/runners/admin_spec.rb[1:7:1] | passed | 0.00014 seconds |
|
|
15
|
+
./spec/legion/extensions/neo4j/runners/cypher_spec.rb[1:1:1] | passed | 0.00011 seconds |
|
|
16
|
+
./spec/legion/extensions/neo4j/runners/cypher_spec.rb[1:1:2] | passed | 0.00012 seconds |
|
|
17
|
+
./spec/legion/extensions/neo4j/runners/cypher_spec.rb[1:1:3] | passed | 0.0001 seconds |
|
|
18
|
+
./spec/legion/extensions/neo4j/runners/cypher_spec.rb[1:2:1] | passed | 0.0001 seconds |
|
|
19
|
+
./spec/legion/extensions/neo4j/runners/cypher_spec.rb[1:2:2:1] | passed | 0.00055 seconds |
|
|
20
|
+
./spec/legion/extensions/neo4j/runners/cypher_spec.rb[1:3:1] | passed | 0.00011 seconds |
|
|
21
|
+
./spec/legion/extensions/neo4j/runners/cypher_spec.rb[1:4:1] | passed | 0.00011 seconds |
|
|
22
|
+
./spec/legion/extensions/neo4j/runners/graph_data_science_spec.rb[1:1:1] | passed | 0.00012 seconds |
|
|
23
|
+
./spec/legion/extensions/neo4j/runners/graph_data_science_spec.rb[1:2:1] | passed | 0.00011 seconds |
|
|
24
|
+
./spec/legion/extensions/neo4j/runners/graph_data_science_spec.rb[1:2:2] | passed | 0.0001 seconds |
|
|
25
|
+
./spec/legion/extensions/neo4j/runners/graph_data_science_spec.rb[1:3:1] | passed | 0.00009 seconds |
|
|
26
|
+
./spec/legion/extensions/neo4j/runners/graph_data_science_spec.rb[1:4:1] | passed | 0.00011 seconds |
|
|
27
|
+
./spec/legion/extensions/neo4j/runners/graph_data_science_spec.rb[1:4:2] | passed | 0.00011 seconds |
|
|
28
|
+
./spec/legion/extensions/neo4j/runners/graph_data_science_spec.rb[1:4:3] | passed | 0.00009 seconds |
|
|
29
|
+
./spec/legion/extensions/neo4j/runners/graph_data_science_spec.rb[1:5:1] | passed | 0.00011 seconds |
|
|
30
|
+
./spec/legion/extensions/neo4j/runners/graph_data_science_spec.rb[1:5:2] | passed | 0.0001 seconds |
|
|
31
|
+
./spec/legion/extensions/neo4j/runners/graph_data_science_spec.rb[1:6:1] | passed | 0.00011 seconds |
|
|
32
|
+
./spec/legion/extensions/neo4j/runners/indexes_spec.rb[1:1:1] | passed | 0.00012 seconds |
|
|
33
|
+
./spec/legion/extensions/neo4j/runners/indexes_spec.rb[1:2:1] | passed | 0.00011 seconds |
|
|
34
|
+
./spec/legion/extensions/neo4j/runners/indexes_spec.rb[1:2:2] | passed | 0.00012 seconds |
|
|
35
|
+
./spec/legion/extensions/neo4j/runners/indexes_spec.rb[1:2:3] | passed | 0.0001 seconds |
|
|
36
|
+
./spec/legion/extensions/neo4j/runners/indexes_spec.rb[1:3:1] | passed | 0.00011 seconds |
|
|
37
|
+
./spec/legion/extensions/neo4j/runners/indexes_spec.rb[1:3:2] | passed | 0.00008 seconds |
|
|
38
|
+
./spec/legion/extensions/neo4j/runners/indexes_spec.rb[1:4:1] | passed | 0.00013 seconds |
|
|
39
|
+
./spec/legion/extensions/neo4j/runners/indexes_spec.rb[1:5:1] | passed | 0.00012 seconds |
|
|
40
|
+
./spec/legion/extensions/neo4j/runners/indexes_spec.rb[1:5:2] | passed | 0.0001 seconds |
|
|
41
|
+
./spec/legion/extensions/neo4j/runners/indexes_spec.rb[1:6:1] | passed | 0.0001 seconds |
|
|
42
|
+
./spec/legion/extensions/neo4j/runners/indexes_spec.rb[1:6:2] | passed | 0.0001 seconds |
|
|
43
|
+
./spec/legion/extensions/neo4j/runners/indexes_spec.rb[1:7:1] | passed | 0.00009 seconds |
|
|
44
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:1:1] | passed | 0.00011 seconds |
|
|
45
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:1:2] | passed | 0.00414 seconds |
|
|
46
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:2:1] | passed | 0.00015 seconds |
|
|
47
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:3:1] | passed | 0.00011 seconds |
|
|
48
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:3:2] | passed | 0.0001 seconds |
|
|
49
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:4:1] | passed | 0.00012 seconds |
|
|
50
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:4:2] | passed | 0.00009 seconds |
|
|
51
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:5:1] | passed | 0.00013 seconds |
|
|
52
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:5:2] | passed | 0.00012 seconds |
|
|
53
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:5:3] | passed | 0.0001 seconds |
|
|
54
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:6:1] | passed | 0.00012 seconds |
|
|
55
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:6:2] | passed | 0.0001 seconds |
|
|
56
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:7:1] | passed | 0.00043 seconds |
|
|
57
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:7:2] | passed | 0.00013 seconds |
|
|
58
|
+
./spec/legion/extensions/neo4j/runners/nodes_spec.rb[1:8:1] | passed | 0.00012 seconds |
|
|
59
|
+
./spec/legion/extensions/neo4j/runners/relationships_spec.rb[1:1:1] | passed | 0.00013 seconds |
|
|
60
|
+
./spec/legion/extensions/neo4j/runners/relationships_spec.rb[1:2:1] | passed | 0.0002 seconds |
|
|
61
|
+
./spec/legion/extensions/neo4j/runners/relationships_spec.rb[1:3:1] | passed | 0.00011 seconds |
|
|
62
|
+
./spec/legion/extensions/neo4j/runners/relationships_spec.rb[1:3:2] | passed | 0.00008 seconds |
|
|
63
|
+
./spec/legion/extensions/neo4j/runners/relationships_spec.rb[1:4:1] | passed | 0.00008 seconds |
|
|
64
|
+
./spec/legion/extensions/neo4j/runners/relationships_spec.rb[1:5:1] | passed | 0.00012 seconds |
|
|
65
|
+
./spec/legion/extensions/neo4j/runners/relationships_spec.rb[1:5:2] | passed | 0.0001 seconds |
|
|
66
|
+
./spec/legion/extensions/neo4j/runners/relationships_spec.rb[1:6:1] | passed | 0.00011 seconds |
|
|
67
|
+
./spec/legion/extensions/neo4j/runners/relationships_spec.rb[1:6:2] | passed | 0.00018 seconds |
|
|
68
|
+
./spec/legion/extensions/neo4j/runners/relationships_spec.rb[1:6:3] | passed | 0.00012 seconds |
|
|
69
|
+
./spec/legion/extensions/neo4j/runners/relationships_spec.rb[1:6:4] | passed | 0.00012 seconds |
|
|
70
|
+
./spec/legion/extensions/neo4j/runners/relationships_spec.rb[1:7:1] | passed | 0.00011 seconds |
|
|
71
|
+
./spec/legion/extensions/neo4j/runners/relationships_spec.rb[1:8:1] | passed | 0.00017 seconds |
|
|
72
|
+
./spec/legion/extensions/neo4j/runners/transactions_spec.rb[1:1:1] | passed | 0.00015 seconds |
|
|
73
|
+
./spec/legion/extensions/neo4j/runners/transactions_spec.rb[1:1:2] | passed | 0.00013 seconds |
|
|
74
|
+
./spec/legion/extensions/neo4j/runners/transactions_spec.rb[1:2:1] | passed | 0.00014 seconds |
|
|
75
|
+
./spec/legion/extensions/neo4j/runners/transactions_spec.rb[1:2:2:1] | passed | 0.00014 seconds |
|
|
76
|
+
./spec/legion/extensions/neo4j/runners/transactions_spec.rb[1:3:1] | passed | 0.00014 seconds |
|
|
77
|
+
./spec/legion/extensions/neo4j/runners/transactions_spec.rb[1:4:1] | passed | 0.00019 seconds |
|
|
78
|
+
./spec/legion/extensions/neo4j_spec.rb[1:1] | passed | 0.00002 seconds |
|
|
79
|
+
./spec/legion/extensions/neo4j_spec.rb[1:2] | passed | 0.00002 seconds |
|
|
80
|
+
./spec/legion/extensions/neo4j_spec.rb[1:3] | passed | 0.00002 seconds |
|
data/.rubocop.yml
ADDED
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0] - 2026-05-28
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Initial release
|
|
7
|
+
- Cypher runner (raw query, single result, multi-statement)
|
|
8
|
+
- Nodes runner (find, get, create, update, delete, merge, count, list labels)
|
|
9
|
+
- Relationships runner (find, get, create, update, delete, merge, neighbors, shortest path, list types)
|
|
10
|
+
- Indexes runner (list/create/drop indexes, list/create/drop constraints — uniqueness and existence)
|
|
11
|
+
- Transactions runner (begin, execute, commit, rollback)
|
|
12
|
+
- Admin runner (server info, discovery, databases, procedures, functions, APOC stats)
|
|
13
|
+
- Graph Data Science runner (project/drop graphs, PageRank, Louvain, node similarity)
|
|
14
|
+
- Standalone Client class with read_only guard and configurable database
|
|
15
|
+
- Basic auth (username/password) via Faraday
|
|
16
|
+
- CypherError raised on Neo4j query errors
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# lex-neo4j
|
|
2
|
+
|
|
3
|
+
Legion Extension connecting LegionIO to Neo4j graph database via the HTTP Cypher transaction API. Provides runners for Cypher queries, nodes, relationships, indexes/constraints, transactions, administration, and Graph Data Science (GDS) algorithms.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Legion::Extensions::Neo4j
|
|
9
|
+
├── Runners/
|
|
10
|
+
│ ├── Cypher # Raw query execution, single/multi-statement
|
|
11
|
+
│ ├── Nodes # CRUD + merge, count, labels
|
|
12
|
+
│ ├── Relationships # CRUD + merge, neighbors, shortest path, types
|
|
13
|
+
│ ├── Indexes # Indexes and constraints (uniqueness, existence, fulltext)
|
|
14
|
+
│ ├── Transactions # Explicit transaction lifecycle (begin/execute/commit/rollback)
|
|
15
|
+
│ ├── Admin # Server info, databases, procedures, functions, APOC stats
|
|
16
|
+
│ └── GraphDataScience # GDS graph projections, PageRank, Louvain, node similarity
|
|
17
|
+
├── Helpers/Client # Faraday connection (Basic auth)
|
|
18
|
+
├── Errors # ReadOnlyError, CypherError
|
|
19
|
+
└── Client # Standalone client class
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Key Design Decisions
|
|
23
|
+
|
|
24
|
+
- Uses Neo4j HTTP Cypher Transaction API (`/db/{database}/tx/commit` for auto-commit, `/db/{database}/tx` for explicit transactions)
|
|
25
|
+
- Basic auth with `Authorization: Basic base64(user:pass)` header
|
|
26
|
+
- Default database is `neo4j` (configurable)
|
|
27
|
+
- Default URL is `http://localhost:7474` (configurable)
|
|
28
|
+
- `CypherError` raised when Neo4j returns errors in the response body
|
|
29
|
+
- **Read-Only Guard**: write operations raise `ReadOnlyError` when `read_only: true`
|
|
30
|
+
- Node/relationship IDs use Neo4j 5.x `elementId()` (string format like `4:uuid:offset`)
|
|
31
|
+
- GDS runner assumes the Graph Data Science plugin is installed
|
|
32
|
+
- Admin operations against `system` database where required (e.g., SHOW DATABASES, CREATE DATABASE)
|
|
33
|
+
- Depends on `faraday` (>= 2.0)
|
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 LegionIO
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# lex-neo4j
|
|
2
|
+
|
|
3
|
+
Legion Extension for [Neo4j](https://neo4j.com/) graph database via the HTTP Cypher transaction API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
gem 'lex-neo4j'
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
client = Legion::Extensions::Neo4j::Client.new(
|
|
15
|
+
url: 'http://localhost:7474',
|
|
16
|
+
username: 'neo4j',
|
|
17
|
+
password: 'your-password',
|
|
18
|
+
database: 'neo4j'
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# Raw Cypher
|
|
22
|
+
client.query('MATCH (n:Person) RETURN n LIMIT 10')
|
|
23
|
+
client.query('CREATE (n:Person {name: $name}) RETURN n', parameters: { name: 'Alice' })
|
|
24
|
+
|
|
25
|
+
# Nodes
|
|
26
|
+
client.find_nodes(label: 'Person', properties: { name: 'Alice' })
|
|
27
|
+
client.create_node(label: 'Person', properties: { name: 'Bob', age: 30 })
|
|
28
|
+
client.merge_node(label: 'Person', match_properties: { email: 'bob@example.com' }, on_create: { created_at: Time.now.to_s })
|
|
29
|
+
client.count_nodes(label: 'Person')
|
|
30
|
+
client.list_labels
|
|
31
|
+
|
|
32
|
+
# Relationships
|
|
33
|
+
client.create_relationship(from_id: node1_id, to_id: node2_id, type: 'KNOWS', properties: { since: 2020 })
|
|
34
|
+
client.neighbors(id: node_id, direction: :outgoing, type: 'KNOWS')
|
|
35
|
+
client.shortest_path(from_id: node1_id, to_id: node2_id)
|
|
36
|
+
client.list_relationship_types
|
|
37
|
+
|
|
38
|
+
# Indexes & Constraints
|
|
39
|
+
client.list_indexes
|
|
40
|
+
client.create_index(label: 'Person', properties: ['name', 'email'], name: 'idx_person_name_email')
|
|
41
|
+
client.create_uniqueness_constraint(label: 'Person', property: 'email', name: 'uniq_person_email')
|
|
42
|
+
client.create_existence_constraint(label: 'Person', property: 'name')
|
|
43
|
+
|
|
44
|
+
# Explicit Transactions
|
|
45
|
+
tx = client.begin_transaction
|
|
46
|
+
client.execute_in_transaction(transaction_url: tx[:location], statements: ['CREATE (n:Temp) RETURN n'])
|
|
47
|
+
client.commit_transaction(transaction_url: tx[:location])
|
|
48
|
+
|
|
49
|
+
# Administration
|
|
50
|
+
client.server_info
|
|
51
|
+
client.list_databases
|
|
52
|
+
client.list_procedures
|
|
53
|
+
client.call_procedure(name: 'dbms.components')
|
|
54
|
+
|
|
55
|
+
# Graph Data Science (requires GDS plugin)
|
|
56
|
+
client.project_graph(name: 'social', node_projection: 'Person', relationship_projection: 'KNOWS')
|
|
57
|
+
client.run_pagerank(graph_name: 'social')
|
|
58
|
+
client.run_louvain(graph_name: 'social')
|
|
59
|
+
client.run_node_similarity(graph_name: 'social')
|
|
60
|
+
client.drop_graph(name: 'social')
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Read-Only Mode
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
client = Legion::Extensions::Neo4j::Client.new(
|
|
67
|
+
url: 'http://localhost:7474',
|
|
68
|
+
username: 'neo4j',
|
|
69
|
+
password: 'pass',
|
|
70
|
+
read_only: true
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
client.find_nodes(label: 'Person') # works
|
|
74
|
+
client.create_node(label: 'Person') # raises ReadOnlyError
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Runners
|
|
78
|
+
|
|
79
|
+
| Runner | Purpose |
|
|
80
|
+
|--------|---------|
|
|
81
|
+
| Cypher | Raw query execution, multi-statement batches |
|
|
82
|
+
| Nodes | Node CRUD, merge, count, label listing |
|
|
83
|
+
| Relationships | Relationship CRUD, merge, traversal, shortest path |
|
|
84
|
+
| Indexes | Index and constraint management (range, fulltext, uniqueness, existence) |
|
|
85
|
+
| Transactions | Explicit transaction lifecycle |
|
|
86
|
+
| Admin | Server info, database management, procedures, functions |
|
|
87
|
+
| GraphDataScience | GDS graph projections and algorithms (PageRank, Louvain, similarity) |
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
MIT
|
data/lex-neo4j.gemspec
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/neo4j/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-neo4j'
|
|
7
|
+
spec.version = Legion::Extensions::Neo4j::VERSION
|
|
8
|
+
spec.authors = ['Matthew Iverson']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'LEX Neo4j'
|
|
12
|
+
spec.description = 'Connects LegionIO to Neo4j graph database via HTTP Cypher transaction API'
|
|
13
|
+
spec.homepage = 'https://github.com/LegionIO/lex-neo4j'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
spec.required_ruby_version = '>= 3.4'
|
|
16
|
+
|
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
18
|
+
spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-neo4j'
|
|
19
|
+
spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-neo4j'
|
|
20
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-neo4j'
|
|
21
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-neo4j/issues'
|
|
22
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
23
|
+
|
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
25
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
26
|
+
end
|
|
27
|
+
spec.require_paths = ['lib']
|
|
28
|
+
|
|
29
|
+
spec.add_dependency 'faraday', '>= 2.0'
|
|
30
|
+
spec.add_dependency 'legion-cache', '>= 1.3.11'
|
|
31
|
+
spec.add_dependency 'legion-crypt', '>= 1.4.9'
|
|
32
|
+
spec.add_dependency 'legion-data', '>= 1.4.17'
|
|
33
|
+
spec.add_dependency 'legion-json', '>= 1.2.1'
|
|
34
|
+
spec.add_dependency 'legion-logging', '>= 1.3.2'
|
|
35
|
+
spec.add_dependency 'legion-settings', '>= 1.3.14'
|
|
36
|
+
spec.add_dependency 'legion-transport', '>= 1.3.9'
|
|
37
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/neo4j/helpers/client'
|
|
4
|
+
require 'legion/extensions/neo4j/runners/cypher'
|
|
5
|
+
require 'legion/extensions/neo4j/runners/nodes'
|
|
6
|
+
require 'legion/extensions/neo4j/runners/relationships'
|
|
7
|
+
require 'legion/extensions/neo4j/runners/indexes'
|
|
8
|
+
require 'legion/extensions/neo4j/runners/transactions'
|
|
9
|
+
require 'legion/extensions/neo4j/runners/admin'
|
|
10
|
+
require 'legion/extensions/neo4j/runners/graph_data_science'
|
|
11
|
+
|
|
12
|
+
module Legion
|
|
13
|
+
module Extensions
|
|
14
|
+
module Neo4j
|
|
15
|
+
class Client
|
|
16
|
+
include Helpers::Client
|
|
17
|
+
include Runners::Cypher
|
|
18
|
+
include Runners::Nodes
|
|
19
|
+
include Runners::Relationships
|
|
20
|
+
include Runners::Indexes
|
|
21
|
+
include Runners::Transactions
|
|
22
|
+
include Runners::Admin
|
|
23
|
+
include Runners::GraphDataScience
|
|
24
|
+
|
|
25
|
+
attr_reader :opts
|
|
26
|
+
|
|
27
|
+
def initialize(url: 'http://localhost:7474', username: 'neo4j', password: nil, database: 'neo4j',
|
|
28
|
+
read_only: false, **extra)
|
|
29
|
+
@opts = { url: url, username: username, password: password, database: database,
|
|
30
|
+
read_only: read_only, **extra }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def connection(**override)
|
|
34
|
+
super(**@opts.merge(override.compact))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def execute_cypher(query, parameters: {}, database: nil, **override)
|
|
38
|
+
db = database || @opts[:database]
|
|
39
|
+
super(query, parameters: parameters, database: db, **@opts.merge(override.compact))
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'base64'
|
|
5
|
+
|
|
6
|
+
module Legion
|
|
7
|
+
module Extensions
|
|
8
|
+
module Neo4j
|
|
9
|
+
module Helpers
|
|
10
|
+
module Client
|
|
11
|
+
def connection(url: nil, username: nil, password: nil, _database: 'neo4j', **_opts)
|
|
12
|
+
Faraday.new(url: url) do |conn|
|
|
13
|
+
conn.request :json
|
|
14
|
+
conn.response :json, content_type: /\bjson$/
|
|
15
|
+
conn.headers['Authorization'] = "Basic #{Base64.strict_encode64("#{username}:#{password}")}" if username && password
|
|
16
|
+
conn.headers['Content-Type'] = 'application/json'
|
|
17
|
+
conn.headers['Accept'] = 'application/json'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def execute_cypher(query, parameters: {}, database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
22
|
+
payload = { statements: [{ statement: query, parameters: parameters }] }
|
|
23
|
+
resp = connection(url: url, username: username, password: password)
|
|
24
|
+
.post("/db/#{database}/tx/commit", payload)
|
|
25
|
+
body = resp.body
|
|
26
|
+
raise CypherError, body[:errors].map { |e| e['message'] }.join('; ') if body.is_a?(Hash) && body[:errors] && !body[:errors].empty?
|
|
27
|
+
|
|
28
|
+
body
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/neo4j/helpers/client'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Neo4j
|
|
8
|
+
module Runners
|
|
9
|
+
module Admin
|
|
10
|
+
include Legion::Extensions::Neo4j::Helpers::Client
|
|
11
|
+
|
|
12
|
+
def server_info(url: nil, username: nil, password: nil, **)
|
|
13
|
+
resp = connection(url: url, username: username, password: password).get('/')
|
|
14
|
+
resp.body
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def discovery(url: nil, username: nil, password: nil, **)
|
|
18
|
+
resp = connection(url: url, username: username, password: password).get('/db/data/')
|
|
19
|
+
resp.body
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def list_databases(url: nil, username: nil, password: nil, **)
|
|
23
|
+
execute_cypher('SHOW DATABASES', parameters: {}, database: 'system',
|
|
24
|
+
url: url, username: username, password: password)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def database_info(name: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
28
|
+
execute_cypher('SHOW DATABASE $name', parameters: { name: name }, database: 'system',
|
|
29
|
+
url: url, username: username, password: password)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def create_database(name:, url: nil, username: nil, password: nil, read_only: false, **)
|
|
33
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
34
|
+
|
|
35
|
+
execute_cypher("CREATE DATABASE #{name}", parameters: {}, database: 'system',
|
|
36
|
+
url: url, username: username, password: password)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def drop_database(name:, url: nil, username: nil, password: nil, read_only: false, **)
|
|
40
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
41
|
+
|
|
42
|
+
execute_cypher("DROP DATABASE #{name} IF EXISTS", parameters: {}, database: 'system',
|
|
43
|
+
url: url, username: username, password: password)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def list_procedures(database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
47
|
+
execute_cypher('SHOW PROCEDURES', parameters: {}, database: database,
|
|
48
|
+
url: url, username: username, password: password)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def list_functions(database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
52
|
+
execute_cypher('SHOW FUNCTIONS', parameters: {}, database: database,
|
|
53
|
+
url: url, username: username, password: password)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def call_procedure(name:, args: [], database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
57
|
+
arg_placeholders = args.each_index.map { |i| "$arg#{i}" }.join(', ')
|
|
58
|
+
params = args.each_with_index.to_h { |v, i| ["arg#{i}", v] }
|
|
59
|
+
cypher = "CALL #{name}(#{arg_placeholders})"
|
|
60
|
+
execute_cypher(cypher, parameters: params, database: database,
|
|
61
|
+
url: url, username: username, password: password)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def db_stats(database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
65
|
+
execute_cypher(
|
|
66
|
+
'CALL apoc.meta.stats() YIELD labels, relTypes, nodeCount, relCount ' \
|
|
67
|
+
'RETURN labels, relTypes, nodeCount, relCount',
|
|
68
|
+
parameters: {}, database: database,
|
|
69
|
+
url: url, username: username, password: password
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
74
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/neo4j/helpers/client'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Neo4j
|
|
8
|
+
module Runners
|
|
9
|
+
module Cypher
|
|
10
|
+
include Legion::Extensions::Neo4j::Helpers::Client
|
|
11
|
+
|
|
12
|
+
def query(statement, parameters: {}, database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
13
|
+
execute_cypher(statement, parameters: parameters, database: database,
|
|
14
|
+
url: url, username: username, password: password)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def query_single(statement, parameters: {}, database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
18
|
+
result = execute_cypher(statement, parameters: parameters, database: database,
|
|
19
|
+
url: url, username: username, password: password)
|
|
20
|
+
results = result.dig('results', 0, 'data')
|
|
21
|
+
return nil if results.nil? || results.empty?
|
|
22
|
+
|
|
23
|
+
results.first
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def multi_statement(statements, database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
27
|
+
payload = {
|
|
28
|
+
statements: statements.map do |s|
|
|
29
|
+
if s.is_a?(String)
|
|
30
|
+
{ statement: s, parameters: {} }
|
|
31
|
+
else
|
|
32
|
+
{ statement: s[:statement], parameters: s[:parameters] || {} }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
}
|
|
36
|
+
resp = connection(url: url, username: username, password: password)
|
|
37
|
+
.post("/db/#{database}/tx/commit", payload)
|
|
38
|
+
body = resp.body
|
|
39
|
+
raise CypherError, body[:errors].map { |e| e['message'] }.join('; ') if body.is_a?(Hash) && body[:errors] && !body[:errors].empty?
|
|
40
|
+
|
|
41
|
+
body
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
45
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/neo4j/helpers/client'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Neo4j
|
|
8
|
+
module Runners
|
|
9
|
+
module GraphDataScience
|
|
10
|
+
include Legion::Extensions::Neo4j::Helpers::Client
|
|
11
|
+
|
|
12
|
+
def list_graphs(database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
13
|
+
execute_cypher('CALL gds.graph.list() YIELD graphName, nodeCount, relationshipCount ' \
|
|
14
|
+
'RETURN graphName, nodeCount, relationshipCount',
|
|
15
|
+
parameters: {}, database: database,
|
|
16
|
+
url: url, username: username, password: password)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def project_graph(name:, node_projection:, relationship_projection:, database: 'neo4j',
|
|
20
|
+
url: nil, username: nil, password: nil, read_only: false, **)
|
|
21
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
22
|
+
|
|
23
|
+
execute_cypher('CALL gds.graph.project($name, $nodes, $rels)',
|
|
24
|
+
parameters: { name: name, nodes: node_projection, rels: relationship_projection },
|
|
25
|
+
database: database, url: url, username: username, password: password)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def drop_graph(name:, database: 'neo4j', url: nil, username: nil, password: nil, read_only: false, **)
|
|
29
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
30
|
+
|
|
31
|
+
execute_cypher('CALL gds.graph.drop($name)',
|
|
32
|
+
parameters: { name: name }, database: database,
|
|
33
|
+
url: url, username: username, password: password)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def run_pagerank(graph_name:, write_property: nil, database: 'neo4j', url: nil, username: nil,
|
|
37
|
+
password: nil, read_only: false, **)
|
|
38
|
+
if write_property
|
|
39
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
40
|
+
|
|
41
|
+
execute_cypher('CALL gds.pageRank.write($name, {writeProperty: $prop}) ' \
|
|
42
|
+
'YIELD nodePropertiesWritten, ranIterations',
|
|
43
|
+
parameters: { name: graph_name, prop: write_property },
|
|
44
|
+
database: database, url: url, username: username, password: password)
|
|
45
|
+
else
|
|
46
|
+
execute_cypher('CALL gds.pageRank.stream($name) YIELD nodeId, score ' \
|
|
47
|
+
'RETURN gds.util.asNode(nodeId).name AS name, score ' \
|
|
48
|
+
'ORDER BY score DESC LIMIT 25',
|
|
49
|
+
parameters: { name: graph_name },
|
|
50
|
+
database: database, url: url, username: username, password: password)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def run_louvain(graph_name:, write_property: nil, database: 'neo4j', url: nil, username: nil,
|
|
55
|
+
password: nil, read_only: false, **)
|
|
56
|
+
if write_property
|
|
57
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
58
|
+
|
|
59
|
+
execute_cypher('CALL gds.louvain.write($name, {writeProperty: $prop}) ' \
|
|
60
|
+
'YIELD communityCount, modularity',
|
|
61
|
+
parameters: { name: graph_name, prop: write_property },
|
|
62
|
+
database: database, url: url, username: username, password: password)
|
|
63
|
+
else
|
|
64
|
+
execute_cypher('CALL gds.louvain.stream($name) YIELD nodeId, communityId ' \
|
|
65
|
+
'RETURN gds.util.asNode(nodeId).name AS name, communityId ' \
|
|
66
|
+
'ORDER BY communityId LIMIT 100',
|
|
67
|
+
parameters: { name: graph_name },
|
|
68
|
+
database: database, url: url, username: username, password: password)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def run_node_similarity(graph_name:, limit: 25, database: 'neo4j', url: nil, username: nil,
|
|
73
|
+
password: nil, **)
|
|
74
|
+
execute_cypher('CALL gds.nodeSimilarity.stream($name) YIELD node1, node2, similarity ' \
|
|
75
|
+
'RETURN gds.util.asNode(node1).name AS from, ' \
|
|
76
|
+
'gds.util.asNode(node2).name AS to, similarity ' \
|
|
77
|
+
'ORDER BY similarity DESC LIMIT $limit',
|
|
78
|
+
parameters: { name: graph_name, limit: limit },
|
|
79
|
+
database: database, url: url, username: username, password: password)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
83
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/neo4j/helpers/client'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Neo4j
|
|
8
|
+
module Runners
|
|
9
|
+
module Indexes
|
|
10
|
+
include Legion::Extensions::Neo4j::Helpers::Client
|
|
11
|
+
|
|
12
|
+
def list_indexes(database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
13
|
+
execute_cypher('SHOW INDEXES', parameters: {}, database: database,
|
|
14
|
+
url: url, username: username, password: password)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create_index(label:, properties:, name: nil, database: 'neo4j', url: nil, username: nil, password: nil,
|
|
18
|
+
read_only: false, **)
|
|
19
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
20
|
+
|
|
21
|
+
props = Array(properties).map { |p| "n.#{p}" }.join(', ')
|
|
22
|
+
cypher = 'CREATE INDEX'
|
|
23
|
+
cypher += " #{name}" if name
|
|
24
|
+
cypher += " FOR (n:#{label}) ON (#{props})"
|
|
25
|
+
execute_cypher(cypher, parameters: {}, database: database,
|
|
26
|
+
url: url, username: username, password: password)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def create_fulltext_index(name:, labels:, properties:, database: 'neo4j', url: nil, username: nil,
|
|
30
|
+
password: nil, read_only: false, **)
|
|
31
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
32
|
+
|
|
33
|
+
label_list = Array(labels).map { |l| "'#{l}'" }.join(', ')
|
|
34
|
+
prop_list = Array(properties).map { |p| "'#{p}'" }.join(', ')
|
|
35
|
+
cypher = "CREATE FULLTEXT INDEX #{name} FOR (n:#{label_list}) ON EACH [#{prop_list}]"
|
|
36
|
+
execute_cypher(cypher, parameters: {}, database: database,
|
|
37
|
+
url: url, username: username, password: password)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def drop_index(name:, database: 'neo4j', url: nil, username: nil, password: nil, read_only: false, **)
|
|
41
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
42
|
+
|
|
43
|
+
execute_cypher("DROP INDEX #{name} IF EXISTS", parameters: {}, database: database,
|
|
44
|
+
url: url, username: username, password: password)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def list_constraints(database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
48
|
+
execute_cypher('SHOW CONSTRAINTS', parameters: {}, database: database,
|
|
49
|
+
url: url, username: username, password: password)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def create_uniqueness_constraint(label:, property:, name: nil, database: 'neo4j', url: nil, username: nil,
|
|
53
|
+
password: nil, read_only: false, **)
|
|
54
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
55
|
+
|
|
56
|
+
cypher = 'CREATE CONSTRAINT'
|
|
57
|
+
cypher += " #{name}" if name
|
|
58
|
+
cypher += " FOR (n:#{label}) REQUIRE n.#{property} IS UNIQUE"
|
|
59
|
+
execute_cypher(cypher, parameters: {}, database: database,
|
|
60
|
+
url: url, username: username, password: password)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def create_existence_constraint(label:, property:, name: nil, database: 'neo4j', url: nil, username: nil,
|
|
64
|
+
password: nil, read_only: false, **)
|
|
65
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
66
|
+
|
|
67
|
+
cypher = 'CREATE CONSTRAINT'
|
|
68
|
+
cypher += " #{name}" if name
|
|
69
|
+
cypher += " FOR (n:#{label}) REQUIRE n.#{property} IS NOT NULL"
|
|
70
|
+
execute_cypher(cypher, parameters: {}, database: database,
|
|
71
|
+
url: url, username: username, password: password)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def drop_constraint(name:, database: 'neo4j', url: nil, username: nil, password: nil, read_only: false, **)
|
|
75
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
76
|
+
|
|
77
|
+
execute_cypher("DROP CONSTRAINT #{name} IF EXISTS", parameters: {}, database: database,
|
|
78
|
+
url: url, username: username, password: password)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
82
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/neo4j/helpers/client'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Neo4j
|
|
8
|
+
module Runners
|
|
9
|
+
module Nodes
|
|
10
|
+
include Legion::Extensions::Neo4j::Helpers::Client
|
|
11
|
+
|
|
12
|
+
def find_nodes(label:, properties: {}, limit: 25, database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
13
|
+
where_clause = properties.keys.map { |k| "n.#{k} = $#{k}" }.join(' AND ')
|
|
14
|
+
cypher = "MATCH (n:#{label})"
|
|
15
|
+
cypher += " WHERE #{where_clause}" unless where_clause.empty?
|
|
16
|
+
cypher += ' RETURN n LIMIT $limit'
|
|
17
|
+
execute_cypher(cypher, parameters: properties.merge(limit: limit),
|
|
18
|
+
database: database, url: url, username: username, password: password)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def get_node(id:, database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
22
|
+
execute_cypher('MATCH (n) WHERE elementId(n) = $id RETURN n',
|
|
23
|
+
parameters: { id: id }, database: database,
|
|
24
|
+
url: url, username: username, password: password)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def create_node(label:, properties: {}, database: 'neo4j', url: nil, username: nil, password: nil,
|
|
28
|
+
read_only: false, **)
|
|
29
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
30
|
+
|
|
31
|
+
execute_cypher("CREATE (n:#{label} $props) RETURN n",
|
|
32
|
+
parameters: { props: properties }, database: database,
|
|
33
|
+
url: url, username: username, password: password)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def update_node(id:, properties: {}, database: 'neo4j', url: nil, username: nil, password: nil,
|
|
37
|
+
read_only: false, **)
|
|
38
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
39
|
+
|
|
40
|
+
execute_cypher('MATCH (n) WHERE elementId(n) = $id SET n += $props RETURN n',
|
|
41
|
+
parameters: { id: id, props: properties }, database: database,
|
|
42
|
+
url: url, username: username, password: password)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def delete_node(id:, detach: false, database: 'neo4j', url: nil, username: nil, password: nil,
|
|
46
|
+
read_only: false, **)
|
|
47
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
48
|
+
|
|
49
|
+
delete_keyword = detach ? 'DETACH DELETE' : 'DELETE'
|
|
50
|
+
execute_cypher("MATCH (n) WHERE elementId(n) = $id #{delete_keyword} n",
|
|
51
|
+
parameters: { id: id }, database: database,
|
|
52
|
+
url: url, username: username, password: password)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def merge_node(label:, match_properties: {}, on_create: {}, on_match: {}, database: 'neo4j',
|
|
56
|
+
url: nil, username: nil, password: nil, read_only: false, **)
|
|
57
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
58
|
+
|
|
59
|
+
cypher = "MERGE (n:#{label} $match_props)"
|
|
60
|
+
cypher += ' ON CREATE SET n += $on_create' unless on_create.empty?
|
|
61
|
+
cypher += ' ON MATCH SET n += $on_match' unless on_match.empty?
|
|
62
|
+
cypher += ' RETURN n'
|
|
63
|
+
execute_cypher(cypher,
|
|
64
|
+
parameters: { match_props: match_properties, on_create: on_create, on_match: on_match },
|
|
65
|
+
database: database, url: url, username: username, password: password)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def count_nodes(label: nil, database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
69
|
+
cypher = label ? "MATCH (n:#{label}) RETURN count(n) AS count" : 'MATCH (n) RETURN count(n) AS count'
|
|
70
|
+
execute_cypher(cypher, parameters: {}, database: database,
|
|
71
|
+
url: url, username: username, password: password)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def list_labels(database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
75
|
+
execute_cypher('CALL db.labels() YIELD label RETURN label',
|
|
76
|
+
parameters: {}, database: database,
|
|
77
|
+
url: url, username: username, password: password)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
81
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/neo4j/helpers/client'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Neo4j
|
|
8
|
+
module Runners
|
|
9
|
+
module Relationships
|
|
10
|
+
include Legion::Extensions::Neo4j::Helpers::Client
|
|
11
|
+
|
|
12
|
+
def find_relationships(type:, properties: {}, limit: 25, database: 'neo4j',
|
|
13
|
+
url: nil, username: nil, password: nil, **)
|
|
14
|
+
where_clause = properties.keys.map { |k| "r.#{k} = $#{k}" }.join(' AND ')
|
|
15
|
+
cypher = "MATCH (a)-[r:#{type}]->(b)"
|
|
16
|
+
cypher += " WHERE #{where_clause}" unless where_clause.empty?
|
|
17
|
+
cypher += ' RETURN a, r, b LIMIT $limit'
|
|
18
|
+
execute_cypher(cypher, parameters: properties.merge(limit: limit),
|
|
19
|
+
database: database, url: url, username: username, password: password)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def get_relationship(id:, database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
23
|
+
execute_cypher('MATCH (a)-[r]->(b) WHERE elementId(r) = $id RETURN a, r, b',
|
|
24
|
+
parameters: { id: id }, database: database,
|
|
25
|
+
url: url, username: username, password: password)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def create_relationship(from_id:, to_id:, type:, properties: {}, database: 'neo4j',
|
|
29
|
+
url: nil, username: nil, password: nil, read_only: false, **)
|
|
30
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
31
|
+
|
|
32
|
+
cypher = 'MATCH (a), (b) WHERE elementId(a) = $from_id AND elementId(b) = $to_id ' \
|
|
33
|
+
"CREATE (a)-[r:#{type} $props]->(b) RETURN a, r, b"
|
|
34
|
+
execute_cypher(cypher,
|
|
35
|
+
parameters: { from_id: from_id, to_id: to_id, props: properties },
|
|
36
|
+
database: database, url: url, username: username, password: password)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def update_relationship(id:, properties: {}, database: 'neo4j', url: nil, username: nil, password: nil,
|
|
40
|
+
read_only: false, **)
|
|
41
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
42
|
+
|
|
43
|
+
execute_cypher('MATCH ()-[r]->() WHERE elementId(r) = $id SET r += $props RETURN r',
|
|
44
|
+
parameters: { id: id, props: properties }, database: database,
|
|
45
|
+
url: url, username: username, password: password)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def delete_relationship(id:, database: 'neo4j', url: nil, username: nil, password: nil,
|
|
49
|
+
read_only: false, **)
|
|
50
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
51
|
+
|
|
52
|
+
execute_cypher('MATCH ()-[r]->() WHERE elementId(r) = $id DELETE r',
|
|
53
|
+
parameters: { id: id }, database: database,
|
|
54
|
+
url: url, username: username, password: password)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def merge_relationship(from_id:, to_id:, type:, on_create: {}, on_match: {}, database: 'neo4j',
|
|
58
|
+
url: nil, username: nil, password: nil, read_only: false, **)
|
|
59
|
+
raise ReadOnlyError, 'Write operations disabled (read_only mode)' if read_only
|
|
60
|
+
|
|
61
|
+
cypher = 'MATCH (a), (b) WHERE elementId(a) = $from_id AND elementId(b) = $to_id ' \
|
|
62
|
+
"MERGE (a)-[r:#{type}]->(b)"
|
|
63
|
+
cypher += ' ON CREATE SET r += $on_create' unless on_create.empty?
|
|
64
|
+
cypher += ' ON MATCH SET r += $on_match' unless on_match.empty?
|
|
65
|
+
cypher += ' RETURN a, r, b'
|
|
66
|
+
execute_cypher(cypher,
|
|
67
|
+
parameters: { from_id: from_id, to_id: to_id, on_create: on_create, on_match: on_match },
|
|
68
|
+
database: database, url: url, username: username, password: password)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def list_relationship_types(database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
72
|
+
execute_cypher('CALL db.relationshipTypes() YIELD relationshipType RETURN relationshipType',
|
|
73
|
+
parameters: {}, database: database,
|
|
74
|
+
url: url, username: username, password: password)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def neighbors(id:, direction: :both, type: nil, limit: 25, database: 'neo4j',
|
|
78
|
+
url: nil, username: nil, password: nil, **)
|
|
79
|
+
rel = type ? ":#{type}" : ''
|
|
80
|
+
pattern = case direction
|
|
81
|
+
when :outgoing then "(a)-[r#{rel}]->(b)"
|
|
82
|
+
when :incoming then "(a)<-[r#{rel}]-(b)"
|
|
83
|
+
else "(a)-[r#{rel}]-(b)"
|
|
84
|
+
end
|
|
85
|
+
cypher = "MATCH #{pattern} WHERE elementId(a) = $id RETURN b, r LIMIT $limit"
|
|
86
|
+
execute_cypher(cypher, parameters: { id: id, limit: limit },
|
|
87
|
+
database: database, url: url, username: username, password: password)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def shortest_path(from_id:, to_id:, max_depth: 15, database: 'neo4j',
|
|
91
|
+
url: nil, username: nil, password: nil, **)
|
|
92
|
+
cypher = 'MATCH (a), (b) WHERE elementId(a) = $from_id AND elementId(b) = $to_id ' \
|
|
93
|
+
"MATCH p = shortestPath((a)-[*..#{max_depth}]-(b)) RETURN p"
|
|
94
|
+
execute_cypher(cypher, parameters: { from_id: from_id, to_id: to_id },
|
|
95
|
+
database: database, url: url, username: username, password: password)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
99
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/neo4j/helpers/client'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Neo4j
|
|
8
|
+
module Runners
|
|
9
|
+
module Transactions
|
|
10
|
+
include Legion::Extensions::Neo4j::Helpers::Client
|
|
11
|
+
|
|
12
|
+
def begin_transaction(database: 'neo4j', url: nil, username: nil, password: nil, **)
|
|
13
|
+
resp = connection(url: url, username: username, password: password)
|
|
14
|
+
.post("/db/#{database}/tx", { statements: [] })
|
|
15
|
+
location = resp.headers&.[]('location')
|
|
16
|
+
{ body: resp.body, location: location }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def execute_in_transaction(transaction_url:, statements:, url: nil, username: nil, password: nil, **)
|
|
20
|
+
payload = {
|
|
21
|
+
statements: statements.map do |s|
|
|
22
|
+
if s.is_a?(String)
|
|
23
|
+
{ statement: s, parameters: {} }
|
|
24
|
+
else
|
|
25
|
+
{ statement: s[:statement], parameters: s[:parameters] || {} }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
}
|
|
29
|
+
resp = connection(url: url, username: username, password: password)
|
|
30
|
+
.post(transaction_url, payload)
|
|
31
|
+
body = resp.body
|
|
32
|
+
raise CypherError, body[:errors].map { |e| e['message'] }.join('; ') if body.is_a?(Hash) && body[:errors] && !body[:errors].empty?
|
|
33
|
+
|
|
34
|
+
body
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def commit_transaction(transaction_url:, url: nil, username: nil, password: nil, **)
|
|
38
|
+
resp = connection(url: url, username: username, password: password)
|
|
39
|
+
.post("#{transaction_url}/commit", { statements: [] })
|
|
40
|
+
body = resp.body
|
|
41
|
+
raise CypherError, body[:errors].map { |e| e['message'] }.join('; ') if body.is_a?(Hash) && body[:errors] && !body[:errors].empty?
|
|
42
|
+
|
|
43
|
+
body
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def rollback_transaction(transaction_url:, url: nil, username: nil, password: nil, **)
|
|
47
|
+
connection(url: url, username: username, password: password).delete(transaction_url)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
51
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/neo4j/version'
|
|
4
|
+
require 'legion/extensions/neo4j/errors'
|
|
5
|
+
require 'legion/extensions/neo4j/helpers/client'
|
|
6
|
+
require 'legion/extensions/neo4j/runners/cypher'
|
|
7
|
+
require 'legion/extensions/neo4j/runners/nodes'
|
|
8
|
+
require 'legion/extensions/neo4j/runners/relationships'
|
|
9
|
+
require 'legion/extensions/neo4j/runners/indexes'
|
|
10
|
+
require 'legion/extensions/neo4j/runners/transactions'
|
|
11
|
+
require 'legion/extensions/neo4j/runners/admin'
|
|
12
|
+
require 'legion/extensions/neo4j/runners/graph_data_science'
|
|
13
|
+
require 'legion/extensions/neo4j/client'
|
|
14
|
+
|
|
15
|
+
module Legion
|
|
16
|
+
module Extensions
|
|
17
|
+
module Neo4j
|
|
18
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core, false
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-neo4j
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Matthew Iverson
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: faraday
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: legion-cache
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 1.3.11
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 1.3.11
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: legion-crypt
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 1.4.9
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 1.4.9
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: legion-data
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 1.4.17
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 1.4.17
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: legion-json
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: 1.2.1
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 1.2.1
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: legion-logging
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: 1.3.2
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: 1.3.2
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: legion-settings
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: 1.3.14
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: 1.3.14
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: legion-transport
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: 1.3.9
|
|
117
|
+
type: :runtime
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: 1.3.9
|
|
124
|
+
description: Connects LegionIO to Neo4j graph database via HTTP Cypher transaction
|
|
125
|
+
API
|
|
126
|
+
email:
|
|
127
|
+
- matthewdiverson@gmail.com
|
|
128
|
+
executables: []
|
|
129
|
+
extensions: []
|
|
130
|
+
extra_rdoc_files: []
|
|
131
|
+
files:
|
|
132
|
+
- ".github/CODEOWNERS"
|
|
133
|
+
- ".github/dependabot.yml"
|
|
134
|
+
- ".github/workflows/ci.yml"
|
|
135
|
+
- ".gitignore"
|
|
136
|
+
- ".rspec"
|
|
137
|
+
- ".rspec_status"
|
|
138
|
+
- ".rubocop.yml"
|
|
139
|
+
- CHANGELOG.md
|
|
140
|
+
- CLAUDE.md
|
|
141
|
+
- Gemfile
|
|
142
|
+
- LICENSE
|
|
143
|
+
- README.md
|
|
144
|
+
- lex-neo4j.gemspec
|
|
145
|
+
- lib/legion/extensions/neo4j.rb
|
|
146
|
+
- lib/legion/extensions/neo4j/client.rb
|
|
147
|
+
- lib/legion/extensions/neo4j/errors.rb
|
|
148
|
+
- lib/legion/extensions/neo4j/helpers/client.rb
|
|
149
|
+
- lib/legion/extensions/neo4j/runners/admin.rb
|
|
150
|
+
- lib/legion/extensions/neo4j/runners/cypher.rb
|
|
151
|
+
- lib/legion/extensions/neo4j/runners/graph_data_science.rb
|
|
152
|
+
- lib/legion/extensions/neo4j/runners/indexes.rb
|
|
153
|
+
- lib/legion/extensions/neo4j/runners/nodes.rb
|
|
154
|
+
- lib/legion/extensions/neo4j/runners/relationships.rb
|
|
155
|
+
- lib/legion/extensions/neo4j/runners/transactions.rb
|
|
156
|
+
- lib/legion/extensions/neo4j/version.rb
|
|
157
|
+
homepage: https://github.com/LegionIO/lex-neo4j
|
|
158
|
+
licenses:
|
|
159
|
+
- MIT
|
|
160
|
+
metadata:
|
|
161
|
+
homepage_uri: https://github.com/LegionIO/lex-neo4j
|
|
162
|
+
source_code_uri: https://github.com/LegionIO/lex-neo4j
|
|
163
|
+
documentation_uri: https://github.com/LegionIO/lex-neo4j
|
|
164
|
+
changelog_uri: https://github.com/LegionIO/lex-neo4j
|
|
165
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-neo4j/issues
|
|
166
|
+
rubygems_mfa_required: 'true'
|
|
167
|
+
rdoc_options: []
|
|
168
|
+
require_paths:
|
|
169
|
+
- lib
|
|
170
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
171
|
+
requirements:
|
|
172
|
+
- - ">="
|
|
173
|
+
- !ruby/object:Gem::Version
|
|
174
|
+
version: '3.4'
|
|
175
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
176
|
+
requirements:
|
|
177
|
+
- - ">="
|
|
178
|
+
- !ruby/object:Gem::Version
|
|
179
|
+
version: '0'
|
|
180
|
+
requirements: []
|
|
181
|
+
rubygems_version: 3.6.9
|
|
182
|
+
specification_version: 4
|
|
183
|
+
summary: LEX Neo4j
|
|
184
|
+
test_files: []
|