# Caylir
Cayley driver for Elixir
__Note__: If you are reading this on [GitHub](https://github.com/mneudert/caylir) then the information in this file may be out of sync with the [Hex package](https://hex.pm/packages/caylir). If you are using this library through Hex please refer to the appropriate documentation on [HexDocs](https://hexdocs.pm/caylir).
## Cayley Support
Tested cayley versions:
- `0.7.0`
- `0.7.1`
- `0.7.2`
- `0.7.3`
- `0.7.4`
- `0.7.5`
(see [`.travis.yml`](https://github.com/mneudert/caylir/blob/v0.11.0/.travis.yml) to be sure)
## Package Setup
Add caylir as a dependency to your `mix.exs` file:
```elixir
defp deps do
[
# ...
{:caylir, "~> 0.11"},
# ...
]
end
```
## Application Setup
### Graph Definition
Defining a graph connection requires defining a module:
```elixir
defmodule MyApp.MyGraph do
use Caylir.Graph, otp_app: :my_app
end
```
The `:otp_app` name and the name of the module can be freely chosen but have to be linked to a corresponding configuration entry. This defined graph module needs to be hooked up into your supervision tree:
```elixir
children = [
# ...
MyApp.MyGraph,
# ...
]
```
### Configuration (static)
The most simple way is to use a completely static configuration:
```elixir
config :my_app, MyApp.MyGraph,
host: "localhost",
pool: [max_overflow: 10, size: 50],
port: 64210
```
### Configuration (dynamic)
If you cannot, for whatever reason, use a static application config you can configure an initializer module that will be called every time your graph is started (or restarted) in your supervision tree:
```elixir
# {mod, fun}
config :my_app, MyApp.MyGraph,
init: {MyInitModule, :my_init_mf}
# {mod, fun, args}
config :my_app, MyApp.MyGraph,
init: {MyInitModule, :my_init_mfa, [:foo, :bar]}
defmodule MyInitModule do
@spec my_init_mf(module) :: :ok
def my_init_mf(graph), do: my_init_mfa(graph, :foo, :bar)
@spec my_init_mfa(module, atom, atom) :: :ok
def my_init_mfa(graph, :foo, :bar) do
config =
Keyword.merge(
graph.config(),
host: "localhost",
port: 64210
)
Application.put_env(:my_app, graph, config)
end
end
```
When the graph is started the function will be called with the graph module as the first parameter with optional `{m, f, a}` parameters from your configuration following. This will be done before the graph is available for use.
The function is expected to always return `:ok`.
### Configuration (inline defaults)
For some use cases (e.g. testing) it may be sufficient to define hardcoded configuration defaults outside of your application environment:
```elixir
defmodule MyApp.MyGraph do
use Caylir.Graph,
otp_app: :my_app,
config: [
host: "localhost",
port: 64210
]
end
```
These values will be overwritten by and/or merged with the application environment values when the configuration is accessed.
## Usage
Writing Data:
```elixir
# single quad
MyApp.MyGraph.write(%{
subject: "graph",
predicate: "connection",
object: "target"
})
# multiple quads (bulk write)
MyApp.MyGraph.write([quad_1, quad_2])
```
Querying data:
```elixir
# Gizmo syntax!
query = "graph.Vertex('graph').Out('connection').All()"
# Default result limit
MyApp.MyGraph.query(query)
# Custom result limit (limited + unlimited)
MyApp.MyGraph.query(query, limit: 3)
MyApp.MyGraph.query(query, limit: -1)
```
Deleting Data:
```elixir
# single quad
MyApp.MyGraph.delete(%{
subject: "graph",
predicate: "connection",
object: "target"
})
# multiple quads (bulk delete)
MyApp.MyGraph.delete([quad_1, quad_2])
```
### JSON Configuration
By default the library used for encoding/decoding JSON is `:jason`. For the time `:caylir` directly depends on it to ensure it is available.
If you want to use another library you can switch it:
```elixir
config :my_app, MyGraph,
json_decoder: MyJSONLibrary,
json_encoder: MyJSONLibrary
config :my_app, MyGraph,
json_decoder: {MyJSONLibrary, :decoder_argless},
json_encoder: {MyJSONLibrary, :encoder_argless}
config :my_app, MyGraph,
json_decoder: {MyJSONLibrary, :decode_it, [[keys: :atoms]]},
json_encoder: {MyJSONLibrary, :encode_it, []}
```
If you configure only a module name it will be called as `module.decode!(binary)` and `module.encode!(map)`. When using a more complete `{m, f}` or `{m, f, a}` configuration the data to decode/encode will passed as the first argument with your configured extra arguments following.
### Query Language Configuration
If you are using a query language other than the default `:gizmo` you can configure your graph to use a different endpoint:
```elixir
config :my_app, MyApp.MyGraph,
language: :graphql
```
### Query Limit Configuration
You can define a default query limit by adding it to your graph config:
```elixir
config :my_app, MyApp.MyGraph,
limit: -1
```
### Query Timeout Configuration
Using all default values and no specific parameters each query is allowed to take up to 5000 milliseconds (`GenServer.call/2` timeout) to complete. That may be too long or not long enough in some cases.
To change that timeout you can configure your graph:
```elixir
# lowering timeout to 500 ms
config :my_app, MyApp.MyGraph,
query_timeout: 500
```
or pass an individual timeout for a single query:
```elixir
MyApp.MyGraph.query(query, timeout: 250)
```
A passed or graph wide timeout configuration override any `:recv_timeout` of your `:hackney` (HTTP client) configuration.
This does not apply to write requests. They are currently only affected by configured `:recv_timeout` values. Setting a graph timeout enables you to have a different timeout for read and write requests.
For the underlying worker pool you can define a separate timeout:
```elixir
config :my_app, MyApp.MyGraph,
pool_timeout: 500
```
This configuration will be used to wait for an available worker to execute a query and defaults to `5_000`.
## License
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)