---
title: "Quickstart: Path expressions"
description: "Learn how to use Aerospike path expressions to filter and select elements from nested maps and lists with code demos."
---

# Quickstart: path expressions

> For the complete documentation index see: [llms.txt](https://aerospike.com/docs/llms.txt)
> 
> All documentation pages available in markdown.

This page contains a quickstart guide for [path expressions](https://aerospike.com/docs/develop/expressions/path/).

Path expressions enable granular indexing and querying from a nested [map](https://aerospike.com/docs/develop/data-types/collections/map/) or [list](https://aerospike.com/docs/develop/data-types/collections/list/) collection data type (CDT) using [expressions](https://aerospike.com/docs/develop/expressions).

### Prerequisites

-   An Aerospike Database 8.1.2 or later. See the [database quickstart](https://aerospike.com/docs/database/quick-start/).
    
-   A [fully compatible](https://aerospike.com/docs/develop/client-matrix#full-clientserver-feature-compatibility) version of an [Aerospike client library](https://aerospike.com/docs/develop/) for your preferred programming language.
    
-   (Optional) The [Aerospike Tools package](https://aerospike.com/download/tools/) for monitoring and querying your database instance.
    

## Code Demo

All the Java code mentioned in our path expressions documentation is available in [GitHub](https://github.com/aerospike-examples/path-expressions-java-preview). You can clone, explore, run and modify it, or just read along.

1.  **Verify the server version.**
    
    Verify that the Aerospike server is running with the [`asadm`](https://aerospike.com/docs/database/tools/asadm/) command:
    
    Terminal window
    
    ```bash
    # connect to your Aerospike Database server on the port it is listening on
    
    asadm -p 3000
    ```
    
    Expected output similar to:
    
    ```json
    Seed:        [('127.0.0.1', 3000, None)]
    
    Config_file: /Users/dbuser/.aerospike/astools.conf, /etc/aerospike/astools.conf
    
    Aerospike Interactive Shell, version 2.12.0
    
    Found 1 nodes
    
    Online:  127.0.0.1:3000
    
    Admin> info
    ```
    
    The **Build** should be version >= 8.1.2.0
    
    Exit `asadm` with the command `exit`.
    
2.  **Download the demo code.**
    
    Use the following command to clone the app from GitHub:
    
    Terminal window
    
    ```bash
    git clone https://github.com/aerospike-examples/path-expressions-java-preview.git
    
    cd path-expressions-java-preview
    ```
    
    There are four folders:
    
    -   bookstore/ has the code used in the [overview](https://aerospike.com/docs/develop/expressions/path) page
    -   catalog/ has the code used in this quickstart page and [advanced usage](https://aerospike.com/docs/develop/expressions/path/advanced)
    -   booking/ has the code used in the [performance](https://aerospike.com/docs/develop/expressions/path/performance) page
    -   misc/ has miscellaneous code examples for path expression use, such as chaining `mapKeysIn`/`andFilter` pairs
3.  **Build the application with Maven.**
    
    Back at the shell command prompt, go into the folder you want to use and run the following command:
    
    Terminal window
    
    ```bash
    mvn compile exec:java
    ```
    

## Catalog example

This example uses a simplified ecommerce product catalog stored as a single record in Aerospike. Products are modeled as a map and organized under a single catalog map bin, keyed by product IDs (e.g. `10000001`, `50000009`).

```json
{

  "inventory": {

    "10000001": {

      "category": "clothing",

      "featured": true,

      "name": "Classic T-Shirt",

      "description": "A lightweight cotton T-shirt perfect for everyday wear.",

      "variants": {

        "2001": { "size": "S", "price": 25, "quantity": 100 },

        "2002": { "size": "M", "price": 25, "quantity": 0 },

        "2003": { "size": "L", "price": 27, "quantity": 50 }

      }

    },

    "10000002": {

      "category": "clothing",

      "featured": false,

      "name": "Casual Polo Shirt",

      "description": "A soft polo shirt suitable for work or leisure.",

      "variants": {

        "2004": { "size": "M", "price": 30, "quantity": 20 },

        "2005": { "size": "XL", "price": 32, "quantity": 10 }

      }

    },

    "50000006": {

      "category": "electronics",

      "featured": true,

      "name": "Laptop Pro 14",

      "description": "High-performance laptop designed for professionals.",

      "variants": {

        "3001": { "spec": "8GB RAM", "price": 599, "quantity": 0 }

      }

    },

    "50000009": {

      "category": "electronics",

      "featured": true,

      "name": "Smart TV",

      "description": "Ultra HD smart television with built-in streaming apps.",

      "variants": [

        { "sku": 3007, "spec": "1080p", "price": 199, "quantity": 60 },

        { "sku": 3008, "spec": "4K", "price": 399, "quantity": 30 }

      ]

    }

  }

}
```

### Filtering dimensions

```txt
catalog (bin)

 └── inventory (map of 4 maps)

      └── <product ID>

           ├── category

           ├── featured   <-- product-level filter

           ├── name

           ├── description

           └── variants

                ├── { "2001": {...}, "2002": {...} }   <-- map-backed variants

                └── [ {sku:3007,...}, {sku:3008,...} ] <-- list-backed variants

                        ^ variant-level filter
```

-   **Inventory level**: select `inventory` (map key navigation)
-   **Product level**: keyed by the product ID, filter on the `featured` field inside each product map
-   **Variant level**: filter by the `quantity` field inside either:
    -   a map-backed variant (keyed by SKU), or
    -   a list-backed variant (list of map structs)

### Featured products with in-stock inventory

Query the [catalog example](#catalog-example) above, using two filter expressions and combine them with traversal contexts to select featured products with in-stock variants:

::: note
If you plan to batch path expression operations in production, see [Batch inlining with large records](https://aerospike.com/docs/develop/expressions/path/performance#batch-inlining-with-large-records) for tuning guidance on in-memory namespaces.
:::

-   [Java](#tab-panel-2749)
-   [Python](#tab-panel-2750)
-   [Go](#tab-panel-2751)
-   [C](#tab-panel-2752)
-   [C#](#tab-panel-2753)
-   [Node.js](#tab-panel-2754)

```java
// Product-level filter: featured == true

Exp filterOnFeatured = Exp.eq(

   MapExp.getByKey(

       MapReturnType.VALUE, Type.BOOL,

       Exp.val("featured"),

       Exp.mapLoopVar(LoopVarPart.VALUE)   // during path resolution the value of the current element is assigned to a map-typed loop variable

   ),

   Exp.val(true)

);

// Variant-level filter: quantity > 0

Exp filterOnVariantInventory = Exp.gt(

    MapExp.getByKey(

        MapReturnType.VALUE, Type.INT,

        Exp.val("quantity"),

        Exp.mapLoopVar(LoopVarPart.VALUE)  // during path resolution the value of the current element is assigned to a map-typed loop variable

    ),

    Exp.val(0)

);

// Path expression: featured products with in-stock variants

Record record = client.operate(null, key,

    CdtOperation.selectByPath("catalog", SelectFlags.MATCHING_TREE,

        CTX.allChildren(),                                        // dive into all products

        CTX.allChildrenWithFilter(filterOnFeatured),              // only featured products

        CTX.mapKey(Value.get("variants")),                        // descend into variants

        CTX.allChildrenWithFilter(filterOnVariantInventory))      // only in-stock

);
```

```python
import aerospike

from aerospike import MAP_RETURN_VALUE

from aerospike_helpers import expressions as exp, cdt_ctx

from aerospike_helpers.operations import expression_operations, map_operations, operations

# Product-level filter: featured == true

filter_on_featured = exp.Eq(

    exp.MapGetByKey(

        ctx=None,

        return_type=MAP_RETURN_VALUE,

        value_type=exp.ResultType.BOOLEAN,

        key=exp.Val("featured"),

        bin=exp.LoopVarMap(aerospike.EXP_LOOPVAR_VALUE)

    ),

    exp.Val(True)

).compile()

# Variant-level filter: quantity > 0

filter_on_variant_inventory = exp.GT(

    exp.MapGetByKey(

        ctx=None,

        return_type=MAP_RETURN_VALUE,

        value_type=exp.ResultType.INTEGER,

        key=exp.Val("quantity"),

        bin=exp.LoopVarMap(aerospike.EXP_LOOPVAR_VALUE)

    ),

    exp.Val(0)

).compile()

# Path expression: featured products with in-stock variants

ctx = [

   cdt_ctx.cdt_ctx_all_children(),                                       # dive into all products

   cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_featured),         # only featured products

   cdt_ctx.cdt_ctx_map_key('variants'),                                  # descend into variants

   cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_variant_inventory) # only in-stock

]

ops = [

    operations.select_by_path(

        bin_name="catalog",

        ctx=ctx,

        flags=aerospike.EXP_PATH_SELECT_MATCHING_TREE

    )

]

config = {

  "hosts": [

      ("127.0.0.1", 3000)

  ]

}

client = aerospike.client(config)

key = ("test", "products", "catalog")

_, _, bins = client.operate(key, ops)

client.close()
```

```go
// Product-level filter: featured == true

filterOnFeatured := aero.ExpEq(

    aero.ExpMapGetByKey(

        aero.MapReturnType.VALUE,

        aero.ExpTypeBOOL,

        aero.ExpStringVal("featured"),

        aero.ExpMapLoopVar(aero.VALUE),

    ),

    aero.ExpBoolVal(true),

)

// Variant-level filter: quantity > 0

filterOnVariantInventory := aero.ExpGreater(

    aero.ExpMapGetByKey(

        aero.MapReturnType.VALUE,

        aero.ExpTypeINT,

        aero.ExpStringVal("quantity"),

        aero.ExpMapLoopVar(aero.VALUE),

    ),

    aero.ExpIntVal(0),

)

// Path expression: featured products with in-stock variants

record, err := client.Operate(nil, key,

    aero.SelectByPath("catalog", aero.EXP_PATH_SELECT_MATCHING_TREE,

        aero.CtxAllChildren(),                                        // dive into all products

        aero.CtxAllChildrenWithFilter(filterOnFeatured),              // only featured products

        aero.CtxMapKey(aero.NewValue("variants")),                    // descend into variants

        aero.CtxAllChildrenWithFilter(filterOnVariantInventory),      // only in-stock

    ),

)
```

```c
// Product-level filter: featured == true

as_exp_build(filter_on_featured,

    as_exp_cmp_eq(

        as_exp_map_get_by_key(

            NULL, AS_MAP_RETURN_VALUE, AS_EXP_TYPE_BOOL,

            as_exp_str("featured"),

            as_exp_loopvar_map(AS_EXP_LOOPVAR_VALUE)  // loop variable points to each product map

        ),

        as_exp_bool(true)

    )

);

if (!filter_on_featured) {

    goto fail_filter_on_featured;

}

// Variant-level filter: quantity > 0

as_exp_build(filter_on_variant_inventory,

    as_exp_cmp_gt(

        as_exp_map_get_by_key(

            NULL, AS_MAP_RETURN_VALUE, AS_EXP_TYPE_INT,

            as_exp_str("quantity"),

            as_exp_loopvar_map(AS_EXP_LOOPVAR_VALUE)  // loop variable points to each variant object

        ),

        as_exp_int(0)

    )

);

if (!filter_on_variant_inventory) {

    goto fail_filter_on_variant_inventory;

}

// Path expression: featured products with in-stock variants

as_cdt_ctx ctx;

as_cdt_ctx_inita(&ctx, 4);

as_cdt_ctx_add_all_children(&ctx);                                              // dive into all products

as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_featured);              // only featured products

as_cdt_ctx_add_map_key(&ctx, (as_val*)as_string_new("variants", false));        // descend into variants

as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_variant_inventory);     // only in-stock

as_operations ops;

as_operations_inita(&ops, 1);

status = as_operations_select_by_path(&err, &ops, "catalog", &ctx,

    AS_EXP_PATH_SELECT_MATCHING_TREE);

if (status != AEROSPIKE_OK) {

    goto fail_select_by_path;

}

as_record* rec = NULL;

status = aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);

if (status != AEROSPIKE_OK) {

    goto fail_key_operate;

}
```

```csharp
// Product-level: featured == true

Exp filterOnFeatured = Exp.EQ(

  MapExp.GetByKey(

    MapReturnType.VALUE, Exp.Type.BOOL,

    Exp.Val("featured"),

    Exp.MapLoopVar(LoopVarPart.VALUE)  // during path resolution the value of the current element is assigned to a map-typed loop variable

  ),

  Exp.Val(true)

);

// Variant-level: quantity > 0

Exp filterOnVariantInventory = Exp.GT(

  MapExp.GetByKey(

    MapReturnType.VALUE, Exp.Type.INT,

    Exp.Val("quantity"),

    Exp.MapLoopVar(LoopVarPart.VALUE)  // during path resolution the value of the current element is assigned to a map-typed loop variable

  ),

  Exp.Val(0)

);

// Path expression: featured products with in-stock variants

Record record = client.Operate(null, key,

  CDTOperation.SelectByPath("catalog", SelectFlag.MATCHING_TREE,

  CTX.AllChildren(),                                        // dive into all products

  CTX.AllChildrenWithFilter(filterOnFeatured),              // only featured products

  CTX.MapKey(Value.Get("variants")),                        // descend into variants

  CTX.AllChildrenWithFilter(filterOnVariantInventory)       // only in-stock variants

  )

);
```

```js
// Product-level: featured == true

const filterOnFeatured = exp.eq(

  exp.maps.getByKey(

    exp.loopVarMap(exp.loopVarPart.VALUE), // during path resolution the value of the current element is assigned to a map-typed loop variable

    exp.str('featured'),

    exp.type.BOOL,

    maps.returnType.VALUE

  ),

  exp.bool(true)

)

// Variant-level: quantity > 0

const filterOnVariantInventory = exp.gt(

  exp.maps.getByKey(

    exp.loopVarMap(exp.loopVarPart.VALUE), // during path resolution the value of the current element is assigned to a map-typed loop variable

    exp.str('quantity'),

    exp.type.INT,

    maps.returnType.VALUE

  ),

  exp.int(0)

)

// Path expression: featured products with in-stock variants

const context = new Context()

context.addAllChildren()                                        // dive into all products

context.addAllChildrenWithFilter(filterOnFeatured)              // only featured products

context.addMapKey('variants')                                   // descend into variants

context.addAllChildrenWithFilter(filterOnVariantInventory)      // only in-stock

const ops = [

  op.selectByPath("catalog", exp.pathSelectFlags.MATCHING_TREE, context)

]

const result = await client.operate(key, ops)
```

**Output:**

```json
{

  "inventory" : {

    "10000001" : {

      "variants" : {

        "2001" : {

          "size" : "S",

          "price" : 25,

          "quantity" : 100

        },

        "2003" : {

          "size" : "L",

          "price" : 27,

          "quantity" : 50

        }

      }

    },

    "50000009" : {

      "variants" : [ {

        "quantity" : 60,

        "sku" : 3007,

        "price" : 199,

        "spec" : "1080p"

      }, {

        "quantity" : 30,

        "sku" : 3008,

        "price" : 399,

        "spec" : "4K"

      } ]

    },

    "50000006" : {

      "variants" : { }

    }

  }

}
```

-   ✅ Item `50000009` keeps both variants.
-   ✅ Item `10000001`, Classic T-Shirt, keeps variant items `2001` and `2003` (both have `quantity > 0`).
-   ❌ Item `10000002`, Casual Polo Shirt, excluded (`featured = false`).
-   ❌ Variant `2002` excluded from item `10000001` (`quantity = 0`).
-   ❌ Item `50000006` excluded, variant `3001` (`quantity = 0`).

::: jsonpath comparison
The closest JSONPath equivalent to the query above is:

```json
$.inventory[?(@.featured == true)].variants[?(@.quantity > 0)]
```

However, JSONPath returns a flat list of the matching leaf elements, losing the parent product structure:

```json
[

  {"size":"S", "price":25, "quantity":100},

  {"size":"L", "price":27, "quantity":50},

  {"sku":3007, "spec":"1080p", "price":199, "quantity":60},

  {"sku":3008, "spec":"4K", "price":399, "quantity":30}

]
```

This flat result is equivalent to using the `SELECT_VALUE` flag (as shown in the [overview](https://aerospike.com/docs/develop/expressions/path) examples). The quickstart uses `SELECT_MATCHING_TREE` instead, which preserves the full nested hierarchy so each matching variant stays grouped under its parent product.
:::

## Feature summary

With path expressions you can:

-   traverse all elements of a nested map or list collection data type, and use filter expressions to match and select elements at each layer.
    
-   Retrieve only relevant subtrees (products + in-stock variants).
    
-   Avoid denormalization and client-side filtering.
    
-   Build faster, cleaner APIs for real-world use cases like product catalogs.
    

## Next steps

Explore additional code examples and use cases on the [Advanced usage](https://aerospike.com/docs/develop/expressions/path/advanced) page.