fix: improve filter schema

This commit is contained in:
Anirudh Parmar 2026-08-16 19:04:59 +05:30
parent e651045ac5
commit 72e6177580
3 changed files with 76 additions and 8 deletions

View file

@ -28,6 +28,7 @@ import {
SettingsRequestSchema,
UpdateContainerTagSettingsRequestSchema,
} from "../validation/api"
import { SearchFiltersSchema } from "../validation/schemas"
const UpdateSettingsResponseSchema = z.object({
orgId: z.string(),
@ -252,6 +253,7 @@ export const apiSchema = createSchema({
page: z.number().optional(),
status: z.string().optional(),
containerTags: z.array(z.string()).optional(),
filters: SearchFiltersSchema.optional(),
})
.optional(),
output: ListMemoriesResponseSchema,

View file

@ -6,17 +6,11 @@ import {
MemoryEntrySchema,
OrganizationSettingsSchema,
RequestTypeEnum,
SearchFiltersSchema,
} from "./schemas"
export const MetadataSchema = BaseMetadataSchema
export const SearchFiltersSchema = z
.object({
AND: z.array(z.unknown()).optional(),
OR: z.array(z.unknown()).optional(),
})
.or(z.record(z.unknown()))
const exampleMetadata: Record<string, string | number | boolean> = {
category: "technology",
isPublic: true,
@ -247,10 +241,18 @@ export const ListMemoriesQuerySchema = z
"Optional tags this memory should be containerized by. This can be an ID for your user, a project ID, or any other identifier you wish to use to group memories.",
example: ["user_123", "project_123"],
}),
// TODO: Improve filter schema
filters: z
.string()
.optional()
.transform((raw,ctx) => {
if (!raw) return undefined
try {
return SearchFiltersSchema.parse(JSON.parse(raw))
} catch {
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "Invalid filters JSON" })
return z.NEVER
}
})
.openapi({
description: "Optional filters to apply to the search",
example: JSON.stringify({

View file

@ -8,6 +8,70 @@ export type Metadata = z.infer<typeof MetadataSchema>
export const VisibilityEnum = z.enum(["public", "private", "unlisted"])
export type Visibility = z.infer<typeof VisibilityEnum>
const MetadataKeySchema = z
.string()
.min(1)
.max(64)
.regex(/^[a-zA-Z0-9_.-]+$/, "Invalid metadata key")
const MetadataValueSchema = z.union([z.string(), z.number(), z.boolean()])
const StringEqualityFilterSchema = z.object({
key: MetadataKeySchema,
value: MetadataValueSchema,
negate: z.boolean().optional(),
})
const StringContainsFilterSchema = z.object({
filterType: z.literal("string_contains"),
key: MetadataKeySchema,
value: z.string(),
negate: z.boolean().optional(),
ignoreCase: z.boolean().optional(),
})
const NumericOperatorSchema = z.enum(["=", "!=", "<", "<=", ">", ">="])
const NumericFilterSchema = z.object({
filterType: z.literal("numeric"),
key: MetadataKeySchema,
value: z.string(),
numericOperator: NumericOperatorSchema,
negate: z.boolean().optional(),
})
const ArrayContainsFilterSchema = z.object({
filterType: z.literal("array_contains"),
key: MetadataKeySchema,
value: z.string(),
negate: z.boolean().optional(),
})
const FilterConditionSchema = z.discriminatedUnion("filterType", [
StringContainsFilterSchema,
NumericFilterSchema,
ArrayContainsFilterSchema,
]).or(StringEqualityFilterSchema)
type FilterExpression =
| z.infer<typeof FilterConditionSchema>
| { AND: FilterExpression[] }
| { OR: FilterExpression[] }
export const FilterExpressionSchema: z.ZodType<FilterExpression> = z.lazy(() =>
z.union([
FilterConditionSchema,
z.object({ AND: z.array(FilterExpressionSchema).min(1) }),
z.object({ OR: z.array(FilterExpressionSchema).min(1) }),
])
)
export const SearchFiltersSchema = z.union([
z.object({ AND: z.array(FilterExpressionSchema).min(1) }),
z.object({ OR: z.array(FilterExpressionSchema).min(1) }),
])
export const DocumentTypeEnum = z.enum([
"text",
"pdf",