Query Documents
Tigris provides powerful query functionality for specifying which documents you want to retrieve. There is no need to index any field as Tigris allows querying on any field of a document.
Specifically:
- Filter to filter queries on collections
- Sort to sort collection queries
- Pagination to paginate through query results
Filter
Filter provides the following comparison operators with the same semantics as in virtually all programming languages.
- $eq: equal to is used for exact matching.
- $lt: less than is used for matching documents using less than criteria.
- $lte: less than or equal to is similar to $lt but also matches for equality.
- $gt: greater than is used for matching documents using greater than criteria.
- $gte: greater than or equal to is similar to $gt but also matches for equality.
- $not: not is used for matching documents using not equal criteria.
- $regex: regex is used for matching documents using a regular expression.
- $contains: contains is used for matching documents with fields containing the given substring.
For multiple conditions, there are two logical operators supported.
- $or: Combines multiple filter operators and returns documents when either condition is met.
- $and: Combines multiple filter operators and returns documents when all the conditions are met.
Filter examples
The first step is to create the collection object.
const catalog = db.getCollection<Catalog>("catalog");
Assuming an e-commerce website that has the above collection catalog and has 5 products(documents) in it.
id | name | price | brand | labels | popularity | reviews |
---|---|---|---|---|---|---|
1 | fiona handbag | 99.9 | michael kors | purses | 8 | {"author": "alice", "rating": 7} |
2 | tote bag | 49 | coach | handbags | 9 | {"author": "olivia", "rating": 8.3} |
3 | sling bag | 75 | coach | purses | 9 | {"author": "alice", "rating": 9.2} |
4 | sneakers shoes | 40 | adidas | shoes | 10 | {"author": "olivia", "rating": 9} |
5 | running shoes | 89 | nike | shoes | 10 | {"author": "olivia", "rating": 8.5} |
Find one
A straightforward query is to read document by applying a filter on a field. As an example, using filter
in above collection to get a product where brand = "adidas"
const product = await catalog.findOne({ filter: { brand: "adidas" } });
console.log(product);
Above query would return a single document matching the filter. To read multiple documents:
const productsCursor = catalog.findMany({
filter: {
brand: "adidas",
},
});
for await (const product of productsCursor) {
console.log(product);
}
Find many
Or to read all documents from the collection:
const productsCursor = catalog.findMany();
for await (const product of productsCursor) {
console.log(product);
}
By default, string comparison is case-sensitive. To learn, how to make case-insensitive queries, see the case-insensitive queries section.
findMany() operation returns a Cursor that provides access to data. Cursor fundamentals section explains in detail the different ways to retrieve data from Cursor.
Filtering on multiple fields
Single field filtering is useful but what if you need to also filter by price
. Following is an example where we are
reading all the products where brand = "adidas"
and price < 50
const productsCursor = catalog.findMany({
filter: {
"$and": [
{
brand: "adidas",
},
{
price: {
"$lt": 50,
},
},
]
},
});
for await (const product of productsCursor) {
console.log(product);
}
Reading specific fields
Instead of reading all the fields of a document, fields projection allows reading specific fields. As an above example,
let's say you only need to read name
, price
and brand
fields from a document.
const productsCursor = catalog.findMany({
filter: {
"$and": [
{
brand: "adidas",
},
{
price: {
"$lt": 50,
},
},
]
},
fields: {
include: ["name", "price", "brand"],
},
});
for await (const product of productsCursor) {
console.log(product);
}
Users can either specify include
fields or exclude
fields, but not both.
Applying range conditions
Many times the need for filtering is based on range on a numeric field. A range can be applied to any numeric field and
in fact multiple numeric fields can be part of a single filter. Let’s take an example of reading all the products that
are price
less than 50 and have a popularity
score of greater than or equal to 8.
const productsCursor = catalog.findMany({
filter: {
"$and": [
{
price: {
"$lt": 50,
},
popularity: {
"$gte": 8,
}
},
]
},
});
for await (const product of productsCursor) {
console.log(product);
}
Querying by metadata
Tigris automatically generates following metadata fields for the document:
- created_at: Time when this document was added to database.
- updated_at: Time when this document was last modified. This field is only generated once an existing document is modified.
These generated fields are queryable by user. For example, to fetch documents inserted within a 24 hour period:
const productsCursor = catalog.findMany({
filter: {
"$and": [
{
created_at: {
"$gte": "2022-01-01T17:29:28.000Z",
},
},
{
created_at: {
"$lt": "2022-01-01T17:29:28.000Z",
},
}
],
},
});
for await (const product of productsCursor) {
console.log(product);
}
Applying multiple logical filters
Even after applying multiple AND conditions, what if you need something even more complex? What about reading documents
where we need a logical OR
on brand
but also need to apply logical AND
on some other fields. Let's read products where the
brand
is either "adidas" or "coach" but the price
should be less than 50 and the product should be popular
.
const rangeFilter: Filter<Catalog> = {
"$or": [
{
price: {
"$lt": 50,
},
popularity: {
"$gte": 8,
}
},
{
"$and": [
{
price: {
"$lt": 50,
},
},
{
popularity: {
"$gte": 8,
}
}
]
},
]
};
const productsCursor = catalog.findMany({ filter: rangeFilter });
for await (const product of productsCursor) {
console.log(product);
}
Querying nested fields
As we can see all the above examples are for top level fields but what if you have an object, and you want to filter
documents based on one of the nested field. Taking the above data, if you want to get all the products which have labels
set as "shoes" but should have rating
greater than 7.
const productsCursor = catalog.findMany({
filter: {
"$and": [
{
labels: "shoes",
},
{
"reviews.ratings": {
"$gt": 7,
},
},
],
},
});
for await (const product of productsCursor) {
console.log(product);
}
Case-insensitive queries
By default, all String comparisons are case-sensitive. However, if you need to ignore the case
then set the case to ci
in the collation object. The following example demonstrates a case-insensitive
query for brand
"Adidas"
const productsCursor = catalog.findMany({
filter: { brand: "Adidas" },
options: { collation: Case.CaseInsensitive },
});
for await (const product of productsCursor) {
console.log(product);
}
Above query will match terms ["adidas", "aDiDas", "Adidas", "adiDas"]
etc.
Sort
You can also apply a sort to the query results by specifying the field to sort and the sort order:
- $asc: Sort ascending
- $desc: Sort decending
Sort examples
The first step is to create the collection object.
const catalog = db.getCollection<Catalog>("catalog");
Assuming an e-commerce website that has the above collection catalog and has 5 products(documents) in it.
id | name | price | brand | labels | popularity | reviews |
---|---|---|---|---|---|---|
1 | fiona handbag | 99.9 | michael kors | purses | 8 | {"author": "alice", "rating": 7} |
2 | tote bag | 49 | coach | handbags | 9 | {"author": "olivia", "rating": 8.3} |
3 | sling bag | 75 | coach | purses | 9 | {"author": "alice", "rating": 9.2} |
4 | sneakers shoes | 40 | adidas | shoes | 10 | {"author": "olivia", "rating": 9} |
5 | running shoes | 89 | nike | shoes | 10 | {"author": "olivia", "rating": 8.5} |
You can specify the field the results are to be sorted on and the order in which they are sorted. You can also sort on more than one field.
const byPopularityDescCursor = catalog.findMany({
sort: { field: "popularity", order: "$desc" },
});
for await (const product of byPopularityDescCursor) {
console.log(product);
}
const byPriceAscCursor = catalog.findMany({
sort: { field: "price", order: "$asc" },
});
for await (const product of byPriceAscCursor) {
console.log(product);
}
const multiSortCursor = catalog.findMany({
sort: [
{ field: "price", order: "$asc" },
{ field: "popularity", order: "$desc" },
],
});
for await (const product of multiSortCursor) {
console.log(product);
}
Pagination
You set the following options to enable pagination of query results:
- new FindQueryOptions(limit?: number): Limit the number of results
- new FindQueryOptions(limit?: number, skip: number): Skip a number or results
Pagination example
// Page 1 with 100 results
const firstPageCursor = catalog.findMany({
filter: { brand: "Adidas" },
// limit 100 and skip 0
options: new FindQueryOptions(100, 0),
});
for await (const product of firstPageCursor) {
console.log(product);
}
// Page 2 with 100 results
const secondPageCursor = catalog.findMany({
filter: { brand: "Adidas" },
// limit 100 and skip the 100
options: new FindQueryOptions(100, 100),
});
for await (const product of secondPageCursor) {
console.log(product);
}