This library combines the power of Ecto with the Bolt protocol provided by Bolt Sips
You can use the whole ecto suite you love for validations and structures plus an elegant DSL to retrieve data from Neo4j.
All Ex4j.Node
have Ecto.Schema
, Ecto.Changeset
and Ex4j.Cypher
automatically imported for convenience.
The package can be installed by adding ex4j
to your list of dependencies in mix.exs
:
def deps do
[
{:ex4j, "~> 0.1.0"}
]
end
Add the connection settings
config :ex4j, Bolt,
url: "bolt://localhost:7687",
basic_auth: [username: "neo4j", password: "zEb0zryxK62NNRXKWxJKd7qeEFkO3mLIgcGwuUA4lvg"],
pool_size: 10,
ssl: false
The docs can be found at https://hexdocs.pm/ex4j.
All the entities you query must have a schema Node.
like below associated in your code:
defmodule Node.User do
use Ex4j.Node
graph do
field(:name, :string)
field(:age, :integer)
field(:email, :string)
end
end
defmodule Node.Has do
use Ex4j.Node
graph do
field(:date, :utc_datetime_usec)
end
end
defmodule Node.Comment do
use Ex4j.Node
graph do
field(:content, :string)
end
end
The module you intend to use for queries must use Ex4j.Cypher
:
defmodule App do
use Ex4j.Cypher
def execute do
match(Node.User, as: :u)
|> return(:u)
|> run()
end
end
iex> App.execute()
{:ok, [
%{"u" => %Node.User{uuid: nil, name: "Tiago", age: 38, email: nil}},
%{"u" => %Node.User{uuid: nil, name: "Davi", age: 35, email: nil}}
]}
defmodule App do
use Ex4j.Cypher
def execute do
match(Node.User, as: :u)
|> vertex(Node.Comment, as: :c)
|> edge(Node.Has, as: :h, from: :u, to: :c, type: :out)
|> where(:u, "u.age IN [35,38] AND u.name CONTAINS 'Ti'")
|> where(:c, "c.content CONTAINS 'Article'")
|> where(:h, "h.date > date('2023-12-15')")
|> return(:u)
|> return(:c)
|> run()
end
end
iex> User.execute()
{:ok, [
%{
"c" => %Node.Comment{uuid: nil, content: "Tiago's Comment"},
"u" => %Node.User{uuid: nil, name: "Tiago", age: 38, email: nil}
},
%{
"c" => %Node.Comment{uuid: nil, content: "Davi's Comment"},
"u" => %Node.User{uuid: nil, name: "Davi", age: 35, email: nil}
}
]}
defmodule App do
use Ex4j.Cypher
def execute do
match(Node.User, as: :u)
|> edge(Node.Has, as: :h, from: :u, to: :c, type: :out)
|> vertex(Node.Comment, as: :c)
|> return(:h)
|> run()
end
end
iex> User.execute()
{:ok, [
%{"h" => %Node.Has{uuid: nil, role: "Tiago's Role"}},
%{"h" => %Node.Has{uuid: nil, role: "Davi's Role"}}
]}
defmodule App do
use Ex4j.Cypher
def execute do
match(Node.User, as: :u)
|> edge(Node.Has, as: :h, from: :u, to: :c, type: :out)
|> vertex(Node.Comment, as: :c)
|> return(:u, [:name])
|> return(:c, [:content])
|> run()
end
end
iex> User.execute()
{:ok,[
{"c" => %{"content" => "Tiago's Comment"}, "u" => %{"name" => "Tiago"}},
%{"c" => %{"content" => "Davi's Comment"}, "u" => %{"name" => "Davi"}}
]}
defmodule App do
use Ex4j.Cypher
def execute do
match(Node.User, as: :u)
|> edge(Node.Has, as: :h, from: :u, to: :c, type: :out)
|> vertex(Node.Comment, as: :c)
|> return(:u, [:name])
|> return(:c, [:content])
|> cypher()
end
end
iex> User.execute()
"MATCH (u:User)-[h:Has]->(c:Comment)\nRETURN c.content,u.name"
You can always execute a query directly like so:
defmodule App do
use Ex4j.Cypher
def execute do
query =
"""
MATCH (n:Comment)
RETURN n
LIMIT 25
"""
run(query)
end
end
iex> App.execute()
{:ok,
%Bolt.Sips.Response{
results: [
%{
"n" => %Bolt.Sips.Types.Node{
id: 2,
properties: %{"text" => "Tiago's Comment"},
labels: ["Comment"]
}
},
%{
"n" => %Bolt.Sips.Types.Node{
id: 3,
properties: %{"content" => "Davi's Comment"},
labels: ["Comment"]
}
},
],
fields: ["n"],
records: [
[
%Bolt.Sips.Types.Node{
id: 2,
properties: %{"text" => "Tiago's Comment"},
labels: ["Comment"]
}
],
[
%Bolt.Sips.Types.Node{
id: 3,
properties: %{"content" => "Davi's Comment"},
labels: ["Comment"]
}
]
],
plan: nil,
notifications: [],
stats: [],
profile: nil,
type: "r",
bookmark: "FB:kcwQOoduCuitRfejxXkLly0aBBiQ"
}}
- Add support for migrations
- Add suport for more Cypher clauses like: CREATE, SKIP
Ex4j source code is released under Apache License 2.0.