---
gitea: none
include_toc: true
---

# Queries

The `Query` class is the main way to fetch records. Inject it via the Laravel container.

```php
public function __construct(private Query $query) {}
```

## Return Formats

Queries return results in two formats:

**Graph** — via `->run()`. Returns a `Graph` object with `records` and `edges` collections. Useful for network-style data.

**Tree** — via `->tree()`. Returns a flat `Collection` of records where each record has `_children` and `_parents` arrays populated based on the requested depth. This is the most common format.

## Chaining Methods

All methods return `$this` and can be chained:

| Method | Description |
|---|---|
| `->filter(array)` | Add an AND filter |
| `->orFilter(array)` | Add an OR filter (grouped) |
| `->limit(int)` | Max number of root records to return |
| `->skip(int)` | Offset for pagination |
| `->sort(string)` | Sort by field. Prefix with `-` for descending e.g. `"-_sys.updatedAt"` |
| `->status(array)` | Filter by status array e.g. `["published", "draft"]` |
| `->onlyPublished()` | Shorthand for `->status(["published"])` |
| `->childrenDepth(int)` | How many levels of children to load |
| `->childrenLimit(int)` | Max children per record |
| `->childrenFields(array)` | Only follow specific relationship fields |
| `->parentsDepth(int)` | How many levels of parents to load |
| `->parentsLimit(int)` | Max parents per record |
| `->parentFields(array)` | Only follow specific relationship fields |
| `->notLinked(string)` | Return only records with no parents |
| `->run()` | Execute and return a `Graph` |
| `->tree()` | Execute and return a `Collection` (tree format) |
| `->runWithCount()` | Execute and return a `Graph` with a `total` count |


## Filters

Filter keys use the format `field_operator`. When no operator suffix is given, `eq` is assumed.

```php
$query->filter(["field_operator" => "value"]);

// No operator = eq
$query->filter(["schema" => "blogPosts"]);

// With operator
$query->filter(["_sys.updatedAt_gte" => "2024-01-01"]);
```

### Operator Reference

| Operator | Description |
|---|---|
| _(none)_ or `eq` | Equals (string) |
| `ne` | Not equals (string) |
| `eqnum` | Equals (numeric) |
| `neqnum` | Not equals (numeric) |
| `eqtrue` | Equals `true` |
| `eqfalse` | Equals `false` |
| `netrue` | Not equals `true` |
| `nefalse` | Not equals `false` |
| `regex` | Regular expression match |
| `in` | Value is in array (strings) |
| `nin` | Value is not in array (strings) |
| `innum` | Value is in array (numeric) |
| `ninnum` | Value is not in array (numeric) |
| `lt` | Less than |
| `lte` | Less than or equal |
| `gt` | Greater than |
| `gte` | Greater than or equal |
| `null` | Field is null |
| `nnull` | Field is not null |
| `exists` | Field key exists |
| `nexists` | Field key does not exist |
| `filter` | Raw filter passthrough |

### OR Filters

```php
$query
    ->filter(["schema" => "blogPosts"])
    ->orFilter(["title_regex" => "search", "slug_regex" => "search"]);
```

Each `orFilter` call groups its arguments with OR between them. Multiple `orFilter` / `filter` calls are AND-ed together.

### Cross-Schema (Children) Filters

You can filter records by properties of their related records using the `children.` prefix:

```php
$query->filter([
    "schema" => "blogPosts",
    "children.categories.data.slug" => "sports",
]);
```

This returns `blogPosts` that have a child in `categories` where `slug` equals `sports`.


## Nested Data Fields

Use dot notation to filter on JSON data fields and system fields:

```php
// Filter on a data field
$query->filter(["data.status_eq" => "draft"]);

// Filter on a system field
$query->filter(["_sys.updatedAt_gte" => "2024-01-01"]);
```


## Examples

**Get published blog posts, newest first:**

```php
$posts = $query
    ->filter(["schema" => "blogPosts"])
    ->onlyPublished()
    ->sort("-_sys.updatedAt")
    ->limit(10)
    ->tree();
```

**Get posts from a specific category with children loaded:**

```php
$posts = $query
    ->filter([
        "schema" => "blogPosts",
        "children.categories.data.slug" => "sports",
    ])
    ->childrenDepth(1)
    ->childrenFields(["categories"])
    ->limit(10)
    ->tree();
```

**Paginate records:**

```php
$graph = $query
    ->filter(["schema" => "blogPosts"])
    ->limit(20)
    ->skip(40)
    ->runWithCount();

$posts = $graph->tree();
$total = $graph->total;
```

**Search across fields:**

```php
$results = $query
    ->filter(["schema" => "blogPosts"])
    ->orFilter(["title_regex" => $term, "slug_regex" => $term])
    ->limit(10)
    ->tree();
```
