README.md

# EctoNeo4j

[![Build Status](https://travis-ci.org/dominique-vassard/ecto_neo4j.svg?branch=master)](https://travis-ci.org/dominique-vassard/ecto_neo4j)
[![Coverage Status](https://coveralls.io/repos/github/dominique-vassard/ecto_neo4j/badge.svg?branch=master)](https://coveralls.io/github/dominique-vassard/ecto_neo4j?branch=master)

**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](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](https://github.com/florinpatrascu/bolt_sips#usage)

## Your repo
Add a module with the folling code:
```elixir
defmodule MyApp.MyRepo do
  use EctoNeo4j.Repo
end
```

Add it to your supervision tree:
```elixir
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:
```elixir
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:
```elixir
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
```elixir
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
```elixir
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
```elixir
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
```elixir
MyRepo.get(MyApp.Post, 3)
```
`get!/2` is also available. 


# Updating
```elixir
MyRepo.get(MyApp.Post, 3)
|> MyApp.Post.changeset(%{title: "New title"})
|> MyRepo.update()
```
`update!/2` is also available.

# Deleting
```elixir
MyRepo.get(MyApp.Post, 3)
|> MyRepo.delete()
```
`delete!/2` is also available.

# Raw cypher query
```elixir
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!