2.X/6. Modeling Your Data

6-2. Nested Objects

drscg 2017. 9. 23. 15:12

Given the fact that creating, deleting, and updating a single document in Elasticsearch is atomic, it makes sense to store closely related entities within the same document. For instance, we could store an order and all of its order lines in one document, or we could store a blog post and all of its comments together, by passing an array of comments:

Elasticsearch에서 단일 document를 생성, 수정, 삭제하는 것은 원자성(atomic)을 가진다는 사실을 고려해 보면, 밀접하게 관련된 entity는 동일한 document내에 저장하는 것이 합리적이다. 예를 들어, 어떤 주문과 해당 주문의 거래선을 단일 document에 저장할 수 있다. 또는 어떤 블로그 게시물과, comments(댓글) 을 배열로 전달하여, 모든 댓글을 함께 저장할 수 있다.

PUT /my_index/blogpost/1
{
  "title": "Nest eggs",
  "body":  "Making your money work...",
  "tags":  [ "cash", "shares" ],
  "comments": [ 
    {
      "name":    "John Smith",
      "comment": "Great article",
      "age":     28,
      "stars":   4,
      "date":    "2014-09-01"
    },
    {
      "name":    "Alice White",
      "comment": "More like this please",
      "age":     31,
      "stars":   5,
      "date":    "2014-10-22"
    }
  ]
}

dynamic mapping을 사용한다면, comments field는 object field로 자동 생성될 것이다.

Because all of the content is in the same document, there is no need to join blog posts and comments at query time, so searches perform well.

모든 내용이 동일한 document에 있기 때문에, query시에 블로그 게시물과 댓글을 join할 필요가 없다. 따라서 검색은 잘 동작한다.

The problem is that the preceding document would match a query like this:

문제는 위의 document가 아래 query에 일치한다는 것이다.

GET /_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "comments.name": "Alice" }},
        { "match": { "comments.age":  28      }} 
      ]
    }
  }
}

Alice는 28살이 아니고, 31살이다.

The reason for this cross-object matching, as discussed in Arrays of Inner Objects, is that our beautifully structured JSON document is flattened into a simple key-value format in the index that looks like this:

Arrays of Inner Objects에서 언급했었지만, 이렇게 오브젝트가 겹쳐서 일치하는 이유는, 멋지게 구조화된 JSON document가, index에서는 아래처럼, 단순한 key-value 형식으로 평평하게 되어 있기 때문이다.

{
  "title":            [ eggs, nest ],
  "body":             [ making, money, work, your ],
  "tags":             [ cash, shares ],
  "comments.name":    [ alice, john, smith, white ],
  "comments.comment": [ article, great, like, more, please, this ],
  "comments.age":     [ 28, 31 ],
  "comments.stars":   [ 4, 5 ],
  "comments.date":    [ 2014-09-01, 2014-10-22 ]
}

The correlation between Alice and 31, or between John and 2014-09-01, has been irretrievably lost. While fields of type object (see Multilevel Objects) are useful for storing a single object, they are useless, from a search point of view, for storing an array of objects.

Alice 와 31 또는 John 과 2014-09-01 사이의 연관성을 완전히 잃어버렸다. object type의 field(Multilevel Objects 참고)는 단일 오브젝트를 저장하는 경우에는 유용하지만, 검색의 관점에서 보면, 오브젝트의 배열을 저장하는 경우에는 쓸모가 없다.

This is the problem that nested objects are designed to solve. By mapping the comments field as type nested instead of type object, each nested object is indexed as a hidden separate document, something like this:

nested object 는 이 문제를 풀기 위해 설계되었다. comments field를 object 대신에, nested type으로 mapping하면, 각각의 nested object는, 아래처럼, 숨겨진 개별 document 로 색인된다.

{ 
  "comments.name":    [ john, smith ],
  "comments.comment": [ article, great ],
  "comments.age":     [ 28 ],
  "comments.stars":   [ 4 ],
  "comments.date":    [ 2014-09-01 ]
}
{ 
  "comments.name":    [ alice, white ],
  "comments.comment": [ like, more, please, this ],
  "comments.age":     [ 31 ],
  "comments.stars":   [ 5 ],
  "comments.date":    [ 2014-10-22 ]
}
{ 
  "title":            [ eggs, nest ],
  "body":             [ making, money, work, your ],
  "tags":             [ cash, shares ]
}

첫 번째 nested object

두 번째 nested object

root 또는 부모 document

By indexing each nested object separately, the fields within the object maintain their relationships. We can run queries that will match only if the match occurs within the same nested object.

각각의 nested object를 별도로 색인함으로써, object 내의 field들은 그들 사이의 관계를 유지한다. 동일한 nested object 내에서 일치하는 경우에만, 일치하도록 query를 실행할 수 있다.

Not only that, because of the way that nested objects are indexed, joining the nested documents to the root document at query time is fast—almost as fast as if they were a single document.

뿐만 아니라, nested object가 색인되는 방식 때문에, 검색 시에 nested document를 root document에 join하는 것도 빠르다. 거의 단일 document인 것처럼 빠르다.

These extra nested documents are hidden; we can’t access them directly. To update, add, or remove a nested object, we have to reindex the whole document. It’s important to note that, the result returned by a search request is not the nested object alone; it is the whole document.

이렇게 추가된 nested document는 숨겨져 있다. 그것을 직접 액세스할 수 없다. nested object를 추가, 업데이트, 삭제하기 위해서는, 전체 document를 재색인해야 한다. 중요한 것은, 검색 request에 의해 반환되는 결과는, nested object 단독이 아닌, 전체 document이다.

'2.X > 6. Modeling Your Data' 카테고리의 다른 글

6-1-4. Denormalization and Concurrency  (0) 2017.09.23
6-1-5. Solving Concurrency Issues  (0) 2017.09.23
6-2-1. Nested Object Mapping  (0) 2017.09.23
6-2-2. Querying a Nested Object  (0) 2017.09.23
6-2-3. Sorting by Nested Fields  (0) 2017.09.23