Skip to content

Commit

Permalink
Improve error recovery
Browse files Browse the repository at this point in the history
Added example app and data
  • Loading branch information
cichacz committed Dec 7, 2023
1 parent 8fc8f5f commit b574783
Show file tree
Hide file tree
Showing 17 changed files with 10,256 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .formatter.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
inputs: ["{mix,.formatter}.exs", "{config,lib,test,example_app}/**/*.{ex,exs}"]
]
10 changes: 5 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/_build
/cover
/deps
/doc
_build
cover
deps
doc
config/*.secret.exs
/.fetch
erl_crash.dump
*.ez
*.beam
/config/*.secret.exs
.elixir_ls/
.DS_Store
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Currently only simple quering using raw Cypher queries is implemented, but there

### Bolt_sips

One may say "there is already a library for communication with Neo4j". They are right **BUT** first and foremost, `bolt_sips` is left unmaintained ([discussion](https://github.com/florinpatrascu/bolt_sips/issues/109)). There were few attempts to continue that, but last activity is from January 2023. Secondly, `bolt_sips` is just a driver. This library purpose will be to provide complete user experience when interacting with the DB.
One may say "there is already a library for communication with Neo4j". They are right **BUT** first and foremost, `bolt_sips` is left unmaintained ([discussion](https://github.com/florinpatrascu/bolt_sips/issues/109)). There were few attempts to continue that, but there is still no library that would take advantage of Elixir structs, protocols and behaviours to provide robust extensibility. Secondly, `bolt_sips` is just a driver. This library purpose will be to provide complete user experience when interacting with the DB.
This should be solved by building Ecto-like support for the Cypher query language.

## Installation
Expand Down Expand Up @@ -69,6 +69,16 @@ Configuration is very similar to the Ecto, so the ones familiar with it should h

Ecto-like Cypher DSL is one of the things that are on the Roadmap

## Example data
This repository contains small app that starts Neo4ex connection and Neo4j server.
After running `docker-compose up` go to the web interface (`http://localhost:7474`) and execute import command:
```
LOAD CSV WITH HEADERS FROM 'file:///example_data/customers-10000.csv' AS row
CALL apoc.create.node(['Customer'], row) YIELD node
RETURN node
```
After that you can start application located in `example_app` and play with the data.
## Roadmap
- [x] Implement Database driver using latest Bolt Protocol (v4+)
Expand Down
18 changes: 18 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: '3.1'

services:
graph_db:
image: neo4j:4.4.28-community
environment:
NEO4J_AUTH: 'neo4j/letmein'
NEO4JLABS_PLUGINS: '["apoc"]'
ports:
- "7474:7474"
- "7687:7687"
volumes:
- graph_db:/data
- ./example_data:/var/lib/neo4j/import/example_data

volumes:
graph_db:
driver: local
13 changes: 13 additions & 0 deletions example_app/config/config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Config

config :example_app, ExampleApp.Connector,
hostname: "localhost",
principal: "neo4j",
credentials: "letmein",
pool_size: 1

config :bolt_sips, Bolt,
url: "bolt://localhost:7687",
basic_auth: [username: "neo4j", password: "letmein"],
pool_size: 1,
max_overflow: 0
50 changes: 50 additions & 0 deletions example_app/lib/example_app.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
defmodule ExampleApp do
@moduledoc """
Documentation for `ExampleApp`.
"""

alias Neo4ex.BoltProtocol.Structure.Message.Summary.Success
alias Neo4ex.BoltProtocol.Structure.Graph.Node
alias Neo4ex.Cypher
alias ExampleApp.Connector
alias ExampleApp.Schema.Customer

def hello do
query = """
MATCH (customer:Customer {company: $company})
RETURN customer
LIMIT 10
"""

{:ok, _query, results} =
Connector.run(%Cypher.Query{query: query, params: %{company: "Davenport Inc"}})

results
|> Enum.reject(fn msg -> match?(%Success{}, msg) end)
|> Enum.map(fn [%Node{properties: properties}] ->
properties = Map.new(properties, fn {k, v} -> {String.to_existing_atom(k), v} end)
struct(Customer, properties)
end)
end

def hello_stream do
query = """
MATCH (customer:Customer {company: $company})
RETURN customer
LIMIT 10
"""

%Cypher.Query{query: query, params: %{company: "Davenport Inc"}}
|> Connector.stream(fn msg, acc ->
case msg do
[%Node{properties: properties}] ->
properties = Map.new(properties, fn {k, v} -> {String.to_existing_atom(k), v} end)
{:cont, [struct(Customer, properties) | acc]}

_ ->
{:halt, acc}
end
end)
|> Enum.to_list()
end
end
20 changes: 20 additions & 0 deletions example_app/lib/example_app/application.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
defmodule ExampleApp.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false

use Application

@impl true
def start(_type, _args) do
children = [
ExampleApp.Connector,
{Bolt.Sips, Application.get_env(:bolt_sips, Bolt)},
]

# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: ExampleApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
4 changes: 4 additions & 0 deletions example_app/lib/example_app/connector.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
defmodule ExampleApp.Connector do
use Neo4ex.Connector,
otp_app: :example_app
end
16 changes: 16 additions & 0 deletions example_app/lib/example_app/schema/customer.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
defmodule ExampleApp.Schema.Customer do
defstruct [
:index,
:customer_id,
:first_name,
:last_name,
:company,
:city,
:country,
:phone_1,
:phone_2,
:email,
:subscription_date,
:website
]
end
40 changes: 40 additions & 0 deletions example_app/lib/mix/tasks/bolt_sips_benchmark.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
defmodule Mix.Tasks.ExampleApp.BoltSipsBenchmark do
use Mix.Task

alias Neo4ex.Cypher

alias Bolt.Sips, as: Neo

alias ExampleApp.Connector

@requirements ["app.start"]

@shortdoc "Runs benchmark to compare with bolt_sips library"
def run(_args) do
Benchee.run(%{
"Neo4ex" => fn -> neo4ex() end,
"Bolt.Sips" => fn -> bolt_sips() end
})
end

def neo4ex() do
%{query: query, params: params} = customer_query()
Connector.run(%Cypher.Query{query: query, params: params})
end

def bolt_sips() do
%{query: query, params: params} = customer_query()
Neo.query!(Neo.conn(), query, params)
end

def customer_query() do
query = """
MATCH (customer)
RETURN customer, rand() as r
ORDER BY r
LIMIT 10
"""

%{query: query, params: %{}}
end
end
32 changes: 32 additions & 0 deletions example_app/mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
defmodule ExampleApp.MixProject do
use Mix.Project

def project do
[
app: :example_app,
version: "0.1.0",
elixir: "~> 1.14",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end

# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger],
mod: {ExampleApp.Application, []}
]
end

# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:neo4ex, path: "../"},
{:bolt_sips, git: "https://github.com/florinpatrascu/bolt_sips", branch: "master"},
{:faker, "~> 0.17.0"},
{:jason, "~> 1.2"},
{:benchee, "~> 1.0"}
]
end
end
11 changes: 11 additions & 0 deletions example_app/mix.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
%{
"benchee": {:hex, :benchee, "1.2.0", "afd2f0caec06ce3a70d9c91c514c0b58114636db9d83c2dc6bfd416656618353", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "ee729e53217898b8fd30aaad3cce61973dab61574ae6f48229fe7ff42d5e4457"},
"bolt_sips": {:git, "https://github.com/florinpatrascu/bolt_sips", "b21901a46ed19b17d1c87a9ef9e56002f83f345c", [branch: "master"]},
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
"db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
"faker": {:hex, :faker, "0.17.0", "671019d0652f63aefd8723b72167ecdb284baf7d47ad3a82a15e9b8a6df5d1fa", [:mix], [], "hexpm", "a7d4ad84a93fd25c5f5303510753789fc2433ff241bf3b4144d3f6f291658a6a"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
}
Loading

0 comments on commit b574783

Please sign in to comment.