Schema
General
A graph is composed of two objects: node and relationship, each with its own specifities.
Node has:
- one or more labels
- 0 or more properties
Relationship has:
- one type
- 0 or more properties
- a direction
Because of how ecto is designed, it is not possible for now to have more than one label per node.
In order to ease the schema building, we can use Ecto.Adapters.Neo4j.Schema which is a layer on top of Ecto.Schema. It sets the default primary key to :uuid instead of :id to avoid problem with Neo4j's internal identifier's system.
Describing relationships
Additionally, Ecto.Adapters.Neo4j.Schema offers two macros to work with relationships:
outgoing_relationship/2andoutgoing_relationship/3allows to describe outgoing relationship. Their usage is the samehas_*macros: they take a name as first parameter and the related schema as second parameter. The third parameter is for options, only one is available for now: :unique. Set totrue, it indicates that the relationship is 1-1, set to false t's a 1-many relationship.incoming_relationship/2allows to describe an incoming relationship. First parameter is the name, the eseond is the related schema.
Note that the name is formated as: [lowercase_relationship_type][child_schema_name] and have to be the same in parent and child schema.
For example:
Considering (:Actor)-[:PLAYS_IN]->(:Movie), we will have:
defmodule MyApp.Actor do
schema "Actor" do
...
outgoing_relationship :plays_in_movie, MyApp.Movie
end
end
defmodule MyApp.Movie do
schema "Movie" do
...
outgoing_relationship :plays_in_movie, MyApp.Actor
end
end
About relationship properties
Relationship data will be in the child schema, and be fed when preload is used.
It is recommended to:
- have the field name set to rel\[lowercased_relationship_type]
- have field type set to
:map
This field is mandatory, even if there will be no properties to use. It is still useful for querying purpose.
Putting up the GraphApp schemas
Remember the model

a User can only have one UserProfile.
a User can write multiple Posts.
a User can read multiple Posts.
a User can write multiple Comments.
a Post can have multiple Comments.
User properties:
- firstName
- lastName
UserProfile properties:
- avatarUrl
- age
Post properties:
- title
- text
Comment properties:
- text
:WROTE (from User to Post, and from User to Comment) properties:
- when
Now, let's write our schemas:
User
# lib/graph_app/account/user.ex
defmodule GraphApp.Account.User do
use Ecto.Adapters.Neo4j.Schema
schema "User" do
field :firstName, :string
field :lastName, :string
# (:User)-[:HAS]->(:UserProfile)
# A user has only one UserProfile
outgoing_relationship :has_userprofile, GraphApp.Account.UserProfile, unique: true
# (:User)-[:WROTE]->(:Post)
outgoing_relationship :wrote_post, GraphApp.Blog.Post
# (:User)-[:READ]->(:Post)
outgoing_relationship :read_post, GraphApp.Blog.Post
# (:User)-[:READ]->(:Comment)
outgoing_relationship :wrote_comment, GraphApp.Blog.Comment
end
end
UserProfile
# lib/graph_app/account/user_profile.ex
defmodule GraphApp.Account.UserProfile do
use Ecto.Adapters.Neo4j.Schema
schema "UserProfile" do
field :avatar, :string
# Field for :HAS relationship's properties
field :rel_has, :map
# (:User)-[:HAS]->(:UserProfile)
# Note that the name of relationship is the same as in GraphApp.Account.User
incoming_relationship :has_userprofile, GraphApp.Account.User
end
end
Post
# lib/graph_app/blog/post.ex
defmodule GraphApp.Blog.Post do
use Ecto.Adapters.Neo4j.Schema
schema "Post" do
field :title, :string
field :text, :string
# Field for :WROTE relationship's properties
field :rel_wrote, :map
# Field for :READ relationship's properties
field :rel_read, :map
# (:User)-[:WROTE]->(:Post)
incoming_relationship :wrote_post, GraphApp.Account.User
# (:User)-[:READ]->(:Post)
incoming_relationship :read_post, GraphApp.Account.User
# (:Post)-[:HAS]->(:Comment)
outgoing_relationship :has_comment, GraphApp.Blog.Comment
end
end
Comment
# lib/graph_app/blog/comment.ex
defmodule GraphApp.Blog.Comment do
use Ecto.Adapters.Neo4j.Schema
schema "Comment" do
field :text, :string
# Field for :WROTE relationship's properties
field :rel_wrote, :map
# Field for :HAS relationship's properties
field :rel_has, :map
# (:User)-[:WROTE]->(:Comment)
incoming_relationship :wrote_comment, GraphApp.Account.User
# (:Post)-[:HAS]->(:Comment)
incoming_relationship :has_comment, GraphApp.Blog.Post
end
end