搜索API对接指南-05-索引接口(Indexer).md 21.2 KB

搜索API对接指南-05-索引接口(Indexer)

本篇覆盖数据同步/索引构建相关的所有接口(原文第 5 章),用于 external indexerIndexer 服务 的对接。

索引接口

本节内容与 api/routes/indexer.py 中的索引相关服务一致,包含以下接口:

接口 方法 路径 说明
全量重建索引 POST /indexer/reindex 将指定租户所有 SPU 导入 ES(不删现有索引)
增量索引 POST /indexer/index 按 SPU ID 列表索引/删除,支持自动检测删除与显式删除
查询文档 POST /indexer/documents 按 SPU ID 列表查询 ES 文档,不写入 ES
构建 ES 文档(正式) POST /indexer/build-docs 由上游提供 MySQL 行数据,返回 ES-ready 文档,不写 ES
构建 ES 文档(测试) POST /indexer/build-docs-from-db 由本服务查库并构建文档,仅测试/调试用
索引健康检查 GET /indexer/health 检查索引服务与数据库连接状态

5.0 支撑外部 indexer 的三种方式

本服务对外部 indexer 程序(如 Java 索引系统)提供三种对接方式,可按需选择:

方式 说明 适用场景
1)doc 填充接口 调用 POST /indexer/build-docsPOST /indexer/build-docs-from-db,由本服务基于 MySQL 行数据构建 ES 文档(含多语言、向量、规格等),不写入 ES,由调用方自行写入。 希望一站式拿到 ES-ready doc,由己方控制写 ES 的时机与索引名。
2)微服务组合 单独调用翻译向量化外部内容理解服务等能力,由 indexer 程序自己组装 doc 并写入 ES。翻译与向量化见第 7 节;内容理解字段生成已迁移到独立项目,不再由本仓库维护。 需要灵活编排、或希望将 LLM/向量等耗时步骤与主链路解耦(如异步补齐 qanchors/tags)。
3)本服务直接写 ES 调用全量索引 POST /indexer/reindex、增量索引 POST /indexer/index(指定 SPU ID 列表),由本服务从 MySQL 拉数并直接写入 ES。 自建运维、联调或不需要由 Java 写 ES 的场景。
  • 方式 1方式 2 下,ES 的写入方均为外部 indexer(或 Java),职责清晰。
  • 方式 3 下,本服务同时负责读库、构建 doc 与写 ES。

5.1 为租户创建索引

为租户创建索引需要两个步骤:

  1. 创建索引结构(可选,仅在需要更新 mapping 或在新环境首次创建时执行)

    • 使用脚本创建 ES 索引结构(基于 mappings/search_products.json
    • 如果索引已存在,会提示用户确认(会删除现有数据)
  2. 导入数据(必需)

    • 使用全量索引接口 /indexer/reindex 导入数据

创建索引结构(支持多环境 namespace)

# 以 UAT 环境为例:
# 1. 准备 UAT 环境的 .env(包含 UAT 的 ES_HOST/DB_HOST 等)
# 2. 设置环境前缀(也可以直接在 .env 中配置):
export RUNTIME_ENV=uat
export ES_INDEX_NAMESPACE=uat_

# 3. 为 tenant_id=170 创建索引结构
./scripts/create_tenant_index.sh 170

脚本会自动从项目根目录的 .env 文件加载 ES 配置,并根据 ES_INDEX_NAMESPACE 创建:

  • prod 环境(ES_INDEX_NAMESPACE 为空):search_products_tenant_170
  • UAT 环境(ES_INDEX_NAMESPACE=uat_):uat_search_products_tenant_170

注意事项

  • ⚠️ 如果索引已存在,脚本会提示确认,确认后会删除现有数据
  • 创建索引后,必须调用 /indexer/reindex 导入数据
  • 如果只是更新数据而不需要修改索引结构,直接使用 /indexer/reindex 即可

5.2 全量索引接口

  • 端点: POST /indexer/reindex
  • 描述: 全量索引,将指定租户的所有SPU数据导入到ES索引(不会删除现有索引)。推荐仅用于自测/运维场景;生产环境下更推荐由 Java 等上游控制调度与写 ES。

请求参数

{
  "tenant_id": "162",
  "batch_size": 500
}
参数 类型 必填 默认值 说明
tenant_id string Y - 租户ID
batch_size integer N 500 批量导入大小

响应格式

成功响应(200 OK)(示例,实际 index_name 会带上 tenant 和环境前缀):

{
  "success": true,
  "total": 1000,
  "indexed": 1000,
  "failed": 0,
  "elapsed_time": 12.34,
  "index_name": "search_products_tenant_162",
  "tenant_id": "162"
}

错误响应:

  • 400 Bad Request: 参数错误
  • 503 Service Unavailable: 服务未初始化

请求示例

全量索引(不会删除现有索引):

curl -X POST "http://localhost:6004/indexer/reindex" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "162",
    "batch_size": 500
  }'

查看日志:

# 查看API日志(包含索引操作日志)
tail -f logs/api.log

# 或者查看所有日志文件
tail -f logs/*.log

⚠️ 重要提示:如需 创建索引结构,请参考 5.1 为租户创建索引 章节,使用 ./scripts/create_tenant_index.sh <tenant_id>。创建后需要调用 /indexer/reindex 导入数据。

查看索引日志:

索引操作的所有关键信息都会记录到 logs/indexer.log 文件中(JSON 格式),包括:

  • 请求开始和结束时间
  • 租户ID、SPU ID、操作类型
  • 每个SPU的处理状态
  • ES批量写入结果
  • 成功/失败统计和详细错误信息
# 实时查看索引日志(包含全量和增量索引的所有操作)
tail -f logs/indexer.log

# 使用 grep 查询(简单方式)
# 查看全量索引日志
grep "\"index_type\":\"bulk\"" logs/indexer.log | tail -100

# 查看增量索引日志
grep "\"index_type\":\"incremental\"" logs/indexer.log | tail -100

# 查看特定租户的索引日志
grep "\"tenant_id\":\"162\"" logs/indexer.log | tail -100

# 使用 jq 查询(推荐,更精确的 JSON 查询)
# 安装 jq: sudo apt-get install jq 或 brew install jq

# 查看全量索引日志
cat logs/indexer.log | jq 'select(.index_type == "bulk")' | tail -100

# 查看增量索引日志
cat logs/indexer.log | jq 'select(.index_type == "incremental")' | tail -100

# 查看特定租户的索引日志
cat logs/indexer.log | jq 'select(.tenant_id == "162")' | tail -100

# 查看失败的索引操作
cat logs/indexer.log | jq 'select(.operation == "request_complete" and .failed_count > 0)'

# 查看特定SPU的处理日志
cat logs/indexer.log | jq 'select(.spu_id == "123")'

# 查看最近的索引请求统计
cat logs/indexer.log | jq 'select(.operation == "request_complete") | {timestamp, index_type, tenant_id, total_count, success_count, failed_count, elapsed_time}'

5.3 增量索引接口

  • 端点: POST /indexer/index
  • 描述: 增量索引接口,根据指定的SPU ID列表进行索引,直接将数据写入ES。用于增量更新指定商品。推荐仅作为内部/调试入口;正式对接建议改用 /indexer/build-docs,由上游写 ES。

删除说明

  • spu_ids中的SPU:如果数据库deleted=1,自动从ES删除,响应状态为deleted
  • delete_spu_ids中的SPU:直接删除,响应状态为deletednot_foundfailed

请求参数

{
  "tenant_id": "162",
  "spu_ids": ["123", "456", "789"],
  "delete_spu_ids": ["100", "101"]
}
参数 类型 必填 说明
tenant_id string Y 租户ID
spu_ids array[string] N SPU ID列表(1-100个),要索引的SPU。如果为空,则只执行删除操作
delete_spu_ids array[string] N 显式指定要删除的SPU ID列表(1-100个),可选。无论数据库状态如何,都会从ES中删除这些SPU

注意

  • spu_idsdelete_spu_ids 不能同时为空
  • 每个列表最多支持100个SPU ID
  • 如果SPU在spu_ids中且数据库deleted=1,会自动从ES删除(自动检测删除)

响应格式

{
  "spu_ids": [
    {
      "spu_id": "123",
      "status": "indexed"
    },
    {
      "spu_id": "456",
      "status": "deleted"
    },
    {
      "spu_id": "789",
      "status": "failed",
      "msg": "SPU not found (unexpected)"
    }
  ],
  "delete_spu_ids": [
    {
      "spu_id": "100",
      "status": "deleted"
    },
    {
      "spu_id": "101",
      "status": "not_found"
    },
    {
      "spu_id": "102",
      "status": "failed",
      "msg": "Failed to delete from ES: Connection timeout"
    }
  ],
  "total": 6,
  "success_count": 4,
  "failed_count": 2,
  "elapsed_time": 1.23,
  "index_name": "search_products",
  "tenant_id": "162"
}
字段 类型 说明
spu_ids array spu_ids对应的响应列表,每个元素包含 spu_idstatus
spu_ids[].status string 状态:indexed(已索引)、deleted(已删除,自动检测)、failed(失败)
spu_ids[].msg string 当status为failed时,包含失败原因(可选)
delete_spu_ids array delete_spu_ids对应的响应列表,每个元素包含 spu_idstatus
delete_spu_ids[].status string 状态:deleted(已删除)、not_found(ES中不存在)、failed(失败)
delete_spu_ids[].msg string 当status为failed时,包含失败原因(可选)
total integer 总处理数量(spu_ids数量 + delete_spu_ids数量)
success_count integer 成功数量(indexed + deleted + not_found)
failed_count integer 失败数量
elapsed_time float 耗时(秒)
index_name string 索引名称
tenant_id string 租户ID

状态说明

  • spu_ids 的状态:
    • indexed: SPU已成功索引到ES
    • deleted: SPU在数据库中被标记为deleted=1,已从ES删除(自动检测)
    • failed: 处理失败,会包含msg字段说明失败原因
  • delete_spu_ids 的状态:
    • deleted: SPU已从ES成功删除
    • not_found: SPU在ES中不存在(也算成功,可能已经被删除过)
    • failed: 删除失败,会包含msg字段说明失败原因

请求示例

示例1:普通增量索引(自动检测删除):

curl -X POST "http://localhost:6004/indexer/index" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "162",
    "spu_ids": ["123", "456", "789"]
  }'

说明:如果SPU 456在数据库中deleted=1,会自动从ES删除,在响应中spu_ids列表里456的状态为deleted

示例2:显式删除(批量删除):

curl -X POST "http://localhost:6004/indexer/index" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "162",
    "spu_ids": ["123", "456"],
    "delete_spu_ids": ["100", "101", "102"]
  }'

说明:SPU 100、101、102会被显式删除,无论数据库状态如何。

示例3:仅删除(不索引):

curl -X POST "http://localhost:6004/indexer/index" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "162",
    "spu_ids": [],
    "delete_spu_ids": ["100", "101"]
  }'

说明:只执行删除操作,不进行索引。

示例4:混合操作(索引+删除):

curl -X POST "http://localhost:6004/indexer/index" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "162",
    "spu_ids": ["123", "456", "789"],
    "delete_spu_ids": ["100", "101"]
  }'

说明:同时执行索引和删除操作。

日志说明

增量索引操作的所有关键信息都会记录到 logs/indexer.log 文件中(JSON格式),包括:

  • 请求开始和结束时间
  • 每个SPU的处理状态(获取、转换、索引、删除)
  • ES批量写入结果
  • 成功/失败统计
  • 详细的错误信息

日志查询方式请参考5.1节查看索引日志部分。

5.4 查询文档接口

  • 端点: POST /indexer/documents
  • 描述: 查询文档接口,根据SPU ID列表获取ES文档数据(不写入ES)。用于查看、调试或验证SPU数据。

请求参数

{
  "tenant_id": "162",
  "spu_ids": ["123", "456", "789"]
}
参数 类型 必填 说明
tenant_id string Y 租户ID
spu_ids array[string] Y SPU ID列表(1-100个)

响应格式

{
  "success": [
    {
      "spu_id": "123",
      "document": {
        "tenant_id": "162",
        "spu_id": "123",
        "title": {
          "zh": "商品标题"
        },
        ...
      }
    },
    {
      "spu_id": "456",
      "document": {...}
    }
  ],
  "failed": [
    {
      "spu_id": "789",
      "error": "SPU not found or deleted"
    }
  ],
  "total": 3,
  "success_count": 2,
  "failed_count": 1
}
字段 类型 说明
success array 成功获取的SPU列表,每个元素包含 spu_iddocument(完整的ES文档数据)
failed array 失败的SPU列表,每个元素包含 spu_iderror(失败原因)
total integer 总SPU数量
success_count integer 成功数量
failed_count integer 失败数量

请求示例

单个SPU查询:

curl -X POST "http://localhost:6004/indexer/documents" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "162",
    "spu_ids": ["123"]
  }'

批量SPU查询:

curl -X POST "http://localhost:6004/indexer/documents" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "162",
    "spu_ids": ["123", "456", "789"]
  }'

/indexer/index 的区别

接口 功能 是否写入ES 返回内容
/indexer/documents 查询SPU文档数据 返回完整的ES文档数据
/indexer/index 增量索引 返回成功/失败列表和统计信息

使用场景

  • /indexer/documents:用于查看、调试或验证SPU数据,不修改ES索引
  • /indexer/index:用于实际的增量索引操作,将更新的SPU数据同步到ES

5.5 索引健康检查接口

  • 端点: GET /indexer/health
  • 描述: 检查索引服务健康状态(与 api/routes/indexer.pyindexer_health_check 一致)

响应格式

{
  "status": "available",
  "database": "connected",
  "preloaded_data": {
    "category_mappings": 150
  }
}
字段 类型 说明
status string available(服务可用)、unavailable(未初始化)、error(异常)
database string 数据库连接状态,如 connecteddisconnected: ...
preloaded_data.category_mappings integer 已加载的分类映射数量

请求示例

curl -X GET "http://localhost:6004/indexer/health"

5.6 文档构建接口(正式对接推荐)

5.6.1 POST /indexer/build-docs

  • 描述:
    基于调用方(通常是 Java 索引程序)提供的 MySQL 行数据 构建 ES 文档(doc),不写入 ES
    由本服务负责“如何构建 doc”(多语言、翻译、向量、规格聚合等),由调用方负责“何时调度 + 如何写 ES”。

请求参数

{
  "tenant_id": "170",
  "items": [
    {
      "spu": { "id": 223167, "tenant_id": 170, "title": "..." },
      "skus": [
        { "id": 3988393, "spu_id": 223167, "price": 25.99, "compare_at_price": 25.99 }
      ],
      "options": []
    }
  ]
}
参数 类型 必填 说明
tenant_id string Y 租户 ID
items array Y 需构建 doc 的 SPU 列表(每项含 spuskusoptions),单次最多 200 条

spu / skus / options 字段应当直接使用从 shoplazza_product_spu / shoplazza_product_sku / shoplazza_product_option 查询出的行字段。

请求示例(完整 curl)

完整请求体参考 tests/manual/test_build_docs_api.py 中的 build_sample_request()

# 单条 SPU 示例(含 spu、skus、options)
curl -X POST "http://localhost:6004/indexer/build-docs" \
  -H "Content-Type: application/json" \
  -d '{
  "tenant_id": "162",
  "items": [
    {
      "spu": {
        "id": 10001,
        "title": "测试T恤 纯棉短袖",
        "brief": "舒适纯棉,多色可选",
        "description": "这是一款适合日常穿着的纯棉T恤,透气吸汗。",
        "vendor": "测试品牌",
        "category": "服装/上衣/T恤",
        "category_id": 100,
        "category_level": 2,
        "category_path": "服装/上衣/T恤",
        "fake_sales": 1280,
        "image_src": "https://oss.essa.cn/98532128-cf8e-456c-9e30-6f2a5ea0c19f.jpg",
        "enriched_tags": ["T恤", "纯棉"],
        "create_time": "2024-01-01T00:00:00Z",
        "update_time": "2024-01-01T00:00:00Z"
      },
      "skus": [
        {
          "id": 20001,
          "spu_id": 10001,
          "price": 99.0,
          "compare_at_price": 129.0,
          "sku": "SKU-TSHIRT-001",
          "inventory_quantity": 50,
          "option1": "黑色",
          "option2": "M",
          "option3": null
        },
        {
          "id": 20002,
          "spu_id": 10001,
          "price": 99.0,
          "compare_at_price": 129.0,
          "sku": "SKU-TSHIRT-002",
          "inventory_quantity": 30,
          "option1": "白色",
          "option2": "L",
          "option3": null
        }
      ],
      "options": [
        {"id": 1, "position": 1, "name": "颜色"},
        {"id": 2, "position": 2, "name": "尺码"}
      ]
    }
  ]
}'

生产环境替换 localhost:6004 为实际 Indexer 地址,如 http://43.166.252.75:6004

响应示例(节选)

{
  "tenant_id": "170",
  "docs": [
    {
      "tenant_id": "170",
      "spu_id": "223167",
      "title": { "en": "...", "zh": "..." },
      "enriched_tags": ["Floerns", "Clothing", "Shoes & Jewelry"],
      "skus": [
        {
          "sku_id": "3988393",
          "price": 25.99,
          "compare_at_price": 25.99,
          "stock": 100
        }
      ],
      "min_price": 25.99,
      "max_price": 25.99,
      "compare_at_price": 25.99,
      "total_inventory": 100,
      "title_embedding": [/* 1024 维向量 */]
      // 其余字段与 mappings/search_products.json 一致
    }
  ],
  "total": 1,
  "success_count": 1,
  "failed_count": 0,
  "failed": []
}
字段 类型 说明
tenant_id string 租户 ID
docs array 构建成功的 ES 文档列表,与 mappings/search_products.json 一致
total integer 请求的 items 总数
success_count integer 成功构建数量
failed_count integer 失败数量
failed array 失败项列表,每项含 spu_iderror

使用建议

  • 生产环境推荐流程
    1. Java 根据业务逻辑决定哪些 SPU 需要(全量/增量)处理;
    2. Java 从 MySQL 查询 SPU/SKU/Option 行,拼成 items
    3. 调用 /indexer/build-docs 获取 ES-ready docs
    4. Java 使用自己的 ES 客户端写入 search_products_tenant_{tenant_id}

5.7 文档构建接口(测试 / 自测)

5.7.1 POST /indexer/build-docs-from-db

  • 描述:
    仅用于测试/调试:调用方只提供 tenant_idspu_ids,由 indexer 服务内部从 MySQL 查询 SPU/SKU/Option,然后调用与 /indexer/build-docs 相同的文档构建逻辑,返回 ES-ready doc。生产环境请使用 /indexer/build-docs,由上游查库并写 ES。

请求参数

{
  "tenant_id": "170",
  "spu_ids": ["223167", "223168"]
}
参数 类型 必填 说明
tenant_id string Y 租户 ID
spu_ids array[string] Y SPU ID 列表,单次最多 200 个

响应格式

/indexer/build-docs 相同:tenant_iddocstotalsuccess_countfailed_countfailed

请求示例

curl -X POST "http://127.0.0.1:6004/indexer/build-docs-from-db" \
  -H "Content-Type: application/json" \
  -d '{"tenant_id": "170", "spu_ids": ["223167"]}'

返回结构与 /indexer/build-docs 相同,可直接用于对比 ES 实际文档或调试字段映射问题。

5.8 内容理解字段生成能力(已迁出)

/indexer/enrich-content 已迁移到独立项目,本仓库当前的 Indexer 服务(默认端口 6004不再暴露该接口,也不再在 /indexer/build-docs/indexer/build-docs-from-db/indexer/reindex/indexer/index 的构建链路里内置生成 qanchorsenriched_attributesenriched_tagsenriched_taxonomy_attributes

当前建议的对接方式:

  1. 调用本仓库的 POST /indexer/build-docsPOST /indexer/build-docs-from-db 生成基础 ES 文档。
  2. 调用独立内容理解服务生成 qanchors / enriched_* 字段。
  3. 由上游索引程序自行合并字段后写入 ES。

补充说明:

  • search_products mapping 仍保留上述字段,便于独立内容理解服务继续产出并写入。
  • suggestion 等消费侧仍可读取 ES 中已有的 qanchors 字段;迁移的是“生成实现”,不是字段模型本身。
  • 本文档不再维护独立内容理解服务的请求/响应细节,请以对应独立项目的文档为准。