欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 产业 > Node.js 操作 ElasticSearch 完整指南:从安装到实战

Node.js 操作 ElasticSearch 完整指南:从安装到实战

2025/4/23 18:31:52 来源:https://blog.csdn.net/weixin_63443072/article/details/147407773  浏览:    关键词:Node.js 操作 ElasticSearch 完整指南:从安装到实战

本文将手把手教你如何搭建 ElasticSearch 环境,并通过 Node.js 实现高效数据检索。包含 10+ 个可直接复用的代码片段,助你快速掌握搜索、聚合等核心功能!

环境搭建篇

1. ElasticSearch 安装要点

下载

es下载连接
下载下来后,进入 bin 目录,终端运行第一个文件,即可启动es。
在这里插入图片描述

修改密码

进入 bin 目录下,终端输入:

 .\elasticsearch-reset-password -u elastic -i

输入两次密码即可修改超级用户 elastic 的密码。

然后访问 http://localhost:9200 。输入密码和账号后,若返回以下信息则代表修改密码成功:

{"name" : "Win10-2024UVSXG","cluster_name" : "elasticsearch","cluster_uuid" : "oan-H91LSSiReCuNSDWKIA","version" : {"number" : "8.15.0","build_flavor" : "default","build_type" : "zip","build_hash" : "1a77947f34deddb41af25e6f0ddb8e830159c179","build_date" : "2024-08-05T10:05:34.233336849Z","build_snapshot" : false,"lucene_version" : "9.11.1","minimum_wire_compatibility_version" : "7.17.0","minimum_index_compatibility_version" : "7.0.0"},"tagline" : "You Know, for Search"
}

2. Kibana 联动配置

下载

Kibana 下载链接

注意: 下载的 kibana 的版本要与 es 一致,否则可能会报错,无法访问 Kibana

修改Kibana配置文件

需要进入到Kibana目录中,修改 /config/kibana.yml 文件。设置访问端口、ip、es账号密码。

注意: es的账号密码不能使用 elastic 超级用户,但是默认有一个 kibana_system 用户,只需在es中修改 kibana_system 用户密码即可。
在这里插入图片描述

启动

进入项目的 bin 目录中,打开终端运行第一个文件即可。
在这里插入图片描述
最后访问 http://localhost:5601 即可。

Node.js 核心操作篇

ElasticSearch 和 Kibana 的安装与使用指南

引言

ElasticSearch 是一个强大的开源搜索和分析引擎,而 Kibana 则是 ElasticSearch 的可视化工具。本文将详细介绍如何下载、安装和配置 ElasticSearch 和 Kibana,以及如何在 Node.js 中使用 ElasticSearch 进行数据操作。


ElasticSearch 部分

下载 ElasticSearch

  1. 访问 ElasticSearch 官方下载页面。
  2. 选择适合您操作系统的版本进行下载。

启动 ElasticSearch

  1. 下载完成后,解压文件并进入 bin 目录。
  2. 在终端中运行第一个文件(Windows 用户运行 .bat 文件,Linux/macOS 用户运行 .sh 文件)。
  3. 启动成功后,ElasticSearch 默认运行在 http://localhost:9200

修改 ElasticSearch 密码

  1. 进入 bin 目录,在终端输入以下命令:
    .\elasticsearch-reset-password -u elastic -i
    
  2. 按照提示输入两次新密码。
  3. 访问 http://localhost:9200,使用账号 elastic 和新密码登录。如果返回类似以下信息,则表示密码修改成功:
    {"name": "Win10-2024UVSXG","cluster_name": "elasticsearch","cluster_uuid": "oan-H91LSSiReCuNSDWKIA","version": {"number": "8.15.0","build_flavor": "default","build_type": "zip","build_hash": "1a77947f34deddb41af25e6f0ddb8e830159c179","build_date": "2024-08-05T10:05:34.233336849Z","build_snapshot": false,"lucene_version": "9.11.1","minimum_wire_compatibility_version": "7.17.0","minimum_index_compatibility_version": "7.0.0"},"tagline": "You Know, for Search"
    }
    

Kibana 部分

下载 Kibana

  1. 访问 Kibana 官方下载页面。
  2. 注意: 下载的 Kibana 版本必须与 ElasticSearch 版本一致,否则可能会出现兼容性问题。

修改 Kibana 配置文件

  1. 进入 Kibana 目录,找到 /config/kibana.yml 文件。
  2. 修改以下配置项:
    • server.port: Kibana 的访问端口(默认为 5601)。
    • server.host: Kibana 的访问 IP(默认为 localhost)。
    • elasticsearch.usernameelasticsearch.password: 使用 kibana_system 用户的账号密码(需先在 ElasticSearch 中修改该用户的密码)。
  3. 保存配置文件。

启动 Kibana

  1. 进入 Kibana 的 bin 目录。
  2. 在终端中运行第一个文件(Windows 用户运行 .bat 文件,Linux/macOS 用户运行 .sh 文件)。
  3. 启动成功后,访问 http://localhost:5601 即可进入 Kibana 界面。

Node.js 中使用 ElasticSearch

安装依赖

在 Node.js 项目中安装 ElasticSearch 客户端库:

npm install @elastic/elasticsearch

基本使用

以下是一些常见的 ElasticSearch 操作示例:

1. 初始化客户端 (支持多种认证方式)
const { Client } = require('@elastic/elasticsearch');
// 基础认证
const client = new Client({node: 'http://localhost:9200',auth: { username: 'elastic', password: 'yourpassword' }
});
// API Key 认证
const apiKeyClient = new Client({node: 'http://localhost:9200',auth: { apiKey: 'base64EncodedKey' }
});
// 云服务连接
const cloudClient = new Client({cloud: { id: 'my-cloud-id' },auth: { username: 'elastic', password: 'cloudpassword' }
});
2. 创建索引并添加数据
const user = await client.index({index: 'user-data',document: {user: 1,age: 18,name: 'jack',}
});
3. 查询数据
const response = await client.get({index: 'user-data',id: user._id // 可以指定 ID 或使用自动生成的 ID
});
4. 搜索数据
const result = await client.search({index: 'user-data',query: {match: {name: 'jack' // 模糊查询}},size: 1 // 返回结果数量
});
console.log(result.hits.hits); // 打印搜索结果
5. 删除数据
await client.delete({index: 'user-data',id: user._id
});
6. 搜索所有数据
const response = await client.search({index: 'users',query: {match_all: {}, // 空对象表示匹配所有},size: 100 // 返回 100 条数据
});
7. 索引管理
创建索引(带映射)
async function createIndexWithMapping() {try {const response = await client.indices.create({index: 'products',body: {mappings: {properties: {name: { type: 'text' },price: { type: 'float' },description: { type: 'text' },tags: { type: 'keyword' },created_at: { type: 'date' }}}}});console.log('索引创建成功:', response);} catch (error) {console.error('索引创建失败:', error.meta.body.error);}
}
检查索引是否存在
async function checkIndexExists(indexName) {try {const exists = await client.indices.exists({ index: indexName });console.log(`索引 ${indexName} 存在:`, exists);return exists;} catch (error) {console.error('检查索引失败:', error);return false;}
}
删除索引
async function deleteIndex(indexName) {try {const response = await client.indices.delete({ index: indexName });console.log('索引删除成功:', response);return response;} catch (error) {console.error('索引删除失败:', error.meta.body.error);throw error;}
}
8. 文档操作
批量插入文档
async function bulkInsert() {const dataset = [{ id: 1, name: 'iPhone 13', price: 799, category: 'phone' },{ id: 2, name: 'MacBook Pro', price: 1299, category: 'laptop' },{ id: 3, name: 'AirPods Pro', price: 249, category: 'accessory' }];const body = dataset.flatMap(doc => [{ index: { _index: 'products', _id: doc.id } },doc]);try {const { body: bulkResponse } = await client.bulk({ body });if (bulkResponse.errors) {console.log('批量插入部分失败:', bulkResponse.items);} else {console.log('批量插入成功');}} catch (error) {console.error('批量插入失败:', error);}
}
更新文档
async function updateDocument(index, id, updates) {try {const response = await client.update({index,id,body: {doc: updates}});console.log('文档更新成功:', response);return response;} catch (error) {console.error('文档更新失败:', error.meta.body.error);throw error;}
}// 使用示例
// updateDocument('products', 1, { price: 849 });
部分更新与脚本更新
async function updateWithScript() {try {const response = await client.update({index: 'products',id: 1,body: {script: {source: 'ctx._source.price += params.price_diff',lang: 'painless',params: {price_diff: 50}}}});console.log('脚本更新成功:', response);} catch (error) {console.error('脚本更新失败:', error);}
}
9. 高级搜索查询
多条件复合查询
async function complexSearch() {try {const response = await client.search({index: 'products',body: {query: {bool: {must: [{ match: { category: 'phone' } }],filter: [{ range: { price: { gte: 500, lte: 1000 } } }],should: [{ match: { name: 'pro' } }],minimum_should_match: 1}},sort: [{ price: { order: 'desc' } }],highlight: {fields: {name: {},description: {}}}}});console.log('搜索结果:', response.hits.hits);return response.hits.hits;} catch (error) {console.error('搜索失败:', error);throw error;}
}
聚合查询
async function aggregateSearch() {try {const response = await client.search({index: 'products',body: {size: 0,aggs: {categories: {terms: { field: 'category.keyword', size: 10 },aggs: {avg_price: { avg: { field: 'price' } },max_price: { max: { field: 'price' } }}},price_stats: {stats: { field: 'price' }}}}});console.log('分类聚合结果:', response.aggregations.categories.buckets);console.log('价格统计:', response.aggregations.price_stats);return response.aggregations;} catch (error) {console.error('聚合查询失败:', error);throw error;}
}
全文搜索与高亮
async function fullTextSearch() {try {const response = await client.search({index: 'products',body: {query: {multi_match: {query: 'pro',fields: ['name^3', 'description'], // name字段权重更高type: 'best_fields'}},highlight: {pre_tags: ['<em>'],post_tags: ['</em>'],fields: {name: {},description: {}}}}});console.log('高亮搜索结果:');response.hits.hits.forEach(hit => {console.log(`ID: ${hit._id}, 分数: ${hit._score}`);console.log('高亮:', hit.highlight);});} catch (error) {console.error('全文搜索失败:', error);}
}
10. 实战案例:电商商品搜索
class ProductSearch {constructor() {this.client = new Client({ node: 'http://localhost:9200' });this.indexName = 'ecommerce_products';}async initIndex() {try {const exists = await this.client.indices.exists({ index: this.indexName });if (!exists) {await this.client.indices.create({index: this.indexName,body: {mappings: {properties: {name: { type: 'text', analyzer: 'ik_max_word' },description: { type: 'text', analyzer: 'ik_max_word' },price: { type: 'float' },stock: { type: 'integer' },categories: { type: 'keyword' },attributes: {type: 'nested',properties: {name: { type: 'keyword' },value: { type: 'keyword' }}},created_at: { type: 'date' }}}}});console.log('索引初始化完成');}} catch (error) {console.error('索引初始化失败:', error);}}async indexProduct(product) {try {const response = await this.client.index({index: this.indexName,body: product});await this.client.indices.refresh({ index: this.indexName });return response;} catch (error) {console.error('商品索引失败:', error);throw error;}}async searchProducts(query, filters = {}, page = 1, pageSize = 10) {try {const from = (page - 1) * pageSize;const body = {query: {bool: {must: [],filter: []}},from,size: pageSize,sort: [{ _score: 'desc' }, { created_at: 'desc' }]};// 添加全文搜索条件if (query) {body.query.bool.must.push({multi_match: {query,fields: ['name^3', 'description^2', 'categories'],type: 'best_fields'}});}// 添加过滤条件if (filters.categories) {body.query.bool.filter.push({terms: { categories: Array.isArray(filters.categories) ? filters.categories : [filters.categories] }});}if (filters.priceRange) {body.query.bool.filter.push({range: { price: filters.priceRange }});}// 添加嵌套属性过滤if (filters.attributes) {filters.attributes.forEach(attr => {body.query.bool.filter.push({nested: {path: 'attributes',query: {bool: {must: [{ term: { 'attributes.name': attr.name } },{ term: { 'attributes.value': attr.value } }]}}}});});}const response = await this.client.search({index: this.indexName,body});return {total: response.hits.total.value,products: response.hits.hits.map(hit => ({...hit._source,id: hit._id,score: hit._score}))};} catch (error) {console.error('商品搜索失败:', error);throw error;}}async getSuggestions(query) {try {const response = await this.client.search({index: this.indexName,body: {suggest: {name_suggest: {prefix: query,completion: {field: 'name_suggest',fuzzy: {fuzziness: 2}}},category_suggest: {text: query,term: {field: 'categories'}}}}});return {nameSuggestions: response.suggest.name_suggest[0].options.map(opt => opt.text),categorySuggestions: response.suggest.category_suggest[0].options.map(opt => opt.text)};} catch (error) {console.error('获取建议失败:', error);return { nameSuggestions: [], categorySuggestions: [] };}}
}// 使用示例
/*
const productSearch = new ProductSearch();
await productSearch.initIndex();// 添加商品
await productSearch.indexProduct({name: 'Apple iPhone 13 Pro',description: '最新款iPhone专业版',price: 999,stock: 100,categories: ['phone', 'apple'],attributes: [{ name: 'color', value: 'graphite' },{ name: 'storage', value: '256GB' }],created_at: new Date()
});// 搜索商品
const results = await productSearch.searchProducts('iphone',{ categories: 'phone',priceRange: { gte: 500, lte: 1200 },attributes: [{ name: 'color', value: 'graphite' }]},1,10
);// 获取搜索建议
const suggestions = await productSearch.getSuggestions('ipho');
*/
11. 错误处理与性能优化
重试机制
const { Client } = require('@elastic/elasticsearch');const client = new Client({node: 'http://localhost:9200',maxRetries: 5, // 最大重试次数requestTimeout: 60000, // 请求超时时间sniffOnStart: true, // 启动时嗅探节点sniffInterval: 60000, // 定期嗅探节点sniffOnConnectionFault: true // 连接故障时嗅探
});// 自定义重试策略
client.on('request', (err, result) => {if (err) {console.error('请求失败:', err.meta ? err.meta.body.error : err.message);}
});// 使用Promise.catch处理错误
async function safeSearch() {try {const response = await client.search({index: 'products',body: { query: { match_all: {} } }}).catch(err => {console.error('搜索失败:', err.meta.body.error);throw err;});return response;} catch (error) {console.error('捕获到错误:', error);throw error;}
}
批量操作优化
async function optimizedBulkInsert(documents, batchSize = 1000) {try {for (let i = 0; i < documents.length; i += batchSize) {const batch = documents.slice(i, i + batchSize);const body = batch.flatMap(doc => [{ index: { _index: 'products' } },doc]);const { body: bulkResponse } = await client.bulk({ body });if (bulkResponse.errors) {console.log(`批量插入批次 ${i / batchSize + 1} 部分失败`);} else {console.log(`批量插入批次 ${i / batchSize + 1} 成功`);}// 每批处理完成后稍作休息if (i + batchSize < documents.length) {await new Promise(resolve => setTimeout(resolve, 200));}}} catch (error) {console.error('批量插入失败:', error);throw error;}
}

结语

本文提供了从基础到高级的 Node.js 操作 ElasticSearch 的完整指南,涵盖了索引管理、文档操作、复杂搜索、聚合分析等核心功能,并通过电商商品搜索的实战案例展示了如何在实际项目中应用 ElasticSearch。

希望这些示例代码能帮助您更好地在 Node.js 项目中集成 ElasticSearch。根据实际业务需求,您可以进一步扩展和优化这些代码。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词