쿼리 DSL은 ORM에 있어서도, 검색엔진을 사용하는 검색 쿼리 사용에 있어서도

복잡한 검색기능을 단순하게 구현 하기위한 좋은 방법중에 하나이며

닷넷 어플리케이션내에서 DSL검색 방식을 사용하기에 적합한 NEST를 선택하였습니다.

설정

주요 Nuget 의존성 라이브러리

<PackageReference Include="NEST" Version="7.5.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.3" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" />

엘라서틱 검색엔진 API를 활용하기위해 NEST가 사용되었으며, 

RDB의 데이터와 검색 인덱스의 데이터 동기화에 드는 개발 비용을 줄이기 위해

ORM이 활용되었습니다.   ORM은 검색 인덱싱을 하는 과정에서만 사용되며,

사용자의 검색 요청은 엘라서틱 서치엔진 API를 직접 사용할 예정이기때문에 이 경우 ORM의 성능은 큰 이슈가 되지 못합니다.

인덱싱이 수정 되고 있을때(풀인덱싱,부분 대량업데이트,실시간성 부분업데이트) 검색(Read)의 성능을 영향받지 않고 -인덱스 데이터의 스왑

RDB보다 훨씬 다양하고 복합적인(풀텍스트,반경검색,검색과 동시에 집계처리등) 순간검색을 위해 인덱스를 효율적으로 설정하고 관리하는 전략이 필요합니다.   


다음과 같이 대량 업데이트에 최적화된 Bulk 전략도 필요하게 됩니다.

App 설정

appsetting.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "AppSettings": {
    "DBConnection": "server=localhost;port=13306;database=search;user=root;password=root;"
  },
  "elasticsearch": {
    "index": "baseitem",
    "url": "http://localhost:9200/"
  }
}


엘라서틱 Config

using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Nest;
using SearchApi.Entity;

namespace SearchApi.Config
{
    public static class ElasticsearchExtensions
    {
        public static void AddElasticsearch(
            this IServiceCollection services, IConfiguration configuration)
        {
            var url = configuration["elasticsearch:url"];
            var defaultIndex = configuration["elasticsearch:index"];

            var settings = new ConnectionSettings(new Uri(url))
                .DefaultIndex(defaultIndex)
                .DefaultMappingFor<SearchGoods>(m => m
                    .PropertyName(p => p.no, "id")
                );
            var client = new ElasticClient(settings);
            services.AddSingleton<IElasticClient>(client);
        }
    }
}


StartUp 설정

Startup.cs
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddElasticsearch(Configuration);
.......


검색 기능 추가

검색 Entity정의

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace SearchApi.Entity
{
    [Table("tbl_search_goods")]
    public class SearchGoods
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int no { get; set; }

        [Column("goods_no")]        
        public string goodsNo { get; set; }

        [Column("name_kr")]
        public string nameKr { get; set; }

        [Column("name_en")]
        public string nameEn { get; set; }

        public string category1 { get; set; }

        public string category2 { get; set; }

        public string category3 { get; set; }

        public int price { get; set; }

        [Column("view_cnt")]
        public int viewCnt { get; set; }

        [Column("sale_cnt")]
        public int saleCnt { get; set; }

        public string terms { get; set; }

        public string tags { get; set; }

        [Column("url_pc")]
        public string urlPc { get; set; }

        [Column("url_mobile")]
        public string urlMobile { get; set; }

        [Column("image_url1")]
        public string imageUrl1 { get; set; }

        [Column("image_url2")]
        public string imageUrl2 { get; set; }
    }
}

검색데이터의 정의는 익숙한 RDB 모델(엔티티)로부터 먼저 시작하여

검색엔진의 문서의 검색 속성을 일치화할것입니다. 

검색 Repository for RDB

using System;
using Microsoft.EntityFrameworkCore;
using SearchApi.Config;
using SearchApi.Entity;


namespace SearchApi.Repositories
{
    public class SearchRepository : DbContext
    {
        private const string database = "search";
        
        private readonly AppSettings appSettings;

        private readonly bool isEncript;
        
        public string DebugConString { get; set; }

        public DbSet<SearchGoods> searchGoods { get; set; }

        public SearchRepository(DbContextOptions<SearchRepository> options,
            AppSettings _appSettings) : base(options)
        {
            appSettings = _appSettings;            
            if (!appSettings.DBConnection.Contains("localhost"))
            {
                isEncript = true;
            }
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                string dbOption = "";
                string dbConnectionString = string.Empty;
                if (isEncript)
                {
                    throw new NotImplementedException("암호화 모듈을 적용하시오~");
                }
                else
                {
                    dbConnectionString = appSettings.DBConnection + $"database={database};" + dbOption;
                }                
                optionsBuilder.UseMySql(dbConnectionString);
            }
        }
    }
}

검색 데이터의 원본은 RDB 이며 ORM을 활용합니다.

닷넷코어에서 Entity설정은 다음을 추가참고합니다.

Link : https://docs.microsoft.com/en-us/ef/core/


DI를 통한 엘라서틱 서치 클라이언트 사용

using Microsoft.AspNetCore.Mvc;
using Nest;

namespace SearchApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        private readonly IElasticClient _elasticClient;

        public TestController(IElasticClient elasticClient)
        {            
            _elasticClient = elasticClient;
        }     
    }
}

IElasticClient 객체가 실제 엘라서틱서버 연결설정이 완료되어

사용준비가 되면 기본 설정이 완료 되었으며

다음장에서는 최초 검색데이터를 인덱싱→인덱스 한 상태에서

기본적인 검색 기능을 구현하는것에대해 알아 보겠습니다.


참고링크:





  • No labels