TenantsDB Search indexes every write that passes through the proxy and makes it instantly queryable. A single request hits PostgreSQL tables, MySQL tables, MongoDB collections, and Redis hashes at the same time, returning ranked results from all of them in one response.
Tenant isolation is not a query-time check. Each tenant's data is written into its own partitioned namespace at index time. There is no filter that could be misconfigured or bypassed. When a tenant searches, the engine physically only has access to their namespace.
Your application does not change. No SDK to add, no index to maintain, no sync job to run. Every INSERT, UPDATE, or document write is picked up automatically.
# Write a row (OQL or native SQL - does not matter) $ tdb query --tenant demo --blueprint App1 \ --query ':CREATE User WITH name:"OQLTest1", email:"[email protected]"' # Search for it immediately - no delay, no setup $ tdb search --tenant demo --query "OQLTest1" DATABASE COLLECTION ID SCORE CREATED CONTENT PostgreSQL users 50 3.093 Mar 05 00:15 email: [email protected] | name: OQLTest1 1 results in 112ms
Most search solutions assume a single data source - one database, one schema, one tenant. Scaling that to multi-tenant means writing an indexing pipeline per database type, partitioning the index per tenant, keeping everything in sync as schemas evolve, and making sure no tenant's query ever touches another's data. That is a significant engineering project on its own.
TenantsDB Search solves all of that as a single primitive. Because TenantsDB sits between your application and every database, it sees every write across every tenant and every database type. The indexer uses that position to build a unified, tenant-partitioned search index automatically.
Write an indexing pipeline per database type
Partition the index per tenant manually
Maintain index mappings on every schema change
Query each database type separately
Merge and re-rank results in your own code
Pay for and operate extra infrastructure
Nothing to configure
Tenant isolation is structural, not a filter
Schema changes picked up automatically
All database types in one API call
Results ranked and merged for you
Included in the platform, all tiers
POST /tenants/{id}/search from your backend with the authenticated tenant's ID. Results come back with source_db and collection so you can render them differently by type. Each tenant only ever sees their own data.POST /tenants/_all/search to scan across your entire customer base. Find all tenants with overdue invoices, accounts in a specific state, or records matching a compliance keyword. Results include tenant_id so you can act per customer.TenantsDB runs an internal indexer that subscribes to the message bus. Every time a write completes through the proxy, the indexer receives the event and indexes the affected rows or documents into a tenant-partitioned namespace. By the time your application could issue a search, the data is already there.
| Database | Indexed | What Gets Indexed |
|---|---|---|
| ✓ | All rows from all tables. Every column value indexed as a searchable field. id and created_at extracted automatically when present. |
|
| ✓ | Same as PostgreSQL. All rows and columns indexed per write event. | |
| ✓ | Full documents indexed field by field. Nested fields are flattened. GridFS collections are excluded automatically. | |
| ✓ | Hash fields (HSET) indexed. Plain strings, lists, and keys with TTL under 60 seconds are excluded. |
When a write is indexed, it is written into a namespace derived from your project ID and the tenant ID. The namespace is immutable and tied to the data at the moment of indexing. When a search query arrives for tenant acme, the engine resolves the namespace for acme and only that namespace is queried. There is no filter that runs at query time that could be misconfigured, bypassed, or accidentally omitted.
| Tenant Status | POST /tenants/{id}/search | POST /tenants/_all/search |
|---|---|---|
| ready | ✓ Yes | ✓ Yes |
| suspended | ✓ Yes | ✓ Yes |
| deleted (soft) | ✗ No | ✗ No |
| provisioning / migrating | Partial | Partial |
_all to serve per-tenant search results in your product. Always use POST /tenants/{id}/search with the authenticated tenant's ID. The _all endpoint is for your own internal admin and analytics use.Each result includes a score field. Scores are floating-point relevance values computed using TF-IDF weighting: how often the term appears in the document versus how rare the term is across all documents in the namespace. Results are returned sorted by score descending.
| What Increases Score | What Decreases Score |
|---|---|
| Query term appears multiple times in the document | Term is very common across all documents in the namespace |
| Term appears in a short field (high density) | Document has many fields where the term does not appear |
| Exact match on the full query string | Only a partial token match |
Scores are not normalized to a fixed range. A score around 3.0 is typical for a strong single-field match. Use scores to rank results, not as an absolute threshold.
Both databases and collections filters are applied inside the search engine at query time, reducing scope before scoring runs. Filtered searches are faster and return tighter results.
# Search only PostgreSQL and MongoDB, skip MySQL and Redis
$ tdb search --tenant acme --query "Alice" --databases PostgreSQL,MongoDB
# Search only the invoices and orders tables $ tdb search --tenant acme --query "overdue" --collections invoices,orders # Combine both filters $ tdb search --tenant acme --query "overdue" \ --databases PostgreSQL \ --collections invoices,orders
| Field | Type | Description | |
|---|---|---|---|
| query | string | required | Keyword search string. Matched against all indexed field values. |
| databases | string[] | optional | Limit to specific database types: PostgreSQL, MySQL, MongoDB, Redis. |
| collections | string[] | optional | Limit to specific tables or collections (e.g., ["users", "orders"]). |
| limit | int | optional | Maximum results to return. Default: 20. |
curl -X POST https://api.tenantsdb.com/tenants/acme/search \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "NativeCheck1", "databases": ["PostgreSQL"], "limit": 10 }'
{
"success": true,
"total": 1,
"took_ms": 106,
"results": [
{
"source_db": "PostgreSQL",
"collection": "users",
"score": 3.138,
"content": {
"id": 49,
"name": "NativeCheck1",
"email": "[email protected]",
"created_at": "2026-03-04T23:52:00Z"
}
}
]
}
| Field | Type | Description | |
|---|---|---|---|
| total | int | Total number of matching results. | |
| took_ms | int | Search execution time in milliseconds. | |
| results[].source_db | string | Database type this result came from: PostgreSQL, MySQL, MongoDB, or Redis. | |
| results[].collection | string | Table or collection name. | |
| results[].score | float | Relevance score. Higher is a stronger match. Sorted descending. | |
| results[].content | object | The full indexed document. id (or _id for MongoDB) extracted into the ID column. All results include an indexed timestamp in the CREATED column. |
| Flag | Default | Description | |
|---|---|---|---|
| --tenant | required | - | Tenant ID to search, or _all for all active tenants. |
| --query | required | - | Keyword search string. |
| --databases | optional | all | Comma-separated database types (e.g., PostgreSQL,MongoDB). |
| --collections | optional | all | Comma-separated table or collection names (e.g., users,orders). |
| --limit | optional | 20 | Maximum number of results to return. |
| --json | optional | false | Output full raw JSON instead of the formatted table. |
# Search a single tenant $ tdb search --tenant demo --query "OQLTest1" DATABASE COLLECTION ID SCORE CREATED CONTENT PostgreSQL users 50 3.093 Mar 05 00:15 email: [email protected] | name: OQLTest1 1 results in 112ms # Insert with native SQL, then search $ tdb query --tenant demo --blueprint App1 \ --query "INSERT INTO users (name, email) VALUES ('NativeCheck1', '[email protected]')" $ tdb search --tenant demo --query "NativeCheck1" DATABASE COLLECTION ID SCORE CREATED CONTENT PostgreSQL users 49 3.138 Mar 04 23:52 name: NativeCheck1 | email: [email protected] 1 results in 106ms # Filter by database type $ tdb search --tenant acme --query "Alice" --databases PostgreSQL # Filter by collection $ tdb search --tenant acme --query "overdue" --collections invoices,orders # Search all tenants $ tdb search --tenant _all --query "overdue invoice" --collections invoices --limit 50 # Raw JSON output $ tdb search --tenant acme --query "Alice" --json
By default results print as a table. System fields (id, _id, created_at, updated_at) are extracted into dedicated columns. All remaining fields appear in the CONTENT column as key: value pairs, truncated at 100 characters.
Use --json for the full raw API response with all content fields, exact scores, and no truncation. Useful for scripting or piping into other tools.