Reusable SearchQuery
RushDB uses one core query object shape across multiple APIs.
You can reuse the same query logic across five perspectives on data:
- Records
- Properties
- Labels
- Relationships
- Values
Once you learn SearchQuery once, you can apply it almost everywhere.
Canonical SearchQuery Shape
- Python
- TypeScript
- shell
# SearchQuery is a plain dict in Python
search_query = {
"labels": ["..."], # optional
"where": {}, # optional
"select": {}, # optional
"groupBy": ["..."], # optional
"orderBy": "asc" | {"field": "asc"}, # optional
"limit": 100, # optional
"skip": 0 # optional
}
type SearchQuery = {
labels?: string[]
where?: Record<string, unknown>
select?: Record<string, unknown>
groupBy?: string[]
orderBy?: 'asc' | 'desc' | Record<string, 'asc' | 'desc'>
limit?: number
skip?: number
}
# SearchQuery is a JSON body passed to any search endpoint
{
"labels": ["..."],
"where": {},
"select": {},
"groupBy": ["..."],
"orderBy": {"field": "asc"},
"limit": 100,
"skip": 0
}
Supported by:
records.find//api/v1/records/searchrecords.delete//api/v1/records/deleterelationships.find//api/v1/relationships/searchlabels.find//api/v1/labels/searchproperties.find//api/v1/properties/search
And paired with values exploration via /api/v1/properties/:id/values.
Why It Matters
- Reduced learning curve
- Reusable filtering logic across endpoints
- Predictable behavior for filtering, aggregation, and pagination
- Easier AI-assisted query generation because the shape stays stable
One Intent, Five Perspectives
SearchQuery's ultimate feature is intent reuse.
You can express one business question and view it from different perspectives:
- Records: return matching entities
- Properties: discover which fields participate in that slice
- Labels: discover which entity types match
- Relationships: inspect graph links within the same slice
- Values: enumerate canonical values for a chosen property in that slice
This means your app can move from listing to discovery to analytics without changing mental model.
Where Clause Essentials
Primitive and operator filters
- Python
- TypeScript
- shell
"where": {
"status": {"$in": ["active", "pending"]},
"amount": {"$gte": 1000, "$lt": 5000},
"name": {"$contains": "acme"},
"isArchived": {"$ne": True}
}
where: {
status: { $in: ['active', 'pending'] },
amount: { $gte: 1000, $lt: 5000 },
name: { $contains: 'acme' },
isArchived: { $ne: true }
}
"where": {
"status": {"$in": ["active", "pending"]},
"amount": {"$gte": 1000, "$lt": 5000},
"name": {"$contains": "acme"},
"isArchived": {"$ne": true}
}
Logical composition
- Python
- TypeScript
- shell
"where": {
"$or": [
{"status": "active"},
{
"$and": [
{"status": "pending"},
{"priority": {"$gte": 8}}
]
}
]
}
where: {
$or: [
{ status: 'active' },
{
$and: [{ status: 'pending' }, { priority: { $gte: 8 } }]
}
]
}
"where": {
"$or": [
{"status": "active"},
{
"$and": [
{"status": "pending"},
{"priority": {"$gte": 8}}
]
}
]
}
Relationship traversal
In SearchQuery, relationship traversal uses label keys directly.
- Python
- TypeScript
- shell
"where": {
"DEPARTMENT": {
"$alias": "$department",
"name": "Engineering",
"PROJECT": {
"$alias": "$project",
"EMPLOYEE": {
"$alias": "$employee",
"role": {"$in": ["Developer", "Lead"]}
}
}
}
}
where: {
DEPARTMENT: {
$alias: '$department',
name: 'Engineering',
PROJECT: {
$alias: '$project',
EMPLOYEE: {
$alias: '$employee',
role: { $in: ['Developer', 'Lead'] }
}
}
}
}
"where": {
"DEPARTMENT": {
"$alias": "$department",
"name": "Engineering",
"PROJECT": {
"$alias": "$project",
"EMPLOYEE": {
"$alias": "$employee",
"role": {"$in": ["Developer", "Lead"]}
}
}
}
}
Important:
- Use the label key itself (
DEPARTMENT,PROJECT,EMPLOYEE) to traverse - Use
$aliasto reference traversed nodes inselectandgroupBy - Do not use unsupported traversal fields such as
$label,$as,$through,$of
Date range rule
For range operators on datetimes, use component objects.
- Python
- TypeScript
- shell
"where": {
"createdAt": {
"$gte": {"$year": 2025, "$month": 1, "$day": 1},
"$lt": {"$year": 2026, "$month": 1, "$day": 1}
}
}
where: {
createdAt: {
$gte: { $year: 2025, $month: 1, $day: 1 },
$lt: { $year: 2026, $month: 1, $day: 1 }
}
}
"where": {
"createdAt": {
"$gte": {"$year": 2025, "$month": 1, "$day": 1},
"$lt": {"$year": 2026, "$month": 1, "$day": 1}
}
}
GroupBy
SearchQuery supports grouped analytics using the select and groupBy clauses.
Per-record flat metrics
- Python
- TypeScript
- shell
rows = db.records.find({
"labels": ["PROJECT"],
"where": {
"EMPLOYEE": {"$alias": "$employee"}
},
"select": {
"projectName": "$record.name",
"headcount": {"$count": "$employee"},
"totalSalary": {"$sum": "$employee.salary"}
},
"limit": 100
})
const rows = await db.records.find({
labels: ['PROJECT'],
where: {
EMPLOYEE: { $alias: '$employee' }
},
select: {
projectName: '$record.name',
headcount: { $count: '$employee' },
totalSalary: { $sum: '$employee.salary' }
},
limit: 100
})
curl -X POST "https://api.rushdb.com/api/v1/records/search" \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"labels": ["PROJECT"],
"where": {"EMPLOYEE": {"$alias": "$employee"}},
"select": {
"projectName": "$record.name",
"headcount": {"$count": "$employee"},
"totalSalary": {"$sum": "$employee.salary"}
},
"limit": 100
}'
Dimensional groupBy
- Python
- TypeScript
- shell
by_status = db.records.find({
"labels": ["ORDER"],
"select": {
"count": {"$count": "*"},
"revenue": {"$sum": "$record.total"}
},
"groupBy": ["$record.status"],
"orderBy": {"revenue": "desc"}
})
const byStatus = await db.records.find({
labels: ['ORDER'],
select: {
count: { $count: '*' },
revenue: { $sum: '$record.total' }
},
groupBy: ['$record.status'],
orderBy: { revenue: 'desc' }
})
curl -X POST "https://api.rushdb.com/api/v1/records/search" \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"labels": ["ORDER"],
"select": {
"count": {"$count": "*"},
"revenue": {"$sum": "$record.total"}
},
"groupBy": ["$record.status"],
"orderBy": {"revenue": "desc"}
}'
Self-group (single-row KPI)
- Python
- TypeScript
- shell
totals = db.records.find({
"labels": ["ORDER"],
"select": {
"totalRevenue": {"$sum": "$record.total"}
},
"groupBy": ["totalRevenue"],
"orderBy": {"totalRevenue": "asc"}
})
const totals = await db.records.find({
labels: ['ORDER'],
select: {
totalRevenue: { $sum: '$record.total' }
},
groupBy: ['totalRevenue'],
orderBy: { totalRevenue: 'asc' }
})
curl -X POST "https://api.rushdb.com/api/v1/records/search" \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"labels": ["ORDER"],
"select": {
"totalRevenue": {"$sum": "$record.total"}
},
"groupBy": ["totalRevenue"],
"orderBy": {"totalRevenue": "asc"}
}'
Critical Limit Rules
- Do not use
limitin self-group KPI queries - Do not use
limitin dimensional groupBy unless intentionally asking for top N groups limitis valid for listing/browsing and per-record flat aggregation- For self-group KPI queries, include
orderByon the select key to force full-scan aggregation behavior
Reusable Patterns Across APIs
1. Search records
- Python
- TypeScript
- shell
result = db.records.find({
"labels": ["PRODUCT"],
"where": {
"active": True,
"price": {"$gte": 10, "$lte": 50},
"tags": {"$in": ["featured"]}
},
"orderBy": {"price": "asc"},
"limit": 20,
"skip": 0
})
products = result.data
const products = await db.records.find({
labels: ['PRODUCT'],
where: {
active: true,
price: { $gte: 10, $lte: 50 },
tags: { $in: ['featured'] }
},
orderBy: { price: 'asc' },
limit: 20,
skip: 0
})
curl -X POST "https://api.rushdb.com/api/v1/records/search" \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"labels": ["PRODUCT"],
"where": {
"active": true,
"price": {
"$gte": 10,
"$lte": 50
},
"tags": { "$in": ["featured"] }
},
"orderBy": {"price": "asc"},
"limit": 20,
"skip": 0
}'
2. Delete records with the same where logic
- Python
- TypeScript
- shell
db.records.delete({
"labels": ["PRODUCT"],
"where": {
"discontinued": True,
"inventory": 0
}
})
await db.records.delete({
labels: ['PRODUCT'],
where: {
discontinued: true,
inventory: 0
}
})
curl -X PUT "https://api.rushdb.com/api/v1/records/delete" \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"labels": ["PRODUCT"],
"where": {
"discontinued": true,
"inventory": 0
}
}'
3. Search relationships
- Python
- TypeScript
- shell
created_relationships = db.relationships.find({
"where": {
"type": "CREATED",
"weight": {"$gte": 0.5}
},
"limit": 50
})
const createdRelationships = await db.relationships.find({
where: {
type: 'CREATED',
weight: { $gte: 0.5 }
},
limit: 50
})
# Find all CREATED relationships by users in the admin group
curl -X POST "https://api.rushdb.com/api/v1/relationships/search" \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"where": {
"type": "CREATED",
"weight": { "$gte": 0.5 }
},
"limit": 50
}'
4. Find labels by data constraints
- Python
- TypeScript
- shell
labels = db.labels.find({
"where": {
"region": {"$in": ["US", "CA", "MX"]}
}
})
const labels = await db.labels.find({
where: {
region: { $in: ['US', 'CA', 'MX'] }
}
})
# Find all labels used on records in North America region
curl -X POST "https://api.rushdb.com/api/v1/labels/search" \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"where": {
"region": {"$in": ["US", "CA", "MX"]}
}
}'
5. Discover properties for a label
- Python
- TypeScript
- shell
product_props = db.properties.find({
"labels": ["PRODUCT"],
"where": {
"type": "number"
}
})
const productProps = await db.properties.find({
labels: ['PRODUCT'],
where: {
type: 'number'
}
})
curl -X POST "https://api.rushdb.com/api/v1/properties/search" \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"labels": ["PRODUCT"],
"where": {
"type": "number"
}
}'
6. Explore values for a selected property
Use values as the final perspective when you need canonical filter options for UI facets, prompts, and dynamic query builders.
Typical flow:
- Use
properties.findto locate a property and get its id - Call
/api/v1/properties/:id/valuesfor value discovery in the same data context - Feed returned canonical values back into your next SearchQuery (
$in,$nin, exact match)
Common Pitfalls to Avoid
- Using
limiton KPI/self-group queries - Using traversal keys like
$labelinstead of real label keys (EMPLOYEE,PROJECT) - Treating
groupByself-group keys as property paths instead ofselectkey names - Using plain date strings with
$gt/$gte/$lt/$lteinstead of datetime component objects
API Reference Links
| Endpoint | Docs |
|---|---|
/api/v1/records/search | Records API |
/api/v1/records/delete | Delete Records |
/api/v1/relationships/search | Relationships API |
/api/v1/labels/search | Labels API |
/api/v1/properties/search | Properties API |
/api/v1/properties/:id/values | Properties API |
Next Step
If you want advanced examples (nested collect, multi-hop traversal, time buckets, KPI patterns), continue with the deep-dive tutorial: