Как получить последнюю запись массива с помощью Elasticsearch и PHP

#php #laravel #elasticsearch

#php #laravel #elasticsearch

Вопрос:

Я использую elasticsearch in my laravel -app и пытаюсь использовать range -query . У меня есть массив компаний, в которых в разные периоды было разное количество сотрудников, но меня интересует только самый новый период, который в данном случае означает последний элемент массива employees .

итак, в основном массив выглядит так:

 "company" => [
   "name" => "some company",
   "company_number" => "1234567",
   "status" => "normal",
   "employees" => [
      "period_1" => [
         "amount" => 10
       ],
       "period_2" => [
         "amount" => 15
       ],
       "period_3" => [
         "amount" => 24
       ],
       etc etc...
    ]
 ]
 

итак, во внешнем интерфейсе вы можете ввести минимальное и максимальное значение для поиска компаний с определенным количеством сотрудников. Затем в моем контроллере я делаю это:

 "query":{
    "bool": {
        "should" : [
          { "match" : { "company.status" : "normal" },
          {
           "range": {
              "company.employees": { // I WANT THE LAST ITEM FROM THIS ARRAY
                 "gte": "'. $min . '",
                 "lt" : "'.$max .'"
               }
            }
          }
        ]
    }
}
 

Это в основном работает, но, конечно, не дает мне последнюю запись массива employees.

Как я могу это решить? Пожалуйста, помогите…

Обновить

хорошо, теперь я добавил код, который был предложен:

   "query": {
      "bool": {
        "should" : [
          { "match" : { "company.status" : "normal" },
          {
           "range": {
              "company.employees": { // I WANT THE LAST ITEM FROM THIS ARRAY
                 "gte": "'. $min . '",
                 "lt" : "'.$max .'"
               }
            }
          }
        ]
      },
      "script": {
           "source": """        
                def period_keys = new ArrayList(ctx._source.company.employees.keySet());
                Collections.sort(period_keys);
                Collections.reverse(period_keys);
                
                def latest_period = period_keys[0];
                def latest_amount = ctx._source.company.employees[latest_period].amount;
                
                ctx._source.company.current_employees = ["period": latest_period, "amount": latest_amount];
                """
            }
        }
    }
 

Но я получаю сообщение об ошибке: Unexpected character ('{' (code 123)): was expecting comma to separate Object entries

Поскольку я все еще учусь, я должен сказать, я понятия не имею, что происходит, и сообщения об ошибках от Elasticsearch ужасны.

В любом случае, у кого-нибудь есть подсказка? Заранее спасибо

Ответ №1:

Поиск чего-то подобного во время выполнения довольно сложен и недостаточно оптимизирован. Вот альтернатива.

Я предполагаю, что количество сотрудников данной компании меняется не так часто — это означает, что когда они меняются (т. Е. Вы Обновляете этот документ), Вы можете запустить следующий _update_by_query скрипт, чтобы получить информацию о сотрудниках за последний период и сохранить ее на уровне компании, оставив раздел сотрудников нетронутым:

 POST companies_index/_update_by_query
{
  "query": {
    "match_all": {}
  },
  "script": {
    "source": """        
      def period_keys = new ArrayList(ctx._source.company.employees.keySet());
      Collections.sort(period_keys);
      Collections.reverse(period_keys);
      
      def latest_period = period_keys[0];
      def latest_amount = ctx._source.company.employees[latest_period].amount;
      
      ctx._source.company.current_employees = ['period': latest_period, 'amount': latest_amount];
    """
  }
}
 

Однострочный:

 POST companies_index/_update_by_query
{"query":{"match_all":{}},"script":{"source":"      def period_keys = new ArrayList(ctx._source.company.employees.keySet());n      Collections.sort(period_keys);n      Collections.reverse(period_keys);n      n      def latest_period = period_keys[0];n      def latest_amount = ctx._source.company.employees[latest_period].amount;n      n      ctx._source.company.current_employees = ['period': latest_period, 'amount': latest_amount];"}}
 

Обратите внимание, что когда приведенный выше запрос пуст, скрипт будет применяться ко всем документам в вашем индексе. Но, конечно, вы могли бы ограничить это только одной компанией.

После этого вызова ваши документы будут выглядеть следующим образом:

 {
  "company" : {
    "company_number" : "1234567",
    "name" : "some company",
    "current_employees" : {        <---
      "period" : "period_3",
      "amount" : 24
    },
    "employees" : {
      ...
    },
    ...
  }
}
 

и запрос диапазона сверху становится проще простого:

   ...
  "range": {
    "company.current_employees.amount": {     <--
       "gte": "'. $min . '",
       "lt" : "'.$max .'"
     }
  ...
 

Кстати, я также предположил, что ключи периода могут быть отсортированы в алфавитном порядке, но если они содержат даты, скрипту потребуется корректировка в виде компаратора для анализа даты.

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

1. Хм, script вещь возвращается Unexpected character ('"' (code 34)): was expecting comma to separate Object entries … :-/

2. Обновите мой ответ — тройные кавычки можно использовать только в kibana; в противном случае их нужно экранировать…

3. Странно. Вы уверены, что запустили один лайнер?

4. Да, а также «скрипт» должен быть внутри «запроса»…

5. Нет, потому что это _update_by_query не _search так.