Skip to content

Commit f168796

Browse files
authored
Merge pull request #7 from doximity/th/th/produce-consume-trained-at-mofonetprofiles-161
Allow Relationships to have attributes that differentiate them
2 parents 2df0560 + 3fd0977 commit f168796

File tree

9 files changed

+114
-9
lines changed

9 files changed

+114
-9
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
Changelog
22
=========
33

4+
## v1.1.0 07/26/2022
5+
* Adds ability for relationships to function with a primary key attr
6+
47
## v1.0.2 07/19/2022
58
* Adds an access_mode setting to cypher queries
69

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
neo4j-http (1.0.2)
4+
neo4j-http (1.1.0)
55
activesupport (>= 5.2)
66
faraday (< 2)
77
faraday-retry

lib/neo4j/http/client.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module Http
55
class Client
66
CYPHER_CLIENT_METHODS = %i[execute_cypher].freeze
77
NODE_CLIENT_METHODS = %i[delete_node find_node_by find_nodes_by upsert_node].freeze
8-
RELATIONSHIP_CLIENT_METHODS = %i[delete_relationship upsert_relationship].freeze
8+
RELATIONSHIP_CLIENT_METHODS = %i[delete_relationship upsert_relationship delete_relationship_on_primary_key].freeze
99
CLIENT_METHODS = (CYPHER_CLIENT_METHODS + NODE_CLIENT_METHODS + RELATIONSHIP_CLIENT_METHODS).freeze
1010

1111
class << self

lib/neo4j/http/node.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ module Neo4j
44
module Http
55
class Node < ObjectWrapper
66
DEFAULT_PRIMARY_KEY_NAME = "uuid"
7-
def initialize(label:, graph_node_primary_key_name: DEFAULT_PRIMARY_KEY_NAME, **attributes)
7+
def initialize(label:, primary_key_name: DEFAULT_PRIMARY_KEY_NAME, **attributes)
88
super
9+
@key_value = @attributes.delete(key_name)
910
end
1011
end
1112
end

lib/neo4j/http/object_wrapper.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@ class ObjectWrapper
99
:label,
1010
:original_attributes
1111

12-
def initialize(label:, graph_node_primary_key_name: nil, **attributes)
12+
def initialize(label:, primary_key_name: nil, **attributes)
1313
@original_attributes = (attributes || {}).with_indifferent_access
1414
@attributes = original_attributes.dup.with_indifferent_access
15-
@key_name = graph_node_primary_key_name
16-
@key_value = @attributes.delete(key_name)
15+
@key_name = primary_key_name
1716
@label = label
1817
end
1918

lib/neo4j/http/relationship.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
module Neo4j
44
module Http
55
class Relationship < ObjectWrapper
6+
def initialize(label:, primary_key_name: nil, **attributes)
7+
super
8+
@key_value = @attributes.dig(key_name)
9+
end
610
end
711
end
812
end

lib/neo4j/http/relationship_client.rb

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def upsert_relationship(relationship:, from:, to:, create_nodes: false)
4343
results&.first
4444
end
4545

46-
def find_relationship(from:, relationship:, to:)
46+
def find_relationships(from:, relationship:, to:)
4747
from_match_clause = build_match_selector(:from, from)
4848
to_match_clause = build_match_selector(:to, to)
4949
relationship_clause = build_match_selector(:relationship, relationship)
@@ -52,13 +52,17 @@ def find_relationship(from:, relationship:, to:)
5252
RETURN from, to, relationship
5353
CYPHER
5454

55-
results = @cypher_client.execute_cypher(
55+
@cypher_client.execute_cypher(
5656
cypher,
5757
from: from,
5858
to: to,
5959
relationship: relationship,
6060
access_mode: "READ"
6161
)
62+
end
63+
64+
def find_relationship(from:, relationship:, to:)
65+
results = find_relationships(from: from, to: to, relationship: relationship)
6266
results&.first
6367
end
6468

@@ -72,6 +76,7 @@ def delete_relationship(relationship:, from:, to:)
7276
from_selector = build_match_selector(:from, from)
7377
to_selector = build_match_selector(:to, to)
7478
relationship_selector = build_match_selector(:relationship, relationship)
79+
7580
cypher = <<-CYPHER
7681
MATCH (#{from_selector}) - [#{relationship_selector}] - (#{to_selector})
7782
WITH from, to, relationship
@@ -82,6 +87,23 @@ def delete_relationship(relationship:, from:, to:)
8287
results = @cypher_client.execute_cypher(cypher, from: from, to: to)
8388
results&.first
8489
end
90+
91+
def delete_relationship_on_primary_key(relationship:)
92+
# protection against mass deletion of relationships
93+
return if relationship.key_name.nil?
94+
95+
relationship_selector = build_match_selector(:relationship, relationship)
96+
97+
cypher = <<-CYPHER
98+
MATCH () - [#{relationship_selector}] - ()
99+
WITH relationship
100+
DELETE relationship
101+
RETURN relationship
102+
CYPHER
103+
104+
results = @cypher_client.execute_cypher(cypher, relationship: relationship)
105+
results&.first
106+
end
85107
end
86108
end
87109
end

lib/neo4j/http/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module Neo4j
22
module Http
3-
VERSION = "1.0.2"
3+
VERSION = "1.1.0"
44
end
55
end

spec/neo4j/http/relationship_client_spec.rb

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,46 @@
6565
end
6666

6767
it "updates attributes on an existing relationship" do
68+
create_node(from)
69+
create_node(to)
70+
71+
relationship = Neo4j::Http::Relationship.new(label: "KNOWS", uuid: "RelationshipUuid", age: 21)
72+
create_relationship(from, relationship, to)
73+
74+
updated_relationship = Neo4j::Http::Relationship.new(label: "KNOWS", uuid: "RelationshipUuid", age: 33)
75+
result = create_relationship(from, updated_relationship, to)
76+
77+
expect(result["relationship"].keys).to eq(%w[uuid age _neo4j_meta_data])
78+
expect(result["relationship"]["uuid"]).to eq("RelationshipUuid")
79+
expect(result["relationship"]["age"]).to eq(33)
80+
81+
rel = Neo4j::Http::Relationship.new(label: "KNOWS")
82+
relationships = client.find_relationships(relationship: rel, from: from, to: to)
83+
expect(relationships.count).to eq(1)
84+
end
85+
86+
it "allows relationships with same labels between same nodes if primary key is set and different" do
87+
create_node(from)
88+
create_node(to)
89+
90+
relationship_friend = Neo4j::Http::Relationship.new(label: "KNOWS", primary_key_name: "uuid", uuid: "FriendUuid", how: "friend")
91+
edge_a = create_relationship(from, relationship_friend, to)
92+
93+
relationship_colleague = Neo4j::Http::Relationship.new(label: "KNOWS", primary_key_name: "uuid", uuid: "ColleagueUuid", how: "colleague")
94+
edge_b = create_relationship(from, relationship_colleague, to)
95+
96+
expect(edge_a["relationship"]["uuid"]).to eq("FriendUuid")
97+
expect(edge_a["relationship"]["how"]).to eq("friend")
98+
99+
expect(edge_b["relationship"]["uuid"]).to eq("ColleagueUuid")
100+
expect(edge_b["relationship"]["how"]).to eq("colleague")
101+
102+
result = client.find_relationships(relationship: relationship, from: from, to: to)
103+
104+
expect(result.count).to eq(2)
105+
expect(result[0]["from"]["uuid"]).to eq(result[1]["from"]["uuid"])
106+
expect(result[0]["to"]["uuid"]).to eq(result[1]["to"]["uuid"])
107+
expect(result[0]["relationship"]["how"]).not_to eq(result[1]["relationship"]["how"])
68108
end
69109
end
70110

@@ -99,6 +139,38 @@
99139
end
100140
end
101141

142+
describe "delete_relationship_on_primary_key" do
143+
it "removes the correct relationship" do
144+
relationship1 = Neo4j::Http::Relationship.new(label: "KNOWS", primary_key_name: "how", how: "friend")
145+
relationship2 = Neo4j::Http::Relationship.new(label: "KNOWS", primary_key_name: "how", how: "colleague")
146+
client.upsert_relationship(relationship: relationship1, from: from, to: to, create_nodes: true)
147+
client.upsert_relationship(relationship: relationship2, from: from, to: to, create_nodes: true)
148+
149+
expect(client.find_relationships(relationship: relationship, from: from, to: to).count).to eq(2)
150+
151+
result = client.delete_relationship_on_primary_key(relationship: relationship2)
152+
expect(result.keys).to eq(["relationship"])
153+
154+
rels = client.find_relationships(relationship: relationship, from: from, to: to)
155+
expect(rels.count).to eq(1)
156+
expect(rels.first["relationship"]["how"]).to eq("friend")
157+
end
158+
159+
it "doesn't delete if primary key is missing" do
160+
relationship1 = Neo4j::Http::Relationship.new(label: "KNOWS", primary_key_name: "how", how: "friend")
161+
relationship2 = Neo4j::Http::Relationship.new(label: "KNOWS", primary_key_name: "how", how: "colleague")
162+
client.upsert_relationship(relationship: relationship1, from: from, to: to, create_nodes: true)
163+
client.upsert_relationship(relationship: relationship2, from: from, to: to, create_nodes: true)
164+
165+
expect(client.find_relationships(relationship: relationship, from: from, to: to).count).to eq(2)
166+
167+
result = client.delete_relationship_on_primary_key(relationship: relationship)
168+
expect(result).to be_nil
169+
170+
expect(client.find_relationships(relationship: relationship, from: from, to: to).count).to eq(2)
171+
end
172+
end
173+
102174
def verify_relationship(from, relationship, to)
103175
results = Neo4j::Http::CypherClient.default.execute_cypher(
104176
"MATCH (from:Bot {uuid: $from})-[relationship:#{relationship}]-(to:Bot {uuid: $to})
@@ -116,4 +188,8 @@ def verify_relationship(from, relationship, to)
116188
def create_node(node)
117189
Neo4j::Http::NodeClient.default.upsert_node(node)
118190
end
191+
192+
def create_relationship(from, relationship, to)
193+
Neo4j::Http::RelationshipClient.default.upsert_relationship(from: from, relationship: relationship, to: to)
194+
end
119195
end

0 commit comments

Comments
 (0)