28 Feb 2022

Sample Analysis: Filtering Specific Elements by Model Properties API

By Xiaodong Liang

In the last blog, we introduced the typical workflow on how to query specific elements by Model Properties API. With possible scenario, we produced one sample that filters specific elements. To demo query result more visually, this sample loads full model in left view, after indexing and querying (by input conditions), the specific objects will be loaded in the right viewer (aka. partial loading).

See the sample here.

Note: the viewers are for viewing, instead of the prerequisites of using Model Properties API. 

As mentioned in the last blog, the first is to ask Model Properties API to build indexing of the model, then query with conditions to filter out specific elements. So this sample follows the sequence with two buttons: Index and Query. While the two can also  be combined if you want to improve it (see the last demo of the previous blog). 

The most tricky section of this sample is it uses open-source library JqueryBuilder for the user to input filtering conditions. This is one of the cool components that provides interactive UI for user to input various conditions. These conditions can even be nested. The tutorial on JqueryBuilder has introduced a lot on how to use this component. In this blog, we highlight a few relevant tricks with the sample of Model Properties API.

The basic structure of JqueryBuilder component is comprised of Rules, Filters, Group Condition, Operators and Values. Rules are combined with AND or OR. Filters depend what fields to filter. The operators contain equal, less than, greater than, between, is null, is not null etc. Values can be string, double, radio, dropdown list etc.

1

This sample initializes some filters with the available Fields from Model Properties API. This configures the filter types and the unique keys.
 

{
        id: 'p153cb174',
        label: 'Name',
        denote: 'p153cb174',
        type: 'string'
}
{
        id: 'p849f0aa2',
        label: 'Length',
        denote: 'p849f0aa2',
        type: 'double'
}

When the sample is launched, the dummy rules are loaded. It has one top group with two rules in AND, one is single rule, the other is a sub-group with three rules in OR. JqueryBuilder can match the rules/filters by the unique key which are defined in initialization. 

2

this.rules_basic = {
      condition: 'AND',
      rules: [{
        id: 'p01bbdcf2',
        operator: 'equal',
        value: 0
      }, {
        condition: 'OR',
        rules: [{
          id: 'p5eddc473',
          operator: 'equal',
          value: ''
        }, {
          id: 'pf9083fcd',
          operator: 'equal',
          value: 1
        },
        {
          id: 'p2508403c',
          operator: 'less',
          value: 0.11
        }]
      }]
    };

After the user inputs Rules, we can get the input in json, while this cannot be used with Model Properties API directly. So the code will translate the json to the corresponding schemas. The function (query button click event ) in queryBuilder.js  does this job. Since we only need the object ids for partial loading, the sample only transforms the SVF2Id (in column), without other properties information.

e.g. if one single rule (find elements by name), the json of rule will be 

3

 

{
    "condition": "AND",
    "rules": [
        {
            "id": "p153cb174",
            "field": "p153cb174",
            "type": "string",
            "input": "text",
            "operator": "equal",
            "value": "Revit Windows"
        }
    ],
    "valid": true
}

the payload of query will be like below (note the single quote mark for string type)

{
    "query": {
        "$and": [
            {
                "$eq": [
                    "s.props.p153cb174",
                    "'Revit Windows'"
                ]
            }
        ]
    },
    "columns": {
        "svf2Id": "s.svf2Id"
    }
}

For a complex input with sub-groups, the code will iterate each rule, nested rule and conditions and build the final schema for query schema. e.g. in the demo video, we filter specific elements that match the rules.

4

 

{
    "condition": "OR",
    "rules": [
        {
            "condition": "AND",
            "rules": [
                {
                    "id": "p5eddc473",
                    "field": "p5eddc473",
                    "type": "string",
                    "input": "text",
                    "operator": "equal",
                    "value": "Revit Ducts"
                },
                {
                    "id": "p849f0aa2",
                    "field": "p849f0aa2",
                    "type": "double",
                    "input": "number",
                    "operator": "greater",
                    "value": 6
                }
            ]
        },
        {
            "condition": "AND",
            "rules": [
                {
                    "id": "p5eddc473",
                    "field": "p5eddc473",
                    "type": "string",
                    "input": "text",
                    "operator": "equal",
                    "value": "Revit Duct Fittings"
                },
                {
                    "id": "p2c0bde18",
                    "field": "p2c0bde18",
                    "type": "string",
                    "input": "text",
                    "operator": "equal",
                    "value": "Supply Air"
                }
            ]
        }
    ],
    "valid": true
}

The payload of query will be like below (note the single quote mark for string type)

{
    "query": {
        "$or": [
            {
                "$and": [
                    {
                        "$eq": [
                            "s.props.p5eddc473",
                            "'Revit Ducts'"
                        ]
                    },
                    {
                        "$gt": [
                            "s.props.p849f0aa2",
                            6
                        ]
                    }
                ]
            },
            {
                "$and": [
                    {
                        "$eq": [
                            "s.props.p5eddc473",
                            "'Revit Duct Fittings'"
                        ]
                    },
                    {
                        "$eq": [
                            "s.props.p2c0bde18",
                            "'Supply Air'"
                        ]
                    }
                ]
            }
        ]
    },
    "columns": {
        "svf2Id": "s.svf2Id"
    }
}

This is just a simple demo as it does not mean the condition options of JqueryBuilder are exactly to those of Model Properties API. e.g.

  • The option is called 'equal' in JqueryBuilder, while will be '$eq' in Model Properties API. This sample transforms some options at operators_map, but not all are transformed.
  • Model Properties API can support $like, while JqueryBuilder does not have such option by default (unless you extend its options list)

In addition, the indexing of model can provide a lot of properties. In this sample, only a few properties options are initialized for testing Revit model specifically.  We suggest you try with the demo Revit models which can be downloaded in Postman Collection. Please add other properties options yourself if needed.

Another general limitation with Model Properties API is: it has not supported to query Value Options yet. e.g. possible Levels in the Revit model. Of course, you could analyze the full raw properties to sort it out, but we logged the wish with Model Properties API, which will be more ideal approach. This sample currently workaround by downloading AEC data when the full model is loaded in left viewer.

        viewer.model.getDocumentNode().
        getDocument().downloadAecModelData(aecdata=>{
          if(aecdata){
              //get available levels from AEC Data
              global_queryBuilder.aec_data = aecdata
              const levels = aecdata.levels.map(a=>a.name) 
              //p01bbdcf2 is the key of Level in Model Properties API
              global_queryBuilder.filters.find(i=>i.id=='p01bbdcf2').values = levels
           }
           global_queryBuilder.reset()
         })

 

Related Article