Firestore dashboards – StructuredQuery cheat sheet

SquaredUp is an easy way to dashboard and monitor data in Firestore (the document database in Firebase, also available as a GCP service). However, anything beyond a simple collection query in Firestore requires a JSON-formatted StructuredQuery.

The JSON is quite verbose and fussy, so here’s a cheat sheet for some common queries.

These examples can be pasted into the ‘Firestore Structured Query’ data stream in SquaredUp. See the official docs

Every query will need a from clause to select the collection. The official docs say this should be an array, but it takes a single object and it’s very unusual to query across multiple collections in Firestore. This example uses accounts as the collection name. It’s not yet possible to query subcollections in SquaredUp (a collectionId with / does not work).

{
  "from": {
    "collectionId": "accounts"
  }   
}

Collections can be very large, so you probably want to add a limit:

{
  "from": {
    "collectionId": "accounts"
  },
  "limit": 100
}

To sort, use orderBy. The default is ASCENDING. As with from this supports an array or a single object (more common):

{
  "from": {
    "collectionId": "accounts"
  },
  "orderBy": {
    "field": {
      "fieldPath": "name"
    }
  },
  "limit": 100
}

To sort DESCENDING:

{
  "from": {
    "collectionId": "accounts"
  },
  "orderBy": {
    "field": {
      "fieldPath": "created"
    },
    "direction": "DESCENDING"
  },
  "limit": 100
}

Finally, some common where clauses.

It is important to remember that Firestore automatically creates an index for every field, but if you want to filter and sort across multiple fields in the same query you will need to create an index.

A simple string equality filter. The only other string operator is NOT_EQUAL. Firebase does not support a ‘contains’ string filter or RegEx.

{
  "from": {
    "collectionId": "accounts"
  },
  "where": {
   "fieldFilter": {
      "field": {
          "fieldPath": "name"
      },
      "op": "EQUAL",
      "value": {
          "stringValue": "SquaredUp"
      }
    }
  }
}

A number field filter requires you to know whether the field type is integer or double (use doubleValue in place of integerValue below). Supported operators are LESS_THAN, LESS_THAN_OR_EQUAL, GREATER_THAN, GREATER_THAN_OR_EQUAL, EQUAL, NOT_EQUAL.

{
  "from": {
    "collectionId": "accounts"
  },
  "where": {
   "fieldFilter": {
      "field": {
          "fieldPath": "score"
      },
      "op": "EQUAL",
      "value": {
          "integerValue": 100
      }
    }
  }
}

A boolean filter:

{
  "from": {
    "collectionId": "accounts"
  },
  "where": {
    "fieldFilter": {
      "field": {
        "fieldPath": "isActive"
      },
      "op": "EQUAL",
      "value": {
        "booleanValue": true
      }
    }
  }
}

A logical AND query. OR can be used in place of AND.

{
  "from": {
    "collectionId": "accounts"
  },
  "where": {
    "compositeFilter": {
      "op": "AND",
      "filters": [
        {
          "fieldFilter": {
            "field": {
              "fieldPath": "score"
            },
            "op": "EQUAL",
            "value": {
              "integerValue": 100
            }
          }
        },
        {
          "fieldFilter": {
            "field": {
              "fieldPath": "isActive"
            },
            "op": "EQUAL",
            "value": {
              "booleanValue": true
            }
          }
        }
      ]
    }
  }
}

A logical OR filter against the same property can also be achieved using the IN filter:

{
  "from": {
    "collectionId": "accounts"
  },
  "where": {
    "fieldFilter": {
      "field": {
        "fieldPath": "country"
      },
      "op": "IN",
      "value": {
        "arrayValue": {
          "values": [
            {
              "stringValue": "United Kingdom"
            },
            {
              "stringValue": "Canada"
            }
          ]
        }
      }
    }
  }
}

Filtering array fields using ARRAY_CONTAINS. Replace with ARRAY_CONTAINS_ANY and specify an array value for an OR filter against an array.

{
  "from": {
    "collectionId": "accounts"
  },
  "where": {
    "fieldFilter": {
      "field": {
        "fieldPath": "tags"
      },
      "op": "ARRAY_CONTAINS",
      "value": {
        "stringValue": "paid"
      }
    }
  }
}

The last filter to mention is the unary IS_NOT_NULL which is useful to check the existence of a field (specifically that it exists and is not null).

{
  "from": {
    "collectionId": "accounts"
  },
  "where": {
    "unaryFilter": {
      "field": {
        "fieldPath": "subscriptionId"
      },
      "op": "IS_NOT_NULL"
    }
  }
}

I hope this helps to get up and running with Firestore StructuredQuery. The full docs for StructuredQuery are here