Как подсчитать количество уникальных документов по вложенному полю в Elasticsearch?

Я пытаюсь подсчитать документы с уникальным значением вложенного поля (а затем и сами документы). Похоже, получение уникальных документов работает. Но когда я пытаюсь выполнить запрос на count, я получаю следующее сообщение об ошибке:

Подавлено: org.elasticsearch.client.ResponseException: метод [POST], хост [http://localhost:9200] , URI [/ package / _count? Ignore_throttled = true & ignore_unavailable = false & expand_wildcards = open & allow_no_indices = true], строка состояния [HTTP / 1.1 400 Bad Request] {"error": {"root_cause": [{"type": "parsing_exception", " причина ":" запрос не поддерживает [свернуть] "," строка ": 1," столбец ": 216}]," тип ":" parsing_exception "," причина ":" запрос не поддерживает [свернуть] "," line ": 1," col ": 216}," status ": 400}

Код:

        BoolQueryBuilder innerTemplNestedBuilder = QueryBuilders.boolQuery();
        NestedQueryBuilder templatesNestedQuery = QueryBuilders.nestedQuery("attachment", innerTemplNestedBuilder, ScoreMode.None);
        BoolQueryBuilder mainQueryBuilder = QueryBuilders.boolQuery().must(templatesNestedQuery);
        if (!isEmpty(templateName)) {
            innerTemplNestedBuilder.filter(QueryBuilders.termQuery("attachment.name", templateName));
        }
        SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.searchSource()
                    .collapse(new CollapseBuilder("attachment.uuid"))
                    .query(mainQueryBuilder);
    // NEXT LINE CAUSE ERROR
        long count = client.count(new CountRequest("package").source(searchSourceBuilder), RequestOptions.DEFAULT).getCount(); <<<<<<<<<< ERROR HERE
        // THIS WORKS 
        SearchResponse searchResponse = client.search(
                    new SearchRequest(
                            new String[] {"package"},
                            searchSourceBuilder.timeout(new TimeValue(20, TimeUnit.SECONDS)).from(offset).size(limit)
                    ).indices("package").searchType(SearchType.DFS_QUERY_THEN_FETCH),
                    RequestOptions.DEFAULT
        );
        return ....;

Общая цель подхода - получить часть документов и количество всех таких документов. Может быть, для этой потребности уже существует другой подход. Если я пытаюсь получить count с помощью aggregations и cardinality - я получаю нулевой результат, и похоже, что это не работает с вложенными полями.

Запрос на подсчет:

{
    "query": {
        "bool": {
            "must": [
                {
                    "nested": {
                        "query": {
                            "bool": {
                                "adjust_pure_negative": true,
                                "boost": 1.0
                            }
                        },
                        "path": "attachment",
                        "ignore_unmapped": false,
                        "score_mode": "none",
                        "boost": 1.0
                    }
                }
            ],
            "adjust_pure_negative": true,
            "boost": 1.0
        }
    },
    "collapse": {
        "field": "attachment.uuid"
    }
}

Как создавалось отображение:

curl -X DELETE "localhost:9200/package?pretty"
curl -X PUT    "localhost:9200/package?include_type_name=true&pretty" -H 'Content-Type: application/json' -d '{
    "settings" :  {
        "number_of_shards" : 1,
        "number_of_replicas" : 1
    }}'
curl -X PUT    "localhost:9200/package/_mappings?pretty" -H 'Content-Type: application/json' -d'
{
      "dynamic": false,
      "properties" : {
        "attachment": {
            "type": "nested",
            "properties": {
                "uuid" : { "type" : "keyword" },
                "name" : { "type" : "text" }
            }
        },
        "uuid" : {
          "type" : "keyword"
        }
      }
}
'

запрос результата, сгенерированный кодом, должен быть примерно таким:

curl -X POST "localhost:9200/package/_count?&pretty" -H 'Content-Type: application/json' -d' { "query" :
    {
        "bool": {
            "must": [
                {
                    "nested": {
                        "query": {
                            "bool": {
                                "adjust_pure_negative": true,
                                "boost": 1.0
                            }
                        },
                        "path": "attachment",
                        "ignore_unmapped": false,
                        "score_mode": "none",
                        "boost": 1.0
                    }
                }
            ],
            "adjust_pure_negative": true,
            "boost": 1.0
        }
    },
    "collapse": {
        "field": "attachment.uuid"
    }
}'

person user2220154    schedule 06.03.2020    source источник
comment
Можете ли вы сбросить запрос, который на самом деле создает ваш построитель запросов java (?)? Также были бы полезны образцы ваших документов и карта. Вы не сможете размещать их в комментариях, поэтому просто отредактируйте свой вопрос, пожалуйста.   -  person Joe Sorocin    schedule 06.03.2020
comment
@jzzfs edited - добавлен запрос подсчета и отображение индекса   -  person user2220154    schedule 10.03.2020
comment
@jzzfs обновлено также сообщение об ошибке для более точного   -  person user2220154    schedule 10.03.2020
comment
Если я пытаюсь получить общее количество с помощью _search - все равно получаю несвернутое значение как «общее».   -  person user2220154    schedule 10.03.2020


Ответы (1)


Сворачивание можно только используется в контексте _search, а не в _count.

Во-вторых, что вообще делает ваш запрос? У вас там много избыточных параметров, таких как boost:1 и т. Д. С таким же успехом можно сказать:

POST /package/_count?&pretty
{
  "query": {
    "bool": {
      "must": [
        {
          "nested": {
            "path": "attachment",
            "query": {
              "match_all": {}
            }
          }
        }
      ]
    }
  }
}

который на самом деле ничего не делает :)


Чтобы ответить на ваш исходный вопрос о "подсчете документов с уникальным значением вложенного поля",

представим 3 документа, 2 из которых имеют одинаковое значение attachment.uuid:

[
  {
    "attachment":{
      "uuid":"04144e14-62c3-11ea-bc55-0242ac130003"
    }
  },
  {
    "attachment":{
      "uuid":"04144e14-62c3-11ea-bc55-0242ac130003"
    }
  },
  {
    "attachment":{
      "uuid":"100b9632-62c3-11ea-bc55-0242ac130003"
    }
  }
]

Чтобы получить разбивку terms uuids, запустите

GET package/_search
{
  "size": 0,
  "aggs": {
    "nested_uniques": {
      "nested": {
        "path": "attachment"
      },
      "aggs": {
        "subagg": {
          "terms": {
            "field": "attachment.uuid"
          }
        }
      }
    }
  }
}

который дает

...
{
  "aggregations":{
    "nested_uniques":{
      "doc_count":3,
      "subagg":{
        "doc_count_error_upper_bound":0,
        "sum_other_doc_count":0,
        "buckets":[
          {
            "key":"04144e14-62c3-11ea-bc55-0242ac130003",
            "doc_count":2
          },
          {
            "key":"100b9632-62c3-11ea-bc55-0242ac130003",
            "doc_count":1
          }
        ]
      }
    }
  }
}

Чтобы получить количество уникальных вложенных полей в родительском документе, нам нужно немного поумнее:

GET package/_search
{
  "size": 0,
  "aggs": {
    "nested_uniques": {
      "nested": {
        "path": "attachment"
      },
      "aggs": {
        "scripted_uniques": {
          "scripted_metric": {
            "init_script": "state.my_map = [:];",
            "map_script": """
              if (doc.containsKey('attachment.uuid')) {
                state.my_map[doc['attachment.uuid'].value.toString()] = 1;
              }
            """,
            "combine_script": """
              def sum = 0;
              for (c in state.my_map.entrySet()) {
                sum += 1
              }
              return sum
            """,
            "reduce_script": """
              def sum = 0;
              for (agg in states) {
                sum += agg;
              }
              return sum;
            """
          }
        }
      }
    }
  }
}

который возвращается

...
{
  "aggregations":{
    "nested_uniques":{
      "doc_count":3,
      "scripted_uniques":{
        "value":2
      }
    }
  }
}

и это scripted_uniques: 2 именно то, что вам нужно.


Примечание: я решил этот вариант использования, используя вложенные скриптовые метрики aggs, но если кто-то из вас знает более чистый подход, я более чем счастлив узнать его!

person Joe Sorocin    schedule 10.03.2020
comment
Спасибо, это работает. Не уверен, как это будет работать под нагрузкой на большом количестве документов, но пока это единственный рабочий подход ... - person user2220154; 10.03.2020
comment
Нет проблем. У меня он работает с ~ 500k сильно вложенными документами. Это занимает несколько секунд, но никогда не дает сбоев / тайм-аутов. - person Joe Sorocin; 10.03.2020