본문 바로가기

Monitoring/Elasticsearch

09. Compound Query

Compound Query
 
앞서 이미 query 의 종류에 대해 살펴보았고 지금까지 본 모든 query 는 leaf query  로 
 
이는 Elasticsearch query 의 기본이 되며 주어진 fields 에 대해
 
값을 검색시 하나의 operation 만 허용한다. ( n field + 1 operation )
 
이에 반해 compound query 란 이런 leaf query 의 조합으로
 
다수의 operation 을 조합하는 것이다.
 
 
 
Query type context vs Filter type context
 
Compound query 는 query type context 와 filter type context 를 구분하여 조합하게 되는데
 
context 는 한글로 표현하자면 "절" 이다.
 
바꿔말하면 query 절과 filter 절이 있는데 이런 절 안에는
 
Elasticsearch 에서 제공하는 query 를 채울 수 있다.
 
query ( queries ) 
 
두 절의 다른 점은 query 절에는 relevant score 를 계산하는 full-text query 로만 구성해야 하며
 
"How well does this document match this query clause"
 
filter 절에 넣은 query 는 작동시 Releavant Score 를 사용하지 않고 단순히 일치 / 불일치만 계산하므로
 
Does this document match this query clause? 
 
filter 절에는 이런 특성을 가지는 term level query 로 구성해야 한다.
 
예를 들어 아래 compound query 는 must 라는 query 절과 filter 라는 filter 절을 포함하며
 
must query 절에는 match 라는 full-text query  ( relevant score 고려한 결과를 내는 쿼리 ) 를 사용하였고
 
filter 절에는 term , range 처럼 term level query 를 사용하였다.
 
GET /my_index/default/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title":   "Search"        }},
        { "match": { "content": "Elasticsearch" }}  
      ],
      "filter": [
        { "term":  { "status": "published" }},
        { "range": { "publish_date": { "gte": "2015-01-01" }}}
      ]
    }
  }
}
 
 
 
 
bool query ( bool )
 
bool query 는 총 4가지 옵션 필터를 제공하는데 must, must_not, should, filter 옵션이다.
 
이중 must, must_not, should 는 query type context 로 구성해야 하며
 
filter 옵션은 filter type context 로 구성해야 한다.
 
각 옵션의 의미는 아래와 같다.
 
must : 해당 절에 지정된 모든 query 결과가  true 가 되어야 가 일치 document 으로 간주한다.
 
must_not : 해당 절에 지정된 모든 query 결과가 false 가 되어야 일치 document 으로 간주한다.
 
should  : 해당 절에 지정된 query 가 많이 일치할 수록 Relevance Score 를 높혀 검색 순위를 높힌다.
 
filter  : 해당 절에 지정된 모든 query 가 true 가 되어야 일치 document 로 간주한다.
          단 이 절은 relevance score 계산에 포함되지 않는 filter type context 이다.
 
 
must, filter
 
이중 먼저 가장 많이 사용되는 must , filter 옵션을 사용해 보자.
 
아래와 같이 테스트 해보면 검색 결과로 ingredients.name 이 parmesan 이 포함되고 
 
perparation_time_minutes 가 15 이하인 행들이 검색될 것이다.
 
GET /recipe/default/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match":{
            "ingredients.name":"parmesan"
          }
        }
      ],
      "filter": [
        {
          "range":{
            "preparation_time_minutes": {
              "lte":15
            }
          }
        }
      ]
    }
  }
}
{
  "took" : 10,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 1.447008,
    "hits" : [
      {
        "_index" : "recipe",
        "_type" : "default",
        "_id" : "1",
        "_score" : 1.447008,
        "_source" : {
          "title" : "....",
          "preparation_time_minutes" : 12,
          "servings" : {
            "min" : 4,
            "max" : 6
          },
          "steps" : [
            "...."
          ],
          "ingredients" : [
           ...
            {
              "name" : "Parmesan cheese"
            }
          ],
          ...
        }
      },
      {
        "_index" : "recipe",
        "_type" : "default",
        "_id" : "10",
        "_score" : 1.0727434,
        "_source" : {
          "title" : "Penne With Hot-As-You-Dare Arrabbiata Sauce",
          "description" : "....",
          "preparation_time_minutes" : 15,
          "servings" : {
            "min" : 4,
            "max" : 4
          },
          "steps" : [
            "....
           ],
          "ingredients" : [
            ...
            {
              "name" : "Finely grated Parmesan cheese",
              "quantity" : "60g"
            },
            {
              "name" : "Minced flat-leaf parsley leaves",
              "quantity" : "Small handful"
            }
          ],
          ...
        }
      },
      {
        "_index" : "recipe",
        "_type" : "default",
        "_id" : "11",
        "_score" : 0.44000342,
        "_source" : {
          "title" : "Spaghetti Puttanesca (Pasta or Spaghetti With Capers, Olives, and Anchovies)",
          "description" : "....",
          "preparation_time_minutes" : 15,
          "servings" : {
            "min" : 2,
            "max" : 3
          },
          "steps" : [
            "...."
          ],
          "ingredients" : [
            ...
            {
              "name" : "Finely grated Pecorino Romano or Parmesan cheese",
              "quantity" : "30g"
            },
            {
              "name" : "Freshly ground black pepper"
            },
            {
              "name" : "Can oil-packed tuna",
              "quantity" : "140g"
            }
          ],
          ...
        }
      }
    ]
  }
}
 
 
must_not
 
여기에 더해 이번에는 tuna 가 포함되지 않는 결과만 보고 싶다고 하면 아래와 같이 하면 되겠다.
 
 GET /recipe/default/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match":{
            "ingredients.name":"parmesan"
          }
        }
      ],
      "must_not": [
        {
          "match":{
            "ingredients.name":"tuna"
          }
        }
      ],
      "filter": [
        {
          "range":{
            "preparation_time_minutes": {
              "lte":15
            }
          }
        }
      ]
    }
  }
}
{
  "took" : 10,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 1.447008,
    "hits" : [
      {
        "_index" : "recipe",
        "_type" : "default",
        "_id" : "1",
        "_score" : 1.447008,
        "_source" : {
          "title" : "Fast and Easy Pasta With Blistered Cherry Tomato Sauce",
          "description" : "..."
          "preparation_time_minutes" : 12,
          "servings" : {
            "min" : 4,
            "max" : 6
          },
          "steps" : [
            "..."
          ],
          "ingredients" : [
            {
              "name" : "Dry pasta",
              "quantity" : "450g"
            },
            {
              "name" : "Kosher salt"
            },
            {
              "name" : "Cloves garlic",
              "quantity" : "4"
            },
            {
              "name" : "Extra-virgin olive oil",
              "quantity" : "90ml"
            },
            {
              "name" : "Cherry tomatoes",
              "quantity" : "750g"
            },
            {
              "name" : "Fresh basil leaves",
              "quantity" : "30g"
            },
            {
              "name" : "Freshly ground black pepper"
            },
            {
              "name" : "Parmesan cheese"
            }
          ],
          "created" : "2017/03/29",
          "ratings" : [
            4.5,
            5.0,
            3.0,
            4.5
          ]
        }
      },
      {
        "_index" : "recipe",
        "_type" : "default",
        "_id" : "10",
        "_score" : 1.0727434,
        "_source" : {
          "title" : "Penne With Hot-As-You-Dare Arrabbiata Sauce",
          "description" : "..."
          "preparation_time_minutes" : 15,
          "servings" : {
            "min" : 4,
            "max" : 4
          },
          "steps" : [
             "..."
          ],
          "ingredients" : [
            {
              "name" : "Kosher salt"
            },
            {
              "name" : "Penne pasta",
              "quantity" : "450g"
            },
            {
              "name" : "Extra-virgin olive oil",
              "quantity" : "3 tablespoons"
            },
            {
              "name" : "Clove garlic",
              "quantity" : "1"
            },
            {
              "name" : "Crushed red pepper"
            },
            {
              "name" : "Can whole peeled tomatoes",
              "quantity" : "400g"
            },
            {
              "name" : "Finely grated Parmesan cheese",
              "quantity" : "60g"
            },
            {
              "name" : "Minced flat-leaf parsley leaves",
              "quantity" : "Small handful"
            }
          ],
          ...
        }
      }
    ]
  }
}
 
 
 
should
 
마지막으로 파슬리를 선호한다고 가정하면 아래와 같이 적용해 보자. 
 
기존 2 개 결과가 서로 순서가 바뀌는 것을 확인할 수 있게 된다.
 
 GET /recipe/default/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match":{
            "ingredients.name":"parmesan"
          }
        }
      ],
      "must_not": [
        {
          "match":{
            "ingredients.name":"tuna"
          }
        }
      ],
      "should": [
        {
          "match":{
            "ingredients.name":"parsley"
          }
        }
      ],
      "filter": [
        {
          "range":{
            "preparation_time_minutes": {
              "lte":15
            }
          }
        }
      ]
    }
  }
}
{
  "took" : 9,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 1.7949219,
    "hits" : [
      {
        "_index" : "recipe",
        "_type" : "default",
        "_id" : "10",
        "_score" : 1.7949219,
        "_source" : {
          "title" : "Penne With Hot-As-You-Dare Arrabbiata Sauce",
          "description" : "..."
          "preparation_time_minutes" : 15,
          "servings" : {
            "min" : 4,
            "max" : 4
          },
          "steps" : [
            "..."
          ],
          "ingredients" : [
            {
              "name" : "Kosher salt"
            },
            {
              "name" : "Penne pasta",
              "quantity" : "450g"
            },
            {
              "name" : "Extra-virgin olive oil",
              "quantity" : "3 tablespoons"
            },
            {
              "name" : "Clove garlic",
              "quantity" : "1"
            },
            {
              "name" : "Crushed red pepper"
            },
            {
              "name" : "Can whole peeled tomatoes",
              "quantity" : "400g"
            },
            {
              "name" : "Finely grated Parmesan cheese",
              "quantity" : "60g"
            },
            {
              "name" : "Minced flat-leaf parsley leaves",
              "quantity" : "Small handful"
            }
          ],
          ...
      },
      {
        "_index" : "recipe",
        "_type" : "default",
        "_id" : "1",
        "_score" : 1.447008,
        "_source" : {
          "title" : "Fast and Easy Pasta With Blistered Cherry Tomato Sauce",
          "description" : "..."
          "preparation_time_minutes" : 12,
          "servings" : {
            "min" : 4,
            "max" : 6
          },
          "steps" : [
            "..."
          ],
          "ingredients" : [
            {
              "name" : "Dry pasta",
              "quantity" : "450g"
            },
            {
              "name" : "Kosher salt"
            },
            {
              "name" : "Cloves garlic",
              "quantity" : "4"
            },
            {
              "name" : "Extra-virgin olive oil",
              "quantity" : "90ml"
            },
            {
              "name" : "Cherry tomatoes",
              "quantity" : "750g"
            },
            {
              "name" : "Fresh basil leaves",
              "quantity" : "30g"
            },
            {
              "name" : "Freshly ground black pepper"
            },
            {
              "name" : "Parmesan cheese"
            }
          ],
          ...
        }
      }
    ]
  }
}
 
디버깅을 위해서는 _name 필드를 사용하면 된다.
 
GET /recipe/default/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match":{
            "ingredients.name":{
              "query":"parmesan",
              "_name":"parmesan_must"
            }
          }
        }
      ],
      "must_not": [
        {
          "match":{
            "ingredients.name":{
              "query":"tuna",
              "_name":"tuna_must_not"
            }
          }
        }
      ],
      "should": [
        {
          "match":{
            "ingredients.name":{
              "query":"parsley",
              "_name":"parsley_should"
            }
          }
        }
      ],
      "filter": [
        {
          "range":{
            "preparation_time_minutes": {
              "lte":15,
              "_name":"preparation_time_filter"
            }
          }
        }
      ]
    }
  }
}
{
  "took" : 10,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 1.7949219,
    "hits" : [
      {
        "_index" : "recipe",
        "_type" : "default",
        "_id" : "10",
        "_score" : 1.7949219,
        "_source" : {
          "title" : "Penne With Hot-As-You-Dare Arrabbiata Sauce",
          "description" : "..."
          "preparation_time_minutes" : 15,
          "servings" : {
            "min" : 4,
            "max" : 4
          },
          "steps" : [
            "..."
          ],
          "ingredients" : [
            {
              "name" : "Kosher salt"
            },
            {
              "name" : "Penne pasta",
              "quantity" : "450g"
            },
            {
              "name" : "Extra-virgin olive oil",
              "quantity" : "3 tablespoons"
            },
            {
              "name" : "Clove garlic",
              "quantity" : "1"
            },
            {
              "name" : "Crushed red pepper"
            },
            {
              "name" : "Can whole peeled tomatoes",
              "quantity" : "400g"
            },
            {
              "name" : "Finely grated Parmesan cheese",
              "quantity" : "60g"
            },
            {
              "name" : "Minced flat-leaf parsley leaves",
              "quantity" : "Small handful"
            }
          ],
          "created" : "2017/04/27",
          "ratings" : [
            1.5,
            2.0,
            4.0,
            3.5,
            3.0,
            5.0,
            1.5
          ]
        },
        "matched_queries" : [
          "parmesan_must",
          "preparation_time_filter",
          "parsley_should"
        ]
      },
      {
        "_index" : "recipe",
        "_type" : "default",
        "_id" : "1",
        "_score" : 1.447008,
        "_source" : {
          "title" : "Fast and Easy Pasta With Blistered Cherry Tomato Sauce",
          "description" : "..."
          "preparation_time_minutes" : 12,
          "servings" : {
            "min" : 4,
            "max" : 6
          },
          "steps" : [
            "..."
            ],
          "ingredients" : [
            {
              "name" : "Dry pasta",
              "quantity" : "450g"
            },
            {
              "name" : "Kosher salt"
            },
            {
              "name" : "Cloves garlic",
              "quantity" : "4"
            },
            {
              "name" : "Extra-virgin olive oil",
              "quantity" : "90ml"
            },
            {
              "name" : "Cherry tomatoes",
              "quantity" : "750g"
            },
            {
              "name" : "Fresh basil leaves",
              "quantity" : "30g"
            },
            {
              "name" : "Freshly ground black pepper"
            },
            {
              "name" : "Parmesan cheese"
            }
          ],
          "created" : "2017/03/29",
          "ratings" : [
            4.5,
            5.0,
            3.0,
            4.5
          ]
        },
        "matched_queries" : [
          "parmesan_must",
          "preparation_time_filter"
        ]
      }
    ]
  }
}
 
 
 
 
weight
 
boost 옵션으로 각 쿼리마다 가산점 비율을 조절할 수 있다. ( 기본값은 1 )
 
1보다 크면 점수가 높게 오르고 1보다 적으면 점수가 낮게 오른다.
 
하지만 2를 준다고해서 점수가 2배로 오르는 것은 아니며, 정규화를 거친 후 적용된다.
 
GET /_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": {
              "query": "test1",
              "boost": 1.2
            }
          }
        },
        {
          "match": {
            "keyword": {
              "query": "test2",
              "boost": 2
            }
          }
        }
      ]
    }
  }
}
 
 

'Monitoring > Elasticsearch' 카테고리의 다른 글

11. Query Result Options  (0) 2020.01.17
10. Joining Query  (0) 2020.01.17
08. Query  (0) 2020.01.17
07. Analyzer  (1) 2020.01.17
06. Mapping  (0) 2020.01.17