Elasticsearch, Kibana 및 NestJS를 사용하여 전체 자동 완성 검색 애플리케이션을 만드는 방법 - 3부

안녕하세요 여러분, Elastic, Kibana 및 NestJS 시리즈의 3부로 돌아온 것을 환영합니다. 이 시리즈의 1부에서는 Elasticsearch( )를 설치하고 구성했으며 2부에서는 Elasticsearch를 Kibana와 연결하고 몇 가지 쿼리를 실행했습니다( ).

이 기사에서는 Elasticsearch에 연결하고 쿼리할 NodeJS 코드를 작성합니다.

Elasticsearch에 데이터 로드



코드를 효과적으로 작성하려면 elasticsearch 에 데이터를 로드해야 합니다. Kaggle( Download it here )의 샘플 데이터 세트를 사용할 것입니다.

아래 4단계를 따라 Elasticsearch에 로드합니다.
  • Kibana 열기( http://localhost:5601 )

  • Get started by adding integrations에서 파일 업로드를 클릭합니다.

  • 가져오기를 클릭하고 데이터를 넣을 인덱스의 이름을 입력합니다.

  • 가져오기를 클릭합니다(마지막 페이지).


  • 여기까지 했다면 데이터를 elasticsearch로 성공적으로 가져온 것입니다.

    샘플 문의



    DevTools로 이동(화면 왼쪽 상단의 햄버거 > Management > DevTools까지 아래로 스크롤)

    아래 쿼리 실행(선택 후 재생 버튼 클릭)

    GET tmdb_movies/_search
    


    이것을 보면 우리는 갈 수 있습니다!




    자, 코딩에 빠져볼까요 😊?

    NestJ



    NestJS is a progressive Node. js framework that helps build server-side applications. Nest extends Node. js frameworks like Express or Fastify adding modular organization and a wide range of other libraries to take care of repetitive tasks. It's open-source, uses TypeScript, and is a very versatile Node.



    NestJs 애플리케이션 만들기



    아래 명령을 실행하여 nestcli를 설치하고 새 NestJs 애플리케이션을 생성합니다(기사에서 앱 이름은 nest-elastic임).

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


    패키지 관리자를 선택하라는 메시지가 표시되며 npm, yarn 또는 pnpm을 선택할 수 있습니다. 원사를 선택하겠습니다(원하는 다른 원사 선택 가능 😉). 프로젝트가 설정되고 코딩할 준비가 되었습니다!



    앱에 Elasticsearch 추가



    아래 명령을 실행하여 Elasticsearch를 nest-elastic 및 기타 종속성에 추가합니다.

    yarn add @elastic/elasticsearch @nestjs/elasticsearch @nestjs/config
    


    루트 폴더에 다음 내용이 포함된 .env 파일을 추가합니다.

    ELASTICSEARCH_NODE=https://localhost:9200
    ELASTICSEARCH_USERNAME=elastic
    ELASTICSEARCH_PASSWORD=elasticPasswordGoesHere
    ELASTICSEARCH_MAX_RETRIES=10
    ELASTICSEARCH_REQ_TIMEOUT=50000
    ELASTICSEARCH_INDEX=tmdb_movies
    


    Elasticsearch를 사용하여 검색만 처리하는 별도의 모듈을 만들어 보겠습니다. 간단한 단축키는 아래 명령을 사용하는 것입니다(원하는 경우 수동으로 수행할 수도 있음).

    nest g resource search
    




    아래 내용이 포함되도록 search.module.ts를 업데이트합니다.

    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'),
            },
            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 search(search: {key: string}) {
        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 };
      }
    }
    



    이제 영화 모듈을 추가해 보겠습니다.

    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 {}
    

    package.json는 다음과 같아야 합니다.

    {
      "name": "nest-elastic",
      "version": "0.0.1",
      "description": "",
      "author": "Yusuf Ganiyu",
      "private": true,
      "license": "UNLICENSED",
      "scripts": {
        "prebuild": "rimraf dist",
        "build": "nest build",
        "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
        "start": "nest start",
        "start:dev": "nest start --watch",
        "start:debug": "nest start --debug --watch",
        "start:prod": "node dist/main",
        "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
        "test": "jest",
        "test:watch": "jest --watch",
        "test:cov": "jest --coverage",
        "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
        "test:e2e": "jest --config ./test/jest-e2e.json"
      },
      "dependencies": {
        "@elastic/elasticsearch": "^8.4.0",
        "@nestjs/common": "^9.0.0",
        "@nestjs/config": "^2.2.0",
        "@nestjs/core": "^9.0.0",
        "@nestjs/elasticsearch": "^9.0.0",
        "@nestjs/mapped-types": "*",
        "@nestjs/platform-express": "^9.0.0",
        "reflect-metadata": "^0.1.13",
        "rimraf": "^3.0.2",
        "rxjs": "^7.2.0"
      },
      "devDependencies": {
        "@nestjs/cli": "^9.0.0",
        "@nestjs/schematics": "^9.0.0",
        "@nestjs/testing": "^9.0.0",
        "@types/express": "^4.17.13",
        "@types/jest": "28.1.4",
        "@types/node": "^16.0.0",
        "@types/supertest": "^2.0.11",
        "@typescript-eslint/eslint-plugin": "^5.0.0",
        "@typescript-eslint/parser": "^5.0.0",
        "eslint": "^8.0.1",
        "eslint-config-prettier": "^8.3.0",
        "eslint-plugin-prettier": "^4.0.0",
        "jest": "28.1.2",
        "prettier": "^2.3.2",
        "source-map-support": "^0.5.20",
        "supertest": "^6.1.3",
        "ts-jest": "28.0.5",
        "ts-loader": "^9.2.3",
        "ts-node": "^10.0.0",
        "tsconfig-paths": "4.0.0",
        "typescript": "^4.3.5"
      },
      "jest": {
        "moduleFileExtensions": [
          "js",
          "json",
          "ts"
        ],
        "rootDir": "src",
        "testRegex": ".*\\.spec\\.ts$",
        "transform": {
          "^.+\\.(t|j)s$": "ts-jest"
        },
        "collectCoverageFrom": [
          "**/*.(t|j)s"
        ],
        "coverageDirectory": "../coverage",
        "testEnvironment": "node"
      }
    }
    


    앱 실행


    yarn start:dev를 사용하여 개발 환경에서 앱을 실행할 수 있습니다.



    테스트







    전체source code here에 액세스할 수 있습니다.

    요약



    이 기사에서는 kibana를 사용하여 Elastic으로 데이터를 가져오고 NestJs 백엔드 앱을 Elasticsearch의 기능을 활용하도록 연결할 수 있습니다.

    다음 기사에서는 Elasticsearch의 결과를 실시간으로 쿼리하고 시각화하는 간단한 프런트엔드를 구축할 것입니다.

    읽어 주셔서 감사합니다!

    좋은 웹페이지 즐겨찾기