import{DataStore}from"@routier/datastore";import{s}from"@routier/core/schema";import{MemoryPlugin}from"@routier/memory-plugin";constproductSchema=s.define("products",{_id:s.string().key().identity(),name:s.string(),price:s.number(),category:s.string(),inStock:s.boolean(),tags:s.string("computer","accessory").array(),createdDate:s.date().default(()=>newDate())}).compile();classAppDataStoreextendsDataStore{products=this.collection(productSchema).create();constructor(){super(newMemoryPlugin("app"));}}constdataStore=newAppDataStore();// Multiple where clauses (AND logic)constexpensiveElectronics=awaitdataStore.products.where(p=>p.price>100).where(p=>p.category==="electronics").toArrayAsync();
Parameterized Queries
Use parameters for dynamic filtering with variables. This is required when you want to use variables in your query predicates.
Why Parameterized Queries?
When you need to use variables in your query, you must use parameterized queries. Direct variable usage in predicates will still work, but Routier will fall back to selecting all records because it cannot evaluate the variable values:
>
1
2
3
4
5
6
// ⚠️ This works but selects ALL records first, then filters in memory - less efficientconstminPrice=100;constmaxPrice=500;constproducts=awaitdataStore.products.where((p)=>p.price>=minPrice&&p.price<=maxPrice)// Selects all, filters in memory.toArrayAsync();
Result: You’ll get the correct filtered results, but Routier will first load all records into memory, then apply the filter. This is less efficient than database-level filtering.
You can build queries dynamically by assigning query results back to a variable and chaining additional operations conditionally:
import{DataStore}from"@routier/datastore";import{s}from"@routier/core/schema";import{MemoryPlugin}from"@routier/memory-plugin";constproductSchema=s.define("products",{id:s.string().key().identity(),name:s.string(),category:s.string(),price:s.number(),inStock:s.boolean(),}).compile();classAppDataStoreextendsDataStore{products=this.collection(productSchema).create();constructor(){super(newMemoryPlugin("app"));}}constdataStore=newAppDataStore();// Building a query dynamically based on logic// In this example, we build a query by conditionally adding filtersconstminPrice=100;constcategoryFilter="electronics";// Convert collection to QueryableAsync for building queries dynamicallyletquery=dataStore.products.toQueryable();// Build query conditionally - only add filters that applyif(categoryFilter){query=query.where(([p,params])=>p.category===params.categoryFilter,{categoryFilter});}if(minPrice>0){query=query.where(([p,params])=>p.price>=params.minPrice,{minPrice});}// Execute the built queryconstresults=awaitquery.toArrayAsync();
Key Pattern
Start with a base query and conditionally add filters:
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Start with base collectionletquery=dataStore.products;// Conditionally add filters based on logic// Always use parameterized queries when using variablesif(shouldFilterByCategory){query=query.where(([p,params])=>p.category===params.category,{category:"electronics",});}if(minPrice>0){query=query.where(([p,params])=>p.price>=params.minPrice,{minPrice,});}// Execute after buildingconstresults=awaitquery.toArrayAsync();
Filtering by Arrays
Use parameterized queries with includes() to filter by multiple values:
This pattern is especially useful when building queries in loops or based on conditional logic, as seen in Routier’s internal view computation system.
Notes
where supports either a simple predicate (item) => boolean or a parameterized predicate (item, params) => boolean with a params object
Use parameterized queries when you need variables - non-parameterized queries with variables will select all records and filter in memory (less efficient)
Multiple where clauses are combined with AND logic
For OR logic, use a single where with || operators inside the predicate