2.X/6. Modeling Your Data

6-1-3. Field Collapsing

drscg 2017. 9. 23. 15:19

A common requirement is the need to present search results grouped by a particular field. We might want to return the most relevant blog posts grouped by the user’s name. Grouping by name implies the need for a terms aggregation. To be able to group on the user’s whole name, the name field should be available in its original not_analyzed form, as explained in Aggregations and Analysis:

특정 field로 그룹화된 검색 결과를 제시하는 것은 일반적인 요구 사항이다. 사용자의 이름으로 그룹화된 가장 관련 있는 블로그 게시물을 검색해 보자. 이름으로 그룹화하는 것은 terms aggregation이 필요하다는 의미이다. 사용자의 전체 이름으로 그룹화하려면, Aggregations and Analysis에서 설명하였듯이, name field는 원래의 not_analyzed 형태로 사용할 수 있어야 한다.

PUT /my_index/_mapping/blogpost
{
  "properties": {
    "user": {
      "properties": {
        "name": { 
          "type": "string",
          "fields": {
            "raw": { 
              "type":  "string",
              "index": "not_analyzed"
            }
          }
        }
      }
    }
  }
}

user.name field는 full-text 검색에 사용된다.

user.name.raw field는 terms aggregation에서 그룹화에 사용된다.

Then add some data:

그 다음에, 데이터를 추가한다.

PUT /my_index/user/1
{
  "name": "John Smith",
  "email": "john@smith.com",
  "dob": "1970/10/24"
}

PUT /my_index/blogpost/2
{
  "title": "Relationships",
  "body": "It's complicated...",
  "user": {
    "id": 1,
    "name": "John Smith"
  }
}

PUT /my_index/user/3
{
  "name": "Alice John",
  "email": "alice@john.com",
  "dob": "1979/01/04"
}

PUT /my_index/blogpost/4
{
  "title": "Relationships are cool",
  "body": "It's not complicated at all...",
  "user": {
    "id": 3,
    "name": "Alice John"
  }
}

Now we can run a query looking for blog posts about relationships, by users called John, and group the results by user, thanks to the top_hits aggregation:

이제 John 이라는 사용자의, relationships 블로그 게시물을 검색하기 위해, query를 실행할 수 있다. 그리고, top_hits aggregation를 이용해, 결과를 사용자별로 그룹화할 수 있다.

GET /my_index/blogpost/_search
{
  "size" : 0, 
  "query": { 
    "bool": {
      "must": [
        { "match": { "title":     "relationships" }},
        { "match": { "user.name": "John"          }}
      ]
    }
  },
  "aggs": {
    "users": {
      "terms": {
        "field":   "user.name.raw",      
        "order": { "top_score": "desc" } 
      },
      "aggs": {
        "top_score": { "max":      { "script":  "_score"           }}, 
        "blogposts": { "top_hits": { "_source": "title", "size": 5 }}  
      }
    }
  }
}

size 로 0 으로 설정하여, 일반적인 검색 hits 를 비활성화하였기 때문에, 관심을 가지고 있는 블로그 게시물은 blogposts aggregation 아래로 반환된다.

query 는 John 이라는 사용자의 relationships 에 대한 블로그 게시물을 반환한다.

terms aggregation은 user.name.raw 값 각각에 대한 bucket을 생성한다.

 

top_score aggregation은, 각 bucket의 상위 score를 가진 document로, users aggregation에서 단어를 정렬한다.

top_hits aggregation은 각 user에 대해 가장 관련 있는 5개의 블로그 게시물의 title field만을 반환한다.

The abbreviated response is shown here:

response는 다음과 같다.

...
"hits": {
  "total":     2,
  "max_score": 0,
  "hits":      [] 
},
"aggregations": {
  "users": {
     "buckets": [
        {
           "key":       "John Smith", 
           "doc_count": 1,
           "blogposts": {
              "hits": { 
                 "total":     1,
                 "max_score": 0.35258877,
                 "hits": [
                    {
                       "_index": "my_index",
                       "_type":  "blogpost",
                       "_id":    "2",
                       "_score": 0.35258877,
                       "_source": {
                          "title": "Relationships"
                       }
                    }
                 ]
              }
           },
           "top_score": { 
              "value": 0.3525887727737427
           }
        },
...

size 를 0 으로 설정했기 때문에, hits 배열은 비어있다.

상위 결과에 나타나는 각 user에 대한 bucket이 있다.

각 user의 bucket 아래에, 해당 사용자에 대한 상위 결과를 포함하는 blogposts.hits 배열이 있다.

user bucket은 사용자의 가장 관련 있는 블로그 게시물에 의해 정렬된다.

Using the top_hits aggregation is the equivalent of running a query to return the names of the users with the most relevant blog posts, and then running the same query for each user, to get their best blog posts. But it is much more efficient.

top_hits aggregation의 사용은 가장 관련 있는 블로그 게시물을 가진 사용자의 이름을 반환하는, query를 실행하고, 그 다음에 해당 사용자 최고의 블로그 게시물을 얻기 위해, 각각의 사용자에 대한 동일한 query를 실행하는 것과 동일하다. 하지만 이것이 훨씬 더 효율적이다.

The top hits returned in each bucket are the result of running a light mini-query based on the original main query. The mini-query supports the usual features that you would expect from search such as highlighting and pagination.

각 bucket에서 반환되는 상위 hits는 원래의 주 query를 기반으로 한, 가벼운 mini-query 의 결과이다. mini-query는 하이라이트와 페이지 계산(pagination)처럼, 검색에서 기대할 수 있는 일반적인 기능을 지원한다.

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

6-1-1. Application-side Joins  (0) 2017.09.23
6-1-2. Denormalizing Your Data  (0) 2017.09.23
6-1-4. Denormalization and Concurrency  (0) 2017.09.23
6-1-5. Solving Concurrency Issues  (0) 2017.09.23
6-2. Nested Objects  (0) 2017.09.23