# `Dllb.Query`
[🔗](https://github.com/Oeditus/dllb_ex/blob/v0.1.0/lib/dllb/query.ex#L1)

Query builder that generates dllb SQL strings.

Provides functions to construct CREATE, SELECT, UPDATE, DELETE, RELATE,
and DEFINE statements for the dllb query language. All functions return
plain query strings ready to be sent over the wire.

# `fields`

```elixir
@type fields() :: %{optional(atom()) =&gt; term()}
```

# `select_opts`

```elixir
@type select_opts() :: [
  fields: [String.t()],
  where: String.t(),
  order: String.t(),
  limit: non_neg_integer(),
  fetch: String.t()
]
```

# `create`

```elixir
@spec create(String.t(), fields()) :: String.t()
```

Builds a CREATE statement for inserting a new record.

## Examples

    iex> Dllb.Query.create("user", %{name: "Alice", age: 30})
    "CREATE user SET age = 30, name = 'Alice'"

# `create_with_id`

```elixir
@spec create_with_id(String.t(), String.t(), fields()) :: String.t()
```

Builds a CREATE statement with an explicit record ID.

## Examples

    iex> Dllb.Query.create_with_id("user", "u1", %{name: "Alice"})
    "CREATE user:u1 SET name = 'Alice'"

# `define_field`

```elixir
@spec define_field(String.t(), String.t(), String.t(), keyword()) :: String.t()
```

Builds a DEFINE FIELD statement.

## Options

  * `:required` - if `true`, appends ASSERT $value IS NOT NONE

## Examples

    iex> Dllb.Query.define_field("user", "name", "string", required: true)
    "DEFINE FIELD name ON user TYPE string ASSERT $value IS NOT NONE"

# `define_index`

```elixir
@spec define_index(String.t(), String.t(), [String.t()], atom(), keyword()) ::
  String.t()
```

Builds a DEFINE INDEX statement.

Supported index types: `:btree`, `:fulltext`, `:hnsw`.

## Options (for HNSW vector indexes)

  * `:dimension` - vector dimension
  * `:dist` - distance metric (e.g. `"COSINE"`, `"EUCLIDEAN"`)

## Examples

    iex> Dllb.Query.define_index("user", "idx_name", ["name"], :btree)
    "DEFINE INDEX idx_name ON user FIELDS name SEARCH ANALYZER btree"

    iex> Dllb.Query.define_index("ast_node", "idx_src_embed", ["source_embedding"], :hnsw, dimension: 768, dist: "COSINE")
    "DEFINE INDEX idx_src_embed ON ast_node FIELDS source_embedding HNSW DIMENSION 768 DIST COSINE"

# `define_table`

```elixir
@spec define_table(String.t(), :schemafull | :schemaless) :: String.t()
```

Builds a DEFINE TABLE statement.

Mode can be `:schemafull` or `:schemaless`.

## Examples

    iex> Dllb.Query.define_table("user", :schemafull)
    "DEFINE TABLE user SCHEMAFULL"

# `delete`

```elixir
@spec delete(String.t()) :: String.t()
```

Builds a DELETE statement for a record.

## Examples

    iex> Dllb.Query.delete("user:u1")
    "DELETE user:u1"

# `raw`

```elixir
@spec raw(String.t()) :: String.t()
```

Passes through a raw query string without modification.

## Examples

    iex> Dllb.Query.raw("INFO FOR DB")
    "INFO FOR DB"

# `relate`

```elixir
@spec relate(String.t(), String.t(), String.t(), fields()) :: String.t()
```

Builds a RELATE statement to create a graph edge between two records.

## Examples

    iex> Dllb.Query.relate("user:a", "follows", "user:b", %{since: "2024"})
    "RELATE user:a->follows->user:b SET since = '2024'"

# `select`

```elixir
@spec select(String.t(), select_opts()) :: String.t()
```

Builds a SELECT statement with optional clauses.

## Options

  * `:fields` - list of field names to select (default `["*"]`)
  * `:where` - WHERE clause string
  * `:order` - ORDER BY clause string
  * `:limit` - LIMIT value
  * `:fetch` - graph traversal fetch clause (e.g. `"->calls->fn_node"`)

## Examples

    iex> Dllb.Query.select("user", where: "age > 25", limit: 10)
    "SELECT * FROM user WHERE age > 25 LIMIT 10"

# `update`

```elixir
@spec update(String.t(), fields()) :: String.t()
```

Builds an UPDATE statement for an existing record.

## Examples

    iex> Dllb.Query.update("user:u1", %{name: "Bob"})
    "UPDATE user:u1 SET name = 'Bob'"

---

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