Hi @henkon! We desperately need to create some legible examples on querying. There’s probably a lot we still need to do on this aspect, as we haven’t “flexed” these parts of the server too much yet.
Specifically to your error, that’s coming because the query parameter expects to be a valid json string - and it’s not: you will need to quote all property parameters too, not just their values.
I’m going to add some examples against this commit’s referenced object. I’m starting with selecting specific fields (it’s easier), and I’ll move to actual queries.
Selecting Specific Fields
Using the select clause is super easy: it expects a string array.
query{
stream(id:"7b253e5c4c"){
object(id:"f4a0e0922fff27bc884ad149c828a9c8"){
totalChildrenCount
children(select:["type", "family", "category"] limit:100 depth: 100){
totalCount
cursor
objects{
id
data
}
}
}
}
}
You can also use dot notation inside the select clause to select specific subfields. Below I’m asking for the level name and elevation of each object. Note: since we’re querying an object graph, some might have these properties, and some might not.
query{
stream(id:"7b253e5c4c"){
object(id:"f4a0e0922fff27bc884ad149c828a9c8"){
totalChildrenCount
children(select:["type", "family", "category", "level.name", "level.elevation", "level.id"] limit:100 depth: 100){
totalCount
cursor
objects{
id
data
}
}
}
}
}
Here’s what an object returned by the query above looks like:
{
"id": "03a7b6b5fb8328d6372fba11c4827433",
"data": {
"type": "φ1100",
"family": "PHC",
"category": "Structural Foundations",
"level": {
"name": "設計GL",
"elevation": -100.00000000000108,
"id": "8f6a0b889cb29b5a52da5573543fa95c"
}
}
},
Querying
The query parameter expects a json object - which essentially translates to a nicely formatted json string representation of it. Depending on the client you’ll be using, you might get away with passing a normal js object, or you might need to call JSON.stringify() on it. If you pass it directly in the query itself, and not as a query variable, the latter is a given.
Note that the query parameter does, as you said, expect an array of JSONObjects. What is the format of that JSON Object? Read below.
For example, to get all the elements that are on the level called “5FL” - which I assume in that model corresponds to the “5th floor”, you need the following query:
{
"myQuery": [
{
"field": "level.name",
"value": "5FL",
"operator":"="
}
]
}
The anatomy of an object is rather simple: what field you want to operate against, what value of that field you want to reference against, and with what operator.
How do you pass this query in then? That’s a bit more tricky. The best practice way is to add it as a query variable. For this, we’ll need to edit the top of our original query a bit:
# notice how we've declared up here the 'myQuery' variable as having the same type as the one of the query field in the children node
query($myQuery: [JSONObject!]){
stream(id:"7b253e5c4c"){
object(id:"f4a0e0922fff27bc884ad149c828a9c8"){
totalChildrenCount
# ... and note how we're using it here
children(query: $myQuery select:["speckle_type","type", "family", "category", "level.name", "level.elevation", "level.id"] limit:100 depth: 100){
totalCount
cursor
objects{
id
data
}
}
}
}
}
To actually pass it in, if you’re using the graphql playground, down below you have a tab called “Query Variables”:
And voila! You’re now querying for all the objects on the 5th floor! Please note that for numeric values, you do not need to have them in quotes!
For example, let’s say we want to get all elements that are on a level with an elevation higher than 16900 (mm in this case), the query would become:
{
"myQuery": [
{
"field": "level.elevation",
"value": 16900,
"operator": ">="
}
]
}
What about multiple constraints? Easy! Let’s say we want to get all the columns that are on the 5th floor level, the one we used earlier. The query then becomes:
{
"myQuery": [
{
"field": "level.name",
"value": "5FL",
"operator":"="
},
{
"field":"speckle_type",
"value":"Objects.BuiltElements.Column:Objects.BuiltElements.Revit.RevitColumn",
"operator": "="
}
]
}
There’s more to querying but I’m a bit out of breath for now. I’ll move to another important aspect, pagination.
Pagination
The query above was limiting the number of return objects to 100. Using the same query, I’m going to restrict it to 5 objects only for demonstration purposes:
query($myQuery:[JSONObject!]){
stream(id:"7b253e5c4c"){
object(id:"f4a0e0922fff27bc884ad149c828a9c8"){
totalChildrenCount
# note the limit param below! it's only 5 now.
children(limit:5 query: $myQuery select:["speckle_type","type", "family", "category", "level.name"] ){
totalCount
cursor
objects{
id
data
}
}
}
}
}
What you get back is the following:
{
"data": {
"stream": {
"object": {
"totalChildrenCount": 6625,
"children": {
"totalCount": 23,
"cursor": "eyJmaWVsZCI6ImlkIiwib3BlcmF0b3IiOiI+IiwidmFsdWUiOiI0MjBjZDUwZjA0OTE0MTVlNDE1MTZiZjJiY2VmZjQ3OSJ9",
"objects": [ ... ]
You can see that your query’s total matching result is 23. That’s why the API provides you with a cursor that allows you to get the next “page” of results. It’s essentially an opaque index to your previous end position. To get the next set of results, you just need to pass in that cursor to your query parameters. This would look something like this:
query($myQuery:[JSONObject!]){
stream(id:"7b253e5c4c"){
object(id:"f4a0e0922fff27bc884ad149c828a9c8"){
totalChildrenCount
# note the addition of the cursor.
children(limit:5 cursor:"eyJmaWVsZCI6ImlkIiwib3BlcmF0b3IiOiI+IiwidmFsdWUiOiI0MjBjZDUwZjA0OTE0MTVlNDE1MTZiZjJiY2VmZjQ3OSJ9" query: $myQuery select:["speckle_type","type", "family", "category", "level.name"] ){
totalCount
cursor
objects{
id
data
}
}
}
}
}
If you’re doing a frontend application, it’s usually good practice to keep track of all your previous cursors, so you can easily go “back” to the previous page. Most importantly, it’s usually good practice to not request all the objects at the same time as this may result in really large payloads being sent to the client - we don’t want that, it makes your app feel slow!
As a bonus for reading so far, here’s the model I’m using in the examples above: