for schema, you can already have a look here: https://hexdocs.pm/seraph/schema.html#content
With this, everything you can think of as a graph model can fit in, except for one thing: multiple relationship with same type between same node (ex: multiple :WROTE between :User and :Post) which is possible in Neo4j but does not make sense in real life.
For the DSL, I didn’t take the node/x approach because of the proxmity with Kernel.node/1,2 and go on a more “cypher” path.
Nodes are like this:
{identifier, queryable, properties}
# Example
{u, User, %{firstName: "John"} # is (u:User {firstName: "John"})
# Almost all variations are valid
{u} # (u)
{User , %{firstName: "John"} # (:User {firstName: "John"})
...
{} and {User} won't be valid in MATCH because they have no sense there
Relationships like this:
[start_node, [identifier, queryable, properties], end_node]
# Example
[{User}, [rel, READ, %{nb_times: 3}], {Post}]
# And you can also use the variations
for query, what you wrote will be like this:
today = Timex.now |> Timex.beginning_of_day()
match([
{s, MyUser},
{p, Post},
[{s}, [w, Wrote], {post}]
])
|> where(s.name == "foobar" and p.edited_at > ^today)
|> return(s.name, p.id, w.edited_at)
|> order_by(s.name)
# the match can also be written like this:
match([[{s, MyUser}, [w, Wrote], {post, Post}]])
# and query can also be written like this:
match [
{s, MyUser},
{p, Post},
[{s}, [w, Wrote], {post}]
],
where: s.name == "foobar" and p.edited_at > ^today,
return: s.name, p.id, w.edited_at
order_by: s.name
And, little trick, order DOES matter, writing this
match([{u, User}])
|> return(u)
|> where(u.firstName == "John")
will result in a syntax error from Neo4j because it translates to:
MATCH (u:User)
RETURN u
WHERE u.firstName = "John"
This is maybe this point that can make composition a bit tricky.
But I think it’s mandatory if we want to be able to write quite complex queries, or just to put the OPTIONAL MATCH
exactly where we want.