Nested Filters
NestedFilterCondition enables filtering on nested document fields — that is, arrays of objects stored inside an Elasticsearch document (e.g. order lines, delivery contacts, shipment events). Nested filtering applies the inner filter to each element of the nested array independently, preventing cross-object field mixing.
Type Definition
graphql
input FilterConditionInput {
nested: NestedFilterCondition
}
input NestedFilterCondition {
path: String! # Dot-notation path to the nested field
filter: ComplexFilterInput! # Filter to apply within the nested object
}The filter inside NestedFilterCondition is a full ComplexFilterInput, so you can combine must, mustNot, and should clauses within the nested scope, and even nest further.
Supported Nested Paths
| Domain | Path | Filterable Fields |
|---|---|---|
| Orders | lines | lines.sku, lines.status, lines.quantity |
| Orders | deliveryContact | deliveryContact.country, deliveryContact.city |
| Orders | tags | tags.value |
| Orders | deliveryOrders | deliveryOrders.status, deliveryOrders.type |
| Orders | deliveryOrders.tags | deliveryOrders.tags.level |
| Shipments | events | events.event, events.country |
| Delivery Orders | lines | lines.sku, lines.status |
Field names inside the nested filter must be fully qualified with the path prefix (e.g.
lines.sku, not justsku).
Examples
Filter orders containing a specific SKU
graphql
complexFilters: {
must: [
{
nested: {
path: "lines",
filter: {
must: [
{ property: "lines.sku", operator: in, values: ["TSHIRT-S-BLUE"] }
]
}
}
}
]
}Filter orders delivered to France
graphql
complexFilters: {
must: [
{
nested: {
path: "deliveryContact",
filter: {
must: [
{ property: "deliveryContact.country", operator: in, values: ["FR"] }
]
}
}
}
]
}Filter orders with delivered shipments
graphql
complexFilters: {
must: [
{
nested: {
path: "shipments",
filter: {
must: [
{ property: "shipments.lastEvent", operator: in, values: ["DELIVERED"] }
]
}
}
}
]
}Multi-level filter — OPENED orders shipping to France
Combine a top-level facet filter with a nested filter:
graphql
complexFilters: {
must: [
{ property: "organizationId", operator: in, values: ["org-abc123"] },
{ property: "state", operator: in, values: ["OPENED"] },
{
nested: {
path: "deliveryContact",
filter: {
must: [
{ property: "deliveryContact.country", operator: in, values: ["FR"] }
]
}
}
}
]
}Multi-level nested — orders with delivery orders that have WARNING or ERROR tags
Nesting inside a nested path:
graphql
complexFilters: {
must: [
{ property: "organizationId", operator: in, values: ["org-abc123"] },
{
nested: {
path: "deliveryOrders",
filter: {
must: [
{
nested: {
path: "deliveryOrders.tags",
filter: {
must: [
{
property: "deliveryOrders.tags.level",
operator: in,
values: ["WARNING", "ERROR"]
}
]
}
}
}
]
}
}
}
]
}Filter orders with a specific SKU going to a specific country
Combining two independent nested conditions:
graphql
complexFilters: {
must: [
{ property: "organizationId", operator: in, values: ["org-abc123"] },
{
nested: {
path: "lines",
filter: {
must: [
{ property: "lines.sku", operator: in, values: ["TSHIRT-S-BLUE"] }
]
}
}
},
{
nested: {
path: "deliveryContact",
filter: {
must: [
{ property: "deliveryContact.country", operator: in, values: ["FR"] }
]
}
}
}
]
}Full Example — Filter Orders by SKU and Destination Country
bash
curl -X POST https://api-v3.happycolis.com/graphql \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "query SearchOrders($input: SearchInput!) { orderRecords(input: $input) { hits { objectID invoiceNumber state } nbHits } }",
"variables": {
"input": {
"complexFilters": {
"must": [
{ "property": "organizationId", "operator": "in", "values": ["org-abc123"] },
{
"nested": {
"path": "lines",
"filter": {
"must": [
{ "property": "lines.sku", "operator": "in", "values": ["TSHIRT-S-BLUE"] }
]
}
}
},
{
"nested": {
"path": "deliveryContact",
"filter": {
"must": [
{ "property": "deliveryContact.country", "operator": "in", "values": ["FR"] }
]
}
}
}
]
},
"hitsPerPage": 50,
"page": 0
}
}
}'javascript
const response = await fetch('https://api-v3.happycolis.com/graphql', {
method: 'POST',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `query SearchOrders($input: SearchInput!) {
orderRecords(input: $input) {
hits { objectID invoiceNumber state }
nbHits
}
}`,
variables: {
input: {
complexFilters: {
must: [
{ property: 'organizationId', operator: 'in', values: ['org-abc123'] },
{
nested: {
path: 'lines',
filter: {
must: [
{ property: 'lines.sku', operator: 'in', values: ['TSHIRT-S-BLUE'] },
],
},
},
},
{
nested: {
path: 'deliveryContact',
filter: {
must: [
{ property: 'deliveryContact.country', operator: 'in', values: ['FR'] },
],
},
},
},
],
},
hitsPerPage: 50,
page: 0,
},
},
}),
});
const { data } = await response.json();python
import requests
response = requests.post(
'https://api-v3.happycolis.com/graphql',
headers={
'Authorization': f'Bearer {ACCESS_TOKEN}',
'Content-Type': 'application/json',
},
json={
'query': '''
query SearchOrders($input: SearchInput!) {
orderRecords(input: $input) {
hits { objectID invoiceNumber state }
nbHits
}
}
''',
'variables': {
'input': {
'complexFilters': {
'must': [
{'property': 'organizationId', 'operator': 'in', 'values': ['org-abc123']},
{
'nested': {
'path': 'lines',
'filter': {
'must': [
{'property': 'lines.sku', 'operator': 'in', 'values': ['TSHIRT-S-BLUE']},
],
},
},
},
{
'nested': {
'path': 'deliveryContact',
'filter': {
'must': [
{'property': 'deliveryContact.country', 'operator': 'in', 'values': ['FR']},
],
},
},
},
],
},
'hitsPerPage': 50,
'page': 0,
}
},
}
)
data = response.json()php
<?php
$client = new \GuzzleHttp\Client();
$response = $client->post('https://api-v3.happycolis.com/graphql', [
'headers' => [
'Authorization' => 'Bearer ' . $ACCESS_TOKEN,
'Content-Type' => 'application/json',
],
'json' => [
'query' => 'query SearchOrders($input: SearchInput!) {
orderRecords(input: $input) {
hits { objectID invoiceNumber state }
nbHits
}
}',
'variables' => [
'input' => [
'complexFilters' => [
'must' => [
['property' => 'organizationId', 'operator' => 'in', 'values' => ['org-abc123']],
[
'nested' => [
'path' => 'lines',
'filter' => [
'must' => [
['property' => 'lines.sku', 'operator' => 'in', 'values' => ['TSHIRT-S-BLUE']],
],
],
],
],
[
'nested' => [
'path' => 'deliveryContact',
'filter' => [
'must' => [
['property' => 'deliveryContact.country', 'operator' => 'in', 'values' => ['FR']],
],
],
],
],
],
],
'hitsPerPage' => 50,
'page' => 0,
],
],
],
]);
$data = json_decode($response->getBody(), true);go
package main
import (
"bytes"
"encoding/json"
"net/http"
)
payload, _ := json.Marshal(map[string]interface{}{
"query": `query SearchOrders($input: SearchInput!) {
orderRecords(input: $input) {
hits { objectID invoiceNumber state }
nbHits
}
}`,
"variables": map[string]interface{}{
"input": map[string]interface{}{
"complexFilters": map[string]interface{}{
"must": []map[string]interface{}{
{"property": "organizationId", "operator": "in", "values": []string{"org-abc123"}},
{
"nested": map[string]interface{}{
"path": "lines",
"filter": map[string]interface{}{
"must": []map[string]interface{}{
{"property": "lines.sku", "operator": "in", "values": []string{"TSHIRT-S-BLUE"}},
},
},
},
},
{
"nested": map[string]interface{}{
"path": "deliveryContact",
"filter": map[string]interface{}{
"must": []map[string]interface{}{
{"property": "deliveryContact.country", "operator": "in", "values": []string{"FR"}},
},
},
},
},
},
},
"hitsPerPage": 50,
"page": 0,
},
},
})
req, _ := http.NewRequest("POST", "https://api-v3.happycolis.com/graphql", bytes.NewBuffer(payload))
req.Header.Set("Authorization", "Bearer "+ACCESS_TOKEN)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)Important Notes
- Field names inside a nested filter must include the full path prefix (e.g.
lines.sku, notsku). - Each nested condition independently scopes the filter to matching elements of the nested array, preventing cross-object matching (e.g. a filter on
lines.sku = A AND lines.quantity > 5ensures both conditions apply to the same line, not different lines). - Nesting depth is supported (e.g.
deliveryOrders→deliveryOrders.tags), but each level requires its ownnestedwrapper.
Related
- Complex Filters — Boolean logic wrapping nested conditions
- Facet Filters — Filter on categorical fields within nested objects
- Numeric Filters — Filter on numeric fields within nested objects