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。根据实际业务需求,您可以进一步扩展和优化这些代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/78441.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

硬核科普丨2025年安全、高效网络准入控制系统深度解析

阳途网络准入控制系统&#xff08;Network Access Control&#xff0c;简称NAC&#xff09;是当代网络安全领域的重要工具&#xff0c;有效防止未经授权的访问和数据泄露&#xff0c;保障网络资源的安全性和完整性。本文将深入探讨阳途网络准入控制系统的的重要性和作用。 一、…

搜索二叉树-key的搜索模型

二叉搜索树(Binary Search Tree, BST)是一种重要的数据结构&#xff0c;它有两种基本模型&#xff1a;Key模型和Key/Value模型。 一、Key模型 1.基本概念 Key模型是二叉搜索树中最简单的形式&#xff0c;每个节点只存储一个键值(key)&#xff0c;没有额外的数据值(value)。这…

安卓四大组件之ContentProvider

目录 实现步骤 代码分析 onCreate insert query ContextHolder Cursor 作用与用法 基本步骤&#xff1a; 可能的面试题&#xff1a;为什么使用Cursor&#xff1f; 为什么使用Cursor 使用Cursor的好处 静态内部类实现单例模式 AnndroidManifest.xml配置信息 注释的…

【HTML】【Web开发】滑动条挑战

最近在思考如何开发一些入门级的迷你游戏&#xff0c;于是抽空写了个HTML的滑动条小游戏。 游戏规则如下&#xff1a; 在[0, 100]区间内随机生成一个目标值&#xff0c;显示为&#xff1a;X% 倒计时 3 秒过后&#xff0c;出现 10 秒的挑战倒计时和【停止】按钮 挑战倒计时结…

面试踩过的坑

1、 “”和equals 的区别 “”是运算符&#xff0c;如果是基本数据类型&#xff0c;则比较存储的值&#xff1b;如果是引用数据类型&#xff0c;则比较所指向对象的地址值。equals是Object的方法&#xff0c;比较的是所指向的对象的地址值&#xff0c;一般情况下&#xff0c;重…

专业软件开发全流程实践指南

作为一家拥有十余年行业积淀的专业软件开发服务提供商&#xff0c;我们见证了太多项目从无到有的全过程。今天&#xff0c;我们就用最朴实的语言&#xff0c;跟大家聊聊一个软件产品从构思到上线的完整历程。这些经验不仅适用于自建技术团队的企业&#xff0c;对正在寻找软件外…

聊透多线程编程-线程互斥与同步-12. C# Monitor类实现线程互斥

目录 一、什么是临界区&#xff1f; 二、Monitor类的用途 三、Monitor的基本用法 四、Monitor的工作原理 五、使用示例1-保护共享变量 解释&#xff1a; 六、使用示例2-线程间信号传递 解释&#xff1a; 七、注意事项 八、总结 在多线程编程中&#xff0c;线程之间的…

第R4周:LSTM-火灾温度预测

文章目录 一、前期准备工作1.导入数据2. 数据集可视化 二、构建数据集1. 数据集预处理2. 设置X, y3. 划分数据集 三、模型训练1. 构建模型2. 定义训练函数3. 定义测试函数4. 正式训练模型 四、模型评估1. Loss图片2. 调用模型进行预测3. R2值评估 总结&#xff1a; &#x1f36…

toCharArray作用

toCharArray() 是 Java 中 String 类的一个方法&#xff0c;其作用是将字符串对象转换为一个字符数组。下面为你详细介绍其用法、原理和示例。 方法定义 toCharArray() 方法在 java.lang.String 类里被定义&#xff0c;方法签名如下 public char[] toCharArray() 此方法没有…

STM32八股【6】-----CortexM3的双堆栈(MSP、PSP)设计

STM32的线程模式&#xff08;Thread Mode&#xff09;和内核模式&#xff08;Handler Mode&#xff09;以及其对应的权级和堆栈指针 线程模式&#xff1a; 正常代码执行时的模式&#xff08;如 main 函数、FreeRTOS任务&#xff09; 可以是特权级&#xff08;使用MSP&#xff…

驱动支持的最高CUDA版本与实际安装的Runtime版本

查看电脑上安装的CUDA版本的多种方法&#xff0c;适用于不同系统和场景。 方法一&#xff1a;通过命令行工具 1. 查看CUDA Driver API版本&#xff08;显卡驱动支持的CUDA版本&#xff09; 命令&#xff1a;nvidia-smi操作&#xff1a; 打开终端&#xff08;Windows为CMD/Pow…

Python CT图像预处理——基于ITK-SNAP

Python CT图像预处理——nii格式读取、重采样、窗宽窗位设置_python读取nii-CSDN博客 基于原文指出以下几个问题&#xff1a;文件路径设置模糊&#xff1b;nilabel里面使用的get_data() 方法已经过时&#xff1b;需要导入scikit-image&#xff0c;还要导入一个matplotlib。 一…

【MQ篇】RabbitMQ之消息持久化!

目录 一、 交换机持久化 (Exchange Persistence)二、 队列持久化 (Queue Persistence)三、 消息持久化 (Message Persistence)四、 持久化的“黄金三角” &#x1f531;&#xff1a;三者缺一不可&#xff01;五、 来&#xff0c;完整的代码示例&#xff08;整合持久化和确认机制…

[AI技术(二)]JSONRPC协议MCPRAGAgent

Agent概述(一) AI技术基础(一) JSON-RPC 2.0 协议详解 JSON-RPC 2.0 是一种基于 JSON 的轻量级远程过程调用(RPC)协议,旨在简化跨语言、跨平台的远程通信。以下从协议特性、核心结构、错误处理、批量请求等角度进行详细解析: 一、协议概述 1. 设计原则 • 简单性:…

LeetCode238_除自身以外数组的乘积

LeetCode238_除自身以外数组的乘积 标签&#xff1a;#数组 #前缀和Ⅰ. 题目Ⅱ. 示例0. 个人方法一&#xff1a;暴力循环嵌套0. 个人方法二&#xff1a;前缀和后缀分别求积 标签&#xff1a;#数组 #前缀和 Ⅰ. 题目 给你一个整数数组 nums&#xff0c;返回 数组 answer &#…

算法笔记.spfa算法(bellman-ford算法的改进)

题目&#xff1a;&#xff08;来源于AcWing&#xff09; 给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c; 边权可能为负数。 请你求出 1 号点到 n 号点的最短距离&#xff0c;如果无法从 1 号点走到 n 号点&#xff0c;则输出 impossible。 …

07 Python 字符串全解析

文章目录 一. 字符串的定义二. 字符串的基本用法1. 访问字符串中的字符2. 字符串切片3. 字符串拼接4. 字符串重复5.字符串比较6.字符串成员运算 三. 字符串的常用方法1. len() 函数2. upper() 和 lower() 方法3. strip() 方法4. replace() 方法5. split() 方法 四. 字符串的进阶…

Java集成Zxing和OpenCV实现二维码生成与识别工具类

Java集成Zxing和OpenCV实现二维码生成与识别工具类 本文将介绍如何使用Java集成Zxing和OpenCV库&#xff0c;实现二维码的生成和识别功能。识别方法支持多种输入形式&#xff0c;包括File对象、文件路径和Base64编码。 一、环境准备 添加Maven依赖 <dependencies><…

【专题刷题】二分查找(二)

&#x1f4dd;前言说明&#xff1a; 本专栏主要记录本人的基础算法学习以及LeetCode刷题记录&#xff0c;按专题划分每题主要记录&#xff1a;&#xff08;1&#xff09;本人解法 本人屎山代码&#xff1b;&#xff08;2&#xff09;优质解法 优质代码&#xff1b;&#xff…

Java—ThreadLocal底层实现原理

首先&#xff0c;ThreadLocal 本身并不提供存储数据的功能&#xff0c;当我们操作 ThreadLocal 的时候&#xff0c;实际上操作线程对象的一个名为 threadLocals 成员变量。这个成员变量的类型是 ThreadLocal 的一个内部类 ThreadLocalMap&#xff0c;它是真正用来存储数据的容器…