Skip to content

Commit

Permalink
Merge pull request #7 from doximity/th/th/produce-consume-trained-at-…
Browse files Browse the repository at this point in the history
…mofonetprofiles-161

Allow Relationships to have attributes that differentiate them
  • Loading branch information
tesshead-dox authored Jul 26, 2022
2 parents 2df0560 + 3fd0977 commit f168796
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 9 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
Changelog
=========

## v1.1.0 07/26/2022
* Adds ability for relationships to function with a primary key attr

## v1.0.2 07/19/2022
* Adds an access_mode setting to cypher queries

Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
neo4j-http (1.0.2)
neo4j-http (1.1.0)
activesupport (>= 5.2)
faraday (< 2)
faraday-retry
Expand Down
2 changes: 1 addition & 1 deletion lib/neo4j/http/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Http
class Client
CYPHER_CLIENT_METHODS = %i[execute_cypher].freeze
NODE_CLIENT_METHODS = %i[delete_node find_node_by find_nodes_by upsert_node].freeze
RELATIONSHIP_CLIENT_METHODS = %i[delete_relationship upsert_relationship].freeze
RELATIONSHIP_CLIENT_METHODS = %i[delete_relationship upsert_relationship delete_relationship_on_primary_key].freeze
CLIENT_METHODS = (CYPHER_CLIENT_METHODS + NODE_CLIENT_METHODS + RELATIONSHIP_CLIENT_METHODS).freeze

class << self
Expand Down
3 changes: 2 additions & 1 deletion lib/neo4j/http/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ module Neo4j
module Http
class Node < ObjectWrapper
DEFAULT_PRIMARY_KEY_NAME = "uuid"
def initialize(label:, graph_node_primary_key_name: DEFAULT_PRIMARY_KEY_NAME, **attributes)
def initialize(label:, primary_key_name: DEFAULT_PRIMARY_KEY_NAME, **attributes)
super
@key_value = @attributes.delete(key_name)
end
end
end
Expand Down
5 changes: 2 additions & 3 deletions lib/neo4j/http/object_wrapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ class ObjectWrapper
:label,
:original_attributes

def initialize(label:, graph_node_primary_key_name: nil, **attributes)
def initialize(label:, primary_key_name: nil, **attributes)
@original_attributes = (attributes || {}).with_indifferent_access
@attributes = original_attributes.dup.with_indifferent_access
@key_name = graph_node_primary_key_name
@key_value = @attributes.delete(key_name)
@key_name = primary_key_name
@label = label
end

Expand Down
4 changes: 4 additions & 0 deletions lib/neo4j/http/relationship.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
module Neo4j
module Http
class Relationship < ObjectWrapper
def initialize(label:, primary_key_name: nil, **attributes)
super
@key_value = @attributes.dig(key_name)
end
end
end
end
26 changes: 24 additions & 2 deletions lib/neo4j/http/relationship_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def upsert_relationship(relationship:, from:, to:, create_nodes: false)
results&.first
end

def find_relationship(from:, relationship:, to:)
def find_relationships(from:, relationship:, to:)
from_match_clause = build_match_selector(:from, from)
to_match_clause = build_match_selector(:to, to)
relationship_clause = build_match_selector(:relationship, relationship)
Expand All @@ -52,13 +52,17 @@ def find_relationship(from:, relationship:, to:)
RETURN from, to, relationship
CYPHER

results = @cypher_client.execute_cypher(
@cypher_client.execute_cypher(
cypher,
from: from,
to: to,
relationship: relationship,
access_mode: "READ"
)
end

def find_relationship(from:, relationship:, to:)
results = find_relationships(from: from, to: to, relationship: relationship)
results&.first
end

Expand All @@ -72,6 +76,7 @@ def delete_relationship(relationship:, from:, to:)
from_selector = build_match_selector(:from, from)
to_selector = build_match_selector(:to, to)
relationship_selector = build_match_selector(:relationship, relationship)

cypher = <<-CYPHER
MATCH (#{from_selector}) - [#{relationship_selector}] - (#{to_selector})
WITH from, to, relationship
Expand All @@ -82,6 +87,23 @@ def delete_relationship(relationship:, from:, to:)
results = @cypher_client.execute_cypher(cypher, from: from, to: to)
results&.first
end

def delete_relationship_on_primary_key(relationship:)
# protection against mass deletion of relationships
return if relationship.key_name.nil?

relationship_selector = build_match_selector(:relationship, relationship)

cypher = <<-CYPHER
MATCH () - [#{relationship_selector}] - ()
WITH relationship
DELETE relationship
RETURN relationship
CYPHER

results = @cypher_client.execute_cypher(cypher, relationship: relationship)
results&.first
end
end
end
end
2 changes: 1 addition & 1 deletion lib/neo4j/http/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Neo4j
module Http
VERSION = "1.0.2"
VERSION = "1.1.0"
end
end
76 changes: 76 additions & 0 deletions spec/neo4j/http/relationship_client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,46 @@
end

it "updates attributes on an existing relationship" do
create_node(from)
create_node(to)

relationship = Neo4j::Http::Relationship.new(label: "KNOWS", uuid: "RelationshipUuid", age: 21)
create_relationship(from, relationship, to)

updated_relationship = Neo4j::Http::Relationship.new(label: "KNOWS", uuid: "RelationshipUuid", age: 33)
result = create_relationship(from, updated_relationship, to)

expect(result["relationship"].keys).to eq(%w[uuid age _neo4j_meta_data])
expect(result["relationship"]["uuid"]).to eq("RelationshipUuid")
expect(result["relationship"]["age"]).to eq(33)

rel = Neo4j::Http::Relationship.new(label: "KNOWS")
relationships = client.find_relationships(relationship: rel, from: from, to: to)
expect(relationships.count).to eq(1)
end

it "allows relationships with same labels between same nodes if primary key is set and different" do
create_node(from)
create_node(to)

relationship_friend = Neo4j::Http::Relationship.new(label: "KNOWS", primary_key_name: "uuid", uuid: "FriendUuid", how: "friend")
edge_a = create_relationship(from, relationship_friend, to)

relationship_colleague = Neo4j::Http::Relationship.new(label: "KNOWS", primary_key_name: "uuid", uuid: "ColleagueUuid", how: "colleague")
edge_b = create_relationship(from, relationship_colleague, to)

expect(edge_a["relationship"]["uuid"]).to eq("FriendUuid")
expect(edge_a["relationship"]["how"]).to eq("friend")

expect(edge_b["relationship"]["uuid"]).to eq("ColleagueUuid")
expect(edge_b["relationship"]["how"]).to eq("colleague")

result = client.find_relationships(relationship: relationship, from: from, to: to)

expect(result.count).to eq(2)
expect(result[0]["from"]["uuid"]).to eq(result[1]["from"]["uuid"])
expect(result[0]["to"]["uuid"]).to eq(result[1]["to"]["uuid"])
expect(result[0]["relationship"]["how"]).not_to eq(result[1]["relationship"]["how"])
end
end

Expand Down Expand Up @@ -99,6 +139,38 @@
end
end

describe "delete_relationship_on_primary_key" do
it "removes the correct relationship" do
relationship1 = Neo4j::Http::Relationship.new(label: "KNOWS", primary_key_name: "how", how: "friend")
relationship2 = Neo4j::Http::Relationship.new(label: "KNOWS", primary_key_name: "how", how: "colleague")
client.upsert_relationship(relationship: relationship1, from: from, to: to, create_nodes: true)
client.upsert_relationship(relationship: relationship2, from: from, to: to, create_nodes: true)

expect(client.find_relationships(relationship: relationship, from: from, to: to).count).to eq(2)

result = client.delete_relationship_on_primary_key(relationship: relationship2)
expect(result.keys).to eq(["relationship"])

rels = client.find_relationships(relationship: relationship, from: from, to: to)
expect(rels.count).to eq(1)
expect(rels.first["relationship"]["how"]).to eq("friend")
end

it "doesn't delete if primary key is missing" do
relationship1 = Neo4j::Http::Relationship.new(label: "KNOWS", primary_key_name: "how", how: "friend")
relationship2 = Neo4j::Http::Relationship.new(label: "KNOWS", primary_key_name: "how", how: "colleague")
client.upsert_relationship(relationship: relationship1, from: from, to: to, create_nodes: true)
client.upsert_relationship(relationship: relationship2, from: from, to: to, create_nodes: true)

expect(client.find_relationships(relationship: relationship, from: from, to: to).count).to eq(2)

result = client.delete_relationship_on_primary_key(relationship: relationship)
expect(result).to be_nil

expect(client.find_relationships(relationship: relationship, from: from, to: to).count).to eq(2)
end
end

def verify_relationship(from, relationship, to)
results = Neo4j::Http::CypherClient.default.execute_cypher(
"MATCH (from:Bot {uuid: $from})-[relationship:#{relationship}]-(to:Bot {uuid: $to})
Expand All @@ -116,4 +188,8 @@ def verify_relationship(from, relationship, to)
def create_node(node)
Neo4j::Http::NodeClient.default.upsert_node(node)
end

def create_relationship(from, relationship, to)
Neo4j::Http::RelationshipClient.default.upsert_relationship(from: from, relationship: relationship, to: to)
end
end

0 comments on commit f168796

Please sign in to comment.