Node.js 操作 Elasticsearch (ES) 的指南

发布于:2025-09-14 ⋅ 阅读:(19) ⋅ 点赞:(0)

h5打开以查看

一、核心概念与安装

1. 官方客户端

Elasticsearch 官方提供了 @elastic/elasticsearch Node.js 客户端,这是目前最推荐、最权威的库。

2. 安装

使用 npm 或 yarn 进行安装:

bash

npm install @elastic/elasticsearch
# 或
yarn add @elastic/elasticsearch

二、连接 Elasticsearch 客户端

你需要获取 Elasticsearch 集群的地址和认证信息(如果启用了安全特性)来创建客户端实例。

基本连接(无安全认证)

javascript

const { Client } = require('@elastic/elasticsearch');

const client = new Client({
  node: 'http://localhost:9200', // ES 节点的地址,默认端口是 9200
  // 如果集群有多个节点,可以放入一个数组
  // nodes: ['http://node1:9200', 'http://node2:9200'],
});

// 测试连接
async function checkConnection() {
  try {
    const health = await client.cluster.health();
    console.log('Elasticsearch 集群健康状态:', health.status);
  } catch (error) {
    console.error('连接 Elasticsearch 失败:', error.message);
  }
}

checkConnection();
带安全认证的连接(API Key/用户名密码/Cloud ID)

使用 API Key (推荐)

javascript

const client = new Client({
  node: 'https://your-es-cluster.com:9200',
  auth: {
    apiKey: 'your-base64-encoded-api-key' // 例如: 'a2V5LWlkOmFwaS1rZXk='
  }
});

使用用户名和密码

javascript

const client = new Client({
  node: 'https://your-es-cluster.com:9200',
  auth: {
    username: 'elastic', // 或其他用户名
    password: 'your-password'
  }
});

连接 Elastic Cloud

javascript

const client = new Client({
  cloud: {
    id: 'your-cloud-id-from-elastic-cloud',
  },
  auth: {
    username: 'elastic',
    password: 'your-password'
  }
});

三、基本操作 (CRUD)

我们以一个 products 索引为例,文档类型为 _doc

1. 创建索引 (Create Index)

通常不需要手动创建,在插入第一条数据时会自动创建。但也可以显式创建以指定映射。

javascript

async function createIndex() {
  await client.indices.create({
    index: 'products',
    body: {
      mappings: {
        properties: {
          name: { type: 'text' },
          price: { type: 'float' },
          description: { type: 'text' },
          createdAt: { type: 'date' }
        }
      }
    }
  });
  console.log('索引创建成功');
}
// createIndex().catch(console.error);
2. 索引文档 (Index - Create/Replace)

index 方法会自动创建或全量替换文档。如果指定 id 已存在,则替换。

javascript

async function indexProduct() {
  const response = await client.index({
    index: 'products',
    id: '1', // 如果不指定 id,ES 会自动生成一个
    body: {
      name: 'iPhone 13',
      price: 799.99,
      description: 'A great phone from Apple.',
      createdAt: new Date()
    }
  });
  console.log('文档索引成功:', response.body._id);
}
// indexProduct().catch(console.error);
3. 创建文档 (Create - 必须不存在)

create 方法要求文档 ID 必须不存在,否则会失败。

javascript

async function createProduct() {
  const response = await client.create({
    index: 'products',
    id: '2', // 如果 id=2 已存在,此操作会报错
    body: {
      name: 'Samsung Galaxy',
      price: 699.99,
      description: 'A powerful Android phone.'
    }
  });
  console.log('文档创建成功:', response.body._id);
}
4. 读取文档 (Read)

javascript

async function getProduct() {
  try {
    const response = await client.get({
      index: 'products',
      id: '1'
    });
    console.log('找到文档:', response.body._source);
  } catch (error) {
    if (error.meta.statusCode === 404) {
      console.log('文档不存在');
    } else {
      throw error;
    }
  }
}
// getProduct().catch(console.error);
5. 更新文档 (Update)

使用 update 进行部分更新,性能更好。

javascript

async function updateProduct() {
  const response = await client.update({
    index: 'products',
    id: '1',
    body: {
      doc: { // 要更新的字段放在 `doc` 里
        price: 749.99 // 只更新价格字段
      }
    }
  });
  console.log('文档更新成功');
}
// updateProduct().catch(console.error);
6. 删除文档 (Delete)

javascript

async function deleteProduct() {
  const response = await client.delete({
    index: 'products',
    id: '2'
  });
  console.log('文档删除成功');
}
// deleteProduct().catch(console.error);

四、搜索操作 (Search)

这是 Elasticsearch 最强大的功能。

1. 简单搜索 (Match Query)

javascript

async function searchProducts() {
  const response = await client.search({
    index: 'products',
    body: {
      query: {
        match: { 
          name: 'iPhone' // 在 `name` 字段中搜索 "iPhone"
        }
      },
      // 高亮显示匹配内容
      highlight: {
        fields: {
          name: {}
        }
      },
      // 排序
      sort: [
        { price: { order: 'desc' } }
      ],
      // 分页
      from: 0,
      size: 10
    }
  });

  console.log(`共找到 ${response.body.hits.total.value} 条结果:`);
  response.body.hits.hits.forEach(hit => {
    console.log(`- ${hit._source.name} ($${hit._source.price})`);
    // 如果有高亮结果
    if (hit.highlight) {
      console.log('  高亮:', hit.highlight.name);
    }
  });
}
// searchProducts().catch(console.error);
2. 布尔搜索 (Bool Query)

组合多个查询条件(must=AND, should=OR, must_not=NOT)。

javascript

async function boolSearch() {
  const response = await client.search({
    index: 'products',
    body: {
      query: {
        bool: {
          must: [ // 必须同时满足
            { match: { description: 'phone' } }
          ],
          filter: [ // 过滤,不贡献得分
            { range: { price: { gte: 500, lte: 800 } } }
          ],
          must_not: [ // 必须不满足
            { match: { name: 'Samsung' } }
          ]
        }
      }
    }
  });
  // ... 处理结果
}
3. 聚合分析 (Aggregations)

javascript

async function runAggregation() {
  const response = await client.search({
    index: 'products',
    body: {
      aggs: { // 定义聚合
        avg_price: { // 平均价格聚合
          avg: { field: 'price' }
        },
        price_ranges: { // 范围聚合
          range: {
            field: 'price',
            ranges: [
              { to: 500 },
              { from: 500, to: 800 },
              { from: 800 }
            ]
          }
        }
      },
      size: 0 // 不返回原始命中结果,只返回聚合结果
    }
  });

  console.log('平均价格:', response.body.aggregations.avg_price.value);
  console.log('价格分布:', response.body.aggregations.price_ranges.buckets);
}
// runAggregation().catch(console.error);

五、批量操作 (Bulk API)

对于大量数据的导入或更新,使用 Bulk API 可以极大提升效率。

javascript

async function bulkIndex() {
  const body = [];
  const dataset = [
    { id: 100, name: 'Product A', price: 100 },
    { id: 101, name: 'Product B', price: 200 },
    // ... 更多数据
  ];

  // 构建 Bulk 请求体
  dataset.forEach(doc => {
    body.push({ index: { _index: 'products', _id: doc.id } }); // 操作定义
    body.push(doc); // 文档源数据
  });

  const { body: bulkResponse } = await client.bulk({ body, refresh: true });

  if (bulkResponse.errors) {
    console.error('批量操作中有错误:', bulkResponse.errors);
  } else {
    console.log('批量操作成功');
  }
}
// bulkIndex().catch(console.error);

六、最佳实践与错误处理

  1. 单例模式: 在整个应用中,通常只需要一个 Elasticsearch 客户端实例。请重用这个实例。

  2. 异步/await: 客户端所有 API 都返回 Promise,强烈建议使用 async/await 或 .then().catch() 处理。

  3. 错误处理: 一定要用 try...catch 包裹操作,并检查错误状态码。

  4. 关闭连接: 在应用退出时,优雅地关闭客户端。

    javascript

    // 例如,在 Express 应用关闭时
    process.on('SIGTERM', async () => {
      await client.close();
      process.exit(0);
    });
  5. 使用最新版本: 确保客户端版本与 Elasticsearch 服务器版本兼容(通常主版本号相同即可,如 8.x 客户端连接 8.x 服务端)。

这份指南涵盖了 Node.js 操作 Elasticsearch 的绝大多数常见场景

h5打开以查看