Как отфильтровать совпадение в top 3 — elasticsearch?

#elasticsearch #elasticsearch-5 #elasticsearch-aggregation #elasticsearch-dsl

#elasticsearch #elasticsearch-5 #elasticsearch-агрегация #elasticsearch-dsl

Вопрос:

У меня есть следующие данные в elasticsearch

 {
    "_index": "media",
    "_type": "information",
    "_id": "6838",
    "_source": {
        "demographics_countries": {
            "AE": 0.17543859649122806,
            "CA": 0.013157894736842105,
            "FR": 0.017543859649122806,
            "GB": 0.043859649122807015,
            "IT": 0.02631578947368421,
            "LB": 0.013157894736842105,
            "SA": 0.49122807017543857,
            "TR": 0.017543859649122806,
            "US": 0.09210526315789472
        }
    }
},
{
    "_index": "media",
    "_type": "information",
    "_id": "57696",
    "_source": {
        "demographics_countries": {
            "TN": 0.8125,
            "MA": 0.034375,
            "DZ": 0.032812,
            "FR": 0.0125,
            "EG": 0.0125,
            "IN": 0.009375,
            "SA": 0.009375
    }
}
]
 

Ожидаемый результат:

Найдите документ, в котором определенная страна SA (Саудовская Аравия) входит в топ-3 в demographics_countries

Например:

«_id»: «6838» (первый документ) соответствует, потому SA что (Саудовская Аравия) входит в топ demographics_countries -3 в вышеупомянутом примере документа.

Пробовал ?: Я пытался фильтровать с помощью top_hits, но это работает не так, как ожидалось.

Любое предложение будет благодарно

Комментарии:

1. Совершенно непонятно, чего вы пытаетесь здесь добиться. Какова ваша логика и что вы пытаетесь извлечь?

2. @Evaldas Buinauskas Спасибо. Имея документ в социальных сетях со списком демографических стран, мы должны выяснить, что конкретная страна входит в топ-3 в каждом документе. Например, конкретная страна указана как «SA», что является коротким кодом для Саудовской Аравии. Пожалуйста, дайте мне знать, если что-то нужно уточнить

Ответ №1:

С текущей моделью данных это довольно сложно сделать. То, что я бы предложил, может быть не самым простым способом сделать это, но в конечном итоге это определенно будет самый быстрый запрос.

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

 [
  {
    "_index": "media",
    "_type": "information",
    "_id": "6838",
    "_source": {
      "top_demographics_countries": ["TN", "MA", "DZ"],
      "demographics_countries": {
        "AE": 0.17543859649122806,
        "CA": 0.013157894736842105,
        "FR": 0.017543859649122806,
        "GB": 0.043859649122807015,
        "IT": 0.02631578947368421,
        "LB": 0.013157894736842105,
        "SA": 0.49122807017543857,
        "TR": 0.017543859649122806,
        "US": 0.09210526315789472
      }
    }
  },
  {
    "_index": "media",
    "_type": "information",
    "_id": "57696",
    "_source": {
      "top_demographics_countries": ["TN", "MA", "DZ"],
      "demographics_countries": {
        "TN": 0.8125,
        "MA": 0.034375,
        "DZ": 0.032812,
        "FR": 0.0125,
        "EG": 0.0125,
        "IN": 0.009375,
        "SA": 0.009375
      }
    }
  }
]
 

Игнорируйте значения, которые я выбрал top_demographics_countries . При таком подходе вы всегда можете предварительно вычислить top, а затем использовать простой запрос терминов, чтобы проверить, содержит ли документ это значение или нет:

 {
  "query": {
    "bool": {
      "filter": {
        "term": {
          "top_demographics_countries": "SA"
        }
      }
    }
  }
}
 

Будет дешевле вычислять их один раз во время сохранения по сравнению с постоянным динамическим построением этого предложения.

Ответ №2:

@Evaldas прав — лучше заранее извлечь топ-3.

Но если вы ничего не можете с собой поделать и чувствуете себя вынужденным использовать java / безболезненно, вот один из подходов:

 {
  "query": {
    "bool": {
      "must": [
        {
          "exists": {
            "field": "demographics_countries.SA"
          }
        },
        {
          "script": {
            "script": {
              "source": """
                def tuple_list = new ArrayList();                
                
                for (def c : params.all_countries) {
                  def key = 'demographics_countries.' c;
                  if (!doc.containsKey(key) || doc[key].size() == 0) {
                    continue;
                  }
                  def val = doc[key].value;
                  tuple_list.add([c, val]);
                }
                
                // sort tuple list by the country values
                Collections.sort(tuple_list, (arr1, arr2) -> arr1[1] < arr2[1] ? 1 : -1);
                
                // slice amp; take only the top 3        
                def top_3_countries = tuple_list.subList(0, 3).stream().map(arr -> arr[0]).collect(Collectors.toList());
                
                return top_3_countries.size() >=3 amp;amp; top_3_countries.contains(params.country_of_interest);
              """,
              "params": {
                "country_of_interest": "SA",
                "all_countries": [
                  "AE",
                  "CA",
                  "FR",
                  "GB",
                  "IT",
                  "LB",
                  "SA",
                  "TR",
                  "US",
                  "TN",
                  "MA",
                  "DZ",
                  "EG",
                  "IN"
                ]
              }
            }
          }
        }
      ]
    }
  }
}