likes
comments
collection
share

NestJS小技巧18-如何使用Elasticsearch、Kibana、NestJS和React创建完整的自动完成搜索应用程序(第三章/共四章)

作者站长头像
站长
· 阅读数 15
by 雪隐 from https://juejin.cn/user/1433418895994094
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可联系授权

原文链接3

大家好,欢迎回到Elastic、Kibana和NestJS系列的第3部分。在本系列的第1部分中,我们安装并配置了elasticsearch(如果您错过了,请在此处查看),在第2部分中,将elasticsearch与Kibana连接起来,并运行了一些查询(请在此处检查第2部分)。

在本文中,我们将编写NodeJS代码,用于连接和查询Elasticsearch

将数据加载到Elasticsearch

为了使我们能够有效地编写代码,我们需要将数据加载到弹性搜索中。我们将使用Kaggle的样本数据集(在此处下载)。

按照下面的四个步骤将其加载到Elasticsearch

  1. 打开Kibana (http://localhost:5601)

NestJS小技巧18-如何使用Elasticsearch、Kibana、NestJS和React创建完整的自动完成搜索应用程序(第三章/共四章)

  1. Get started by adding integrations下,单击Upload a file(上载文件)

NestJS小技巧18-如何使用Elasticsearch、Kibana、NestJS和React创建完整的自动完成搜索应用程序(第三章/共四章)

  1. 单击import(导入)并输入要放入数据的索引的名称。别太纠结名字,我在这里随便写了 test1

NestJS小技巧18-如何使用Elasticsearch、Kibana、NestJS和React创建完整的自动完成搜索应用程序(第三章/共四章)

  1. 点击导入(最后一页)

NestJS小技巧18-如何使用Elasticsearch、Kibana、NestJS和React创建完整的自动完成搜索应用程序(第三章/共四章)

如果你做到了这一点,那么你已经成功地将数据导入到Elasticsearch中。

查询例子

转到DevTools(屏幕左上角的汉堡>向下滚动到管理>DevTools)

运行下面的查询(选择它并单击执行按钮)

GET test1/_search

如果你看到这个,那就ok👌了!

NestJS小技巧18-如何使用Elasticsearch、Kibana、NestJS和React创建完整的自动完成搜索应用程序(第三章/共四章)

现在,让我们深入研究编码,好吗😊?

创建NestJs应用程序

运行下面的命令安装nestcli并创建一个新的NestJs应用程序(在本文中,应用程序的名称将是nest-elastic)。

$ npm i -g @nestjs/cli  
$ nest new nest-elastic

您将被要求选择一个包管理器,您可以选择npm、yarn或pnpm。我会选择pnpm(你可以选择任何其他你想要的😉). 您的项目将被设置,我们应该准备好编码

Elasticsearch添加到您的应用程序

运行下面的命令,将elasticsearch添加到您的嵌套elastical和其他依赖项中。

pnpm i @elastic/elasticsearch @nestjs/elasticsearch @nestjs/config

在根文件夹中添加包含以下内容的.env文件

# 注意这里需要https,http的请求方式要配置elastic的。
ELASTICSEARCH_NODE=https://localhost:9200  
ELASTICSEARCH_USERNAME=elastic  
ELASTICSEARCH_PASSWORD=你自己设置的密码  
ELASTICSEARCH_MAX_RETRIES=10  
ELASTICSEARCH_REQ_TIMEOUT=50000  
ELASTICSEARCH_INDEX=test1

让我们创建一个单独的模块,只使用弹性搜索来处理搜索。一个简单的快捷方式是使用下面的命令(如果您也愿意,欢迎手动执行),选restfulapi,不要curd。

nest g resource search

NestJS小技巧18-如何使用Elasticsearch、Kibana、NestJS和React创建完整的自动完成搜索应用程序(第三章/共四章)

更新search.module.ts以获得以下内容(原文的代码有点坑人,配置的是https请求,所以要把证书验证设置成false,不然不能建立正确的连接)

import { Module } from '@nestjs/common';
import { SearchService } from './search.service';
import { SearchController } from './search.controller';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { ElasticsearchModule } from '@nestjs/elasticsearch';

@Module({
  imports: [
    ConfigModule,
    ElasticsearchModule.registerAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        node: configService.get('ELASTICSEARCH_NODE'),
        auth: {
          username: configService.get('ELASTICSEARCH_USERNAME'),
          password: configService.get('ELASTICSEARCH_PASSWORD'),
        },
        // 注意这里一定要配置为false,不然会异常
        tls: {
          rejectUnauthorized: false,
        },
        maxRetries: configService.get('ELASTICSEARCH_MAX_RETRIES'),
        requestTimeout: configService.get('ELASTICSEARCH_REQ_TIMEOUT'),
      }),
      inject: [ConfigService],
    }),
  ],
  controllers: [SearchController],
  providers: [SearchService],
  exports: [SearchService],
})
export class SearchModule {}

使用以下内容更新search.service.ts

import { ConfigService } from '@nestjs/config';
import { Injectable } from '@nestjs/common';
import { ElasticsearchService } from '@nestjs/elasticsearch';

type dataResponse = {
  UnitPrice: number;
  Description: string;
  Quantity: number;
  Country: string;
  InvoiceNo: string;
  InvoiceDate: Date;
  CustomerID: number;
  StockCode: string;
};

@Injectable()
export class SearchService {
  constructor(
    private readonly esService: ElasticsearchService,
    private readonly configService: ConfigService,
  ) {}

  async createIndex() {
    const isIndexExist = await this.esService.indices.exists({
      index: this.configService.get('ELASTICSEARCH_INDEX'),
    });

    if (!isIndexExist) {
      this.esService.indices.create({
        index: this.configService.get('ELASTICSEARCH_INDEX'),
        body: {
          mappings: {
            properties: {
              '@timestamp': {
                type: 'date',
              },
              budget: {
                type: 'long',
              },
              genres: {
                type: 'text',
              },
              homepage: {
                type: 'keyword',
              },
              id: {
                type: 'long',
              },
              keywords: {
                type: 'text',
              },
              original_language: {
                type: 'keyword',
              },
              original_title: {
                type: 'text',
              },
              overview: {
                type: 'text',
              },
              popularity: {
                type: 'double',
              },
              production_companies: {
                type: 'text',
              },
              production_countries: {
                type: 'text',
              },
              release_date: {
                type: 'date',
                format: 'iso8601',
              },
              revenue: {
                type: 'long',
              },
              runtime: {
                type: 'long',
              },
              spoken_languages: {
                type: 'text',
              },
              status: {
                type: 'keyword',
              },
              tagline: {
                type: 'text',
              },
              title: {
                type: 'text',
              },
              vote_average: {
                type: 'double',
              },
              vote_count: {
                type: 'long',
              },
            },
          },
        },
      });
    }
  }

  async search(search: {key: string}): Promise<any> {
    let results = new Set();
    const response = await this.esService.search({
      index: this.configService.get('ELASTICSEARCH_INDEX'),
      body: {
        size: 50,
        query: {
          match_phrase: search
        },
      },
    });
    const hits = response.hits.hits;
    hits.map((item) => {
      results.add(item._source as dataResponse);
    });

    return { results: Array.from(results), total: response.hits.total };
  }
}

现在,让我们添加movies模块

nest g resource movies

使用以下内容更新movies.controller.ts

import { SearchService } from './../search/search.service';
import { Body, Controller, Post } from '@nestjs/common';

@Controller('movies')
export class MoviesController {
  constructor(private readonly searchService: SearchService) {}
  @Post('search')
  async search(@Body() body) {
    return await this.searchService.search(body.data);
  }
}

然后movies.module.ts

import { SearchModule } from './../search/search.module';
import { Module } from '@nestjs/common';
import { MoviesService } from './movies.service';
import { MoviesController } from './movies.controller';

@Module({
  imports: [SearchModule],
  controllers: [MoviesController],
  providers: [MoviesService],
})
export class MoviesModule {}

最后,更新app.module.ts

import { MoviesModule } from './movies/movies.module';
import { ConfigModule } from '@nestjs/config';
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { SearchModule } from './search/search.module';

@Module({
  imports: [MoviesModule, ConfigModule.forRoot(), SearchModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

运行应用程序

您可以使用npm run start:dev在开发环境中启动应用程序

NestJS小技巧18-如何使用Elasticsearch、Kibana、NestJS和React创建完整的自动完成搜索应用程序(第三章/共四章)

测试

NestJS小技巧18-如何使用Elasticsearch、Kibana、NestJS和React创建完整的自动完成搜索应用程序(第三章/共四章)

NestJS小技巧18-如何使用Elasticsearch、Kibana、NestJS和React创建完整的自动完成搜索应用程序(第三章/共四章)

您可以在此处访问完整的源代码

总结

在本文中,我们能够使用kibana将数据导入到elastic中,并将我们的NestJs后端应用程序连接到elasticsearch的功能上。

在下一篇文章(要过一段时间)中,我们将构建一个简单的前端来实时查询和可视化弹性搜索的结果。

感谢阅读!