defmodule RedisGraph.Relationship do
@moduledoc """
An Relationship component relating two Nodes in a Graph.
Relationships must have a source node and destination node, describing a relationship
between two entities in a Graph. Nodes can be represented either as
Node structures(%RedisGraph.Node{}) or id(integer) of respective node.
Relationships must have a type which identifies the type of relationship being
described between entities.
Relationships, like Nodes, may also have properties, a map of values which describe
attributes of the relationship between the associated Nodes.
"""
alias RedisGraph.Util
@type t() :: %__MODULE__{
id: integer(),
alias: atom(),
src_node: Node.t() | number(),
dest_node: Node.t() | number(),
type: String.t(),
properties: %{optional(String.t()) => any()}
}
@enforce_keys [:src_node, :dest_node, :type]
defstruct [:id, :alias, :src_node, :dest_node, :type, properties: %{}]
@doc """
Create a new Relationship from provided argument.
Argument should be a map and key: `type` with value of String type is required.
## Example
```
alias RedisGraph.{Node, Relationship}
john = Node.new(%{
labels: ["person"],
properties: %{
name: "John Doe"
}
})
bob = Node.new(%{
labels: ["person"],
properties: %{
name: "Bob Nilson"
}
})
relationship = Relationship.new(%{
src_node: john,
dest_node: bob,
type: "friend",
properties: %{
best_friend: true
}
})
```
"""
@spec new(map()) :: t()
def new(%{type: type} = map) when is_binary(type) do
relationship = struct(__MODULE__, map)
if(is_nil(relationship.alias), do: set_alias_if_nil(relationship), else: relationship)
end
@spec set_alias_if_nil(t()) :: t()
def set_alias_if_nil(relationship) do
if is_nil(relationship.alias) do
alias = Util.random_string() |> String.to_atom()
%{relationship | alias: alias}
else
relationship
end
end
@doc """
Compare two relationships with respect to equality.
Comparison logic:
* If the ids differ, returns ``false``
* If the source nodes differ, returns ``false``
* If the destination nodes differ, returns ``false``
* If the types differ, returns ``false``
* If the properties differ, returns ``false``
* Otherwise returns `true`
"""
@spec compare(t(), t()) :: boolean()
def compare(left, right) do
cond do
left.id != right.id -> false
left.src_node != right.src_node -> false
left.dest_node != right.dest_node -> false
left.type != right.type -> false
map_size(left.properties) != map_size(right.properties) -> false
not Map.equal?(left.properties, right.properties) -> false
true -> true
end
end
end