O
OOR
文档
OOR
文档
简介
Getting Started
安装
快速开始
Query Flow
Core Concepts
Suffix
Schema
Condition Tree
Adapters
SQLite
PostgreSQL / MySQL
Elasticsearch
Integrations
Drizzle Integration
ES Client

#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

#下一步

  • Elasticsearch
  • Drizzle Integration
  • 安装
  • 最短用法
  • 推荐链路
  • 复杂条件
  • 聚合
  • 建议
  • 下一步