API_EXAMPLES.md
23.4 KB
API 使用示例
本文档提供了搜索引擎 API 的详细使用示例,包括各种常见场景和最佳实践。
目录
基础搜索
示例 1:最简单的搜索
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "芭比娃娃"
}'
响应:
{
"hits": [...],
"total": 118,
"max_score": 8.5,
"took_ms": 45,
"query_info": {
"original_query": "芭比娃娃",
"detected_language": "zh",
"translations": {"en": "barbie doll"}
}
}
示例 2:指定返回数量
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"size": 50
}'
示例 3:分页查询
# 第1页(0-19)
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"size": 20,
"from": 0
}'
# 第2页(20-39)
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"size": 20,
"from": 20
}'
过滤器使用
精确匹配过滤器
示例 1:单值过滤
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"filters": {
"categoryName_keyword": "玩具"
}
}'
示例 2:多值过滤(OR 逻辑)
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "娃娃",
"filters": {
"categoryName_keyword": ["玩具", "益智玩具", "儿童玩具"]
}
}'
说明:匹配类目为"玩具" 或 "益智玩具" 或 "儿童玩具"的商品。
示例 3:多字段过滤(AND 逻辑)
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "娃娃",
"filters": {
"categoryName_keyword": "玩具",
"brandName_keyword": "美泰"
}
}'
说明:必须同时满足"类目=玩具" 并且 "品牌=美泰"。
范围过滤器
示例 1:价格范围
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"range_filters": {
"price": {
"gte": 50,
"lte": 200
}
}
}'
说明:价格在 50-200 元之间(包含边界)。
示例 2:只设置下限
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"range_filters": {
"price": {
"gte": 100
}
}
}'
说明:价格 ≥ 100 元。
示例 3:只设置上限
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"range_filters": {
"price": {
"lt": 50
}
}
}'
说明:价格
示例 4:多字段范围过滤
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"range_filters": {
"price": {
"gte": 50,
"lte": 200
},
"days_since_last_update": {
"lte": 30
}
}
}'
说明:价格在 50-200 元 并且 最近30天内更新过。
组合过滤器
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"filters": {
"categoryName_keyword": ["玩具", "益智玩具"],
"brandName_keyword": "乐高"
},
"range_filters": {
"price": {
"gte": 50,
"lte": 500
}
}
}'
说明:类目是"玩具"或"益智玩具" 并且 品牌是"乐高" 并且 价格在 50-500 元之间。
分面搜索
简单模式
示例 1:基础分面
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"size": 20,
"facets": ["categoryName_keyword", "brandName_keyword"]
}'
响应:
{
"hits": [...],
"total": 118,
"facets": [
{
"field": "categoryName_keyword",
"label": "categoryName_keyword",
"type": "terms",
"values": [
{"value": "玩具", "count": 85, "selected": false},
{"value": "益智玩具", "count": 33, "selected": false}
]
},
{
"field": "brandName_keyword",
"label": "brandName_keyword",
"type": "terms",
"values": [
{"value": "乐高", "count": 42, "selected": false},
{"value": "美泰", "count": 28, "selected": false}
]
}
]
}
高级模式
示例 1:自定义分面大小
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"facets": [
{
"field": "categoryName_keyword",
"size": 20,
"type": "terms"
},
{
"field": "brandName_keyword",
"size": 30,
"type": "terms"
}
]
}'
示例 2:范围分面
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"facets": [
{
"field": "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}
]
}
]
}'
响应:
{
"facets": [
{
"field": "price",
"label": "price",
"type": "range",
"values": [
{"value": "0-50", "count": 23, "selected": false},
{"value": "50-100", "count": 45, "selected": false},
{"value": "100-200", "count": 38, "selected": false},
{"value": "200+", "count": 12, "selected": false}
]
}
]
}
示例 3:混合分面(Terms + Range)
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"facets": [
{"field": "categoryName_keyword", "size": 15},
{"field": "brandName_keyword", "size": 15},
{
"field": "price",
"type": "range",
"ranges": [
{"key": "低价", "to": 50},
{"key": "中价", "from": 50, "to": 200},
{"key": "高价", "from": 200}
]
}
]
}'
排序
示例 1:按价格排序(升序)
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"size": 20,
"sort_by": "min_price",
"sort_order": "asc"
}'
示例 2:按创建时间排序(降序)
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"size": 20,
"sort_by": "create_time",
"sort_order": "desc"
}'
示例 3:排序+过滤
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"filters": {
"categoryName_keyword": "益智玩具"
},
"sort_by": "min_price",
"sort_order": "asc"
}'
图片搜索
示例 1:基础图片搜索
curl -X POST "http://localhost:6002/search/image" \
-H "Content-Type: application/json" \
-d '{
"image_url": "https://example.com/barbie.jpg",
"size": 20
}'
示例 2:图片搜索+过滤器
curl -X POST "http://localhost:6002/search/image" \
-H "Content-Type: application/json" \
-d '{
"image_url": "https://example.com/barbie.jpg",
"size": 20,
"filters": {
"categoryName_keyword": "玩具"
},
"range_filters": {
"price": {
"lte": 100
}
}
}'
布尔表达式
示例 1:AND 查询
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具 AND 乐高"
}'
说明:必须同时包含"玩具"和"乐高"。
示例 2:OR 查询
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "芭比 OR 娃娃"
}'
说明:包含"芭比"或"娃娃"即可。
示例 3:ANDNOT 查询(排除)
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具 ANDNOT 电动"
}'
说明:包含"玩具"但不包含"电动"。
示例 4:复杂布尔表达式
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具 AND (乐高 OR 芭比) ANDNOT 电动"
}'
说明:必须包含"玩具",并且包含"乐高"或"芭比",但不包含"电动"。
示例 5:域查询
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "brand:乐高"
}'
说明:在品牌域中搜索"乐高"。
完整示例
Python 完整示例
#!/usr/bin/env python3
import requests
import json
API_URL = "http://localhost:6002/search/"
def search_products(
query,
size=20,
from_=0,
filters=None,
range_filters=None,
facets=None,
sort_by=None,
sort_order="desc",
debug=False
):
"""执行搜索查询"""
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
if debug:
payload["debug"] = debug
response = requests.post(API_URL, json=payload)
response.raise_for_status()
return response.json()
# 示例 1:简单搜索
result = search_products("芭比娃娃", size=10)
print(f"找到 {result['total']} 个结果")
for hit in result['hits'][:3]:
product = hit['_source']
print(f" - {product['name']}: ¥{product.get('price', 'N/A')}")
# 示例 2:带过滤和分面的搜索
result = search_products(
query="玩具",
size=20,
filters={
"categoryName_keyword": ["玩具", "益智玩具"]
},
range_filters={
"price": {"gte": 50, "lte": 200}
},
facets=[
{"field": "brandName_keyword", "size": 15},
{"field": "categoryName_keyword", "size": 15},
{
"field": "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"
)
# 显示分面结果
print(f"\n分面统计:")
for facet in result.get('facets', []):
print(f"\n{facet['label']} ({facet['type']}):")
for value in facet['values'][:5]:
selected_mark = "✓" if value['selected'] else " "
print(f" [{selected_mark}] {value['label']}: {value['count']}")
# 示例 3:分页查询
page = 1
page_size = 20
total_pages = 5
for page in range(1, total_pages + 1):
result = search_products(
query="玩具",
size=page_size,
from_=(page - 1) * page_size
)
print(f"\n第 {page} 页:")
for hit in result['hits']:
product = hit['_source']
print(f" - {product['name']}")
JavaScript 完整示例
// 搜索引擎客户端
class SearchClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}
async search({
query,
size = 20,
from = 0,
filters = null,
rangeFilters = null,
facets = null,
sortBy = null,
sortOrder = 'desc',
debug = false
}) {
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;
}
if (debug) payload.debug = debug;
const response = await fetch(`${this.baseUrl}/search/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
}
async searchByImage(imageUrl, options = {}) {
const payload = {
image_url: imageUrl,
size: options.size || 20,
filters: options.filters || null,
range_filters: options.rangeFilters || null
};
const response = await fetch(`${this.baseUrl}/search/image`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
}
}
// 使用示例
const client = new SearchClient('http://localhost:6002');
// 简单搜索
const result1 = await client.search({
query: "芭比娃娃",
size: 20
});
console.log(`找到 ${result1.total} 个结果`);
// 带过滤和分面的搜索
const result2 = await client.search({
query: "玩具",
size: 20,
filters: {
categoryName_keyword: ["玩具", "益智玩具"]
},
rangeFilters: {
price: { gte: 50, lte: 200 }
},
facets: [
{ field: "brandName_keyword", size: 15 },
{ field: "categoryName_keyword", size: 15 }
],
sortBy: "price",
sortOrder: "asc"
});
// 显示分面结果
result2.facets.forEach(facet => {
console.log(`\n${facet.label}:`);
facet.values.forEach(value => {
const selected = value.selected ? '✓' : ' ';
console.log(` [${selected}] ${value.label}: ${value.count}`);
});
});
// 显示商品
result2.hits.forEach(hit => {
const product = hit._source;
console.log(`${product.name} - ¥${product.price}`);
});
前端完整示例(Vue.js 风格)
// 搜索组件
const SearchComponent = {
data() {
return {
query: '',
results: [],
facets: [],
filters: {},
rangeFilters: {},
total: 0,
currentPage: 1,
pageSize: 20
};
},
methods: {
async search() {
const response = await fetch('http://localhost:6002/search/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: this.query,
size: this.pageSize,
from: (this.currentPage - 1) * this.pageSize,
filters: this.filters,
range_filters: this.rangeFilters,
facets: [
{ field: 'categoryName_keyword', size: 15 },
{ field: 'brandName_keyword', size: 15 }
]
})
});
const data = await response.json();
this.results = data.hits;
this.facets = data.facets || [];
this.total = data.total;
},
toggleFilter(field, value) {
if (!this.filters[field]) {
this.filters[field] = [];
}
const index = this.filters[field].indexOf(value);
if (index > -1) {
this.filters[field].splice(index, 1);
if (this.filters[field].length === 0) {
delete this.filters[field];
}
} else {
this.filters[field].push(value);
}
this.currentPage = 1;
this.search();
},
setPriceRange(min, max) {
if (min !== null || max !== null) {
this.rangeFilters.price = {};
if (min !== null) this.rangeFilters.price.gte = min;
if (max !== null) this.rangeFilters.price.lte = max;
} else {
delete this.rangeFilters.price;
}
this.currentPage = 1;
this.search();
}
}
};
调试与优化
启用调试模式
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"debug": true
}'
响应包含调试信息:
{
"hits": [...],
"total": 118,
"debug_info": {
"query_analysis": {
"original_query": "玩具",
"normalized_query": "玩具",
"rewritten_query": "玩具",
"detected_language": "zh",
"translations": {"en": "toy"}
},
"es_query": {
"query": {...},
"size": 10
},
"stage_timings": {
"query_parsing": 5.3,
"elasticsearch_search": 35.1,
"result_processing": 4.8
}
}
}
设置最小分数阈值
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"min_score": 5.0
}'
说明:只返回相关性分数 ≥ 5.0 的结果。
常见使用场景
场景 1:电商分类页
# 显示某个类目下的所有商品,按价格排序,提供品牌筛选
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "*",
"filters": {
"categoryName_keyword": "玩具"
},
"facets": [
{"field": "brandName_keyword", "size": 20},
{
"field": "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",
"size": 24
}'
场景 2:搜索结果页
# 用户搜索关键词,提供筛选和排序
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "芭比娃娃",
"facets": [
{"field": "categoryName_keyword", "size": 10},
{"field": "brandName_keyword", "size": 10},
{"field": "price", "type": "range", "ranges": [
{"key": "0-50", "to": 50},
{"key": "50-100", "from": 50, "to": 100},
{"key": "100+", "from": 100}
]}
],
"size": 20
}'
场景 3:促销专区
# 显示特定价格区间的商品
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "*",
"range_filters": {
"price": {
"gte": 50,
"lte": 100
}
},
"facets": ["categoryName_keyword", "brandName_keyword"],
"sort_by": "min_price",
"sort_order": "asc",
"size": 50
}'
场景 4:新品推荐
# 最近更新的商品
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "*",
"range_filters": {
"days_since_last_update": {
"lte": 7
}
},
"sort_by": "create_time",
"sort_order": "desc",
"size": 20
}'
错误处理
示例 1:参数错误
# 错误:range_filters 缺少操作符
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"range_filters": {
"price": {}
}
}'
响应:
{
"error": "Validation error",
"detail": "至少需要指定一个范围边界(gte, gt, lte, lt)",
"timestamp": 1699800000
}
示例 2:空查询
# 错误:query 为空
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": ""
}'
响应:
{
"error": "Validation error",
"detail": "query field required",
"timestamp": 1699800000
}
性能优化建议
1. 合理使用分面
# ❌ 不推荐:请求太多分面
{
"facets": [
{"field": "field1", "size": 100},
{"field": "field2", "size": 100},
{"field": "field3", "size": 100},
// ... 10+ facets
]
}
# ✅ 推荐:只请求必要的分面
{
"facets": [
{"field": "categoryName_keyword", "size": 15},
{"field": "brandName_keyword", "size": 15}
]
}
2. 控制返回数量
# ❌ 不推荐:一次返回太多
{
"size": 100
}
# ✅ 推荐:分页查询
{
"size": 20,
"from": 0
}
3. 使用适当的过滤器
# ✅ 推荐:先过滤后搜索
{
"query": "玩具",
"filters": {
"categoryName_keyword": "玩具"
}
}
高级技巧
技巧 1:获取所有类目
# 使用通配符查询 + 分面
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "*",
"size": 0,
"facets": [
{"field": "categoryName_keyword", "size": 100}
]
}'
技巧 2:价格分布统计
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "玩具",
"size": 0,
"facets": [
{
"field": "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-500", "from": 200, "to": 500},
{"key": "500+", "from": 500}
]
}
]
}'
技巧 3:组合多种查询类型
# 布尔表达式 + 过滤器 + 分面 + 排序
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{
"query": "(玩具 OR 游戏) AND 儿童 ANDNOT 电子",
"filters": {
"categoryName_keyword": ["玩具", "益智玩具"]
},
"range_filters": {
"price": {"gte": 20, "lte": 100},
"days_since_last_update": {"lte": 30}
},
"facets": [
{"field": "brandName_keyword", "size": 20}
],
"sort_by": "min_price",
"sort_order": "asc",
"size": 20
}'
测试数据
如果你需要测试数据,可以使用以下查询:
# 测试类目:玩具
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{"query": "玩具", "size": 5}'
# 测试品牌:乐高
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{"query": "brand:乐高", "size": 5}'
# 测试布尔表达式
curl -X POST "http://localhost:6002/search/" \
-H "Content-Type: application/json" \
-d '{"query": "玩具 AND 乐高", "size": 5}'
文档版本: 3.0
最后更新: 2024-11-12
相关文档: API_DOCUMENTATION.md