Skip to content

Commit 6d4c6b7

Browse files
committed
metadata filtering support
1 parent b79e69a commit 6d4c6b7

File tree

14 files changed

+358
-130
lines changed

14 files changed

+358
-130
lines changed
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 in [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 on 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 for when the object was last modified. The comparison is supported in Unix timestamp format. For example, `value: "1735689600000"` can be used to compare the date of 2025-01-01.
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 (e.g. `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** (e.g. 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 is done using [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: 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+
Using the `search` method you can implement a simple but fast search engine. This example is done using [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+
```
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’s 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).

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)