반응형
출처: https://www.elastic.co/guide/en/elasticsearch/reference/current/full-text-queries.html
출처: https://stackoverflow.com/questions/26001002/elasticsearch-difference-between-term-match-phrase-and-query-string

1. term

하나의 term을 찾는다. 이 term은 analyze 되지 않는다. 따라서 대소문자, 복수형 등이 다 구분된다

{ "user":"Bennett" }

{
  "query": {
    "term" : { "user" : "bennett" }
  }
}
//아무것도 리턴되지 않는다. (대소문자 구분)

 

2. match

가장 기본적인 search로 text, number, date, boolean을 사용할 수 있다. text는 검색하기전 analyze된다.

fuzzy matching을 지원한다(정확하게 일치하지않더라도 연관성이 있다고 판단하면 리턴).

GET /_search
{
    "query": {
        "match" : {
            "message" : {
                "query" : "this is a test"
            }
        }
    }
}

 

3. query_string

입력값을 analyze한다. 사용자가 명시적으로 ""로 둘러싸지않는이상 순서는 상관이 없다.

{ "foo":"I just said hello world" }
{ "foo":"Hello world" }
{ "foo":"World Hello" }

{
  "query": {
    "query_string": {
      "query": "hello World"
    }
  }
}
//얘는 3개를 다 리턴한다

{
  "query": {
    "query_string": {
      "query": "\"Hello World\""
    }
  }
}
//얘는 명시적으로 ""로 싸여져 있기 때문에 match_phrase와 동일하게 처음 2개만을 리턴한다.

query_string과 match의 차이점은 AND와 OR같은 연산자를 사용할 수 있다. 

query_string은 syntax를 먼저 parse한 뒤에 split을 하고 analyze를 한다.

GET /_search
{
    "query": {
        "query_string" : {
            "query" : "(new york city) OR (big apple)",
            "default_field" : "content"
        }
    }
}

주의할 점: query_string은 invalid syntax에 대해 에러를 반환하므로 AND와 OR과 같은 query syntax를 지원해야되는 것이 아니라면 match를 사용하거나 좀 더 덜 엄격한 simple_query_string을 사용하는 것이 낫다.

 

4. Intervals

matching rules를 사용하여 쿼리한다. 여러개의 term들이 원하는 간격안에 존재하는지 쿼리한다.

아래의 쿼리는 "hot"과 "porridge"사이에 10단어 이상 간격이 없으면서 중간에 "salty"가 없어야 된다.

POST _search
{
  "query": {
    "intervals" : {
      "my_text" : {
        "match" : {
          "query" : "hot porridge",
          "max_gaps" : 10,
          "filter" : {
            "not_containing" : {
              "match" : {
                "query" : "salty"
              }
            }
          }
        }
      }
    }
  }
}

 

5. match_phrase

입력값을 analyze한다. 그리고 다음의 조건을 충족하는 document를 찾아서 반환한다.

- 모든 term들이 field에 존재해야한다.

- 모든 term들이 입력된 순서에 맞게 존재해야된다.

{ "foo":"I just said hello world" }
{ "foo":"Hello world" }
{ "foo":"World Hello" }

{
  "query": {
    "match_phrase": {
      "foo": "Hello World"
    }
  }
}
//1번째 2번째만 리턴된다. 3번째는 순서가 틀림

 

6. match_boolean_prefix

마지막 term을 제외한 모든 term은 term query에 사용된다. 마지막 term은 prefix query에 사용된다.

따라서 아래의 match_bool_prefix는 그 밑의 term과 prefix로 이루어진 query와 동일하다

GET /_search
{
    "query": {
        "match_bool_prefix" : {
            "message" : "quick brown f"
        }
    }
}

GET /_search
{
    "query": {
        "bool" : {
            "should": [
                { "term": { "message": "quick" }},
                { "term": { "message": "brown" }},
                { "prefix": { "message": "f"}}
            ]
        }
    }
}

match_phrase_prefix와 상당히 유사하지만 match_phrase_prefix는 입력값을 phrase로 보고 쭉 이어진 하나로 검색을 하는 반면, match_boolean_prefix는 term단위로 검색을 하기때문에 서로 떨어져 있어도 가능하고, 순서가 뒤섞여있어도 가능하다.

 

7. match_phrase_prefix

위에서 언급했던 것처럼 입력값을 하나의 뭉텅이로 검색한다 따라서:

     - quick brown fox (O)

     - two quick brown ferrets (O)

     - the fox is quick and brown (X)

 

8. multi_match

multi_match는 multi query를 바탕으로 여러 field를 동시에 쿼리할 수 있도록 한다.

GET /_search
{
  "query": {
    "multi_match" : {
      "query" : "this is a test",
      "fields" : [ "subject^3", "*_name" ] 
    }
  }
}

위의 코드처럼 subject 필드에 3배의 가중치를 줄 수 있으며 field이름에 * wildcard를 사용할 수 있다.

 

만약 field가 명시되어 있지 않다면 index.query.default_field로 설정된 field에서 검색하고, 이 default_field는 따로 명시하지 않았다면, index의 field는 term query를 수행할 수 있는 모든 field를 추출하여 수행한다.

 

multi_match가 어떻게 작동할지는 type 파라미터에 따라 결정된다.

 - best_fields: 여러개의 field들 중에서 입력값 단어들 중 가장 많은 단어를 가지고 있는 하나의 field의 점수를 사용

 - most_fields: 여러개의 field들의 점수를 산출한뒤 합산하고 field의 개수로 나눈 평균점수를 사용한다

 

 - phrase / phrase_prefix: best_fields와 똑같지만 match_phrase / match_phrase_prefix를 match대신 사용

 

 - cross fields: 여러개의 field가 동시에 만족되야할 경우(예: 성과 이름이 다른 field에 들어가있을때). query string을 individual term으로 analyze한 뒤에 마치 하나의 field를 검색하는 것처럼 모든 field를 살펴본다. 

(위의 best_field와 most_field와는 달리 어느 하나의 field에 모든 term이 존재하면 된다)

(주의: cross_fields에는 fuzziness가 사용될 수 없다)

 

tie_breaker: 점수가 산정되는 방식을 결정한다:

     - 0.0 : 모든 field 중 가장 높은 점수를 가진 field의 점수를 선택

     - 1.0 : 모든 field의 점수를 합함

     - 0.0 < n < 1.0: 가장 높은 field의 점수 + (n * 나머지 matching field 점수)

 

 

반응형
블로그 이미지

개발자_무형

,
반응형
본문은 Node.js Design Patterns - Mario Casciaro 를 번역/정리한 글입니다.

Blocking I/O

- 기존의 Blocking I/O를 웹서버에 사용하게 되면 동시에 여러 접속을 처리하기 위해 각각의 커넥션을 별도의 쓰레드로 분리를 하여야한다. 이렇게 각가의 쓰레드가 하나의 커넥션만을 처리하게 되면 실제로 일을 하는 시간보다 기다리는 시간이 훨씬 많아지게 된다. 그리고 쓰레드를 여러개 사용하는것은 메모리를 많이 잡아먹고 Context Switch를 발생시키므로 효율적이지 않다.

 

Non Blocking I/O

- Non-blocking I/O에서는 system call이 데이터를 읽고 쓰는것을 기다리지 않고 만약 그시점에 처리할 것이 없다면 바로 반환해버린다.

 

Busy Waiting

- Non-blocking I/O에서는 여러 커넥션을 병렬적으로 처리하기 위한 방법 중 가장 간단한 방법으로 리소스를 polling(수시로 체크)하는 busy-waiting이라는 방법이 있다. 아래의 pseudo 코드 방식으로 작동한다:

resources = [socketA, socketB, socketC];
while(!resources.isEmpty()) {
	for(i=0;i < resources.length; i++){
    	resource = resources[i]
        var data = resource.read();
        if (data == NO_DATA_AVAILABLE)
        	continue;
        if (data == RESOURCE_CLOSED)
        	resources.remove(i);
        else
        	consumeData(data)
    }
}

이런방식을 사용하면 동시에 여러개의 접속을 하나의 쓰레드에서 처리할 수 있다. 하지만 이러한 Polling 알고리즘들은 CPU를 너무 낭비한다.

 

Synchronous Event Demultiplexer

- 이를 효율적으로 해결한 방법이 Synchronous Event Demultiplexer 또는 Event Notification Interface이다.

socketA, pipeB;
watchedList.add(socketA, FOR_READ)
watchedList.add(pipeB, FOR_READ)
while( events = demultiplexer.watch(watchedList)){
	foreach(event in events){
		data = event.resource.read()
        if(data == RESOURCE_CLOSED)
        	demultiplexer.unwatch(event.resource)
        else
        	consume(data)
	}
}

1. 여기서 핵심은 demultiplexer.watch(watchedList)인데, demultiplexer가 watchedList를 계속 보면서 다음으로 넘어가지않고 Block하고 있다가 watchedList 중 하나라도 준비가 된다면 다음 foreach문으로 넘어간다.

2. 이 foreach문이 실행될때는 각각의 event들이 준비가 되었다는것이 보장이 되어 처리될때 block이 되지않는다.

3. 모든 event가 처리되고 나면 다시 demultiplexer가 block하며 새로운 event가 발생할때까지 기다린다.

이 일련의 과정을 event loop라고 한다.

 

따라서 정리하자면 각각의 쓰레드에서 커넥션을 1개씩 잡고 있는게 아니라 event demultiplexer가 여러개의 커넥션을 보고있다가 event가 발생하면 이를 모아서 전달해주는데, 이 모여진 event들은 blocking되지 않고 바로 처리할 수 있기 때문에 1개의 쓰레드로 처리가 가능하게 된다.

 

이 방식을 차용하면 쓰레드가 기다리는 idle time이 획기적으로 줄게되고 single thread로 운용이 가능해지기 때문에 race condition이나 multi-thread synchronization같은 문제를 겪지않고 더 직관적이게 병렬처리가 가능해진다.

 

The Reactor Pattern

Reactor 패턴은 앞에서 소개한 알고리즘의 특화된 버전이다. 핵심이 되는 아이디어는 각각의 I/O에 핸들러를 붙이는 것이다(Node.JS에서는 Callback). 그리고 이 핸들러들은 event가 발생하면 호출되어 event loop에 의해 처리된다.

 

Reactor패턴이 처리하는 방식은 다음과 같다:

1. Application은 Event demultiplexer에게 새로운 요청을 보냄으로서 새로운 I/O를 생성한다. Application은 요청을 보낼때 event가 발생하면 호출될 Handler를 설정하여 보낸다. 이 작업은 Non-Blocking이다.

2. I/O 작업이 끝나면 Event Demultiplexer는 새로운 event를 Event Queue에 삽입한다.

3. 이 때가 되면 Event Loop가 Event Queue를 순회한다.

4. 이벤트가 발생하면, 각각에 적용된 핸들러가 호출된다.

5. 어플리케이션 코드의 일부인 핸들러는 본인의 작업이 완료되면 Event Loop에게 control을 다시 넘겨준다. 

6. Event Queue에 있는 모든 작업이 완료되면 Event Demultiplexer가 다음 사이클을 시작할때까지 block 된다.

 

Node.js의 Non-Blocking Engine - libuv

I/O operation은 같은 OS안에서도 resource의 종류에 따라 상당히 다르게 처리될 수 있다. 예를 들자면 Unix의 regular filesystem은 non-blocking operation을 지원하지 않는다. 따라서 non-blocking처럼 행동하게 만들기 위해서는 event loop 밖에 별도의 thread를 사용하여 처리하게 된다. 이렇게 다양한 운영체제에서 Event Demultiplexer를 지원하기 위해서는 high-level abstraction이 필요했다. 그래서 메이저 OS들을 지원하기 위해 Node.js core team은 libuv라는 C 라이브러리를 만들게 되었다.

 

The Callback Pattern

Javascript는 callback을 나타내기 위한 최적의 언어이다. Function은 class object로서 쉽게 변수에 할당될 수 있고, argument로 전달되거나 return으로 반환될 수도 있다. 그리고 closure를 사용하면 function이 생성된 환경을 쉽게 접근할 수 있다. 즉, asynchronouse operation이 생성된 context를 언제 callback이 불러지던 접근할 수 있다.

 

Callback은 다른 함수에 인자로 전달되는 함수로서 함수의 작업이 끝나면 작업의 결과물과 함께 호출되는 함수이다.

이런 방식은 함수형 프로그래밍에서는 Continuation-Passing style(CPS)라고 불리며 Asynchronous operation에서만 사용되는 개념은 아니다. 단지 이 방식은 작업의 결과물을 caller에게 바로 반환하는 것이아니라 다른 함수에게 전달하는 방식을 뜻할 뿐이다.

 

반응형
블로그 이미지

개발자_무형

,
반응형

ElasticSearch를 도입하기전, ElasticSearch에대해 공부하기 위해 레퍼런스를 읽으면서 번역&정리한 글입니다.

문서 전체를 번역한 것이 아니라 개인적으로 정리가 필요하다 싶은 내용만 정리하였습니다.

본문: ElasticSearch 공식레퍼런스

 

Mapping은 document와 document의 field가 어떻게 저장되고 인덱싱되는지를 정하는 것이다.

 

Field Datatypes:

- Simple types like text, keyword, date, long, double, boolean, ip

- JSON형식을 띄는 object, nested

- 특수한 경우에 사용되는 geo_point, geo_shape, completion

 

잘모르는 타입들 정리:

Keyword datatype:

- 주로 이메일주소, 우편번호 등 형식을 갖춘 데이터들을 저장하기 위해 사용된다.

- 주로 필터링, 정렬, 종합을 위해 사용된다.

- 정확한 값으로면 검색이 가능하다

Text datatype:

- text로 지정된 field는 anlayzer를 통해 변환된다.(한글은 anlayzer-nori 플러그인을 별도로 깔아야됨)

- 변환된 후에는 ES가 하나의 단어를 전체 내용중에서 찾을 수 있게 해준다.

Nested datatype:

- nested타입은 object타입의 특수한 형태로써 쉽게 말하자면 object의 list라고 생각하면된다.

- 그리고 ES는 inner object라는 형식을 지원하지 않기때문에 이 list를 flatten하여 처리한다.

PUT my_index/_doc/1
{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

위의 코드블럭이 처리될때는 아래로 변환되어 처리된다.

{
  "group" :        "fans",
  "user.first" : [ "alice", "john" ],
  "user.last" :  [ "smith", "white" ]
}

 

 

 

반응형
블로그 이미지

개발자_무형

,
반응형

ElasticSearch를 도입하기전, ElasticSearch에대해 공부하기 위해 레퍼런스를 읽으면서 번역&정리한 글입니다.

문서 전체를 번역한 것이 아니라 개인적으로 정리가 필요하다 싶은 내용만 정리하였습니다.

본문: ElasticSearch 공식레퍼런스

 

PUT /...customer/_doc/1
{
  "name": "John Doe",
  "characteristics": "he is tall, fat and wearing blue jacket"
}

위의 예시에서

PUT /<index>/<mapping type>/<id>

순이다.

 

그래서 실제로 쿼리를 보내보면:

{
  "_index" : "...customer",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

 이렇게 _type에 _doc이 뜨는 것을 볼 수 있다.

 

mapping type이 7.x버전부터는 없어졌다고 하는데 무엇인지 알아둘겸 읽어보았다.

 

---여기서부터 원문---

 

처음 ElasticSeach(이하 ES)가 출시했을때부터, 각각의 document는 하나의 index와 하나의 mapping type으로 저장되었다. Mapping Type은 document의 타입을 나타내는데 사용되었는데, 예를 들자면 twitter라는 index는 user 타입과 tweet 타입을 가질 수 있다.

 

각각의 mapping type은 고유의 field를 가지고 있다. 그래서 user 타입은 full_name, user_name 필드를 가지고 있고, tweet 타입은 content, tweeted_at 필드를 가질 수 있다.

 

document는 _type 메타 필드를 가지고 있었기 때문에 타입으로 필터링하여 검색을 할 수 있었다.

GET twitter/user,tweet/_search
{
  "query": {
    "match": {
      "user_name": "kimchy"
    }
  }
}

_type필드는 document의 _id와 합쳐져 _uid필드를 만들어낸다. 따라서 다른 타입에 속하는 document들은 같은 _id를 가질 수 있다.

 

그렇다면 왜 mapping type을 없애는 걸까?

처음에 ES에서 소개할때 index는 SQL database의 데이터베이스와 유사하고, type은 테이블과 유사하다고 하였다.

하지만 이것은 유저들이 잘못된 방향으로 사용하도록 유도하였다. 

 

ES의 index에서는 같은 이름을 가졌지만 서로 다른 type에 속하는 field들이 있을때, 2 field는 같은 Lucene field에 속하게 된다. 따라서 A라는 type에 속하는 ABC라는 field과 B라는 type에 속하는 ABC라는 field는 같은 lucene field세 속하고, 따라서 같은 mapping을 가져야한다.

 

대체방안

1. document의 종류별로 각기 다른 index를 만든다. index들은 상호간에 독립적이기 때문에 위에서 언급한 문제가 없다.

 

2. Custom type field를 사용한다. 이를 적용하면:

PUT twitter/user/kimchy
{
  "name": "Shay Banon",
  "user_name": "kimchy",
  "email": "shay@kimchy.com"
}

PUT twitter/tweet/1
{
  "user_name": "kimchy",
  "tweeted_at": "2017-10-24T09:00:00Z",
  "content": "Types are going away"
}

가 이렇게 바뀐다:

PUT twitter/_doc/user-kimchy
{
  "type": "user", 
  "name": "Shay Banon",
  "user_name": "kimchy",
  "email": "shay@kimchy.com"
}

PUT twitter/_doc/tweet-1
{
  "type": "tweet", 
  "user_name": "kimchy",
  "tweeted_at": "2017-10-24T09:00:00Z",
  "content": "Types are going away"
}

 

 

 

 

매핑도 이를 반영하기위에 좀 바뀌는데

원래는:

PUT twitter
{
  "mappings": {
    "user": {
      "properties": {
        "name": { "type": "text" },
        "user_name": { "type": "keyword" },
        "email": { "type": "keyword" }
      }
    },
    "tweet": {
      "properties": {
        "content": { "type": "text" },
        "user_name": { "type": "keyword" },
        "tweeted_at": { "type": "date" }
      }
    }
  }
}

이후에는:

PUT twitter
{
  "mappings": {
    "_doc": {
      "properties": {
        "type": { "type": "keyword" }, 
        "name": { "type": "text" },
        "user_name": { "type": "keyword" },
        "email": { "type": "keyword" },
        "content": { "type": "text" },
        "tweeted_at": { "type": "date" }
      }
    }
  }
}
반응형
블로그 이미지

개발자_무형

,
반응형

ElasticSearch를 도입하기전, ElasticSearch에대해 공부하기 위해 레퍼런스를 읽으면서 번역&정리한 글입니다.

문서 전체를 번역한 것이 아니라 개인적으로 정리가 필요하다 싶은 내용만 정리하였습니다.

본문: ElasticSearch 공식레퍼런스

 

ElasticSearch(이하 ES)는 항상 접근 가능하고 필요에 따라 확장이 가능하도록 설계되었다. 서버(node)를 클러스터에 추가하기만 하면 ES가 자동으로 데이터와 쿼리 로드를 사용가능한 노드들로 분산하여 준다.

 

Shard는 Primary와 Replica 두가지 종류가 있다. Index에 포함된 각각의 document는 1개의 primary shard에 포함된다. replica shard는 primary shard의 복제이다. Replica는 하드웨어 문제에 대응할 수 있도록 해주고 searching이나 document를 불러오는 작업을 처리하는 capacity를 늘려준다.

반응형
블로그 이미지

개발자_무형

,
반응형

ElasticSearch를 도입하기전, ElasticSearch에대해 공부하기 위해 레퍼런스를 읽으면서 번역&정리한 글입니다.

문서 전체를 번역한 것이 아니라 개인적으로 정리가 필요하다 싶은 내용만 정리하였습니다.

본문: ElasticSearch 공식레퍼런스

 

ElasticSearch(이하 ES)는 클러스터 매니징, 인덱싱, 데이터 검색을 위한 사용이 간단한 REST API를 제공한다. 그리고 테스트 목적으로 Command Line이나 Kibana의 개발자 콘솔에서 직접 request를 보낼 수도 있다.

 

ES의 REST API는 구조화된 쿼리, 텍스트 쿼리, 두개가 혼합된 쿼리를 지원한다. 만약 gender와 age field를 employee index에서 검색하고 hire_date에 따라 정렬한다면 Full-text 쿼리는 query string과 매치되는 모든 document를 찾고, 연관도에 따라 정렬한다.

반응형
블로그 이미지

개발자_무형

,
반응형

ElasticSearch를 도입하기전, ElasticSearch에대해 공부하기 위해 레퍼런스를 읽으면서 번역&정리한 글입니다.

문서 전체를 번역한 것이 아니라 개인적으로 정리가 필요하다 싶은 내용만 정리하였습니다.

본문: ElasticSearch 공식레퍼런스

 

ElasticSearch(이하 ES)는 데이터를 분산 저장한다. 그리고 테이블에 행과 열의 방식으로 데이터를 저장하는 것이 아니라 JSON으로 직렬화하여 저장한다. 만약 클러스터가 여러개의 ES 노드로 구성되어 있다면 저장된 document들은 클러스터에 분산 저장되어 어떤 노드에서든 접근이 가능하다.

 

Document가 저장되면 index가 되어 1초안에 거의 실시간으로 검색이 가능하다. ES는 Inverted Index라는 자료구조를 사용하여 매우 빠른 Full-Text-Search(이하 FTS)가 가능하다. Inverted Index는 document안에 존재하는 모든 유니크한 단어들을 정리하여 그 단어가 포함된  document들을 찾아낸다.

 

Index는 최적화된 document들의 모음이라고 할수있다. 그리고 각각의 document들은 key-value로 구성된 데이터들을 저장하는 field들로 구성된다. ES는 기본적으로 모든 field들을 인덱싱(앞의 index와는 조금 다름)하며 각각의 인덱싱된 field들은 최적화된 자료구조를 가지고 있다. 텍스트로 구성될 field들은 inverted indices 자료구조로 저장되고, 숫자와 위치정보 field들은 BKD 트리 구조로 저장된다. 이렇게 각각의 필드에 맞춤 자료구조를 사용하는 것이 ES가 매우 빠른 이유이다.

 

가끔은 하나의 field를 여러가지 방법으로 인덱싱하는 것이 유용할때가 있다. 예를 들면 문자열 field를 Full-Text-Search를 위하여 text field로 인덱싱하고, 데이터 정렬과 수집을 위해서 keyword field로도 인덱싱할 수 있다. 아니면 하나 이상의 language analyzer를 사용하여 인덱싱할 수도 있다.

 

 

 

 

 

반응형
블로그 이미지

개발자_무형

,