第一个搜索应用 #
项目概述 #
我们将创建一个简单的商品搜索应用,实现以下功能:
- 添加商品数据到Algolia
- 实现即时搜索
- 显示搜索结果
- 添加筛选功能
准备工作 #
安装依赖 #
bash
# 创建项目目录
mkdir algolia-search-demo
cd algolia-search-demo
# 初始化项目
npm init -y
# 安装Algolia客户端
npm install algoliasearch
# 安装环境变量管理
npm install dotenv
项目结构 #
text
algolia-search-demo/
├── .env
├── package.json
├── data/
│ └── products.json
├── scripts/
│ └── import-data.js
└── public/
└── index.html
步骤一:准备数据 #
创建示例数据 #
创建 data/products.json:
json
[
{
"objectID": "1",
"name": "iPhone 15 Pro",
"description": "Apple iPhone 15 Pro with A17 Pro chip",
"brand": "Apple",
"category": "Smartphones",
"price": 999,
"stock": 50,
"rating": 4.8,
"image": "https://example.com/iphone15.jpg"
},
{
"objectID": "2",
"name": "Samsung Galaxy S24",
"description": "Samsung Galaxy S24 with AI features",
"brand": "Samsung",
"category": "Smartphones",
"price": 799,
"stock": 100,
"rating": 4.6,
"image": "https://example.com/s24.jpg"
},
{
"objectID": "3",
"name": "MacBook Pro 14",
"description": "Apple MacBook Pro with M3 Pro chip",
"brand": "Apple",
"category": "Laptops",
"price": 1999,
"stock": 30,
"rating": 4.9,
"image": "https://example.com/macbook.jpg"
},
{
"objectID": "4",
"name": "Dell XPS 15",
"description": "Dell XPS 15 with Intel Core i9",
"brand": "Dell",
"category": "Laptops",
"price": 1499,
"stock": 45,
"rating": 4.5,
"image": "https://example.com/xps15.jpg"
},
{
"objectID": "5",
"name": "Sony WH-1000XM5",
"description": "Sony noise canceling headphones",
"brand": "Sony",
"category": "Audio",
"price": 349,
"stock": 200,
"rating": 4.7,
"image": "https://example.com/sony.jpg"
},
{
"objectID": "6",
"name": "AirPods Pro",
"description": "Apple AirPods Pro with active noise cancellation",
"brand": "Apple",
"category": "Audio",
"price": 249,
"stock": 150,
"rating": 4.6,
"image": "https://example.com/airpods.jpg"
}
]
数据字段说明 #
| 字段 | 类型 | 说明 |
|---|---|---|
| objectID | string | 唯一标识符(必需) |
| name | string | 商品名称 |
| description | string | 商品描述 |
| brand | string | 品牌 |
| category | string | 分类 |
| price | number | 价格 |
| stock | number | 库存 |
| rating | number | 评分 |
步骤二:导入数据 #
创建导入脚本 #
创建 scripts/import-data.js:
javascript
require('dotenv').config();
const algoliasearch = require('algoliasearch');
const products = require('../data/products.json');
const client = algoliasearch(
process.env.ALGOLIA_APP_ID,
process.env.ALGOLIA_ADMIN_KEY
);
const index = client.initIndex(process.env.ALGOLIA_INDEX_NAME);
async function importData() {
try {
// 配置索引设置
await index.setSettings({
searchableAttributes: ['name', 'description', 'brand', 'category'],
attributesForFaceting: ['brand', 'category', 'price'],
customRanking: ['desc(rating)', 'desc(popularity)'],
attributesToHighlight: ['name', 'description'],
hitsPerPage: 10
});
console.log('索引设置已更新');
// 导入数据
const { objectIDs } = await index.saveObjects(products);
console.log(`成功导入 ${objectIDs.length} 条数据`);
} catch (error) {
console.error('导入失败:', error);
}
}
importData();
配置环境变量 #
创建 .env:
bash
ALGOLIA_APP_ID=your_app_id
ALGOLIA_ADMIN_KEY=your_admin_key
ALGOLIA_INDEX_NAME=products
运行导入 #
bash
node scripts/import-data.js
输出:
text
索引设置已更新
成功导入 6 条数据
步骤三:创建搜索界面 #
HTML结构 #
创建 public/index.html:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>商品搜索</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f5f5f5;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.search-header {
text-align: center;
margin-bottom: 30px;
}
.search-header h1 {
color: #333;
margin-bottom: 20px;
}
.search-box {
position: relative;
max-width: 600px;
margin: 0 auto;
}
.search-box input {
width: 100%;
padding: 15px 20px;
font-size: 16px;
border: 2px solid #ddd;
border-radius: 8px;
outline: none;
transition: border-color 0.3s;
}
.search-box input:focus {
border-color: #5468ff;
}
.filters {
display: flex;
gap: 10px;
justify-content: center;
margin: 20px 0;
flex-wrap: wrap;
}
.filter-btn {
padding: 8px 16px;
border: 1px solid #ddd;
background: white;
border-radius: 20px;
cursor: pointer;
transition: all 0.3s;
}
.filter-btn:hover,
.filter-btn.active {
background: #5468ff;
color: white;
border-color: #5468ff;
}
.stats {
text-align: center;
color: #666;
margin-bottom: 20px;
}
.results {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 20px;
}
.product-card {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transition: transform 0.3s, box-shadow 0.3s;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
}
.product-image {
width: 100%;
height: 200px;
background: #f0f0f0;
border-radius: 8px;
margin-bottom: 15px;
display: flex;
align-items: center;
justify-content: center;
color: #999;
}
.product-brand {
color: #888;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 1px;
}
.product-name {
font-size: 18px;
font-weight: 600;
color: #333;
margin: 8px 0;
}
.product-description {
color: #666;
font-size: 14px;
line-height: 1.5;
margin-bottom: 15px;
}
.product-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
.product-price {
font-size: 20px;
font-weight: 700;
color: #5468ff;
}
.product-rating {
color: #ffc107;
}
.no-results {
text-align: center;
padding: 40px;
color: #666;
}
.highlight {
background: #fff3cd;
padding: 0 2px;
border-radius: 2px;
}
</style>
</head>
<body>
<div class="container">
<div class="search-header">
<h1>商品搜索</h1>
<div class="search-box">
<input type="text" id="searchInput" placeholder="搜索商品名称、品牌或描述...">
</div>
</div>
<div class="filters" id="filters">
<button class="filter-btn active" data-filter="all">全部</button>
<button class="filter-btn" data-filter="Smartphones">手机</button>
<button class="filter-btn" data-filter="Laptops">笔记本</button>
<button class="filter-btn" data-filter="Audio">音频</button>
</div>
<div class="stats" id="stats"></div>
<div class="results" id="results"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/algoliasearch@4/dist/algoliasearch-lite.umd.js"></script>
<script>
// 初始化客户端
const client = algoliasearch('YOUR_APP_ID', 'YOUR_SEARCH_KEY');
const index = client.initIndex('products');
const searchInput = document.getElementById('searchInput');
const resultsContainer = document.getElementById('results');
const statsContainer = document.getElementById('stats');
const filterButtons = document.querySelectorAll('.filter-btn');
let currentCategory = 'all';
// 搜索函数
async function search(query) {
const filters = currentCategory === 'all'
? ''
: `category:${currentCategory}`;
try {
const { hits, nbHits, processingTimeMS } = await index.search(query, {
filters,
hitsPerPage: 20
});
displayResults(hits);
displayStats(nbHits, processingTimeMS);
} catch (error) {
console.error('搜索失败:', error);
}
}
// 显示结果
function displayResults(hits) {
if (hits.length === 0) {
resultsContainer.innerHTML = `
<div class="no-results">
<p>没有找到相关商品</p>
</div>
`;
return;
}
resultsContainer.innerHTML = hits.map(hit => `
<div class="product-card">
<div class="product-image">商品图片</div>
<div class="product-brand">${hit.brand}</div>
<div class="product-name">${hit._highlightResult.name.value}</div>
<div class="product-description">${hit._highlightResult.description.value}</div>
<div class="product-footer">
<span class="product-price">$${hit.price}</span>
<span class="product-rating">★ ${hit.rating}</span>
</div>
</div>
`).join('');
}
// 显示统计
function displayStats(count, time) {
statsContainer.textContent = `找到 ${count} 个结果,耗时 ${time}ms`;
}
// 搜索输入事件
let debounceTimer;
searchInput.addEventListener('input', (e) => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
search(e.target.value);
}, 300);
});
// 分类筛选事件
filterButtons.forEach(btn => {
btn.addEventListener('click', () => {
filterButtons.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentCategory = btn.dataset.filter;
search(searchInput.value);
});
});
// 初始加载
search('');
</script>
</body>
</html>
步骤四:运行应用 #
本地预览 #
bash
# 使用任意HTTP服务器
npx serve public
# 或使用Python
python -m http.server 8000 -d public
访问 http://localhost:3000 或 http://localhost:8000。
功能验证 #
测试搜索 #
- 输入"iPhone",应该显示iPhone相关产品
- 输入"Apple",应该显示所有Apple产品
- 输入"Sony",应该显示Sony耳机
测试筛选 #
- 点击"手机"按钮,只显示手机类别
- 点击"笔记本"按钮,只显示笔记本类别
- 点击"全部"按钮,显示所有产品
测试高亮 #
搜索结果中的匹配文字应该被高亮显示。
代码解析 #
搜索配置 #
javascript
const { hits, nbHits, processingTimeMS } = await index.search(query, {
filters, // 过滤条件
hitsPerPage: 20 // 每页结果数
});
防抖处理 #
javascript
// 300ms防抖,避免频繁请求
let debounceTimer;
searchInput.addEventListener('input', (e) => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
search(e.target.value);
}, 300);
});
高亮显示 #
javascript
// 使用_highlightResult获取高亮内容
${hit._highlightResult.name.value}
总结 #
本章我们完成了一个完整的搜索应用:
- 准备商品数据
- 导入数据到Algolia
- 创建搜索界面
- 实现即时搜索和筛选
接下来,让我们深入了解 Algolia控制台。
最后更新:2026-03-28