Algolia 搜索
Algolia 搜索集成
Algolia 搜索实现、索引策略、React InstantSearch 及相关性调优的专家级模式
模式 (Patterns)
基于 Hooks 的 React InstantSearch
使用 hooks 实现类型前置搜索(type-ahead search)的现代 React InstantSearch 配置。
使用 react-instantsearch-hooks-web 包和 algoliasearch 客户端。
Widget 是可以通过 classnames 自定义的组件。
核心 hooks:
- useSearchBox:处理搜索输入
- useHits:访问搜索结果
- useRefinementList:分面过滤 (Facet filtering)
- usePagination:结果分页
- useInstantSearch:访问完整状态
代码示例 (Code_example)
// lib/algolia.ts
import algoliasearch from 'algoliasearch/lite';
export const searchClient = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY! // 仅限搜索的 Key!
);
export const INDEX_NAME = 'products';
// components/Search.tsx
'use client';
import { InstantSearch, SearchBox, Hits, Configure } from 'react-instantsearch';
import { searchClient, INDEX_NAME } from '@/lib/algolia';
function Hit({ hit }: { hit: ProductHit }) {
return (
<article>
<h3>{hit.name}</h3>
<p>{hit.description}</p>
<span>${hit.price}</span>
</article>
);
}
export function ProductSearch() {
return (
<InstantSearch searchClient={searchClient} indexName={INDEX_NAME}>
<Configure hitsPerPage={20} />
<SearchBox
placeholder="Search products..."
classNames={{
root: 'relative',
input: 'w-full px-4 py-2 border rounded',
}}
/>
<Hits hitComponent={Hit} />
</InstantSearch>
);
}
// 自定义 hook 用法
import { useSearchBox, useHits, useInstantSearch } from 'react-instantsearch';
function CustomSearch() {
const { query, refine } = useSearchBox();
const { hits } = useHits<ProductHit>();
const { status } = useInstantSearch();
return (
<div>
<input
value={query}
onChange={(e) => refine(e.target.value)}
placeholder="Search..."
/>
{status === 'loading' && <p>Loading...</p>}
<ul>
{hits.map((hit) => (
<li key={hit.objectID}>{hit.name}</li>
))}
</ul>
</div>
);
}
反模式 (Anti_patterns)
- 模式:在前端代码中使用 Admin API key | 原因:Admin key 暴露了包括删除在内的完整索引控制权 | 修复:使用带有权限限制的 search-only API key
- 模式:前端不使用 /lite 客户端 | 原因:完整客户端包含搜索不需要的冗余代码 | 修复:从
algoliasearch/lite导入以减小 bundle 体积
参考资料 (References)
- https://www.algolia.com/doc/api-reference/widgets/react
- https://www.algolia.com/doc/libraries/javascript/v5/methods/search/
Next.js 服务端渲染 (SSR)
使用 react-instantsearch-nextjs 包在 Next.js 中集成 SSR。
使用 <InstantSearchNext> 代替 <InstantSearch> 以实现 SSR。
同时支持 Pages Router 和 App Router(实验性)。
关键注意事项:
- 设置
dynamic = 'force-dynamic'以确保结果实时
- 通过
routing属性处理 URL 同步
- 使用
getServerState获取初始状态
代码示例 (Code_example)
// app/search/page.tsx
import { InstantSearchNext } from 'react-instantsearch-nextjs';
import { searchClient, INDEX_NAME } from '@/lib/algolia';
import { SearchBox }// 强制动态渲染以确保搜索结果最新
export const dynamic = 'force-dynamic';
export default function SearchPage() {
return (
<InstantSearchNext
searchClient={searchClient}
indexName={INDEX_NAME}
routing={{
router: {
cleanUrlOnDispose: false,
},
}}
>
<div className="flex gap-8">
<aside className="w-64">
<h3>Categories</h3>
<RefinementList attribute="category" />
<h3>Brand</h3>
<RefinementList attribute="brand" />
</aside>
<main className="flex-1">
<SearchBox placeholder="Search products..." />
<Hits hitComponent={ProductHit} />
</main>
</div>
</InstantSearchNext>
);
}
// 用于自定义路由(URL 同步)
import { history } from 'instantsearch.js/es/lib/routers';
import { simple } from 'instantsearch.js/es/lib/stateMappings';
<InstantSearchNext
searchClient={searchClient}
indexName={INDEX_NAME}
routing={{
router: history({
getLocation: () =>
typeof window === 'undefined'
? new URL(url) as unknown as Location
: window.location,
}),
stateMapping: simple(),
}}
>
{/* widgets */}
</InstantSearchNext>
反模式 (Anti-patterns)
- 模式:在 Next.js SSR 中使用 InstantSearch 组件 | 原因:普通组件不支持服务端渲染 | 修复:使用
react-instantsearch-nextjs中的InstantSearchNext
- 模式:对搜索页面使用静态渲染 | 原因:搜索结果在每次请求时必须是最新的 | 修复:设置
export const dynamic = 'force-dynamic'
参考资料
- https://www.npmjs.com/package/react-instantsearch-nextjs
- https://www.algolia.com/developers/code-exchange/instantsearch-and-next-js-starter
数据同步与索引
确保 Algolia 与数据保持同步的索引策略。
三种主要方法:
1. 全量重新索引 (Full Reindexing) - 替换整个索引(成本较高)
2. 全量记录更新 (Full Record Updates) - 替换单个记录
3. 部分更新 (Partial Updates) - 仅更新特定属性
最佳实践:
- 批量处理记录(理想情况:每批 10MB,1K-10K 条记录)
- 尽可能使用增量更新
- 仅针对属性更改使用
partialUpdateObjects
- 避免使用
deleteBy(计算开销较大)
代码示例
// lib/algolia-admin.ts (仅限服务端)
import algoliasearch from 'algoliasearch';
// 管理员客户端 - 切勿暴露给前端
const adminClient = algoliasearch(
process.env.ALGOLIA_APP_ID!,
process.env.ALGOLIA_ADMIN_KEY! // 用于索引的管理员密钥
);
const index = adminClient.initIndex('products');
// 批量索引(推荐方法)
export async function indexProducts(products: Product[]) {
const records = products.map((p) => ({
objectID: p.id, // 必需的唯一标识符
name: p.name,
description: p.description,
price: p.price,
category: p.category,
inStock: p.inventory > 0,
createdAt: p.createdAt.getTime(), // 使用时间戳进行排序
}));
// 每批约 1000-5000 条记录进行分块处理
const BATCH_SIZE = 1000;
for (let i = 0; i < records.length; i += BATCH_SIZE) {
const batch = records.slice(i, i + BATCH_SIZE);
await index.saveObjects(batch);
}
}
// 部分更新 - 仅更新特定字段
export async function updateProductPrice(productId: string, price: number) {
await index.partialUpdateObject({
objectID: productId,
price,
updatedAt: Date.now(),
});
}
使用操作进行部分更新
export async function incrementViewCount(productId: string) {
await index.partialUpdateObject({
objectID: productId,
viewCount: {
_operation: 'Increment',
value: 1,
},
});
}// 删除记录(优先使用此方法而非 deleteBy)
export async function deleteProducts(productIds: string[]) {
await index.deleteObjects(productIds);
}// 零停机全量重新索引(原子交换)
export async function fullReindex(products: Product[]) {
const tempIndex = adminClient.initIndex('products_temp');
// 索引到临时索引
await tempIndex.saveObjects(
products.map((p) => ({
objectID: p.id,
...p,
}))
);
// 从主索引复制设置
await adminClient.copyIndex('products', 'products_temp', {
scope: ['settings', 'synonyms', 'rules'],
});
// 原子交换
await adminClient.moveIndex('products_temp', 'products');
}
反模式 (Anti-patterns)
- 模式:使用
deleteBy进行批量删除 | 原因:deleteBy计算开销大且有频率限制 | 修复:使用deleteObjects并传入objectIDs数组
- 模式:一次索引一条记录 | 原因:会产生索引队列,降低处理速度 | 修复:以 1K-10K 为一组进行批量记录处理
- 模式:针对微小更改进行全量重新索引 | 原因:浪费操作次数,且比增量更新慢 | 修复:针对属性更改使用
partialUpdateObject
参考资料
- https://www.algolia.com/doc/guides/sending-and-managing-data/send-and-update-your-data/in-depth/the-different-synchronization-strategies
- https://www.algolia.com/blog/engineering/search-indexing-best-practices-for-top-performance-with-code-samples
API 密钥安全与限制
Algolia 的安全 API 密钥配置。
密钥类型:
- Admin API Key:全权控制(索引、设置、删除)
- Search-Only API Key:适用于前端,安全性高
- Secured API Keys:基于基础密钥生成的带限制密钥
可用限制:
- Indices:限制可访问的索引
- Rate limit:限制每个 IP 每小时的 API 调用次数
- Validity:设置过期时间
- HTTP referrers:限制特定的 URL 来源
- Query parameters:强制执行搜索参数
代码示例
// 绝对不要这样做 - 在前端使用 admin key
// const client = algoliasearch(appId, ADMIN_KEY); // 错误!
// 正确做法:在前端使用 search-only key
const searchClient = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY!
);
// 服务端:生成安全 API 密钥
// lib/algolia-secured-key.ts
import algoliasearch from 'algoliasearch';
const adminClient = algoliasearch(
process.env.ALGOLIA_APP_ID!,
process.env.ALGOLIA_ADMIN_KEY!
);
// 生成用户特定的安全密钥
export function generateSecuredKey(userId: string) {
const searchKey = process.env.ALGOLIA_SEARCH_KEY!;
return adminClient.generateSecuredApiKey(searchKey, {
// 用户只能看到自己的数据
filters: userId:${userId},
// 密钥 1 小时后过期
validUntil: Math.floor(Date.now() / 1000) + 3600,
// 限制在特定索引
restrictIndices: ['user_documents'],
});
}
// 为公共 API 创建限流密钥
export async function createRateLimitedKey() {
const { key } = await adminClient.addApiKey({
acl: ['search'],
indexes: ['products'],
description: 'Public search with rate limit',
maxQueriesPerIPPerHour: 1000,
referers: ['https://mysite.com/*'],
validity: 0, // 永不过期
});
return key;
}
// 获取用户安全密钥的 API 接口
// app/api/search-key/route
e.ts
import { auth } from '@/lib/auth';
import { generateSecuredKey } from '@/lib/algolia-secured-key';
export async function GET() {
const session = await auth();
if (!session?.user) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
const securedKey = generateSecuredKey(session.user.id);
return Response.json({ key: securedKey });
}
反模式 (Anti-patterns)
- 模式:在客户端代码中硬编码 Admin API 密钥 | 原因:将索引的完整控制权暴露给攻击者 | 修复:使用带有权限限制的仅搜索密钥
- 模式:所有用户使用同一个密钥 | 原因:无法针对每个用户限制数据访问 | 修复:使用用户过滤器生成安全 API 密钥
- 模式:公共搜索缺乏频率限制 | 原因:机器人可能会耗尽你的搜索配额 | 修复:在 API 密钥上设置
maxQueriesPerIPPerHour
参考资料
- https://www.algolia.com/doc/guides/security/api-keys
- https://support.algolia.com/hc/en-us/articles/14339249272977-What-are-the-best-practices-to-manage-Algolia-API-keys-in-my-code-and-protect-them
自定义排序与相关性调优
配置可搜索属性和自定义排序以优化相关性。
可搜索属性(顺序很重要):
1. 最重要的字段优先(如 title, name)
2. 次要字段随后(如 description, tags)
3. 排除不可搜索的字段(如 image_url, id)
自定义排序:
- 添加业务指标(如 popularity, rating, date)
- 使用
desc()表示降序,asc()表示升序
代码示例
// scripts/configure-index.ts
import algoliasearch from 'algoliasearch';
const adminClient = algoliasearch(
process.env.ALGOLIA_APP_ID!,
process.env.ALGOLIA_ADMIN_KEY!
);
const index = adminClient.initIndex('products');
async function configureIndex() {
await index.setSettings({
// 可搜索属性,按重要程度排序
searchableAttributes: [
'name', // 最重要
'brand',
'category',
'description', // 最不重要
],
// 用于分面/过滤的属性
attributesForFaceting: [
'category',
'brand',
'filterOnly(inStock)', // 仅用于过滤,不显示
'searchable(tags)', // 可搜索的分面
],
// 自定义排序(在文本相关性之后生效)
customRanking: [
'desc(popularity)', // 最热门优先
'desc(rating)', // 然后按评分
'desc(createdAt)', // 然后按最新日期
],
// 容错设置
typoTolerance: true,
minWordSizefor1Typo: 4,
minWordSizefor2Typos: 8,
// 查询设置
queryLanguages: ['en'],
removeStopWords: ['en'],
// 高亮显示
attributesToHighlight: ['name', 'description'],
highlightPreTag: '<mark>',
highlightPostTag: '</mark>',
// 分页
hitsPerPage: 20,
paginationLimitedTo: 1000,
// 去重 (Distinct)
attributeForDistinct: 'productFamily',
distinct: true,
});
// 添加同义词
await index.saveSynonyms([
{
objectID: 'phone-mobile',
type: 'synonym',
synonyms: ['phone', 'mobile', 'cell', 'smartphone'],
},
{
objectID: 'laptop-notebook',
type: 'oneWaySynonym',
input: 'laptop',
synonyms: ['notebook', 'portable computer'],
},
]);
// 添加规则(基于查询的自定义)
await index.saveRules([
{
objectID: 'boost-sale-items',
condition: {
anchoring: 'contains',
pattern: 'sale',
},
consequence: {
params: {
filters: 'onSale:true',
optionalF
ilters: ['featured:true'],
},
},
},
]);
console.log('Index configured successfully');
}
configureIndex();
反模式 (Anti-patterns)
- 模式:对所有属性进行同等权重搜索 | 原因:降低相关性,导致描述中的匹配项与标题匹配项权重相同 | 修复:按重要程度排列
searchableAttributes
- 模式:缺乏自定义排序 | 原因:仅依赖文本匹配,忽略了业务价值 | 修复:在
customRanking中加入流行度、评分或新鲜度
- 模式:将原始日期作为字符串索引 | 原因:无法正确按日期排序 | 修复:使用时间戳 (
getTime()) 进行日期排序
参考资料
- https://www.algolia.com/doc/guides/managing-results/relevance-overview
- https://www.algolia.com/doc/guides/managing-results/must-do/custom-ranking
分面搜索与过滤 (Faceted Search and Filtering)
使用精炼列表 (refinement lists)、范围滑块 (range sliders) 和层级菜单 (hierarchical menus) 实现分面导航。
组件类型:
- RefinementList:多选复选框
- Menu:单选列表
- HierarchicalMenu:嵌套类别
- RangeInput/RangeSlider:数值范围
- ToggleRefinement:布尔值过滤器
代码示例
'use client';
import {
InstantSearch,
SearchBox,
Hits,
RefinementList,
HierarchicalMenu,
RangeInput,
ToggleRefinement,
ClearRefinements,
CurrentRefinements,
Stats,
SortBy,
} from 'react-instantsearch';
import { searchClient, INDEX_NAME } from '@/lib/algolia';
export function ProductSearch() {
return (
<InstantSearch searchClient={searchClient} indexName={INDEX_NAME}>
<div className="flex gap-8">
{/* 过滤器侧边栏 */}
<aside className="w-64 space-y-6">
<ClearRefinements />
<CurrentRefinements />
{/* 类别层级 */}
<div>
<h3 className="font-semibold mb-2">Categories</h3>
<HierarchicalMenu
attributes={[
'categories.lvl0',
'categories.lvl1',
'categories.lvl2',
]}
limit={10}
showMore
/>
</div>
{/* 品牌过滤器 */}
<div>
<h3 className="font-semibold mb-2">Brand</h3>
<RefinementList
attribute="brand"
searchable
searchablePlaceholder="Search brands..."
showMore
limit={5}
showMoreLimit={20}
/>
</div>
{/* 价格范围 */}
<div>
<h3 className="font-semibold mb-2">Price</h3>
<RangeInput
attribute="price"
precision={0}
classNames={{
input: 'w-20 px-2 py-1 border rounded',
}}
/>
</div>
{/* 库存开关 */}
<ToggleRefinement
attribute="inStock"
label="In Stock Only"
on={true}
/>
{/* 评分过滤器 */}
<div>
<h3 className="font-semibold mb-2">Rating</h3>
<RefinementList
attribute="rating"
transformItems={(items) =>
items.map((item) => ({
...item,
label: '★'.repeat(Number(item.label)),
}))
}
/>
</div>
</aside>
{/* 结果区域 */}
<main className="flex-1">
<div className="flex justify-between items-center mb-4">
<SearchBox placeholder="Search products..." />
<SortBy
items={[
{ label: '相关度', value: 'products' },
{ label: '价格 (从低到高)', value: 'products_price_asc' },
{ label: '价格 (从高到低)', value: 'products_price_desc' },
{ label: '评分', value: 'products_rating_desc' },
]}
/>
</div>
<Stats />
<Hits hitComponent={ProductHit} />
</main>
</div>
</InstantSearch>
);
}
// 排序需创建副本索引 (replica indices)
// products_price_asc: customRanking: ['asc(price)']
// products_price_desc: customRanking: ['desc(price)']
// products_rating_desc: customRanking: ['desc(rating)']
反模式 (Anti-patterns)
- 模式:对非分面属性进行分面 | 原因:必须在设置中声明 attributesForFaceting | 修复:将属性添加到 attributesForFaceting 数组中
- 模式:对隐藏过滤器未使用 filterOnly() | 原因:在不显示的属性上浪费了分面计算资源 | 修复:对不显示的过滤器使用 filterOnly(attribute)
参考资料
- https://www.algolia.com/doc/guides/managing-results/refine-results/faceting
- https://www.algolia.com/doc/api-reference/widgets/refinement-list/react
查询建议与自动补全 (Query Suggestions and Autocomplete)
实现带有查询建议和即时结果的自动补全。
可以使用 @algolia/autocomplete-js 实现独立自动补全,或通过 SearchBox 与 InstantSearch 集成。
查询建议需要由 Algolia 生成的独立索引。
代码示例
// 独立自动补全
// components/Autocomplete.tsx
'use client';
import { autocomplete, getAlgoliaResults } from '@algolia/autocomplete-js';
import algoliasearch from 'algoliasearch/lite';
import { useEffect, useRef } from 'react';
import '@algolia/autocomplete-theme-classic';
const searchClient = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY!
);
export function Autocomplete() {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!containerRef.current) return;
const search = autocomplete({
container: containerRef.current,
placeholder: '搜索产品',
openOnFocus: true,
getSources({ query }) {
if (!query) return [];
return [
// 查询建议
{
sourceId: 'suggestions',
getItems() {
return getAlgoliaResults({
searchClient,
queries: [
{
indexName: 'products_query_suggestions',
query,
params: { hitsPerPage: 5 },
},
],
});
},
templates: {
header() {
return '建议';
},
item({ item, html }) {
return html<span>${item.query}</span>;
},
},
},
// 即时结果
{
sourceId: 'products',
getItems() {
return getAlgoliaResults({
searchClient,
queries: [
{
indexName: 'products',
query,
params: { hitsPerPage: 8 },
},
],
});
},
templates: {
header() {
return '产品';
},
i
tem({ item, html }) {
return html;
<a href="/products/${item.objectID}">
<img src="${item.image}" alt="${item.name}" />
<span>${item.name}</span>
<span>$${item.price}</span>
</a>
},
},
onSelect({ item, setQuery, refresh }) {
// 选择后跳转
window.location.href = /products/${item.objectID};
},
},
];
},
});
return () => search.destroy();
}, []);
return <div ref={containerRef} />;
}
// 与 InstantSearch 结合使用
import { connectSearchBox } from 'react-instantsearch';
import { autocomplete } from '@algolia/autocomplete-js';
// 或使用内置的 Autocomplete 组件
import { Autocomplete as AlgoliaAutocomplete } from 'react-instantsearch';
export function SearchWithAutocomplete() {
return (
<InstantSearch searchClient={searchClient} indexName="products">
<AlgoliaAutocomplete
placeholder="搜索产品..."
detachedMediaQuery="(max-width: 768px)"
/>
<Hits hitComponent={ProductHit} />
</InstantSearch>
);
}
反模式 (Anti-patterns)
- 模式:创建不带防抖的自动补全 | 原因:每次按键都会触发搜索,浪费操作额度 | 修复:Algolia autocomplete 自动处理防抖
- 模式:未使用 Query Suggestions 索引 | 原因:缺失热门查询的搜索分析 | 修复:在 Algolia 控制面板中启用 Query Suggestions
参考资料
- https://www.algolia.com/doc/ui-libraries/autocomplete/introduction/what-is-autocomplete
- https://www.algolia.com/doc/guides/building-search-ui/ui-and-ux-patterns/query-suggestions/how-to/optimizing-query-suggestions-relevance/js
潜在坑点 (Sharp Edges)
前端代码中出现 Admin API Key
严重程度:CRITICAL (极高)索引速率限制与节流 (Throttling)
严重程度:HIGH (高)记录大小与索引限制
严重程度:MEDIUM (中)网络请求中可见索引名称包含 PII (个人可识别信息)
严重程度:MEDIUM (中)可搜索属性的顺序影响相关性
严重程度:MEDIUM (中)全量重新索引会消耗所有操作额度
严重程度:MEDIUM (中)每次按键都被计为一次搜索操作
严重程度:MEDIUM (中)InstantSearch 的 SSR 注水 (Hydration) 不匹配
严重程度:MEDIUM (中)用于排序的副本索引 (Replica Indices) 会增加存储开销
严重程度:LOW (低)分面搜索 (Faceting) 需要声明 attributesForFaceting
严重程度:MEDIUM (中)验证检查 (Validation Checks)
客户端代码中包含 Admin API Key
严重程度:ERRORAdmin API key 绝不能暴露在客户端代码中
提示信息:Admin API key 暴露在客户端。请使用仅限搜索 (search-only) 的 key。
硬编码 Algolia API Key
严重程度:ERRORAPI key 应使用环境变量
提示信息:Algolia 凭据被硬编码。请使用环境变量。
使用搜索 Key 进行索引
严重程度:ERROR索引操作需要 admin key,而非 search key
提示信息:索引操作使用了搜索 key。请在写入操作中使用 admin key。
在循环中进行单条记录索引
严重程度:WARNING应将记录批量处理以提高索引效率
提示信息:循环中存在单条记录索引。请使用 saveObjects 进行批量索引。
使用 deleteBy 进行删除
严重程度:WARNINGdeleteBy 成本较高且有速率限制
提示信息:deleteBy 成本较高。建议使用带有具体 ID 的 deleteObjects。
频繁的全量重新索引
严重程度:WARNING全量重新索引会对未更改的数据浪费操作额度
提示信息:全量重新索引过于频繁。请考虑对未更改的数据进行增量同步。
使用完整客户端而非轻量版
严重程度:INFO
在前端使用轻量级客户端以减小 bundle 体积
消息:导入了完整的 Algolia 客户端。前端请使用 algoliasearch/lite。
在 Next.js 中使用常规 InstantSearch
严重程度:WARNING
使用 react-instantsearch-nextjs 以支持 SSR
消息:正在使用常规 InstantSearch。Next.js SSR 请使用 InstantSearchNext。
缺失可搜索属性配置
严重程度:WARNING
配置 searchableAttributes 以提高相关性
消息:未配置 searchableAttributes。请设置属性优先级以优化相关性。
缺失自定义排序
严重程度:INFO
自定义排序可提升业务相关性
消息:未配置 customRanking。请添加业务指标(如流行度、评分)。
协作
委派触发条件
- 用户需要电商结账 $\rightarrow$ stripe-integration(产品搜索引导至购买)
- 用户需要搜索分析 $\rightarrow$ segment-cdp(追踪搜索查询和结果)
- 用户需要用户认证 $\rightarrow$ clerk-auth(为每个用户提供安全的 API 密钥)
- 用户需要数据库设置 $\rightarrow$ postgres-wizard(索引的源数据)
- 用户需要 Serverless 部署 $\rightarrow$ aws-serverless(用于索引任务的 Lambda)
使用场景
- 用户提到或暗示:添加搜索功能
- 用户提到或暗示:algolia
- 用户提到或暗示:instantsearch
- 用户提到或暗示:搜索 API
- 用户提到或暗示:搜索功能
- 用户提到或暗示:typeahead(类型前瞻)
- 用户提到或暗示:自动补全搜索
- 用户提到或暗示:分面搜索 (faceted search)
- 用户提到或暗示:搜索索引
- 用户提到或暗示:即时搜索 (search as you type)
局限性
- 仅在任务明确符合上述范围时使用此技能。
- 不要将输出视为针对特定环境的验证、测试或专家评审的替代方案。
- 如果缺失必要的输入、权限、安全边界或验收标准,请停止并请求澄清。