ElasticSearch — фильтрация, группировка и подсчет результатов для каждой группы

Я новичок в ElasticSearch, и мне нужна помощь в решении следующих проблем:

У меня есть набор документов, который содержит несколько продуктов. Я хочу отфильтровать свойство продукта product_brand по «Apple» и получить количество продуктов, соответствующих фильтру. Однако результат должен быть сгруппирован по идентификатору документа, который также является частью самого документа (test_id).

Пример документа:

"test" : {
   "test_id" : 19988,
   "test_name" : "Test",
},
"products" : [ 
    {
        "product_id" : 1,
        "product_brand" : "Apple"
    }, 
    {
        "product_id" : 2,
        "product_brand" : "Apple"
    }, 
    {
        "product_id" : 3,
        "product_brand" : "Samsung"
    } 
]

Результат должен быть:

{
   "key" : 19988,
   "count" : 2
},

В SQL это будет выглядеть примерно так:

SELECT test_id, COUNT(product_id) 
FROM `test` 
WHERE product_brand = 'Apple'
GROUP BY test_id;

Как я могу этого добиться?


person sleepless    schedule 02.11.2017    source источник


Ответы (1)


Я думаю, что это должно приблизить вас:

GET /test/_search
{
  "_source": {
    "includes": [
      "test.test_id",
      "_score"
    ]
  },
  "query": {
    "function_score": {
      "query": {
        "match": {
          "products.product_brand.keyword": "Apple"
        }
      },
      "functions": [
        {
          "script_score": {
            "script": {
              "source": "def matches=0; def products = params['_source']['products']; for(p in products){if(p.product_brand == params['brand']){matches++;}} return matches;",
              "params": {
                "brand": "Apple"
              }
            }
          }
        }
      ]
    }
  }
}

В этом подходе используется функция function_score, но вы также можете применить ее к полю сценария, если хотите получить другую оценку. Вышеприведенное будет соответствовать только документам, в которых есть дочерний объект продукта с текстом бренда, точно установленным на «Apple».

Вам просто нужно контролировать ввод двух экземпляров apple. В качестве альтернативы вы можете сопоставлять все в запросе function_score и обращать внимание только на оценку. Ваш вывод может выглядеть так:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 2,
    "hits": [
      {
        "_index": "test",
        "_type": "doc",
        "_id": "AV99vrBpgkgblFY6zscA",
        "_score": 2,
        "_source": {
          "test": {
            "test_id": 19988
          }
        }
      }
    ]
  }
}

И сопоставления в индексе, который я использовал, выглядели так:

{
  "test": {
    "mappings": {
      "doc": {
        "properties": {
          "products": {
            "properties": {
              "product_brand": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "product_id": {
                "type": "long"
              }
            }
          },
          "test": {
            "properties": {
              "test_id": {
                "type": "long"
              },
              "test_name": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
person Miek    schedule 02.11.2017
comment
Я забыл об этом, но я надеялся, что это может быть проще. Но в любом случае, хороший ответ. Благодарю вас! - person sleepless; 17.07.2018