๐ŸคRyusun๐Ÿค 2025. 4. 2. 10:23

Elasticsearch๋ž€?

Elasticsearch๋Š” ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ์—์„œ ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์ธ ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋œ ์˜คํ”ˆ ์†Œ์Šค ๋ถ„์‚ฐ ๊ฒ€์ƒ‰ ์—”์ง„์ž…๋‹ˆ๋‹ค. JSON ๋ฌธ์„œ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ฒ€์ƒ‰ํ•˜๋ฉฐ, ์—ญ์ƒ‰์ธ(Inverted Index) ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด ๋น ๋ฅธ ๊ฒ€์ƒ‰์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

 

Elasticsearch์˜ ์ฃผ์š” ํŠน์ง•

  • ์ „๋ฌธ ๊ฒ€์ƒ‰(Full Text Search)
    • ํ˜•ํƒœ์†Œ ๋ถ„์„์„ ํ†ตํ•ด ํ…์ŠคํŠธ ๊ฒ€์ƒ‰์„ ์ตœ์ ํ™”ํ•˜๊ณ  ๋™์˜์–ด ๋ฐ ์œ ์˜์–ด ๊ฒ€์ƒ‰์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • ์‹ค์‹œ๊ฐ„ ๊ฒ€์ƒ‰
    • ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ ๋ฐ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ถ„์‚ฐ ์‹œ์Šคํ…œ
    • ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋…ธ๋“œ๋กœ ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๊ตฌ์„ฑํ•˜์—ฌ ํ™•์žฅ์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • RESTful API ์ง€์›
    • GET, POST, DELETE ๋“ฑ์˜ REST API๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์†์‰ฝ๊ฒŒ CRUD(์ƒ์„ฑ, ์กฐํšŒ, ์ˆ˜์ •, ์‚ญ์ œ)ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์—ญ์ƒ‰์ธ ๊ตฌ์กฐ(Inverted Index)
    • ์ƒ‰์ธ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํŠน์ • ๋‹จ์–ด์™€ ๊ด€๋ จ๋œ ๋ฌธ์„œ๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์Šคํ‚ค๋งˆ๋ฆฌ์Šค(Schema-less)
    • ๋ฏธ๋ฆฌ ์ •์˜๋œ ์Šคํ‚ค๋งˆ ์—†์ด๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ๋ถ„์„ํ•˜์—ฌ ํ•„๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐ•๋ ฅํ•œ ๋ฐ์ดํ„ฐ ๋ถ„์„ ๊ธฐ๋Šฅ
    • ์ง‘๊ณ„(Aggregation), ๋™์˜์–ด ์ฒ˜๋ฆฌ, ์ž๋™ ์™„์„ฑ ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜์—ฌ ์ •๊ตํ•œ ๋ฐ์ดํ„ฐ ๋ถ„์„์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

Elasticsearch์˜ ํ™œ์šฉ ์‚ฌ๋ก€

  • ๊ฒ€์ƒ‰ ์—”์ง„ (์›น์‚ฌ์ดํŠธ, ์ „์ž์ƒ๊ฑฐ๋ž˜, ๋ธ”๋กœ๊ทธ ๋“ฑ)
  • ๋กœ๊ทธ ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ (ELK Stack: Elasticsearch + Logstash + Kibana)
  • ๋ฐ์ดํ„ฐ ๋ถ„์„ ๋ฐ ์‹œ๊ฐํ™”
  • ์ถ”์ฒœ ์‹œ์Šคํ…œ

 

RDBMS์™€์˜ ์ฐจ์ด์ 

๊ธฐ์กด RDBMS ๊ธฐ๋ฐ˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ํ…์ŠคํŠธ ๊ฒ€์ƒ‰์ด ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ LIKE ๊ฒ€์ƒ‰์— ์˜์กดํ•ด์•ผ ํ•˜์ง€๋งŒ, LIKE ๊ฒ€์ƒ‰์€ ๋™์˜์–ด๋‚˜ ์œ ์˜์–ด๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์•„ ํ•œ๊ณ„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Elasticsearch(ES) ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋ณด๋‹ค ๊ฐ•๋ ฅํ•œ ํ…์ŠคํŠธ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Elasticsearch๋Š” ์—ญ์ƒ‰์ธ(Inverted Index) ๊ตฌ์กฐ ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋น ๋ฅธ ๊ฒ€์ƒ‰์„ ์ง€์›ํ•˜๋ฉฐ, ํ˜•ํƒœ์†Œ ๋ถ„์„ ์„ ํ†ตํ•ด ๊ฒ€์ƒ‰ ์„ฑ๋Šฅ์„ ๊ทน๋Œ€ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ณด๋‹ค ์ •ํ™•ํ•˜๊ณ  ์œ ์—ฐํ•œ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ, Elasticsearch๋Š” ๋‹จ์ˆœํ•œ ํ…์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฅผ Tokenizer, Filter, Analyzer ๋“ฑ์„ ํ™œ์šฉํ•˜์—ฌ ์ €์žฅํ•˜๋ฉฐ, ํ˜•ํƒœ์†Œ ๋ถ„์„ ๋ฐฉ์‹์€ _analyze API๋ฅผ ํ†ตํ•ด ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ๋ฌธ์žฅ์€ ์›๋ณธ ๊ทธ๋Œ€๋กœ ์ €์žฅ๋จ๊ณผ ๋™์‹œ์—, ํ…์ŠคํŠธ ํƒ€์ž…์€ ์—ญ์ƒ‰์ธ(Inverted Index)์ด๋ผ๋Š” ํŠน๋ณ„ํ•œ ์ž๋ฃŒ๊ตฌ์กฐ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

 

์—ญ์ƒ‰์ธ(Inverted Index) ์˜ˆ์‹œ

Elasticsearch๋Š” ๊ฒ€์ƒ‰์„ ๋น ๋ฅด๊ฒŒ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์—ญ์ƒ‰์ธ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์—ญ์ƒ‰์ธ์€ ํŠน์ •ํ•œ ํ† ํฐ(๋‹จ์–ด)์ด ์–ด๋–ค ๋ฌธ์„œ์™€ ์—ฐ๊ด€๋˜์–ด ์žˆ๋Š”์ง€๋ฅผ ์ €์žฅํ•˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์•„๋ž˜์™€ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

Term Id
potato 1,2
wedge 1
better 2

์ด๋Ÿฌํ•œ ๊ตฌ์กฐ ๋•๋ถ„์— ๊ฒ€์ƒ‰ ์‹œ ๋น ๋ฅด๊ฒŒ ๊ฒฐ๊ณผ๋ฅผ ์กฐํšŒํ•˜๊ณ , ์ง‘๊ณ„ํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 


 

ElasticSearch + SpringBoot ์„ธํŒ…

 

gradle ์˜์กด์„ฑ ์ถ”๊ฐ€

    implementation 'org.springframework.boot:spring-boot-starter-data-search'

 

 

ElasticSearchConfig

package com.iruyeon.v1.config.common;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;

@Configuration
@EnableElasticsearchRepositories(basePackages = "org.springframework.data.elasticsearch.repository")
public class ElasticSearchConfig extends ElasticsearchConfiguration {

    @Override
    public ClientConfiguration clientConfiguration() {
        return ClientConfiguration.builder()
                .connectedTo("localhost:9200")
                .build();
    }
}

 

ClientDocumentRepository

package com.iruyeon.v1.domain.search.repository;

import com.iruyeon.v1.domain.search.entity.ClientDocument;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

public interface ClientDocumentRepository extends ElasticsearchRepository<ClientDocument, String> {
}

 

 

ClientDocument

package com.iruyeon.v1.domain.search.entity;

import com.iruyeon.v1.domain.client.entity.Client;
import com.iruyeon.v1.domain.client.entity.Family;
import com.iruyeon.v1.domain.client.enums.MaritalStatus;
import com.iruyeon.v1.domain.common.enums.Status;
import com.iruyeon.v1.domain.member.enums.Gender;
import jakarta.persistence.Id;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.util.List;
import java.util.Set;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Document(indexName = "client")
public class ClientDocument {

    @Id
    private String id;

    private String name;
    private String phoneNumber;
    private String address;
    private int birthYear;
    private MaritalStatus maritalStatus;
    private String highSchool;
    private String university;
    private String eduDegree;
    private String major;
    private String property;
    private String religion;
    private String currentJob;
    private String previousJob;
    private String jobDetail;
    private String info;
    private String homeTown;
    private Gender gender;
    private Status status;
    private int ageGapLower;
    private int ageGapUpper;
    private String interest;
    private String idealType;
    private String personality;

    @Field(name = "has_child", type=FieldType.Boolean)
    private Boolean hasChild;

    @Field(type = FieldType.Keyword)
    private List<String> images;

    @Field(type = FieldType.Nested)
    private Set<Family> family;

    @Builder
    public ClientDocument(String id, String name, String phoneNumber, String address, int birthYear, String highSchool,
                  String university, String major, String property, String religion, String currentJob,
                  String previousJob, String jobDetail, String info, String homeTown, Gender gender, Status status,
                  int ageGapLower, int ageGapUpper, String interest, Set<Family> family, MaritalStatus maritalStatus,
                  String idealType, String personality, boolean hasChild, List<String> images, String eduDegree) {
        this.id = id;
        this.name = name;
        this.phoneNumber = phoneNumber;
        this.address = address;
        this.birthYear = birthYear;
        this.maritalStatus = maritalStatus;
        this.highSchool = highSchool;
        this.university = university;
        this.major = major;
        this.property = property;
        this.religion = religion;
        this.currentJob = currentJob;
        this.previousJob = previousJob;
        this.jobDetail = jobDetail;
        this.info = info;
        this.homeTown = homeTown;
        this.gender = gender;
        this.status = status;
        this.ageGapLower = ageGapLower;
        this.ageGapUpper = ageGapUpper;
        this.interest = interest;
        this.idealType = idealType;
        this.personality = personality;
        this.hasChild = hasChild;
        this.images = images;
        this.family = family;
        this.eduDegree = eduDegree;
    }

    public static ClientDocument entityToDocument(Client client) {
        return ClientDocument.builder()
                .name(client.getName())
                .phoneNumber(client.getPhoneNumber())
                .address(client.getAddress())
                .birthYear(client.getBirthYear())
                .highSchool(client.getHighSchool())
                .university(client.getUniversity())
                .major(client.getMajor())
                .property(client.getProperty())
                .currentJob(client.getCurrentJob())
                .previousJob(client.getPreviousJob())
                .maritalStatus(client.getMaritalStatus())
                .jobDetail(client.getJobDetail())
                .info(client.getInfo())
                .homeTown(client.getHomeTown())
                .gender(client.getGender())
                .status(client.getStatus())
                .ageGapLower(client.getAgeGapLower())
                .ageGapUpper(client.getAgeGapHigher())
                .interest(client.getInterest())
                .idealType(client.getIdealType())
                .personality(client.getPersonality())
                .hasChild(client.getHasChild())
                .images(client.getImages())
                .build();
    }
}

 

ClientSearchService

package com.iruyeon.v1.domain.search.service;

import com.iruyeon.v1.domain.client.dto.ClientRequestDTO;
import com.iruyeon.v1.domain.search.entity.ClientDocument;
import com.iruyeon.v1.domain.search.repository.ClientDocumentRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class ClientSearchService {

    private final ElasticsearchOperations operations;
    private final ClientDocumentRepository clientDocumentRepository;

    public ClientDocument saveClient(ClientDocument document) {
        return clientDocumentRepository.save(document);
    }

    public List<ClientDocument> searchClient(ClientRequestDTO dto, int page) {
        // ํŽ˜์ด์ง€ ์„ค์ •
        Pageable pageable = PageRequest.of(page, 10);

        // ๊ฒ€์ƒ‰ ์กฐ๊ฑด ์„ค์ •
        Criteria criteria = createConditionCriteria(dto);
        Query query = new CriteriaQuery(criteria).setPageable(pageable);

        // Elasticsearch ๊ฒ€์ƒ‰ ์‹คํ–‰
        SearchHits<ClientDocument> searchHits = operations.search(query, ClientDocument.class);

        // ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ๋ณ€ํ™˜ ํ›„ ๋ฐ˜ํ™˜
        return searchHits.getSearchHits()
                .stream()
                .map(SearchHit::getContent)
                .collect(Collectors.toList());
    }

    private Criteria createConditionCriteria(ClientRequestDTO dto) {
        Criteria criteria = new Criteria();

        if (dto == null) {
            return criteria;
        }

        if (dto.getCurrentJob() != null) {
            criteria = criteria.and(Criteria.where("currentJob").is(dto.getCurrentJob()));
        }

        if (dto.getAgeGapHigher() > 0) {
            criteria = criteria.and(Criteria.where("maxAge").greaterThanEqual(dto.getAgeGapHigher())); // โœ… ์ˆ˜์ •
        }

        if (dto.getAgeGapLower() > 0) {
            criteria = criteria.and(Criteria.where("minAge").lessThanEqual(dto.getAgeGapLower())); // โœ… ์ˆ˜์ •
        }

        if (dto.getHasChild() != null) {
            criteria = criteria.and(Criteria.where("hasChild").is(dto.getHasChild()));
        }

        if (dto.getMaritalStatus() != null) {
            criteria = criteria.and(Criteria.where("maritalStatus").is(dto.getMaritalStatus()));
        }

        if (dto.getReligion() != null) {
            criteria = criteria.and(Criteria.where("religion").is(dto.getReligion()));
        }

        if (dto.getUniversity() != null) {
            criteria = criteria.and(Criteria.where("university").is(dto.getUniversity()));
        }

        return criteria;
    }
}

 

  • operations.search(query, ClientDocument.class) ๋™์ž‘ ๋ฐฉ์‹
    • query๋Š” CriteriaQuery ๊ธฐ๋ฐ˜์˜ ๊ฒ€์ƒ‰ ์กฐ๊ฑด์ด ์„ค์ •๋œ ์ฟผ๋ฆฌ ๊ฐ์ฒด
    • ClientDocument.class๋Š” ๊ฒ€์ƒ‰ ๋Œ€์ƒ ์—”ํ‹ฐํ‹ฐ ํƒ€์ž…์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
    • Elasticsearch์— HTTP ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ , ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ JSON ํ˜•ํƒœ๋กœ ์‘๋‹ตํ•˜์ง€๋งŒ, Spring Data Elasticsearch๋Š” ์ด๋ฅผ SearchHits<T> ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    • SearchHits<ClientDocument>๋Š” ๋‹ค์Œ ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค:
      • ์ด ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ๊ฐœ์ˆ˜ (getTotalHits())
      • ๊ฐ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ์˜ ์ ์ˆ˜ (Elasticsearch Relevance Score)
      • ๊ฒ€์ƒ‰๋œ ClientDocument ๋ฐ์ดํ„ฐ (getContent())

 

index์— ํ•ด๋‹นํ•˜๋Š” ์ „์ฒด ๋ฐ์ดํ„ฐ ์กฐํšŒ

http://localhost:9200/client/_search?pretty=true

 

์กฐ๊ฑด์— ๋งž๋Š” ๋ฐ์ดํ„ฐ ์กฐํšŒ

Elasticsearch์—์„œ ์กฐ๊ฑด์— ๋งž๋Š” ๋ฌธ์„œ๋ฅผ ์กฐํšŒํ•˜๊ณ , ํ•ด๋‹น ๊ฒฐ๊ณผ๋ฅผ ์‰ฝ๊ฒŒ ํŽ˜์ด์ง• ์ฒ˜๋ฆฌํ•œ ๊ฒฐ๊ณผ๊ฐ’์„ ํ™•์ธํ• ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

 

 

 

 

[์ฐธ๊ณ ]

 

https://esbook.kimjmin.net/07-settings-and-mappings

 

7. ์ธ๋ฑ์Šค ์„ค์ •๊ณผ ๋งคํ•‘ - Settings & Mappings | Elastic ๊ฐ€์ด๋“œ๋ถ

์ด ๋ฌธ์„œ์˜ ํ—ˆ๊ฐ€๋˜์ง€ ์•Š์€ ๋ฌด๋‹จ ๋ณต์ œ๋‚˜ ๋ฐฐํฌ ๋ฐ ์ถœํŒ์„ ๊ธˆ์ง€ํ•ฉ๋‹ˆ๋‹ค. ๋ณธ ๋ฌธ์„œ์˜ ๋‚ด์šฉ ๋ฐ ๋„ํ‘œ ๋“ฑ์„ ์ธ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” ๊ฒฝ์šฐ ์ถœ์ฒ˜๋ฅผ ๋ช…์‹œํ•˜๊ณ  ๊น€์ข…๋ฏผ(kimjmin@gmail.com)์—๊ฒŒ ์‚ฌ์šฉ ๋‚ด์šฉ์„ ์•Œ๋ ค์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž

esbook.kimjmin.net

 

https://ksb-dev.tistory.com/324

 

์Šคํ”„๋ง๋ถ€ํŠธ๋กœ ์—˜๋ผ์Šคํ‹ฑ์„œ์น˜ ์ฟผ๋ฆฌ ๋‚ ๋ฆฌ๊ธฐ

0. ๊ฐœ์š”์Šคํ”„๋ง๋ถ€ํŠธ๋กœ ์—˜๋ผ์Šคํ‹ฑ์„œ์น˜์— ์ ‘๊ทผํ•ด์„œ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ ค๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.โ””โ”€esโ”‚โ””โ”€src โ””โ”€main โ”œโ”€java โ”‚ โ””โ”€com โ”‚ โ””โ”€example โ”‚ โ””โ”€es_springboot โ”‚ โ”‚ EsSpring

ksb-dev.tistory.com

https://velog.io/@yukina1418/๊ฒ€์ƒ‰์—”์ง„-Elasticsearch์—-๋Œ€ํ•˜์—ฌ

 

๊ฒ€์ƒ‰์—”์ง„ Elasticsearch์— ๋Œ€ํ•˜์—ฌ

Elasticsearch์— ๋Œ€ํ•ด์„œ ์ž์„ธํ•˜๊ฒŒ ์•Œ์•„๋ณด์ž.

velog.io

https://sihyung92.oopy.io/database/elasticsearch/1

 

๊ฒ€์ƒ‰์—”์ง„์— ์“ฐ์ด๋Š” ์—˜๋ผ์Šคํ‹ฑ์„œ์น˜ Elasticsearch์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž!

Elasticsearch๋ž€

sihyung92.oopy.io