LMV-QL is a query language for filtering model elements in Autodesk Platform Services (formerly Forge) viewer
Check out our demo application with a sample viewer extension which allows to run several predefined LMV-QL filters and also allows you to test you own filters on the basic sample model
- Quick tour
- Introduction
- Query language
- Headless LMV-QL: query model without viewer
- Query settings
- install the package:
npm install lmv-ql
- import query:
import { query } from "lmv-ql";
- run your LMV query
const filterOptions = {
displayUnits: viewer.prefs.get(Autodesk.Viewing.Private.Prefs.DISPLAY_UNITS),
displayUnitsPrecision: viewer.prefs.get(Autodesk.Viewing.Private.Prefs.DISPLAY_UNITS_PRECISION)
};
const queryResults = await query(viewer.model, lmvQuery, filterOptions);
if (!queryResults.error) {
const dbIds = queryResults.dbIds;
// do whatever you want with the elements db ids returned by filter engine
const rows = queryResults.rows;
// consume query results if you provided selection expressions in your query
for(const row of queryResults.rows) {
for(const column of queryResults.columns) {
const value = row.values[column];
// do whatever you want with this information
}
}
}
LMV-QL designed to make filters on model element properties. Let's imagine we want to make a simple filter for model floors with specified Area
property value.
There are several possible options to that, depending on what you need:
- you can provide exact path:
Floors.Floor.[Generic 150mm].Floor.Area = 105.9
In that case LMV-QL would search elements only inside that subtree (Floors -> Floor -> Generic 150mm -> Floor) - you can replace any path of your query with
*
symbol:*.Floor.Area = 105.9
- or even search among all elements of your model like:
*.Area = 105.9
But what if some other element of your model hasArea
property and it's value is 105.9 squared meters and you want to searcg only among floors? Then use something like that:Floors.*.Area = 105.9
- or maybe you want to query all elements which areas are more than 50 square meters?
*.Area > 50
LMV-QL designed to be able to make queries on models with a complex structure. Let's say, we want to query rectangular mullions from specific curtain wall from Revit sample file
The main problem is that we need to check properties from different levels of hierarch of the model tree. To do that with LMV-QL we should use logical operators.
First of all we need to distinguish curtain walls from our model. We can do that by dimensions:
So we are ready to write the first part of our filter:
*.[Curtain Wall].Length = 6202 and *.[Curtain Wall].Area = 26.5
Then we need to distinguish rectangular mullions from other curtain wall components. We can do that by checking Type Name
property. If it ends with "rectangular", then we found our mullion object:
So, let's add this to our filter:
*.[Curtain Wall].Length = 6202 and *.[Curtain Wall].Area = 26.5 && *.[Type Name] like "%rectangular"
LMV-QL follows the same rules of formatting and values comparison as Autodesk Platform Services viewer does. You can provide displayUnits
and displayUnitsPrecision
in the options object when you perform your query like
const filterOptions = {
displayUnits: viewer.prefs.get(Autodesk.Viewing.Private.Prefs.DISPLAY_UNITS),
displayUnitsPrecision: viewer.prefs.get(Autodesk.Viewing.Private.Prefs.DISPLAY_UNITS_PRECISION)
};
const queryResults = await query(viewer.model, lmvQuery, filterOptions);
So user can copy numerical values from properties panel directly to the filter query string: *.Floor.Area = 38.5
However, if we change precision, we also need to fix the filter string: *.Floor.Area = 38.46
If we are changing display units, then we also should adjust the filter string: *.Floor.Area = 413.98
You need to join with .
symbol model tree item names and property into a single string To define LMV-QL hierarchy position and property, e.g. SomeCategory.SomeSubCategory.SomeProperty
. If your item or property name contains spaces or dots .
symbols, then put it inside [
and ]
symbols like Floors.Floor.[Generic 150mm].Floor.Area
You can replace any part of the hierarchy path with *
(except the latest), which would mean any
in that case
Valid identificators:
Floors.Floor.[Generic 150mm].Floor.Area
Floors.*.Area
*.Area
Floors.*
- not valid, e.g. any property is not supported
You can provide a valid identificator with following !
sign to filter all sub items from model tree. Valid filters:
[Plumbing Fixtures]!
Walls.[Curtain Wall]!
Walls.[Curtain Wall].[SH_Curtain wall].[Curtain Wall].[System Panel]!
Walls.*!
is not a valid filter
- property value equal to
identificator = value
:Walls.[Basic Wall].*.Width = 300
- greater than value
identificator > value
:*.Floor.Area > 100
- greater than or equal to value
identificator >= value
*.Floor.Area >= 100
- less than value
identificator < value
*.Floor.Area < 100
- less than or equal to value
identificator <= value
:*.Floor.Area <= 100
- property not equal to value
identificator <> value
oridentificator != value
. Both options are valid:*.Floor.Area <> 9
or*.Floor.Area != 9
- property value equal to
identificator = value
:*.Mark = "207"
- property value not equal to
identificator <> value
oridentificator != value
. Both options are valid:*.Mark <> "205"
or*.Mark != "205"
- property value starts with
identificator like "start text%"
:*.name like "RPC%"
- property value ends with
identificator like "%-some further text"
:*.name like "%- 300mm Concrete"
If you want to include a text with quotes like some "value" of something
then you need to escape quotes with \
sign:
category.element.property = "some \"value\" of something"
If you want to include %
sign into like
expression you also need to escape it with \
sign:
category.element.property like "%some \% of something"
LMV-QL also supports reversed order of aruments, e.g. you can write something like
5 <= *.property and property <= 7
LMV-QL also can compare two properties values:
*.Mark = *.[Type Mark]
LMV-QL can check if the model element property is in some list of string or numerical values.
Example:
*.Mark in ["204", "207"]
LMV-QL also allows to check if the model element proerty is not in the list.
Example:
Doors! && *.Mark not in ["204", "207"]
This query selects all doors, , excluding those which Mark property is equal to
204
or207
LMV-QL allows to define AND
filter as:
and
(case insensitive)&&
&
Valid and
filters samples:
Windows! and *.Level = "Level 1 Living Rm."
Walls.[Curtain Wall].*.[Assembly Code] = "B2020200" && *.[Type Name] = "64 x 128 rectangular"
LMV-QL allows to define OR
filter as:
or
(case insensitive)||
|
Valid or
filters samples:
*.Mark = "207" or *.Mark = "204"
*.Level = "Level 1" || *.Level = "Level 1 Living Rm."
LMV-QL allows to define NOT
filter with
not
word (case-insensitive), e.g.not(some_valid_filter)
!
sign, e.g.!(some_valid_filter)
Examples:
!(Walls! or Floors!)
- all elements matches except those inWalls
orFloors
top model tree categorires- all floors, except those which areas is less than 50 or more than 100:
Floors! and not(*.Floor.Area >= 50 and *.Floor.Area <= 100)
LMV-QL allows to combine filters using brackets. Example:
Windows! and (*.Level = "Level 1 Living Rm." or *.name = "M_Skylight")
LMV-QL can contain:
- filter expressions as it was described above
- properties query expressions:
- with filter expression as well
- without filter expression
- with aggregation functions (sum, min, max, avg, count)
- with an ability to group by single or multiple fields
*
- no filter and returns all property values- with filter, returning all property values
You can use LMV-QL to collect required properties value from the entire model using simple selection expressions without filtering part:
*.ElementId as id, *.name as name, *.[Type Name] as type_name
Run this query in our demo application and you'll see query results:
LMV-QL allows to combine selection expressions with filter expressions. Let's collect several properties from demo model model floors:
Floors! -> *.[Type Name] as type_name, *.Area as area, *.Volume as volume, *.Thickness as thickness, *.Perimeter as perimeter
LMV-QL can aggregate the data collected from model elements properties with the following aggregation functions:
- sum (summs numerical properties values)
- min (gets minimal property value among elements)
- max (gets maximum property value among elements)
- avg (gets average property value among elements)
- count (gets elements count)
The following query will calculate total, maximum, mininimum and average area of all floors in the model:
Floors! -> sum(*.Area) as total_area, max(*.Area) as max_area, min(*.Area) as min_area, avg(*.Area) as avg_area, count() as count
You can omit filter part
Floors! ->
to perform calculation on all element in the model
Important note: you can't mix simple properties selection with aggregation functions, but you can use multiple aggregation functions in the same query as shown above
LMV-QL can perform grouping elements by fields you provided in your query.
The following query will calculate total, maximum, mininimum and average area of all walls in the model, grouped by wall type:
Walls! -> sum(*.Area) as total_area, max(*.Area) as max_area, min(*.Area) as min_area, avg(*.Area) as avg_area, count() as count group by *.[Type Name]
You can omit filter part
Walls! ->
to perform calculation on all element in the model
You can tell LMV_QL to return all property values with *
sign like:
Walls! -> *
Or even simpler *
expression is also valid to get all nodes properties values
Important
This is an experimental feature.
You can run LMV-QL queries without adding an entire APS Viewer application, e.g. without Autodesk.Viewer.GuiViewer3D
(but you still need to reference viewer library and initialize the environment with Autodesk.Viewing.Initializer
).
A code snippet:
Autodesk.Viewing.Document.load(urn, async doc => {
const root = doc.getRoot();
const bubbleNode = root.getDefaultGeometry(); // or whatever you want
const queryResults = await headlessQuery(doc, bubbleNode, filterExpression, options);
/* Do whatever you want with query results */
});
Queries settings are defined by:
{
attributesCaseSensitive: boolean;
stringCaseSensitive: boolean;
leafNodesOnly: boolean;
displayUnits: string;
displayUnitsPrecision: number | string;
dbIds: number[];
modelBrowserExcludeRoot: boolean;
modelName: string;
}
attributesCaseSensitive
defines if indentificators (item in the model tree and property names) are case-sensitive. Defaulttrue
stringCaseSensitive
defines if string comparison is case-sensitive or not, e.g. ifstringCaseSensitive
istrue
then*.[some property value] = "some text"
would return the element if it'ssome property value
is "some text", but not "Some text". Defaulttrue
leafNodesOnly
- iftrue
then filter engine returns only leaf nodes from the model tree. Defaulttrue
displayUnits
. Viewer display units. Default is""
which means "File units". Possible values can be taken from:
Autodesk.Viewing.Private.displayUnitsEnum
displayUnitsPrecision
. Viewer display units precision. Default is""
which means "File precision". Possible values can be taken from:
Autodesk.Viewing.Private.displayUnitsPrecisionEnum
-
dbIds
forces LMV-QL to perform query only on the ids were provided by user. Default value is[]
- perform the query on the entire model -
modelBrowserExcludeRoot
. Default value istrue
. Setfalse
to force users to add model name to queries. Value can be taken fromviewer.config.modelBrowserExcludeRoot
. See also Root object not visible in Model browser -
modelName
. Taken into account only ifmodelBrowserExcludeRoot
isfalse
. The value could be taken frommodel.getDocumentNode().getModelName()
or, probably model config.