# `NebulaGraphEx.Graph`
[🔗](https://github.com/VChain/nebula_graph_ex/blob/v0.1.8/lib/nebula_graph_ex/graph.ex#L1)

Public API for querying NebulaGraph.

## The recommended pattern — define a graph module

Borrowing from Ecto's Repo pattern, define a graph module in your application:

    # lib/my_app/graph.ex
    defmodule MyApp.Graph do
      use NebulaGraphEx.Graph, otp_app: :my_app
    end

Put connection options in config:

    # config/config.exs
    config :my_app, MyApp.Graph,
      hostname: "localhost",
      port: 9669,
      username: "root",
      space: "my_graph",
      pool_size: 10

Override secrets at runtime (recommended for passwords):

    # config/runtime.exs
    import Config

    config :my_app, MyApp.Graph,
      hostname: System.fetch_env!("NEBULA_HOST"),
      password: fn -> System.fetch_env!("NEBULA_PASS") end

Add to your supervision tree — that's it:

    # lib/my_app/application.ex
    def start(_type, _args) do
      children = [MyApp.Graph]
      Supervisor.start_link(children, strategy: :one_for_one)
    end

Now query without ever referencing a PID or pool name:

    MyApp.Graph.query("MATCH (v:Player) RETURN v.name, v.age LIMIT 5")
    MyApp.Graph.query!("RETURN 1+1 AS n") |> ResultSet.first!() |> Record.get!("n")

## Direct pool usage (without `use`)

When you need multiple pools, or want full control, start `NebulaGraphEx.Graph`
directly in your supervision tree:

    children = [
      {NebulaGraphEx.Graph,
       name: MyApp.Graph,
       hostname: "localhost",
       port: 9669,
       space: "my_graph",
       pool_size: 10}
    ]

Then pass the name (or PID) to every call:

    NebulaGraphEx.Graph.query(MyApp.Graph, "MATCH (v:Player) RETURN v LIMIT 5")

## Parameterised queries

Always use parameters for user-supplied values — never interpolate into the
statement string.

    MyApp.Graph.query(
      "MATCH (v:Player{name: $name}) RETURN v.age AS age",
      %{"name" => "Tim Duncan"}
    )

## Per-query options

Any pool option can be overridden for a single call:

    MyApp.Graph.query(stmt, %{},
      space: "other_space",
      timeout: 30_000,
      decode_mapper: &NebulaGraphEx.Record.to_map/1
    )

## Bang variants

`query!/3` raises `NebulaGraphEx.Error` on failure instead of returning
`{:error, error}`.

## Connection status

Use `status/1` or `status/2` when you want a health check for a pool. It
verifies that the pool process is alive and, by default, executes `RETURN 1`
to confirm the connection can run a query.

    {:ok, status} = MyApp.Graph.status()
    status.connected?
    #=> true

See `NebulaGraphEx.Options` for the full option reference.

# `__using__`
*macro* 

Injects a graph module into the calling module.

## Options

* `:otp_app` — required. The OTP application atom used to look up config via
  `Application.get_env(otp_app, __MODULE__, [])`.

## Generated API

When you `use NebulaGraphEx.Graph, otp_app: :my_app`, the following functions
are injected into your module, all scoped to the pool that the module itself
manages:

* `start_link/1` — starts the pool (used by your supervision tree).
* `child_spec/1` — makes the module a valid child spec entry.
* `query/1,2,3` — `query(statement, params \\ %{}, opts \\ [])`.
* `query!/1,2,3` — raises on failure.
* `map/2,3,4` — `map(statement, params \\ %{}, opts \\ [], fun)`.
* `status/0,1` — checks whether the pool is alive and can run a simple query.
* `use_space/1,2` — `use_space(space, opts \\ [])`.

All functions are overridable so you can add instrumentation or defaults.

# `map`

```elixir
@spec map(
  DBConnection.conn(),
  String.t(),
  map(),
  keyword(),
  (NebulaGraphEx.ResultSet.t() -&gt; term())
) ::
  {:ok, [term()]} | {:error, NebulaGraphEx.Error.t()}
```

Executes a nGQL statement and applies `fun` to every `%Record{}` row,
returning a list of results.

Equivalent to:

    {:ok, rs} = query(conn, stmt, params, opts)
    Enum.map(ResultSet.rows(rs), fun)

## Example

    NebulaGraphEx.Graph.map(MyApp.Graph, "MATCH (v:Player) RETURN v.name", fn record ->
      record |> NebulaGraphEx.Record.get!("v.name")
    end)
    #=> ["Tim Duncan", "LeBron James", ...]

# `query`

```elixir
@spec query(DBConnection.conn(), String.t(), map(), keyword()) ::
  {:ok, NebulaGraphEx.ResultSet.t()} | {:error, NebulaGraphEx.Error.t()}
```

Executes a nGQL statement and returns `{:ok, %ResultSet{}}` or
`{:error, %NebulaGraphEx.Error{}}`.

## Arguments

* `conn` — pool name or PID from `start_link/1`. When using the `use` macro,
  this is the module itself and is passed automatically.
* `statement` — nGQL string.
* `params` — map of parameter bindings. Default: `%{}`.
* `opts` — per-query option overrides (see `NebulaGraphEx.Options`). Default: `[]`.

## Examples

    {:ok, rs} = NebulaGraphEx.Graph.query(MyApp.Graph, "RETURN 1+1 AS sum")
    rs |> NebulaGraphEx.ResultSet.first!() |> NebulaGraphEx.Record.get!("sum")
    #=> 2

    {:error, %NebulaGraphEx.Error{code: :e_syntax_error}} =
      NebulaGraphEx.Graph.query(MyApp.Graph, "THIS IS NOT VALID nGQL")

# `query!`

```elixir
@spec query!(DBConnection.conn(), String.t(), map(), keyword()) ::
  NebulaGraphEx.ResultSet.t()
```

Like `query/4` but raises `NebulaGraphEx.Error` on failure.

## Example

    NebulaGraphEx.Graph.query!(MyApp.Graph, "MATCH (v:Player) RETURN count(v) AS n")
    |> NebulaGraphEx.ResultSet.first!()
    |> NebulaGraphEx.Record.get!("n")
    #=> 51

# `start_link`

```elixir
@spec start_link(keyword()) :: {:ok, pid()} | {:error, term()}
```

Starts a supervised connection pool and links it to the calling process.

Typically called via a module that uses `NebulaGraphEx.Graph`. When using the
pool directly, pass a `:name` so callers can reference it by atom:

    children = [
      {NebulaGraphEx.Graph,
       name: MyApp.Graph,
       hostname: System.fetch_env!("NEBULA_HOST"),
       password: fn -> System.fetch_env!("NEBULA_PASS") end,
       space: "my_graph",
       pool_size: 20}
    ]

See `NebulaGraphEx.Options` for the full option reference.

# `status`

```elixir
@spec status(
  DBConnection.conn(),
  keyword()
) ::
  {:ok,
   %{
     pid: pid(),
     connected?: true,
     queryable?: boolean(),
     probe_statement: String.t() | nil
   }}
  | {:error,
     %{
       pid: pid() | nil,
       connected?: boolean(),
       queryable?: false,
       probe_statement: String.t() | nil,
       error: term(),
       error_details: map()
     }}
```

Checks whether a pool is alive and able to execute a simple query.

This is an active health check. It first resolves the pool process and then,
unless disabled, executes `RETURN 1 AS status` to verify the checked-out
connection can talk to NebulaGraph successfully.

## Options

* `:probe_statement` — query used for the health check. Default: `"RETURN 1 AS status"`.
* `:probe_query` — whether to run the health-check query. Default: `true`.
* `:query_opts` — options passed to the probe query. Default: `[]`.

## Return value

On success:

    {:ok,
     %{
       pid: #PID<0.123.0>,
       connected?: true,
       queryable?: true,
       probe_statement: "RETURN 1 AS status"
     }}

If the pool process is missing:

    {:error,
     %{
       pid: nil,
       connected?: false,
       queryable?: false,
       error: :not_running,
       error_details: %{reason: :not_running, message: "pool process is not running"}
     }}

If the pool exists but the probe query fails:

    {:error,
     %{
       pid: #PID<0.123.0>,
       connected?: true,
       queryable?: false,
       probe_statement: "RETURN 1 AS status",
       error: %NebulaGraphEx.Error{},
       error_details: %{code: :e_syntax_error, message: "...", category: :query}
     }}

# `use_space`

```elixir
@spec use_space(DBConnection.conn(), String.t(), keyword()) ::
  {:ok, NebulaGraphEx.ResultSet.t()} | {:error, NebulaGraphEx.Error.t()}
```

Issues `USE <space>` on the given connection, switching the active graph
space for that connection's current session.

This is only useful when you hold a checked-out connection directly. With
the pool, prefer passing the `:space` option to `query/4` instead.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
