搜索API对接指南.md 24.1 KB

搜索API接口对接指南

本文档为搜索服务的使用方提供完整的API对接指南,包括接口说明、请求参数、响应格式和使用示例。

目录

  1. 快速开始
  2. 接口概览
  3. 文本搜索接口
  4. 图片搜索接口
  5. 响应格式说明
  6. 常见场景示例
  7. 错误处理
  8. 最佳实践

快速开始

基础信息

  • Base URL: http://your-domain:6002http://120.76.41.98:6002
  • 协议: HTTP/HTTPS
  • 数据格式: JSON
  • 字符编码: UTF-8
  • 请求方法: POST(搜索接口)

最简单的搜索请求

curl -X POST "http://localhost:6002/search/" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "芭比娃娃"
  }'

Python示例

import requests

url = "http://localhost:6002/search/"
response = requests.post(url, json={"query": "芭比娃娃"})
data = response.json()
print(f"找到 {data['total']} 个结果")

JavaScript示例

const response = await fetch('http://localhost:6002/search/', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        query: '芭比娃娃'
    })
});
const data = await response.json();
console.log(`找到 ${data.total} 个结果`);

接口概览

接口 方法 路径 说明
文本搜索 POST /search/ 执行文本搜索查询
图片搜索 POST /search/image 基于图片相似度搜索
搜索建议 GET /search/suggestions 获取搜索建议(框架,暂未实现)
获取文档 GET /search/{doc_id} 根据ID获取单个文档
健康检查 GET /admin/health 检查服务状态

文本搜索接口

接口信息

  • 端点: POST /search/
  • 描述: 执行文本搜索查询,支持多语言、布尔表达式、过滤器和分面搜索

请求参数

完整请求体结构

{
  "query": "string (required)",
  "size": 10,
  "from": 0,
  "filters": {},
  "range_filters": {},
  "facets": [],
  "sort_by": "string",
  "sort_order": "desc",
  "min_score": 0.0,
  "debug": false,
  "user_id": "string",
  "session_id": "string"
}

参数详细说明

参数 类型 必填 默认值 说明
query string - 搜索查询字符串,支持布尔表达式(AND, OR, RANK, ANDNOT)
size integer 10 返回结果数量(1-100)
from integer 0 分页偏移量(用于分页)
filters object null 精确匹配过滤器(见下文)
range_filters object null 数值范围过滤器(见下文)
facets array null 分面配置(见下文)
sort_by string null 排序字段名(如 min_price, max_price, title
sort_order string "desc" 排序方向:asc(升序)或 desc(降序)
min_score float null 最小相关性分数阈值
debug boolean false 是否返回调试信息
user_id string null 用户ID(用于个性化,预留)
session_id string null 会话ID(用于分析,预留)

过滤器详解

1. 精确匹配过滤器 (filters)

用于精确匹配或多值匹配(OR 逻辑)。

格式:

{
  "filters": {
    "category_keyword": "玩具",                    // 单值:精确匹配
    "vendor_keyword": ["乐高", "孩之宝"],          // 数组:匹配任意值(OR)
    "product_type_keyword": "益智玩具"              // 单值:精确匹配
  }
}

支持的值类型:

  • 字符串:精确匹配
  • 整数:精确匹配
  • 布尔值:精确匹配
  • 数组:匹配任意值(OR 逻辑)

常用过滤字段:

  • category_keyword: 类目
  • vendor_keyword: 品牌/供应商
  • product_type_keyword: 商品类型
  • tags_keyword: 标签

2. 范围过滤器 (range_filters)

用于数值字段的范围过滤。

格式:

{
  "range_filters": {
    "min_price": {
      "gte": 50,    // 大于等于
      "lte": 200    // 小于等于
    },
    "max_price": {
      "gt": 100     // 大于
    },
    "create_time": {
      "gte": "2024-01-01T00:00:00Z"  // 日期时间字符串
    }
  }
}

支持的操作符:

  • gte: 大于等于 (>=)
  • gt: 大于 (>)
  • lte: 小于等于 (
  • lt: 小于 (

注意: 至少需要指定一个操作符。

常用范围字段:

  • min_price: 最低价格
  • max_price: 最高价格
  • compare_at_price: 原价
  • create_time: 创建时间
  • update_time: 更新时间

3. 分面配置 (facets)

用于生成分面统计(分组聚合),常用于构建筛选器UI。

简单模式(字符串数组):

{
  "facets": ["category_keyword", "vendor_keyword"]
}

高级模式(配置对象数组):

{
  "facets": [
    {
      "field": "category_keyword",
      "size": 15,
      "type": "terms"
    },
    {
      "field": "min_price",
      "type": "range",
      "ranges": [
        {"key": "0-50", "to": 50},
        {"key": "50-100", "from": 50, "to": 100},
        {"key": "100-200", "from": 100, "to": 200},
        {"key": "200+", "from": 200}
      ]
    }
  ]
}

分面配置参数:

  • field: 字段名(必填)
  • size: 返回的分组数量(默认:10,范围:1-100)
  • type: 分面类型,terms(分组统计)或 range(范围统计)
  • ranges: 范围定义(仅当 type='range' 时需要)

布尔表达式语法

搜索查询支持布尔表达式,提供更灵活的搜索能力。

支持的操作符:

操作符 描述 示例
AND 所有词必须匹配 玩具 AND 乐高
OR 任意词匹配 芭比 OR 娃娃
ANDNOT 排除特定词 玩具 ANDNOT 电动
RANK 排序加权(不强制匹配) 玩具 RANK 乐高
() 分组 玩具 AND (乐高 OR 芭比)

操作符优先级(从高到低):

  1. () - 括号
  2. ANDNOT - 排除
  3. AND - 与
  4. OR - 或
  5. RANK - 排序

示例:

"芭比娃娃"                    // 简单查询
"玩具 AND 乐高"               // AND 查询
"芭比 OR 娃娃"                // OR 查询
"玩具 ANDNOT 电动"            // 排除查询
"玩具 AND (乐高 OR 芭比)"      // 复杂查询

图片搜索接口

接口信息

  • 端点: POST /search/image
  • 描述: 基于图片相似度进行搜索,使用图片向量进行语义匹配

请求参数

{
  "image_url": "string (required)",
  "size": 10,
  "filters": {},
  "range_filters": {}
}

参数说明

参数 类型 必填 默认值 描述
image_url string - 查询图片的 URL
size integer 10 返回结果数量(1-100)
filters object null 精确匹配过滤器
range_filters object null 数值范围过滤器

请求示例

curl -X POST "http://localhost:6002/search/image" \
  -H "Content-Type: application/json" \
  -d '{
    "image_url": "https://example.com/barbie.jpg",
    "size": 20,
    "filters": {
      "category_keyword": "玩具"
    },
    "range_filters": {
      "min_price": {
        "lte": 100
      }
    }
  }'

响应格式说明

标准响应结构

{
  "results": [
    {
      "product_id": "12345",
      "title": "芭比时尚娃娃",
      "handle": "barbie-doll",
      "description": "高品质芭比娃娃",
      "vendor": "美泰",
      "product_type": "玩具",
      "tags": "娃娃, 玩具, 女孩",
      "price": 89.99,
      "compare_at_price": 129.99,
      "currency": "USD",
      "image_url": "https://example.com/image.jpg",
      "in_stock": true,
      "variants": [
        {
          "variant_id": "67890",
          "title": "粉色款",
          "price": 89.99,
          "compare_at_price": 129.99,
          "sku": "BARBIE-001",
          "stock": 100,
          "options": {
            "option1": "粉色",
            "option2": "标准款"
          }
        }
      ],
      "relevance_score": 8.5
    }
  ],
  "total": 118,
  "max_score": 8.5,
  "facets": [
    {
      "field": "category_keyword",
      "label": "category_keyword",
      "type": "terms",
      "values": [
        {
          "value": "玩具",
          "label": "玩具",
          "count": 85,
          "selected": false
        }
      ]
    }
  ],
  "query_info": {
    "original_query": "芭比娃娃",
    "detected_language": "zh",
    "translations": {
      "en": "barbie doll"
    }
  },
  "suggestions": [],
  "related_searches": [],
  "took_ms": 45,
  "performance_info": null,
  "debug_info": null
}

响应字段说明

字段 类型 说明
results array 搜索结果列表(ProductResult对象数组)
results[].product_id string 商品ID
results[].title string 商品标题
results[].price float 价格(min_price)
results[].variants array 变体列表(SKU列表)
results[].relevance_score float 相关性分数
total integer 匹配的总文档数
max_score float 最高相关性分数
facets array 分面统计结果
query_info object 查询处理信息
took_ms integer 搜索耗时(毫秒)

ProductResult字段说明

字段 类型 说明
product_id string 商品ID(SPU ID)
title string 商品标题
handle string 商品URL handle
description string 商品描述
vendor string 供应商/品牌
product_type string 商品类型
tags string 标签
price float 价格(min_price)
compare_at_price float 原价
currency string 货币单位(默认USD)
image_url string 主图URL
in_stock boolean 是否有库存(任意变体有库存即为true)
variants array 变体列表
relevance_score float 相关性分数

VariantResult字段说明

字段 类型 说明
variant_id string 变体ID(SKU ID)
title string 变体标题
price float 价格
compare_at_price float 原价
sku string SKU编码
stock integer 库存数量
options object 选项(颜色、尺寸等)

常见场景示例

场景1:商品列表页搜索

需求: 搜索"玩具",按价格从低到高排序,显示前20个结果

{
  "query": "玩具",
  "size": 20,
  "from": 0,
  "sort_by": "min_price",
  "sort_order": "asc"
}

场景2:带筛选的商品搜索

需求: 搜索"玩具",筛选类目为"益智玩具",价格在50-200之间

{
  "query": "玩具",
  "size": 20,
  "filters": {
    "category_keyword": "益智玩具"
  },
  "range_filters": {
    "min_price": {
      "gte": 50,
      "lte": 200
    }
  }
}

场景3:带分面的商品搜索

需求: 搜索"玩具",获取类目和品牌的分面统计,用于构建筛选器

{
  "query": "玩具",
  "size": 20,
  "facets": [
    "category_keyword",
    "vendor_keyword"
  ]
}

场景4:多条件组合搜索

需求: 搜索"玩具",筛选多个品牌,价格范围,并获取分面统计

{
  "query": "玩具",
  "size": 20,
  "filters": {
    "vendor_keyword": ["乐高", "孩之宝", "美泰"]
  },
  "range_filters": {
    "min_price": {
      "gte": 50,
      "lte": 200
    }
  },
  "facets": [
    {
      "field": "category_keyword",
      "size": 15
    },
    {
      "field": "min_price",
      "type": "range",
      "ranges": [
        {"key": "0-50", "to": 50},
        {"key": "50-100", "from": 50, "to": 100},
        {"key": "100-200", "from": 100, "to": 200},
        {"key": "200+", "from": 200}
      ]
    }
  ],
  "sort_by": "min_price",
  "sort_order": "asc"
}

场景5:布尔表达式搜索

需求: 搜索包含"玩具"和"乐高"的商品,排除"电动"

{
  "query": "玩具 AND 乐高 ANDNOT 电动",
  "size": 20
}

场景6:分页查询

需求: 获取第2页结果(每页20条)

{
  "query": "玩具",
  "size": 20,
  "from": 20
}

错误处理

错误响应格式

{
  "error": "错误信息",
  "detail": "详细错误信息(可选)"
}

常见错误码

HTTP状态码 说明 处理建议
200 成功 -
400 请求参数错误 检查请求参数格式和必填字段
404 接口不存在 检查接口路径
500 服务器内部错误 联系技术支持

错误处理示例

Python:

import requests

try:
    response = requests.post(url, json=payload, timeout=10)
    response.raise_for_status()
    data = response.json()
except requests.exceptions.HTTPError as e:
    print(f"HTTP错误: {e}")
    if response.status_code == 400:
        error_data = response.json()
        print(f"错误详情: {error_data.get('detail')}")
except requests.exceptions.RequestException as e:
    print(f"请求异常: {e}")

JavaScript:

try {
    const response = await fetch(url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload)
    });

    if (!response.ok) {
        const error = await response.json();
        throw new Error(error.error || `HTTP ${response.status}`);
    }

    const data = await response.json();
} catch (error) {
    console.error('搜索失败:', error.message);
}

5. 代码示例

完整的搜索函数(Python):

import requests
from typing import Dict, Any, Optional, List

class SearchClient:
    def __init__(self, base_url: str = "http://localhost:6002"):
        self.base_url = base_url
        self.timeout = 10

    def search(
        self,
        query: str,
        size: int = 20,
        from_: int = 0,
        filters: Optional[Dict] = None,
        range_filters: Optional[Dict] = None,
        facets: Optional[List] = None,
        sort_by: Optional[str] = None,
        sort_order: str = "desc"
    ) -> Dict[str, Any]:
        """
        执行搜索查询

        Args:
            query: 搜索查询字符串
            size: 返回结果数量
            from_: 分页偏移量
            filters: 精确匹配过滤器
            range_filters: 范围过滤器
            facets: 分面配置
            sort_by: 排序字段
            sort_order: 排序方向

        Returns:
            搜索结果字典
        """
        url = f"{self.base_url}/search/"
        payload = {
            "query": query,
            "size": size,
            "from": from_,
        }

        if filters:
            payload["filters"] = filters
        if range_filters:
            payload["range_filters"] = range_filters
        if facets:
            payload["facets"] = facets
        if sort_by:
            payload["sort_by"] = sort_by
            payload["sort_order"] = sort_order

        try:
            response = requests.post(
                url,
                json=payload,
                timeout=self.timeout
            )
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            raise Exception(f"搜索请求失败: {e}")

# 使用示例
client = SearchClient()
result = client.search(
    query="玩具",
    size=20,
    filters={"category_keyword": "益智玩具"},
    range_filters={"min_price": {"gte": 50, "lte": 200}},
    facets=["category_keyword", "vendor_keyword"],
    sort_by="min_price",
    sort_order="asc"
)

print(f"找到 {result['total']} 个结果")
for product in result['results']:
    print(f"{product['title']} - ¥{product['price']}")

完整的搜索函数(JavaScript):

class SearchClient {
    constructor(baseUrl = 'http://localhost:6002') {
        this.baseUrl = baseUrl;
        this.timeout = 10000;
    }

    async search({
        query,
        size = 20,
        from = 0,
        filters = null,
        rangeFilters = null,
        facets = null,
        sortBy = null,
        sortOrder = 'desc'
    }) {
        const url = `${this.baseUrl}/search/`;
        const payload = {
            query,
            size,
            from,
        };

        if (filters) payload.filters = filters;
        if (rangeFilters) payload.range_filters = rangeFilters;
        if (facets) payload.facets = facets;
        if (sortBy) {
            payload.sort_by = sortBy;
            payload.sort_order = sortOrder;
        }

        try {
            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(payload),
                signal: AbortSignal.timeout(this.timeout)
            });

            if (!response.ok) {
                const error = await response.json();
                throw new Error(error.error || `HTTP ${response.status}`);
            }

            return await response.json();
        } catch (error) {
            throw new Error(`搜索请求失败: ${error.message}`);
        }
    }
}

// 使用示例
const client = new SearchClient();
const result = await client.search({
    query: '玩具',
    size: 20,
    filters: { category_keyword: '益智玩具' },
    rangeFilters: { min_price: { gte: 50, lte: 200 } },
    facets: ['category_keyword', 'vendor_keyword'],
    sortBy: 'min_price',
    sortOrder: 'asc'
});

console.log(`找到 ${result.total} 个结果`);
result.results.forEach(product => {
    console.log(`${product.title} - ¥${product.price}`);
});

其他接口

搜索建议(框架)

  • 端点: GET /search/suggestions
  • 描述: 返回搜索建议(自动补全/热词)。当前为框架实现,接口和响应格式已经固定,可平滑扩展。

查询参数

参数 类型 必填 默认值 描述
q string - 查询字符串(至少 1 个字符)
size integer 5 返回建议数量(1-20)
types string query 建议类型(逗号分隔):query, product, category, brand

响应示例

{
  "query": "芭",
  "suggestions": [
    {
      "text": "芭比娃娃",
      "type": "query",
      "highlight": "<em>芭</em>比娃娃",
      "popularity": 850
    }
  ],
  "took_ms": 5
}

请求示例

curl "http://localhost:6002/search/suggestions?q=芭&size=5&types=query,product"

即时搜索(框架)

  • 端点: GET /search/instant
  • 描述: 边输入边搜索,采用轻量参数响应当前输入。底层复用标准搜索能力。

查询参数

参数 类型 必填 默认值 描述
q string - 搜索查询(至少 2 个字符)
size integer 5 返回结果数量(1-20)

请求示例

curl "http://localhost:6002/search/instant?q=玩具&size=5"

获取单个文档

  • 端点: GET /search/{doc_id}
  • 描述: 根据文档 ID 获取单个商品详情,用于点击结果后的详情页或排查问题。

路径参数

参数 类型 描述
doc_id string 商品或文档 ID

响应示例

{
  "id": "12345",
  "source": {
    "title": "芭比时尚娃娃",
    "min_price": 89.99,
    "category_keyword": "玩具"
  }
}

请求示例

curl "http://localhost:6002/search/12345"

管理接口

健康检查

  • 端点: GET /admin/health
  • 描述: 检查服务与依赖(如 Elasticsearch)状态。
{
  "status": "healthy",
  "elasticsearch": "connected",
  "tenant_id": "tenant1"
}

获取配置

  • 端点: GET /admin/config
  • 描述: 返回当前租户的脱敏配置,便于核对索引及排序表达式。
{
  "tenant_id": "tenant1",
  "tenant_name": "Tenant1 Test Instance",
  "es_index_name": "search_tenant1",
  "num_fields": 20,
  "num_indexes": 4,
  "supported_languages": ["zh", "en", "ru"],
  "ranking_expression": "bm25() + 0.2*text_embedding_relevance()",
  "spu_enabled": false
}

索引统计

  • 端点: GET /admin/stats
  • 描述: 获取索引文档数量与磁盘大小,方便监控。
{
  "index_name": "search_tenant1",
  "document_count": 10000,
  "size_mb": 523.45
}

数据模型

商品字段

字段名 类型 描述
product_id keyword 商品 ID(SPU)
sku_id keyword/long SKU ID(主键)
title text 商品名称(中文)
en_title text 商品名称(英文)
ru_title text 商品名称(俄文)
category_keyword keyword 类目(精确匹配)
vendor_keyword keyword 品牌/供应商(精确匹配)
product_type_keyword keyword 商品类型
tags_keyword keyword 标签
min_price double 最低价格
max_price double 最高价格
compare_at_price double 原价
create_time date 创建时间
update_time date 更新时间
in_stock boolean 是否有库存
text_embedding dense_vector 文本向量(1024 维)
image_embedding dense_vector 图片向量(1024 维)

不同租户可自定义字段名称,但最佳实践是对可过滤字段建立 *_keyword 版本,对可排序字段显式建 keyword/数值映射。


常见问题(FAQ)

Q1: 如何判断一个字段应该用哪种过滤器?
filters 针对 keyword/布尔/整数字段做精确匹配;range_filters 针对数值或日期字段做区间查询。

Q2: 可以同时使用多个过滤器吗?
可以,所有过滤条件为 AND 关系。例如:

{
  "filters": {
    "category_keyword": "玩具",
    "vendor_keyword": "乐高"
  },
  "range_filters": {
    "min_price": {"gte": 50, "lte": 200}
  }
}

Q4: 分面结果里的 selected 字段含义是什么?
指示该分面值是否已在当前过滤条件中,前端可据此高亮。

Q5: 如何自定义排序?
设置 sort_bysort_order,常用字段包括 min_price, max_price, title, create_time, update_time, relevance_score

Q6: 如何启用调试模式?
添加 debug: true,即可在响应中看到 debug_info(ES DSL、阶段耗时、打分细节)。


附录

常用字段列表

过滤字段(使用 *_keyword 后缀)

  • category_keyword: 类目
  • vendor_keyword: 品牌/供应商
  • product_type_keyword: 商品类型
  • tags_keyword: 标签

范围字段

  • min_price: 最低价格
  • max_price: 最高价格
  • compare_at_price: 原价
  • create_time: 创建时间
  • update_time: 更新时间

排序字段

  • min_price: 最低价格
  • max_price: 最高价格
  • title: 标题(字母序)
  • create_time: 创建时间
  • update_time: 更新时间
  • relevance_score: 相关性分数(默认)

支持的分析器

分析器 语言 描述
chinese_ecommerce 中文 基于 Ansj 的电商优化中文分析器
english 英文 标准英文分析器
russian 俄文 俄文分析器
arabic 阿拉伯文 阿拉伯文分析器
spanish 西班牙文 西班牙文分析器
japanese 日文 日文分析器

字段类型速查

类型 ES Mapping 用途
TEXT text 全文检索
KEYWORD keyword 精确匹配、聚合、排序
LONG long 整数
DOUBLE double 浮点数
DATE date 日期时间
BOOLEAN boolean 布尔值
TEXT_EMBEDDING dense_vector 文本语义向量
IMAGE_EMBEDDING dense_vector 图片语义向量