#ES Client
oor/es 只生成 query body。
真正执行查询时,直接配合官方 client 即可。
#安装
Bash
pnpm add @elastic/elasticsearch oor zod
#最短用法
TypeScript
import { Client } from '@elastic/elasticsearch'
import { esBody } from 'oor/es'
import { schema } from 'oor'
import { z } from 'zod'
const client = new Client({
node: 'http://127.0.0.1:9200'
})
const query = schema({
status: z.string(),
createdAt: z.date(),
price: z.number()
})
const input = query.parse({
status: 'active',
createdAtDay: '2026-04-08',
page: 1,
size: 20,
sort: 'createdAt',
order: 'desc'
})
const body = esBody(
input,
{
soft: {
mode: 'flag',
field: 'isDeleted',
del: true,
keep: false
}
},
{
map: {
status: 'status.keyword',
createdAt: 'created_at',
isDeleted: 'is_deleted',
price: 'price_cents'
},
sortMap: {
createdAt: 'created_at'
},
track: true
}
)
const res = await client.search({
index: 'order',
body
})
#推荐链路
对 ES,推荐一直走这一条:
TypeScript
raw input -> schema -> esBody -> client.search
这样最稳定。
#复杂条件
如果 suffix 输入不够,先手写 Node,再交给 esBodyNode:
TypeScript
import { esBodyNode } from 'oor/es'
const body = esBodyNode(
{
link: 'OR',
items: [
{ field: 'status', op: 'Equal', value: 'active' },
{ field: 'status', op: 'Equal', value: 'pending' }
]
},
{
page: 1,
size: 10,
sort: 'createdAt',
order: 'desc'
},
undefined,
{
map: {
status: 'status.keyword',
createdAt: 'created_at'
},
sortMap: {
createdAt: 'created_at'
}
}
)
#聚合
TypeScript
import { avg, dateGroup, esBody, terms } from 'oor/es'
const body = esBody(
{ status: 'active', page: 1, size: 0 },
undefined,
{
map: {
status: 'status.keyword',
createdAt: 'created_at',
price: 'price_cents'
},
groups: {
byStatus: terms('status', {
size: 10,
aggs: {
byMonth: dateGroup('createdAt', {
calendar: 'month',
format: 'yyyy-MM'
})
}
}),
avgPrice: avg('price')
}
}
)
#建议
map明确映射到 ES 字段- keyword 字段单独写清楚,例如
status.keyword - 排序字段单独写
sortMap - 把 ES client 放在业务层,
oor/es只负责生成 body