Skip to content

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 ​

DomainPathFilterable Fields
Orderslineslines.sku, lines.status, lines.quantity
OrdersdeliveryContactdeliveryContact.country, deliveryContact.city
Orderstagstags.value
OrdersdeliveryOrdersdeliveryOrders.status, deliveryOrders.type
OrdersdeliveryOrders.tagsdeliveryOrders.tags.level
Shipmentseventsevents.event, events.country
Delivery Orderslineslines.sku, lines.status

Field names inside the nested filter must be fully qualified with the path prefix (e.g. lines.sku, not just sku).


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, not sku).
  • 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 > 5 ensures both conditions apply to the same line, not different lines).
  • Nesting depth is supported (e.g. deliveryOrders → deliveryOrders.tags), but each level requires its own nested wrapper.

HappyColis API Documentation