Skip to content

Commit a31fddf

Browse files
aninibreadOxyjun
andauthored
metadata filtering support (#21870)
* metadata filtering support * changelog * url update * redirect * add link to autorag * Update public/__redirects Co-authored-by: Jun Lee <[email protected]> * PCX Review * update changelog date and content * update cta * ms timestamp --------- Co-authored-by: Jun Lee <[email protected]>
1 parent 5253130 commit a31fddf

File tree

17 files changed

+402
-130
lines changed

17 files changed

+402
-130
lines changed

public/__redirects

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@
183183
# api-shield
184184
/api-shield/security/sequential-abuse-detection/ /api-shield/security/sequence-analytics/ 301
185185

186+
#autorag
187+
/autorag/usage/recipes/ /autorag/how-to/ 301
188+
186189
# bots
187190
/bots/about/plans/ /bots/plans/ 301
188191
/bots/about/plans/biz-and-ent/ /bots/plans/biz-and-ent/ 301
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: Metadata filtering and multitenancy support in AutoRAG
3+
description: Add metadata filters to AutoRAG queries to enable multitenancy and control the scope of retrieved results.
4+
products:
5+
- autorag
6+
date: 2025-04-23T6:00:00Z
7+
---
8+
9+
You can now filter [AutoRAG](/autorag) search results by `folder` and `modified_date` using [metadata filtering](/autorag/configuration/metadata-filtering/) to narrow down the scope of your query.
10+
11+
This makes it easy to build [multitenant experiences](/autorag/how-to/multitenancy/) where each user can only access their own data. By organizing your content into per-tenant folders and applying a `folder` filter at query time, you ensure that each tenant retrieves only their own documents.
12+
13+
**Example folder structure:**
14+
15+
```bash
16+
customer-a/logs/
17+
customer-a/contracts/
18+
customer-b/contracts/
19+
```
20+
21+
**Example query:**
22+
23+
```js
24+
const response = await env.AI.autorag("my-autorag").search({
25+
query: "When did I sign my agreement contract?",
26+
filters: {
27+
type: "eq",
28+
key: "folder",
29+
value: "customer-a/contracts/",
30+
},
31+
});
32+
```
33+
34+
You can use metadata filtering by creating a new AutoRAG or reindexing existing data. To reindex all content in an existing AutoRAG, update any chunking setting and select **Sync index**. Metadata filtering is available for all data indexed on or after **April 21, 2025**.
35+
36+
If you are new to AutoRAG, get started with the [Get started AutoRAG guide](/autorag/get-started/).
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
---
2+
pcx_content_type: concept
3+
title: Metadata filtering
4+
sidebar:
5+
order: 6
6+
---
7+
8+
Metadata filtering narrows down search results based on metadata, so only relevant content is retrieved. The filter narrows down results prior to retrieval, so that you only query the scope of documents that matter.
9+
10+
Here is an example of metadata filtering using [Workers Binding](/autorag/usage/workers-binding/) but it can be easily adapted to use the [REST API](/autorag/usage/rest-api/) instead.
11+
12+
```js
13+
const answer = await env.AI.autorag("my-autorag").search({
14+
query: "How do I train a llama to deliver coffee?",
15+
filters: {
16+
type: "and",
17+
filters: [
18+
{
19+
type: "eq",
20+
key: "folder",
21+
value: "llama/logistics/",
22+
},
23+
{
24+
type: "gte",
25+
key: "modified_date",
26+
value: "1735689600000", // unix timestamp for 2025-01-01
27+
},
28+
],
29+
},
30+
});
31+
```
32+
33+
## Metadata attributes
34+
35+
You can currently filter by the `folder` and `modified_date` of an R2 object. Currently, custom metadata attributes are not supported.
36+
37+
### `folder`
38+
39+
The directory to the object. For example, the `folder` of the object at `llama/logistics/llama-logistics.mdx` is `llama/logistics/`. Note that the `folder` does not include a leading `/`.
40+
41+
Note that `folder` filter only includes files exactly in that folder, so files in subdirectories are not included. For example, specifying `folder: "llama/"` will match files in `llama/` but does not match files in `llama/logistics`.
42+
43+
### `modified_date`
44+
45+
The timestamp indicating when the object was last modified. Comparisons are supported using a 13-digit Unix timestamp (milliseconds), but values will be rounded to 10 digits (seconds). For example, `1735689600999` or `2025-01-01 00:00:00.999 UTC` will be rounded down to `1735689600000`, corresponding to `2025-01-01 00:00:00 UTC`.
46+
47+
## Filter schema
48+
49+
You can create simple comparison filters or an array of comparison filters using a compound filter.
50+
51+
### Comparison filter
52+
53+
You can compare a metadata attribute (for example, `folder` or `modified_date`) with a target value using a comparison filter.
54+
55+
```js
56+
filters: {
57+
type: "operator",
58+
key: "metadata_attribute",
59+
value: "target_value"
60+
}
61+
```
62+
63+
The available operators for the comparison are:
64+
65+
| Operator | Description |
66+
| -------- | ------------------------- |
67+
| `eq` | Equals |
68+
| `ne` | Not equals |
69+
| `gt` | Greater than |
70+
| `gte` | Greater than or equals to |
71+
| `lt` | Less than |
72+
| `lte` | Less than or equals to |
73+
74+
### Compound filter
75+
76+
You can use a compound filter to combine multiple comparison filters with a logical operator.
77+
78+
```js
79+
filters: {
80+
type: "compound_operator",
81+
filters: [...]
82+
}
83+
```
84+
85+
The available compound operators are: `and`, `or`.
86+
87+
Note the following limitations with the compound operators:
88+
89+
- No nesting combinations of `and`'s and `or`'s, meaning you can only pick 1 `and` or 1 `or`.
90+
- When using `or`:
91+
- Only the `eq` operator is allowed.
92+
- All conditions must filter on the **same key** (for example, all on `folder`)
93+
94+
## Response
95+
96+
You can see the metadata attributes of your retrieved data in the response under the property `attributes` for each retrieved chunk. For example:
97+
98+
```js
99+
"data": [
100+
{
101+
"file_id": "llama001",
102+
"filename": "llama/logistics/llama-logistics.md",
103+
"score": 0.45,
104+
"attributes": {
105+
"modified_date": 1735689600000, // unix timestamp for 2025-01-01
106+
"folder": "llama/logistics/",
107+
},
108+
"content": [
109+
{
110+
"id": "llama001",
111+
"type": "text",
112+
"text": "Llamas can carry 3 drinks max."
113+
}
114+
]
115+
}
116+
]
117+
```

src/content/docs/autorag/get-started.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: Get started
2+
title: Getting started
33
pcx_content_type: get-started
44
sidebar:
55
order: 2
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
---
2+
pcx_content_type: concept
3+
title: Bring your own generation model
4+
sidebar:
5+
order: 5
6+
---
7+
8+
import {
9+
Badge,
10+
Description,
11+
Render,
12+
TabItem,
13+
Tabs,
14+
WranglerConfig,
15+
MetaInfo,
16+
Type,
17+
} from "~/components";
18+
19+
When using `AI Search`, AutoRAG leverages a Workers AI model to generate the response. If you want to use a model outside of Workers AI, you can use AutoRAG for search while leveraging a model outside of Workers AI to generate responses.
20+
21+
Here is an example of how you can use an OpenAI model to generate your responses. This example uses [Workers Binding](/autorag/usage/workers-binding/), but can be easily adapted to use the [REST API](/autorag/usage/rest-api/) instead.
22+
23+
```ts
24+
import { openai } from "@ai-sdk/openai";
25+
import { generateText } from "ai";
26+
27+
export interface Env {
28+
AI: Ai;
29+
OPENAI_API_KEY: string;
30+
}
31+
32+
export default {
33+
async fetch(request, env): Promise<Response> {
34+
// Parse incoming url
35+
const url = new URL(request.url);
36+
37+
// Get the user query or default to a predefined one
38+
const userQuery =
39+
url.searchParams.get("query") ??
40+
"How do I train a llama to deliver coffee?";
41+
42+
// Search for documents in AutoRAG
43+
const searchResult = await env.AI.autorag("my-rag").search({
44+
query: userQuery,
45+
});
46+
47+
if (searchResult.data.length === 0) {
48+
// No matching documents
49+
return Response.json({ text: `No data found for query "${userQuery}"` });
50+
}
51+
52+
// Join all document chunks into a single string
53+
const chunks = searchResult.data
54+
.map((item) => {
55+
const data = item.content
56+
.map((content) => {
57+
return content.text;
58+
})
59+
.join("\n\n");
60+
61+
return `<file name="${item.filename}">${data}</file>`;
62+
})
63+
.join("\n\n");
64+
65+
// Send the user query + matched documents to openai for answer
66+
const generateResult = await generateText({
67+
model: openai("gpt-4o-mini"),
68+
messages: [
69+
{
70+
role: "system",
71+
content:
72+
"You are a helpful assistant and your task is to answer the user question using the provided files.",
73+
},
74+
{ role: "user", content: chunks },
75+
{ role: "user", content: userQuery },
76+
],
77+
});
78+
79+
// Return the generated answer
80+
return Response.json({ text: generateResult.text });
81+
},
82+
} satisfies ExportedHandler<Env>;
83+
```
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
pcx_content_type: navigation
3+
title: How to
4+
sidebar:
5+
order: 4
6+
group:
7+
hideIndex: true
8+
---
9+
10+
import { DirectoryListing } from "~/components";
11+
12+
<DirectoryListing />
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
pcx_content_type: concept
3+
title: Create multitenancy
4+
sidebar:
5+
order: 5
6+
---
7+
8+
AutoRAG supports multitenancy by letting you segment content by tenant, so each user, customer, or workspace can only access their own data. This is typically done by organizing documents into per-tenant folders and applying [metadata filters](/autorag/configuration/metadata-filtering/) at query time.
9+
10+
## 1. Organize Content by Tenant
11+
12+
When uploading files to R2, structure your content by tenant using unique folder paths.
13+
14+
Example folder structure:
15+
16+
```bash
17+
customer-a/logs/
18+
customer-a/contracts/
19+
customer-b/contracts/
20+
```
21+
22+
When indexing, AutoRAG will automatically store the folder path as metadata under the `folder` attribute. It is recommended to enforce folder separation during upload or indexing to prevent accidental data access across tenants.
23+
24+
## 2. Search Using Folder Filters
25+
26+
To ensure a tenant only retrieves their own documents, apply a `folder` filter when performing a search.
27+
28+
Example using [Workers Binding](/autorag/usage/workers-binding/):
29+
30+
```js
31+
const response = await env.AI.autorag("my-autorag").search({
32+
query: "When did I sign my agreement contract?",
33+
filters: {
34+
type: "eq",
35+
key: "folder",
36+
value: `customer-a/contracts/`,
37+
},
38+
});
39+
```
40+
41+
To filter across multiple folders, or to add date-based filtering, you can use a compound filter with an array of [comparison filters](/autorag/configuration/metadata-filtering/#compound-filter).
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
pcx_content_type: concept
3+
title: Create a simple search engine
4+
sidebar:
5+
order: 5
6+
---
7+
8+
By using the `search` method, you can implement a simple but fast search engine. This example uses [Workers Binding](/autorag/usage/workers-binding/), but can be easily adapted to use the [REST API](/autorag/usage/rest-api/) instead.
9+
10+
To replicate this example remember to:
11+
12+
- Disable `rewrite_query`, as you want to match the original user query
13+
- Configure your AutoRAG to have small chunk sizes, usually 256 tokens is enough
14+
15+
```ts
16+
export interface Env {
17+
AI: Ai;
18+
}
19+
20+
export default {
21+
async fetch(request, env): Promise<Response> {
22+
const url = new URL(request.url);
23+
const userQuery =
24+
url.searchParams.get("query") ??
25+
"How do I train a llama to deliver coffee?";
26+
const searchResult = await env.AI.autorag("my-rag").search({
27+
query: userQuery,
28+
rewrite_query: false,
29+
});
30+
31+
return Response.json({
32+
files: searchResult.data.map((obj) => obj.filename),
33+
});
34+
},
35+
} satisfies ExportedHandler<Env>;
36+
```

src/content/docs/autorag/index.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ Automatically and continuously index your data source, keeping your content fres
5555

5656
</Feature>
5757

58+
<Feature header="Multitenancy support" href="/autorag/how-to/multitenancy/" cta="Add filters">
59+
60+
Create multitenancy by scoping search to each tenant’s data using folder-based metadata filters.
61+
62+
</Feature>
63+
5864
<Feature header="Workers Binding" href="/autorag/usage/workers-binding/" cta="Add to Worker">
5965

6066
Call your AutoRAG instance for search or AI Search directly from a Cloudflare Worker using the native binding integration.

0 commit comments

Comments
 (0)