基础搜索 #
搜索基础 #
简单搜索 #
javascript
const algoliasearch = require('algoliasearch');
const client = algoliasearch('APP_ID', 'SEARCH_KEY');
const index = client.initIndex('products');
// 最简单的搜索
const results = await index.search('iphone');
console.log(results);
搜索结果结构 #
javascript
{
"hits": [...], // 搜索结果数组
"nbHits": 150, // 总匹配数
"page": 0, // 当前页码
"nbPages": 15, // 总页数
"hitsPerPage": 10, // 每页结果数
"processingTimeMS": 1, // 处理时间(毫秒)
"query": "iphone", // 搜索词
"params": "...", // 搜索参数
"exhaustiveNbHits": true, // 是否精确计数
"exhaustiveFacetsCount": true
}
结果项结构 #
javascript
{
"objectID": "prod-001",
"name": "iPhone 15 Pro",
"brand": "Apple",
"price": 999,
// 高亮结果
"_highlightResult": {
"name": {
"value": "<em>iPhone</em> 15 Pro",
"matchLevel": "full",
"matchedWords": ["iphone"],
"fullyHighlighted": false
}
},
// 代码片段结果
"_snippetResult": {
"description": {
"value": "...latest <em>iPhone</em> with A17 Pro..."
}
},
// 排名信息
"_rankingInfo": {
"nbTypos": 0,
"firstMatchedWord": 0,
"proximityDistance": 1
}
}
搜索方法 #
使用回调 #
javascript
index.search('iphone', (err, results) => {
if (err) {
console.error(err);
return;
}
console.log(results.hits);
});
使用Promise #
javascript
index.search('iphone')
.then(results => {
console.log(results.hits);
})
.catch(err => {
console.error(err);
});
使用async/await #
javascript
try {
const results = await index.search('iphone');
console.log(results.hits);
} catch (err) {
console.error(err);
}
搜索参数 #
基本参数 #
javascript
const results = await index.search('iphone', {
hitsPerPage: 20, // 每页结果数
page: 0, // 页码(从0开始)
attributesToRetrieve: ['name', 'price', 'imageUrl'] // 返回字段
});
分页搜索 #
javascript
async function searchWithPagination(query, page = 0) {
const results = await index.search(query, {
hitsPerPage: 20,
page: page
});
return {
items: results.hits,
currentPage: results.page,
totalPages: results.nbPages,
totalItems: results.nbHits,
hasNextPage: results.page < results.nbPages - 1,
hasPrevPage: results.page > 0
};
}
// 使用
const page1 = await searchWithPagination('iphone', 0);
const page2 = await searchWithPagination('iphone', 1);
限制搜索属性 #
javascript
// 只在name和brand中搜索
const results = await index.search('apple', {
restrictSearchableAttributes: ['name', 'brand']
});
搜索选项 #
拼写容错 #
javascript
// 启用拼写容错(默认)
const results = await index.search('iphne', {
typoTolerance: true
});
// 禁用拼写容错
const results = await index.search('iphne', {
typoTolerance: false
});
忽略复数 #
javascript
// "phone" 可以匹配 "phones"
const results = await index.search('phone', {
ignorePlurals: true
});
移除停用词 #
javascript
// 移除常见停用词(the, a, an等)
const results = await index.search('the iphone', {
removeStopWords: true
});
查询类型 #
javascript
// prefixLast(默认):最后一个词前缀匹配
// "iph" 匹配 "iphone"
const results = await index.search('iph', {
queryType: 'prefixLast'
});
// prefixAll:所有词前缀匹配
const results = await index.search('iph pro', {
queryType: 'prefixAll'
});
// prefixNone:精确匹配
const results = await index.search('iphone', {
queryType: 'prefixNone'
});
搜索结果处理 #
处理高亮 #
javascript
function renderHit(hit) {
const name = hit._highlightResult.name.value;
const description = hit._snippetResult?.description?.value || hit.description;
return `
<div class="product">
<h3>${name}</h3>
<p>${description}</p>
<span class="price">$${hit.price}</span>
</div>
`;
}
自定义高亮标签 #
javascript
const results = await index.search('iphone', {
attributesToHighlight: ['name', 'description'],
highlightPreTag: '<mark>',
highlightPostTag: '</mark>'
});
// 结果: <mark>iPhone</mark> 15 Pro
处理无结果 #
javascript
async function search(query) {
const results = await index.search(query);
if (results.hits.length === 0) {
return {
success: false,
message: `没有找到"${query}"相关的结果`,
suggestions: await getSuggestions(query)
};
}
return {
success: true,
items: results.hits,
total: results.nbHits
};
}
搜索函数封装 #
基础搜索函数 #
javascript
class SearchService {
constructor(appId, apiKey, indexName) {
const client = algoliasearch(appId, apiKey);
this.index = client.initIndex(indexName);
}
async search(query, options = {}) {
const defaultOptions = {
hitsPerPage: 20,
page: 0,
attributesToHighlight: ['name', 'description'],
attributesToRetrieve: ['name', 'price', 'imageUrl', 'brand']
};
const results = await this.index.search(query, {
...defaultOptions,
...options
});
return this.formatResults(results);
}
formatResults(results) {
return {
items: results.hits.map(this.formatHit),
total: results.nbHits,
page: results.page,
totalPages: results.nbPages,
processingTime: results.processingTimeMS
};
}
formatHit(hit) {
return {
id: hit.objectID,
name: hit._highlightResult.name.value,
price: hit.price,
image: hit.imageUrl,
brand: hit.brand
};
}
}
// 使用
const searchService = new SearchService('APP_ID', 'SEARCH_KEY', 'products');
const results = await searchService.search('iphone');
高级搜索函数 #
javascript
async function advancedSearch(options) {
const {
query = '',
filters = '',
facets = [],
page = 0,
hitsPerPage = 20,
sortBy = 'default'
} = options;
// 选择索引(支持排序)
const indexName = sortBy === 'default'
? 'products'
: `products_${sortBy}`;
const index = client.initIndex(indexName);
const searchParams = {
query,
page,
hitsPerPage,
attributesToHighlight: ['name', 'description'],
attributesToSnippet: ['description:100']
};
if (filters) {
searchParams.filters = filters;
}
if (facets.length > 0) {
searchParams.facets = facets;
}
return await index.search(searchParams);
}
搜索状态管理 #
搜索状态对象 #
javascript
const searchState = {
query: '',
page: 0,
filters: {},
sortBy: 'default',
hitsPerPage: 20,
setQuery(query) {
this.query = query;
this.page = 0;
},
setPage(page) {
this.page = page;
},
setFilter(key, value) {
this.filters[key] = value;
this.page = 0;
},
removeFilter(key) {
delete this.filters[key];
this.page = 0;
},
buildFilterString() {
return Object.entries(this.filters)
.map(([key, value]) => `${key}:${value}`)
.join(' AND ');
}
};
执行搜索 #
javascript
async function executeSearch(state) {
const filterString = state.buildFilterString();
const results = await index.search(state.query, {
page: state.page,
hitsPerPage: state.hitsPerPage,
filters: filterString,
facets: ['brand', 'category', 'price_range']
});
return {
...results,
state: { ...state }
};
}
搜索优化 #
防抖处理 #
javascript
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
const debouncedSearch = debounce(async (query) => {
const results = await index.search(query);
renderResults(results);
}, 300);
// 输入时调用
inputElement.addEventListener('input', (e) => {
debouncedSearch(e.target.value);
});
缓存结果 #
javascript
const searchCache = new Map();
async function cachedSearch(query, options = {}) {
const cacheKey = JSON.stringify({ query, ...options });
if (searchCache.has(cacheKey)) {
return searchCache.get(cacheKey);
}
const results = await index.search(query, options);
searchCache.set(cacheKey, results);
// 限制缓存大小
if (searchCache.size > 100) {
const firstKey = searchCache.keys().next().value;
searchCache.delete(firstKey);
}
return results;
}
预取结果 #
javascript
// 预取热门搜索
async function prefetchPopularSearches() {
const popularQueries = ['iphone', 'samsung', 'laptop', 'headphones'];
await Promise.all(
popularQueries.map(query =>
index.search(query, { hitsPerPage: 5 })
)
);
}
错误处理 #
基本错误处理 #
javascript
async function safeSearch(query) {
try {
const results = await index.search(query);
return { success: true, data: results };
} catch (error) {
console.error('Search failed:', error);
return {
success: false,
error: error.message,
data: null
};
}
}
重试机制 #
javascript
async function searchWithRetry(query, maxRetries = 3) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
return await index.search(query);
} catch (error) {
lastError = error;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
throw lastError;
}
搜索示例 #
电商搜索 #
javascript
async function productSearch(query, options = {}) {
const {
category,
brand,
minPrice,
maxPrice,
page = 0
} = options;
const filters = [];
if (category) filters.push(`category:${category}`);
if (brand) filters.push(`brand:${brand}`);
if (minPrice !== undefined) filters.push(`price >= ${minPrice}`);
if (maxPrice !== undefined) filters.push(`price <= ${maxPrice}`);
return await index.search(query, {
filters: filters.join(' AND '),
page,
hitsPerPage: 20,
facets: ['brand', 'category', 'price_range'],
attributesToHighlight: ['name', 'description'],
customRanking: ['desc(rating)', 'desc(popularity)']
});
}
内容搜索 #
javascript
async function contentSearch(query, options = {}) {
const {
type,
author,
tags,
page = 0
} = options;
const filters = [];
if (type) filters.push(`type:${type}`);
if (author) filters.push(`author.id:${author}`);
if (tags?.length) {
filters.push(`tags:${tags.join(' OR tags:')}`);
}
return await index.search(query, {
filters: filters.join(' AND '),
page,
hitsPerPage: 10,
restrictSearchableAttributes: ['title', 'content', 'tags'],
attributesToHighlight: ['title', 'content'],
attributesToSnippet: ['content:200'],
removeStopWords: true,
ignorePlurals: true
});
}
总结 #
基础搜索要点:
| 要点 | 说明 |
|---|---|
| 搜索方法 | search()方法 |
| 结果结构 | hits, nbHits, page等 |
| 参数配置 | hitsPerPage, page, filters等 |
| 结果处理 | 高亮、分页、无结果处理 |
| 优化技巧 | 防抖、缓存、预取 |
| 错误处理 | try/catch、重试机制 |
接下来,让我们学习 分面搜索。
最后更新:2026-03-28