> This page is for version v1.
> For other versions, use one of these documentation indexes:
> - v2 (default): https://autodesk-poc.ferndocs.com/v2/llms.txt
> - v1: https://autodesk-poc.ferndocs.com/v1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://autodesk-poc.ferndocs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://autodesk-poc.ferndocs.com/_mcp/server.

# Model Property Service Query Language Reference

The indexing service is implemented using JSON table scans. Property data from the models specified in the index manifest are extracted and stored as compressed line delimited JSON by the service. Each row in the file represents a row extracted from the underlying SVF PDB (Property Database) files created as part of the SVF file conversions process. When queries are issued to the indexing service, the workflow takes the JSON query expression passed by the client, translates it to SQL-like expression, and applies it line-by-line to each row in the index (table scan). Where rows match the expression, they are written to a cached, compressed, results JSON file, honoring any column restrictions that were passed with the query filter expression.

## Basic Query Body Structure

\> Whether a query targets a basic index or the more complex diff index, the format of the query body passed to the service remains constant.

```json
{
    "columns": <columns>,
    "query": <conditions>
}
```

There is a columns property that allows the `columns`, or projections, returned by the service to be restricted. If this property is `null`, this is equivalent to a `SELECT *` in SQL terms. The second property, `query` carries the query filter expression applied to the each row in the index and is equivalent to a `WHERE` clause in SQL.

### `<columns>`

The following column specification

```json
{
    "columns":
    {
        "alias1": <expression1>,
        "column2": true
    }
}
```

is equivalent to the following SQL expression

```
SELECT <expression1> AS alias1, column2 FROM ...
```

and would yield a result with the following JSON schema.

```json
{
    "alias1": <value>,
    "column2": <value>
}
```

### `<conditions>`

The `query` property consists of a set of conditions that can be combined with `AND` and `OR` semantics. The following table summarizes the various conditions supported by the service. Conditions can contain embedded expressions.

| Condition                                                              | Yields                                                                  |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `[ <condition1>, <condition2>, ... ]`                                  | `<condition1> OR <condition2> OR ...`                                   |
| `{ "<property1>": <expression1>, "<property2>": <expression2>, ... }`  | `(<property1> = <expression1> AND <property2> = <expression2> AND ...)` |
| `{ "$not": <condition> }`                                              | `NOT <condition>`                                                       |
| `{ "$and": [ <condition1>, <condition2>, ... ]}`                       | `(<condition1> AND <condition2> AND ...)`                               |
| `{ "$or": [ <condition1>, <condition2>, ... ]}`                        | `(<condition1> OR <condition2> OR ...)`                                 |
| `{ "$lt": [ <expression1>, <expression2> ] }`                          | `(<expression1> < <expression2>)`                                       |
| `{ "$le": [ <expression1>, <expression2> ] }`                          | `(<expression1> <= <expression2>)`                                      |
| `{ "$eq": [ <expression1>, <expression2> ] }`                          | `(<expression1> = <expression2>)`                                       |
| `{ "$ne": [ <expression1>, <expression2> ] }`                          | `(<expression1> != <expression2>)`                                      |
| `{ "$ge": [ <expression1>, <expression2> ] }`                          | `(<expression1> => <expression2>)`                                      |
| `{ "$gt": [ <expression1>, <expression2> ] }`                          | `(<expression1> > <expression2>)`                                       |
| `{ "$like": [ <expression1>, <expression2> ] }`                        | `(<expression1> LIKE <expression2>)`                                    |
| `{ "$between": [ <expression1>, <expression2>, <expression3> ] }`      | `(<expression1> BETWEEN <expression2> AND <expression3>)`               |
| `{ "$between": { "<propertyName>": [<expression2>, <expression3> ]} }` | `(<propertyName> BETWEEN <expression2> AND <expression3>)`              |
| `{ "$in": [ <expression1>, <expression2>, <expression3>, ... ] }`      | `(<expression1> IN (<expression2>, <expression3>, ...))`                |
| `{ "$in": { <propertyName>: [<expression2>, <expression3>, ... ] } }`  | `(<propertyName> IN (<expression2>, <expression3>, ...))`               |
| `{ "$contains": [ "<propertyName>", <expression> ] }`                  | `(<expression> IN <propertyName>)`                                      |
| `{ "$isnull": <expression> }`                                          | `(<expression> IS NULL)`                                                |
| `{ "$notnull": <expression> }`                                         | `(<expression> IS NOT NULL)`                                            |

### `<expression>`

The following `<expression>` are supported by the service:

| Expression                                                                                                      | Yields                                                                                |
| --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `{ "$neg": <expression> }`                                                                                      | `-(<expression>)`                                                                     |
| `{ "$add": [ <expression1>, <expression2>, ... ]}`                                                              | `(<expression1> + <expression2> + ...)`                                               |
| `{ "$sub": [ <expression1>, <expression2>, ... ]}`                                                              | `(<expression1> - <expression2> - ...)`                                               |
| `{ "$mul": [ <expression1>, <expression2>, ... ]}`                                                              | `(<expression1> * <expression2> * ...)`                                               |
| `{ "$div": [ <expression1>, <expression2>, ... ]}`                                                              | `(<expression1> / <expression2> / ...)`                                               |
| `{ "$cat": [ <expression1>, <expression2>, ... ]}`                                                              | `(<expression1> \|\| <expression2> \|\|  ...)`                                        |
| `{ "$coalesce": [ <expression1>, <expression2>, ... ]}`                                                         | `COALESCE(<expression1>, <expression2>, ...)`                                         |
| `{ "$mod": [ <expression1>, <expression2> ]}`                                                                   | `(<expression1> % <expression2>)`                                                     |
| `{ "$cast": [ <expression1>, <expression2> ]}`                                                                  | `(CAST <expression1> AS <expression2>)`                                               |
| `{ "$nullif": [ <expression1>, <expression2> ]}`                                                                | `NULLIF(<expression1>, <expression2>)`                                                |
| `{ "$date_add": [ <expression1>, <expression2>, <expression3> ]}`                                               | `DATE_ADD(<expression1>, <expression2>, <expression3>)`                               |
| `{ "$date_diff": [ <expression1>, <expression2>, <expression3> ]}`                                              | `DATE_DIFF(<expression1>, <expression2>, <expression3>)`                              |
| `{ "$extract": [ <expression1>, <expression2> ]}`                                                               | `EXTRACT(<expression1> FROM <expression2>)`                                           |
| `{ "$substring": [ <expression1>, <expression2> ]}`                                                             | `SUBSTRING(<expression1> FROM <expression2>)`                                         |
| `{ "$substring": [ <expression1>, <expression2>, <expression3> ]}`                                              | `SUBSTRING(<expression1> FROM <expression2> FOR <expression3>)`                       |
| `{ "$to_string": <expression> }`                                                                                | `TO_STRING(<expression>)`                                                             |
| `{ "$to_timestamp": <expression> }`                                                                             | `TO_TIMESTAMP(<expression>)`                                                          |
| `{ "$char_length": <expression> }`                                                                              | `CHAR_LENGTH(<expression>)`                                                           |
| `{ "$lower": <expression> }`                                                                                    | `LOWER(<expression>)`                                                                 |
| `{ "$upper": <expression> }`                                                                                    | `UPPER(<expression>)`                                                                 |
| `{ "$count": <expression> }`                                                                                    | `COUNT(<expression>)`                                                                 |
| `{ "$sum": <expression> }`                                                                                      | `SUM(<expression>)`                                                                   |
| `{ "$avg": <expression> }`                                                                                      | `AVG(<expression>)`                                                                   |
| `{ "$min": <expression> }`                                                                                      | `MIN(<expression>)`                                                                   |
| `{ "$max": <expression> }`                                                                                      | `MAX(<expression>)`                                                                   |
| `{ "$trim": <expression> }`                                                                                     | `TRIM(<expression>)`                                                                  |
| `{ "$trim": [<expression1>, <expression2> }`                                                                    | `TRIM(<expression1> FROM <expression2>)`                                              |
| `{ "$trim": [<"LEADING"\|"TRAILING"\|"BOTH">, <expression1>, <expression2> }`                                   | `TRIM(<LEADING\|TRAILING\|BOTH> <expression1> FROM <expression2>)`                    |
| `{ "$utcnow": true }`                                                                                           | `UTCNOW()`                                                                            |
| `{ "$case: [ <expression1>, { "$when": <expression2>, "$then": <expression3>}, ... {"$else": <expression4>}] }` | `CASE <expression1> WHEN <expression2> THEN <expression3> ... ELSE <expression4> END` |
| `{ "$case: [ { "$when": <condition1>, "$then": <expression1>}, ... {"$else": <expression2>}] }`                 | `CASE WHEN <condition> THEN <expression1> ... ELSE <expression2> END`                 |
| `<number\|boolean\|string>`                                                                                     | `<number\|boolean\|string>`                                                           |

### String Escaping

SQL string constants have to be single quoted, i.e. `"'Hello'"` is the constant `'Hello'`. Unquoted strings either denote properties (e.g. `"s.p123"`) or keywords (e.g. `"LEADING"`).

## Query by Example

### Get Revit Classification with Column Transform

See the following fields.

```
// Viewer element display name field
{"key":"p153cb174","category":"__name__","type":"String","name":"name","uom":null}
// Revit category name field
{"key":"p20d8441e","category":"__category__","type":"String","name":"_RC","uom":null}
// Revit family name field
{"key":"p30db51f9","category":"__category__","type":"String","name":"_RFN","uom":null}
// Revit type name field
{"key":"p13b6b3a0","category":"__category__","type":"String","name":"_RFT","uom":null}
```

This example query uses the `$and` expression, combined with `$notnull` and a `$gt`, to filter rows where the views array has a `$count` greater than 0 and the row has a Revit classification.

```json
{
    "query": {
        "$and": [
            { "$notnull": "s.props.p20d8441e" },
            { "$notnull": "s.props.p30db51f9" },
            { "$notnull": "s.props.p13b6b3a0" },
            { "$gt": [{ "$count": "s.views" }, 0] }
        ]
    },
    "columns": {
        "s.svf2Id": true,
        "lmvName": "s.props.p153cb174",
        "revitCategory": "s.props.p20d8441e",
        "revitFamily": "s.props.p30db51f9",
        "revitType": "s.props.p13b6b3a0",
        "s.views": true
    }
}
```

This is equivalent to the following S3 SQL:

```
select
    s.svf2Id,
    s.props.p153cb174 as lmvName,
    s.props.p20d8441e as revitCategory,
    s.props.p30db51f9 as revitFamily,
    s.props.p13b6b3a0 as revitType,
    s.views
from S3Object[*] s
where
    s.props.p20d8441e is not null and
    s.props.p30db51f9 is not null and
    s.props.p13b6b3a0 is not null and
    count(s.views) > 0
```

### Fuzzy string matching with a dimension constraint

See the following fields.

```json
{"key":"p6637df3c","category":"Materials and Finishes","type":"String","name":"Structural Material","uom":null}
{"key":"p69a0daab","category":"Dimensions","type":"Double","name":"Ht","uom":"ft"}
```

This example query uses the `$like` expression combined with `%` wildcard matching to find rows with a structural material description containing “steel” that has a Ht dimension of more than 0.5 feet.

```json
{
    "query":
    {
        "$and": [
            {
                "$or": [
                    { "$like": ["s.props.p6637df3c", "'%Steel%'"] },
                    { "$like": ["s.props.p6637df3c", "'%steel%'"] }
                ]
            },
            {
                "$ge": ["s.props.p69a0daab", 0.5]
            }
        ]
    }
}
```

This is equivalent to the following S3 SQL:

```
select s.*
from S3Object[*] s
where
    s.props.p69a0daab >= 0.5 and
    (s.props.p6637df3c like '%Steel%' or s.props.p6637df3c like '%steel%')
```

### Find more examples and sample applications on APS Github

![git hub octocat 200px](/_fern-img/6b8b6821fb72e86574be9470ecb88a7cc4a9e625bf4a65372b5bbf4f8504363d.webp)

Sample models, an extensive set of examples queries and set of PowerShell helper scripts which can be used to test the API are available on APS Github [https://github.com/autodesk-platform-services/aps-model-properties.powershell](https://github.com/autodesk-platform-services/aps-model-properties.powershell)

---

*Imported from the Autodesk Platform Services documentation: [https://aps.autodesk.com/en/docs/acc/v1/tutorials/model-properties/query-ref](https://aps.autodesk.com/en/docs/acc/v1/tutorials/model-properties/query-ref).*