Elasticsearch:使用最新的 Nodejs client 8.x 来创建索引并搜索

2022年6月12日   |   by mebius

针对最新的 Elastic Stack 8.0,Nodejs 的客户端也进行了升级。在今天的文章中,我来介绍如何在 Nodejs 中来连接 Elasticsearch 并创建索引及搜索。最新的 Nodejs client 代码可以在 github 地址GitHub – elastic/elasticsearch-js: Official Elasticsearch client library for Node.js找到。

Elasticsearch:使用最新的 Nodejs client 8.x 来创建索引并搜索

Elasticsearch:使用最新的 Nodejs client 8.x 来创建索引并搜索_哔哩哔哩_bilibili

安装

我们可以通过如下的命令来进行安装:

npm install @npm:@elastic/elasticsearch@

如果我们不指定版本,我们可以通过如下的命令来进行安装。它可以帮我们安装最新的版本:

npm install npm:@elastic/elasticsearch
npm install config
npm install @elastic/elasticsearch

或者:

npm install es8@npm:@elastic/elasticsearch@8.1.0

在上面的命令中,es8 是我们的别名,而 8.1.0 是我们想要安装的版本。如果我们想要安装以前的版本,我们可以使用如下的命令:

npm install es6@npm:@elastic/elasticsearch@6
npm install es7@npm:@elastic/elasticsearch@7

如果你想安装最新的在 Elasticsearch main 分支上的客户端版本,你可以使用如下的命令:

npm install esmain@github:elastic/elasticsearch-js

我们一定要注意的是在 main 分支上的版本可能不是稳定的版本。请注意使用!

我们可以通过如下的命令来检查最新的版本:

$ npm list | grep elasticsearch
└── @elastic/elasticsearch@8.1.0

从上面的展示中,我们可以看到最新的版本是 8.1.0。

快速入门

我们创建一个目录,并在目录中使用如下的命令:

npm init -f
$ pwd
/Users/liuxg/nodejs/elasticsearch-js8
$ npm init -f
$ npm init -f
npm WARN using --force Recommended protections disabled.
Wrote to /Users/liuxg/nodejs/elasticsearch-js8/package.json:

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo tgcode"Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}


$ ls
package.json

我们可以在 package.json 修改 scripts 部分:

package.json

{
  "name": "elasticsearch-js8",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@elastic/elasticsearch": "^8.1.0",
    "es8": "npm:@elastic/elasticsearch@^8.1.0"
  }
}

如上所示,在 main 中定义的是 index.js。我们来创建一个叫做 index.js 的文件。

Elasticsearch 集群不带有任何安全

我们可以参考文章 “如何在 Linux,MacOS 及 Windows 上进行安装 Elasticsearch” 来安装 Elasticsearch。在我们的练习中,我将安装 Elasticsearch 8.1.2 版本。因为我们不想带有任何的安全,请参阅我的文章 “Elastic Stack 8.0 安装 – 保护你的 Elastic Stack 现在比以往任何时候都简单” 中的 “如何配置 Elasticsearch 不带安全性” 章节。

连接到 Elasticsearch

我们还是尝试按照之前文章 “Elasticsearch:Elasticsearch 开发入门 – Nodejs” 介绍的那样来试试:

index.js

const es = require('@elastic/elasticsearch');
const client = es.Client({ host: 'http://localhost:9200' });
 
client.ping()
  .then(res => console.log('connection success', res))
  .catch(err => console.error('wrong connection', err));

我们使用如下的命令来进行运行:

npm start

我们可以看到如下的错误信息:

$ npm start

> elasticsearch-js8@1.0.0 start
> node index.js

/Users/liuxg/nodejs/elasticsearch-js8/index.js:2
const client = es.Client({ host: 'http://localhost:9200' });
                  ^

TypeError: Class constructor Client cannot be invoked without 'new'
    at Object. (/Users/liuxg/nodejs/elasticsearch-js8/index.js:2:19)
    at Module._compile (node:internal/modules/cjs/loader:1097:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1149:10)
    at Module.load (node:internal/modules/cjs/loader:975:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
    at node:internal/main/run_main_module:17:47

Node.js v17.4.0

这说明了,我们的 Nodejs client API 已经发生改变了。我们需要重新修正我们的代码:

index.js

const { Client } = require('es8');
const client = new Client ( {node: 'http://localhost:9200' });
 
client.ping()
  .then(res => console.log('connection success', res))
  .catch(err => console.error('wrong connection', err));

我们运行上面的代码:

$ npm start

> elasticsearch-js8@1.0.0 start
> node index.js

connection success true

它显示我们的连接是成功的。请注意在上面,我们使用了别名 es8。这个是我们在安装 elasticsearch 包时定以的。

创建索引及搜索

关于 API 的描述可以在地址:elasticsearch-js/src/api/api at main elastic/elasticsearch-js GitHub看到。一旦我们能成功地连接到 Elasticsearch,我们可以使用如下的命令来创建一个索引:

index.js

const { Client } = require('es8');
const client = new Client ( {node: 'http://localhost:9200' });
 
client.ping()
  .then(res => console.log('connection success', res))
  .catch(err => console.error('wrong connection', err));

const INDEX_NAME = "megacorp";

async function run () {
  // Let's start by indexing some data
  await client.index({
    index: INDEX_NAME,
    id: 1,
    document: {
      "first_name":"nitin",
      "last_name":"panwar",
      "age": 27,
      "about": "Love to play cricket",
      "interests": ["sports","music"]
    }
  })

  await client.index({
    index: INDEX_NAME,
    document: {
      "first_name" :  "Jane",
      "last_name" :   "Smith",
      "age" :         32,
      "about" :       "I like to collect rock albums",
      "interests":  [ "music" ]
    }
  })

  // here we are forcing an index refresh, otherwise we will not
  // get any result in the consequent search
  await client.indices.refresh({ index: INDEX_NAME })

  // Let's search!
  const result= await client.search({
    index: INDEX_NAME,
    query: {
      match: { first_name : 'nitin' }
    }
  })

  console.log(result.hits.hits)
}

run().catch(console.log)

在上面的代码中,我们使用 index 的方法来写入数据。它们相当于如下的命令:

PUT megacorp/_doc/1
{
  "first_name": "nitin",
  "last_name": "panwar",
  "age": 27,
  "about": "Love to play cricket",
  "interests": ["sports","music"]
}

POST megacorp/_doc
{
  "first_name": "Jane",
  "last_name": "Smith",
  "age": 32,
  "about": "I like to collect rock albums",
  "interests": [
    "music"
  ]
}

在第一个命令中,我们指定文档的 id,而在第二个命令中,我们让 Elasticsearch 生成一个 id。运行完上面的代码后,我们可以看到如下的输出:

$ npm start

> elasticsearch-js8@1.0.0 start
> node index.js

connection success true
[
  {
    _index: 'megacorp',
    _id: '1',
    _score: 0.6931471,
    _source: {
      first_name: 'nitin',
      last_name: 'panwar',
      age: 27,
      about: 'Love to play cricket',
      interests: [Array]
    }
  }
]

在上面,我们看到一个搜索结果的输出。在代码中的搜索:

  // Let's search!
  const result= await client.search({
    index: INDEX_NAME,
    query: {
      match: { first_name : 'nitin' }
    }
  })

相当于如下的搜索命令:

GET  megacorp/_search?filter_path=**.hits
{
  "query": {
    "match": {
      "first_name": "nitin"
    }
  }
}

我们可以在 Kibana 中查看写入的文档:

%title插图%num

上面显示写入的两个文档。其中的一个文档的 id 已经被指定为 1。

我们也可以写一些比较复杂一点的搜索,比如:

index.js

const { Client } = require('es8');
const client = new Client ( {node: 'http://localhost:9200' });
 
client.ping()
  .then(res => console.log('connection success', res))
  .catch(err => console.error('wrong connection', err));

const INDEX_NAME = "megacorp";

async function run () {

  // Let's search!
  const result= await client.search({
    index: INDEX_NAME,
    query: {
      "bool": {
        "must": [
          {
            "match": {
              "first_name": "nitin"
            }
          }
      tgcode  ],
        "should": [
          {
            "range": {
              "age": {
                "gte": 40
              }
            }
          }
        ]
      }
    }
  })

  console.log(result.hits.hits)
}

run().catch(console.log)

上面的搜索相当于如下的命令:

GET megacorp/_search?filter_path=**.hits
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "first_name": "nitin"
          }
        }
      ],
      "should": [
        {
          "range": {
            "age": {
              "gte": 40
            }
          }
        }
      ]
    }
  }
}

我们也可以获得一个文档:

index.js

const { Client } = require('es8');
const client = new Client ( {node: 'http://localhost:9200' });
 
client.ping()
  .then(res => console.log('connection success', res))
  .catch(err => console.error('wrong connection', err));

const INDEX_NAME = "megacorp";

async function run () {
 
  const result = await client.get({
    index: INDEX_NAME,
    id: 1
  })

  console.log(result)
}

run().catch(console.log)

运行上面的代码,我们可以看到:

$ npm start

> elasticsearch-js8@1.0.0 start
> node index.js

{
  _index: 'megacorp',
  _id: '1',
  _version: 1,
  _seq_no: 0,
  _primary_term: 1,
  found: true,
  _source: {
    first_name: 'nitin',
    last_name: 'panwar',
    age: 27,
    about: 'Love to play cricket',
    interests: [ 'sports', 'music' ]
  }
}
connection success true

显然,它输出了我们想要的搜索的结果。

我们也可以删除一个文档。在下面的代码中,我们删除 id 为 1 的文档:

index.js

const { Client } = require('es8');
const client = new Client ( {node: 'http://localhost:9200' });
 
client.ping()
  .then(res => console.log('connection success', res))
  .catch(err => console.error('wrong connection', err));

const INDEX_NAME = "megacorp";

async function run () {
 
  const result = await client.delete({
    index: INDEX_NAME,
    id: 1
  })

  console.log(result)
}

run().catch(console.log)

运行上面的代码:

$ npm start

> elasticsearch-js8@1.0.0 start
> node index.js

connection success true
{
  _index: 'megacorp',
  _id: '1',
  _version: 2,
  result: 'deleted',
  _shards: { total: 2, successful: 1, failed: 0 },
  _seq_no: 2,
  _primary_term: 1
}

上面显示,我们已经成功地删除了这个文档。我们可以到 Kibana 中搜索这个 id 为 1 的文档。我们会发现该文档不存在:

GET megacorp/_doc/1
{
  "_index" : "megacorp",
  "_id" : "1",
  "found" : false
}

更多 API 的使用请参阅文章elasticsearch-js/src/api/api at main elastic/elasticsearch-js GitHub

带有基本安全的 Elasticsearch 集群(不含 HTTPS)

在有些情况下,我们的集群带有基本安全,但是不含有 HTTPS。那么在这种情况下,我们该如何进行连接呢?我们可以参考文章 “Elastic Stack 8.0 安装 – 保护你的 Elastic Stack 现在比以往任何时候都简单” 中的 “如何配置 Elasticsearch 只带有基本安全” 来进行配置。为了说明问题方便,我把 elastic 超级用户的密码设置为 password。

由于添加了用户安全,我们必须修改我们的代码:

index.js

const { Client } = require('es8');
const client = new Client ( {
    node: 'http://localhost:9200',
    auth: {
      username: "elastic",
      password: "password"
    } 
  });
 
client.ping()
  .then(res => console.log('connection success', res))
  .catch(err => console.error('wrong connection', err));

const INDEX_NAME = "megacorp";

async function run () {
 
  // Let's start by indexing some data
  const result = cawait client.index({
    index: INDEX_NAME,
    id: 1,
    document: {
      "first_name":"nitin",
      "last_name":"panwar",
      "age": 27,
      "about": "Love to play cricket",
      "interests": ["sports","music"]
    }
  })

  console.log(result)
}

run().catch(console.log)

在上面,在连接 Elasticsearch 时,我们使用 auth 来定义自己的账号信息:

const client = new Client ( {
    node: 'http://localhost:9200',
    auth: {
      username: "elastic",
      password: "password"
    } 
  });

运行上面的代码,我们可以看到如下的结果:

$ npm start

> elasticsearch-js8@1.0.0 start
> node index.js

connection success true
{
  _index: 'megacorp',
  _id: '1',
  _version: 1,
  result: 'created',
  _shards: { total: 2, successful: 1, failed: 0 },
  _seq_no: 0,
  _primary_term: 1
}

从上面的输出结果中,我们可以看出来,我们的连接是成功的。

在实际的使用中,我们甚至可以简化为:

index.js

const { Client } = require('es8');
const client = new Client ( {
    node: 'http://elastic:password@localhost:9200'
  });
 
client.ping()
  .then(res => console.log('connection success', res))
  .catch(err => console.error('wrong connection', err));

const INDEX_NAME = "megacorp";

async function run () {
 
  // Let's start by indexing some data
  const result = await client.index({
    index: INDEX_NAME,
    id: 1,
    document: {
      "first_name":"nitin",
      "last_name":"panwar",
      "age": 27,
      "about": "Love to play cricket",
      "interests": ["sports","music"]
    }
  })

  console.log(result)
}

run().catch(console.log)

在上面,我们把用户信息放到 node 的 url 中。运行上面的代码,我们可以得到同样的结果。

在实际的使用中,我们甚至可以使用 API key 来进行连接。关于如何创建 API key,我们可以参考我之前的文章 “Elasticsearch:创建 API key 接口访问 Elasticsearch”。我们可以创建一个 API key,然后使用如下的方式来访问:

%title插图%num

%title插图%num

%title插图%num

%title插图%num

我们把上面生成的 Base64 API key 拷贝到如下的代码中:

index.js

const { Client } = require('es8');
const client = new Client ( {
    node: 'http://localhost:9200',
    auth: { apiKey: "Zm5SdVM0QUJrdHVZQUpLLWRudDU6Z0pxSW1sdFZSdDJKTmlndXg0eFh1dw==" }
  });
 
client.ping()
  .then(res => console.log('connection success', res))
  .catch(err => console.error('wrong connection', err));

const INDEX_NAME = "megacorp";

async function run () {
 
  // Let's start by indexing some data
  const result = await client.index({
    index: INDEX_NAME,
    id: 1,
    document: {
      "first_name":"nitin",
      "last_name":"panwar",
      "age": 27,
      "about": "Love to play cricket",
      "interests": ["sports","music"]
    }
  })

  console.log(result)
}

run().catch(console.log)

运行上面的代码,我们可以看到:

$ npm start

> elasticsearch-js8@1.0.0 start
> node index.js

connection success true
{
  _index: 'megacorp',
  _id: '1',
  _version: 3,
  result: 'updated',
  _shards: { total: 2, successful: 1, failed: 0 },
  _seq_no: 2,
  _primary_term: 1
}

它说明我们的连接是成功的。

带有 HTTPS 的 Elasticsearch 集群

在有的时候,我们的集群带有 HTTPS,而且证书是自签名的。这种情况适用于自托管类型的 Elasticsearch 集群。我们可以参考文章 “Elastic Stack 8.0 安装 – 保护你的 Elastic Stack 现在比以往任何时候都简单” 来安装自己的集群。

在 Elasticsearch 启动的过程中,我们需要注意如下的输出:

%title插图%num

我们需要记下超级用户 elastic 的密码。我们修改代码如下:

index.js

const { Client } = require('es8');
const fs = require('fs')
const client = new Client ( {
    node: 'https://localhost:9200',
    auth: { 
      username: "elastic",
      password: "prJ6hMjqVdQwCD8LEH-*"
     }, 
     tls: {
      ca: fs.readFileSync('/Users/liuxg/test/elasticsearch-8.1.2/config/certs/http_ca.crt'),
      rejectUnauthorized: true
     }
  });
 
client.ping()
  .then(res => console.log('connection success', res))
  .catch(err => console.error('wrong connection', err));

const INDEX_NAME = "megacorp";

async function run () {
 
  // Let's start by indexing some data
  const result = await client.index({
    index: INDEX_NAME,
    id: 1,
    document: {
      "first_name":"nitin",
      "last_name":"panwar",
      "age": 27,
      "about": "Love to play cricket",
      "interests": ["sports","music"]
    }
  })

  console.log(result)
}

run().catch(console.log)

请根据自己的配置修改在代码中如下的部分:

const client = new Client ( {
    node: 'https://localhost:9200',
    auth: { 
      username: "elastic",
      password: "prJ6hMjqVdQwCD8LEH-*"
     }, 
     tls: {
      ca: fs.readFileSync('/Users/liuxg/test/elasticsearch-8.1.2/config/certs/http_ca.crt'),
      rejectUnauthorized: true
     }
  });

运行我们的代码:

$ npm start

> elasticsearch-js8@1.0.0 start
> node index.js

connection success true
{
  _index: 'megacorp',
  _id: '1',
  _version: 2,
  result: 'updated',
  _shards: { total: 2, successful: 1, failed: 0 },
  _seq_no: 1,
  _primary_term: 1
}

显然我们的连接是成功的。

我们也可以尝试使用 fingerprint 来进行连接。我们使用如下的命令来获得 fingerprint:

openssl x509 -fingerprint -sha256 -in config/certs/http_ca.crt

%title插图%num

我们把代码修改如下:

index.js

const { Client } = require('es8');
const fs = require('fs')
const client = new Client ( {
    node: 'https://localhost:9200',
    auth: { 
      username: "elastic",
      password: "prJ6hMjqVdQwCD8LEH-*"
     }, 
     caFingerprint: "E5:E9:FE:62:DC:A6:6F:40:67:3A:55:CF:0E:32:25:AC:1A:3C:04:FD:08:91:27:5D:09:23:B6:B5:84:A8:AE:6A",
     tls: {
      rejectUnauthorized: false
     }
  });
 
client.ping()
  .then(res => console.log('connection success', res))
  .catch(err => console.error('wrong connection', err));

const INDEX_NAME = "megacorp";

async function run () {
 
  // Let's start by indexing some data
  const result = await client.index({
    index: INDEX_NAME,
    id: 1,
    document: {
      "first_name":"nitin",
      "last_name":"panwar",
      "age": 27,
      "about": "Love to play cricket",
      "interests": ["sports","music"]
    }
  })

  console.log(result)
}

run().catch(console.log)

如果你的证书是签过名的,你可以修改上面的rejectUnauthorized 为 true。

当然,我们也可以使用 API key 来进行访问:

index.js

const { Client } = require('es8');
const fs = require('fs')
const client = new Client ( {
    node: 'https://localhost:9200', 
     auth: {
       apiKey: "YUtxS1M0QUJBdU1aNGY4azAwNks6TnRSYllIX2VUNWVXMUllVk9lYUZDZw=="
     }, 
     tls: {
      ca: fs.readFileSync('/Users/liuxg/test/elasticsearch-8.1.2/config/certs/http_ca.crt'),
      rejectUnauthorized: true
     }     
  });
 
client.ping()
  .then(res => console.log('connection success', res))
  .catch(err => console.error('wrong connection', err));

const INDEX_NAME = "megacorp";

async function run () {
 
  // Let's start by indexing some data
  const result = await client.index({
    index: INDEX_NAME,
    id: 1,
    document: {
      "first_name":"nitin",
      "last_name":"panwar",
      "age": 27,
      "about": "Love to play cricket",
      "interests": ["sports","music"]
    }
  })

  console.log(result)
}

run().catch(console.log)

在上面我们使用 API key 的方法而不使用用户名及密码的方式来进行访问。针对 API key,我们还有另外一种访问方式:

POST /_security/api_key
{
  "name": "liuxg-api-key",
  "expiration": "7d"
}
{
  "id" : "bqqsS4ABAuMZ4f8k7E6I",
  "name" : "liuxg-api-key",
  "expiration" : 1651141865609,
  "api_key" : "2KVQCwyNTS295mOqPrthrA",
  "encoded" : "YnFxc1M0QUJBdU1aNGY4azdFNkk6MktWUUN3eU5UUzI5NW1PcVBydGhyQQ=="
}

index.js

const { Client } = require('es8');
const fs = require('fs')
const client = new Client ( {
    node: 'https://localhost:9200', 
     auth: {
       apiKey: {
         id: "bqqsS4ABAuMZ4f8k7E6I",
         api_key: "2KVQCwyNTS295mOqPrthrA"
       }
     }, 
     tls: {
      ca: fs.readFileSync('/Users/liuxg/test/elasticsearch-8.1.2/config/certs/http_ca.crt'),
      rejectUnauthorized: true
     }     
  });
 
client.ping()
  .then(res => console.log('connection success', res))
  .catch(err => console.error('wrong connection', err));

const INDEX_NAME = "megacorp";

async function run () {
 
  // Let's start by indexing some data
  const result = await client.index({
    index: INDEX_NAME,
    id: 1,
    document: {
      "first_name":"nitin",
      "last_name":"panwar",
      "age": 27,
      "about": "Love to play cricket",
      "interests": ["sports","music"]
    }
  })

  console.log(result)
}

run().catch(ctgcodeonsole.log)

运行上面的代码:

$ npm start

> elasticsearch-js8@1.0.0 start
> node index.js

connection success true
{
  _index: 'megacorp',
  _id: '1',
  _version: 5,
  result: 'updated',
  _shards: { total: 2, successful: 1, failed: 0 },
  _seq_no: 4,
  _primary_term: 1
}

从上面的输出中,我们可以看出来连接是成功的。

参考:

【1】Ingest data with Node.js on Elasticsearch Service | Elasticsearch Service Documentation | Elastic

【2】Introduction | Elasticsearch JavaScript Client [8.1] | Elastic

文章来源于互联网:Elasticsearch:使用最新的 Nodejs client 8.x 来创建索引并搜索

相关推荐: Kibana:可视化机器学习中的异常

在我之前的文章 “Elastic:使用 Elastic Stack 进行异常值检测 – airbnb 数据分析”,我详细地描述了如何在地图中对异常进行可视化。在另外一篇文章 “Elastic:机器学习异常的可视化呈现”,我详述了如何使用 TSVB 来对异常进行…