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.pswp.cn/bicheng/78441.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/78441.shtml
英文地址,請注明出處:http://en.pswp.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;它是真正用來存儲數據的容器…