EctoNeo4j

Build Status Coverage Status

WARNING: WIP. This project is not production-ready (yet)

Goal

Ecto wrapper

Have a wrapper around Ecto in order to use Ecto.*-style functions.
With EctoNeo4j, it is possible to have a classic Ecto schema with its changeset and use known Ecto Repo functions to persits data in a Neo4j database.

Use Neo4j driver easily

EctoNeo4j allows you to not care about the Bolt.Sips.conn() reuired in all functions.
for example, instead of writing:

conn = Bolt.Sips.conn()
Bolt.Sips.query(conn, "RETURN 1 AS num")

you can simply write:

MyRepo.query("RETURN 1 AS num")

Ultimate

The ultimate goal is to have also an extension of Ecto to manage relationship and have a kind of EctoCypher too.

Requirements

EctoNeo4j requires bolt_sips in order to work.
bolt_sips should be defined as one of your project dependency.
More info bout bolt_sips: https://github.com/florinpatrascu/bolt_sips

Warning: about ids...

As you may know, it is strongly recommended to NOT rely on Neo4j internal ids, as they can be reaffected.
With Ecto.Schema, id can be managed automatically. EctoNeo4j allows to not change this way of working by using a property called nodeId on created/updated nodes. This porprety is automically converted into id when retrieving data from database.

Usage

Database config

See https://github.com/florinpatrascu/bolt_sips#usage

Your repo

Add a module with the folling code:

defmodule MyApp.MyRepo do
  use EctoNeo4j.Repo
end

Add it to your supervision tree:

defmodule MyApp.Application do
  use Application

  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  def start(_type, _args) do
    # Define workers and child supervisors to be supervised
    children = [
      {MyApp.MyRepo, []}
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Considering the following module:

defmodule MyApp.Post do
  use Ecto.Schema
  import Ecto.Changeset

  schema "Post" do
    field(:title, :string)
    field(:content, :string)
  end

  def changeset(test, params \\ %{}) do
    test
    |> cast(params, [:title, :desc])
    |> validate_required([:content, :desc])
end

Usage - What's currently available

Considering the following module:

defmodule MyApp.Post do
  use Ecto.Schema
  import Ecto.Changeset

  schema "Post" do
    field(:title, :string)
    field(:content, :string)
  end

  def changeset(test, params \\ %{}) do
    test
    |> cast(params, [:title, :desc])
    |> validate_required([:content, :desc])
end

Inserting

data = %{title: "Hello World", content: "Neo4j is wonderful, isn't it?"}
changeset = MyApp.Post.changeset(%MyApp.Post{}, data)
MyRepo.insert(changeset)

insert!/2 is also available

Retrieving

One result

MyRepo.one(MyApp.Post)

# with a queryable
import Ecto.Query
query = from p in MyApp.Post, where: p.title == "Hello World", select: p.content

one!/2 is also available.

All results

MyRepo.all(MyApp.Post)

# with a queryable
import Ecto.Query
query = from p in MyApp.Post, where: p.title == "Hello World", select: p.content
MyRepo.all(query)

By id

MyRepo.get(MyApp.Post, 3)

get!/2 is also available.

Updating

MyRepo.get(MyApp.Post, 3)
|> MyApp.Post.changeset(%{title: "New title"})
|> MyRepo.update()

update!/2 is also available.

Deleting

MyRepo.get(MyApp.Post, 3)
|> MyRepo.delete()

delete!/2 is also available.

Raw cypher query

MyRepo.query("MATCH (p:Post {title: {title}})) RETURN p", %{title: "Searched"}

query!/2 is also available.

Queryable limitation

Ecto.Query is not yet fully compatible with EctoNeo4j as the translation work is in progress. And in fact, some part have no meaning in Neo4j (join, etc.). For now yo can use:

  • where:

    • only with and, or, ==, >, >=, <, <=
  • select

More to be covered soon.

Roadmap

  • [ ] Cover all Repo callbacks
  • [ ] Better and easier usage
  • [ ] Cover as many Ecto.Query as possible
  • [ ] Ecto schema extension
  • [ ] Complete cypher integration

Can I contribute?

Yes, you can! Please, do! Just fork, commit and submit pull requests!