description |
---|
이 문서의 허가되지 않은 무단 복제나 배포 및 출판을 금지합니다. 본 문서의 내용 및 도표 등을 인용하고자 하는 경우 출처를 명시하고 김종민([email protected])에게 사용 내용을 알려주시기 바랍니다. |
JSON 에서는 한 필드 안에 하위 필드를 넣는 object, 즉 객체 타입의 값을 사용할 수 있습니다. 보통은 한 요소가 여러 하위 정보를 가지고 있는 경우 object 타입 형태로 사용합니다. 다음은 movie 인덱스에 하위 필드 "name", "age", "side" 를 가진 object 타입 "characters" 필드의 예제입니다.
{% code title="object 타입 characters 필드를 가진 도큐먼트" %}
PUT movie/_doc/1
{
"characters": {
"name": "Iron Man",
"age": 46,
"side": "superhero"
}
}
{% endcode %}
object 필드를 선언 할 때는 다음과 같이 "properties"
를 입력하고 그 아래에 하위 필드 이름과 타입을 지정합니다.
{% code title="매핑에 object 타입 characters 필드 선언" %}
PUT movie
{
"mappings": {
"properties": {
"characters": {
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "byte"
},
"side": {
"type": "keyword"
}
}
}
}
}
}
{% endcode %}
object 필드를 쿼리로 검색 하거나 집계를 할 때는 다음과 같이 마침표 .
를 이용해서 하위 필드에 접근합니다.
{% tabs %} {% tab title="request" %} {% code title="characters 하위의 name 필드 쿼리" %}
GET movie/_search
{
"query": {
"match": {
"characters.name": "Iron Man"
}
}
}
{% endcode %} {% endtab %}
{% tab title="response" %} {% code title="characters 하위의 name 필드 쿼리 결과" %}
{
"took" : 262,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.5753642,
"hits" : [
{
"_index" : "movie",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.5753642,
"_source" : {
"characters" : {
"name" : "Iron Man",
"age" : 46,
"side" : "superhero"
}
}
}
]
}
}
{% endcode %} {% endtab %} {% endtabs %}
{% hint style="info" %} Elasticsearch에는 따로 배열(array) 타입의 필드를 선언하지 않습니다. 필드 타입의 값만 일치하면 다음과 같이 값을 배열로도 넣을 수 있습니다.
-
{ "title": "Romeo and Juliet" }
-
{ "title": [ "Romeo and Juliet", "Hamlet" ] } {% endhint %}
이번에는 다음과 같이 title 필드 값이 각각 "The Avengers", "Avengers: Infinity War" 이고 characters 필드에 object 값이 2개씩 들어있는 두 개의 도큐먼트를 입력 해 보겠습니다.
{% code title="characters 필드에 2개의 ojbect 값들을 배열로 가진 도큐먼트 2개 입력" %}
PUT movie/_doc/2
{
"title": "The Avengers",
"characters": [
{
"name": "Iron Man",
"side": "superhero"
},
{
"name": "Loki",
"side": "villain"
}
]
}
PUT movie/_doc/3
{
"title": "Avengers: Infinity War",
"characters": [
{
"name": "Loki",
"side": "superhero"
},
{
"name": "Thanos",
"side": "villain"
}
]
}
{% endcode %}
두 개 도큐먼트 모두 characters 필드의 하위 필드 값으로 "name": "Loki"
가 있습니다. 그리고 한 도큐먼트는 "name": "Loki" 의 "side" 필드 값이 "villain" 이고 다른 도큐먼트는 "superhero" 입니다. 이제 characters 필드의 name 값은 "Loki" 이고 side 값은 "villain" 인 도큐먼트를 검색 해 보겠습니다.
{% tabs %} {% tab title="request" %} {% code title="characters 하위 필드의 name: Loki, side: villain 검색" %}
GET movie/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"characters.name": "Loki"
}
},
{
"match": {
"characters.side": "villain"
}
}
]
}
}
}
{% endcode %} {% endtab %}
{% tab title="response" %} {% code title="characters 하위 필드의 name: Loki, side: villain 검색 결과" %}
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0611372,
"hits" : [
{
"_index" : "movie",
"_type" : "_doc",
"_id" : "3",
"_score" : 1.0611372,
"_source" : {
"title" : "Avengers: Infinity War",
"characters" : [
{
"name" : "Loki",
"side" : "superhero"
},
{
"name" : "Thanos",
"side" : "villain"
}
]
}
},
{
"_index" : "movie",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.9827781,
"_source" : {
"title" : "The Avengers",
"characters" : [
{
"name" : "Iron Man",
"side" : "superhero"
},
{
"name" : "Loki",
"side" : "villain"
}
]
}
}
]
}
}
{% endcode %} {% endtab %} {% endtabs %}
분명 {"name": "Loki", "side": "villain"}
값을 포함하고 있는 도큐먼트는 "_id" : "2"
인 "title": "The Avengers"
도큐먼트 뿐인데 {"name": "Loki", "side": "superhero"}
를 포함하고 있는 "title": "Avengers: Infinity War"
도큐먼트도 같이 검색이 되었습니다. (심지어 스코어도 더 높습니다)
얼핏 생각했을 때는 "title": "The Avengers"
도큐먼트만 검색이 되어야 맞는 것으로 생각이 되는데 실제 결과는 그렇지가 않습니다. 이유는 Elasticsearch는 위 예제에서 역 색인을 다음과 같은 모양으로 생성하기 때문입니다. 기억하세요. 역 색인은 필드 별로 생성됩니다.
역 색인에서는 object 필드의 하위 필드들은 모두 상위 필드의 이름과 함께 펼쳐져서 한 필드로 저장이 됩니다. 그렇기 때문에 "characters.name": "Loki"
, "characters.side": "villain"
두 필드값들은 "_id" : "2"
, "_id" : "3"
두 도큐먼트 모두를 리턴합니다.
만약에 object 타입 필드에 있는 여러 개의 object 값들이 서로 다른 역 색인 구조를 갖도록 하려면 nested 타입으로 지정해야 합니다. nested 타입으로 지정하려면 매핑에 다음과 같이 "type": "nested"
를 명시합니다. 다른 부분은 object 와 동일합니다.
{% code title="매핑에 nested 타입 characters 필드 선언" %}
PUT movie
{
"mappings": {
"properties": {
"characters": {
"type": "nested",
"properties": {
"name": {
"type": "text"
},
"side": {
"type": "keyword"
}
}
}
}
}
}
{% endcode %}
입력할 데이터는 의 object 예제에서 입력 한 데이터와 동일합니다. 매핑을 위와 같이 nested 형식으로 선언하고 앞에서 했던 title 필드 값이 각각 "The Avengers", "Avengers: Infinity War" 이고 characters 필드에 object 값이 2개씩 들어있는 두 개의 도큐먼트를 다시 한번 입력 해 보도록 합니다. 그 뒤 다시 characters 필드의 name 값은 "Loki" 이고 side 값은 "villain" 인 도큐먼트를 검색 해 보도록 합니다.
{% tabs %} {% tab title="request" %} {% code title="characters 하위 필ㄹ드의 name: Loki, side: villain 검색" %}
GET movie/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"characters.name": "Loki"
}
},
{
"match": {
"characters.side": "villain"
}
}
]
}
}
}
{% endcode %} {% endtab %}
{% tab title="response" %} {% code title="characters 하위 필ㄹ드의 name: Loki, side: villain 검색 결과" %}
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}
{% endcode %} {% endtab %} {% endtabs %}
검색 결과가 하나도 나타나지 않았습니다.
nested 필드를 검색 할 때는 반드시 nested 쿼리를 써야 합니다. nested 쿼리 안에는 path 라는 옵션으로 nested로 정의된 필드를 먼저 명시하고 그 안에 다시 쿼리를 넣어서 입력합니다.
{% tabs %} {% tab title="request" %} {% code title="nested 쿼리로 characters 하위 필드의 name: Loki, side: villain 검색" %}
GET movie/_search
{
"query": {
"nested": {
"path": "characters",
"query": {
"bool": {
"must": [
{
"match": {
"characters.name": "Loki"
}
},
{
"match": {
"characters.side": "villain"
}
}
]
}
}
}
}
}
{% endcode %} {% endtab %}
{% tab title="response" %} {% code title="nested 쿼리로 characters 하위 필드의 name: Loki, side: villain 검색 결과" %}
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.4480599,
"hits" : [
{
"_index" : "movie",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.4480599,
"_source" : {
"title" : "The Avengers",
"characters" : [
{
"name" : "Iron Man",
"side" : "superhero"
},
{
"name" : "Loki",
"side" : "villain"
}
]
}
}
]
}
}
{% endcode %} {% endtab %} {% endtabs %}
결과로 {"name" : "Loki", "side" : "villain"}
값을 포함하고 있는 "_id" : "2"
도큐먼트만 검색이 되었습니다.
nested 쿼리로 검색하면 nested 필드의 내부에 있는 값 들을 모두 별개의 도큐먼트로 취급합니다. 앞의 예제에서 본 object 도큐먼트와 nested 도큐먼트를 그림으로 비교 해 보면 다음과 같습니다.
object 필드 값들은 실제로 하나의 도큐먼트 안에 전부 포함되어 있습니다. 반면에 nested 필드 값들은 내부적으로 별도의 도큐먼트로 분리되어 저장되며 쿼리 결과에서 상위 도큐먼트와 합쳐져서 보여지게 됩니다.