Spec-Zone.ru › Elasticsearch 7
›Elasticsearch Guide [7.17] ›Скрипты

Как писать скрипты

В тех местах Elasticsearch API, где поддерживаются скрипты, синтаксис остаётся одинаковым: вы указываете язык скрипта, предоставляете логику (или исходный код) скрипта и добавляете параметры, которые передаются в скрипт:

  "script": {
    "lang":   "...",
    "source" | "id": "...",
    "params": { ... }
  }
lang
Указывает язык, на котором написан скрипт. По умолчанию это painless.
source, id
Сам скрипт, который вы указываете как source для встроенного скрипта или как id для скрипта, хранящегося в памяти. Используйте API для работы с хранимыми скриптами для создания и управления хранимыми скриптами.
params
Указывает любые именованные параметры, которые передаются в скрипт как переменные. Используйте параметры вместо жёстко заданных значений, чтобы уменьшить время компиляции.

Напишите свой первый скрипт

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

Скрипт Painless структурирован как одна или несколько инструкций и необязательно содержит одну или несколько определённых пользователем функций в начале. Скрипт всегда должен иметь по крайней мере одну инструкцию.

API Painless execute позволяет проверить скрипт с простыми параметрами, определёнными пользователем, и получить результат. Давайте начнём с полного скрипта и рассмотрим его составные части.

Сначала добавим документ с одним полем, чтобы у нас были данные для работы:

PUT my-index-000001/_doc/1
{
  "my_field": 5
}

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

GET my-index-000001/_search
{
  "script_fields": {
    "my_doubled_field": {
      "script": { 
        "source": "doc['my_field'].value * params['multiplier']", 
        "params": {
          "multiplier": 2
        }
      }
    }
  }
}

script объект

script исходный код

script — это стандартный JSON-объект, который определяет скрипты для большинства API в Elasticsearch. Этот объект требует source для определения самого скрипта. Скрипт не указывает язык, поэтому он по умолчанию Painless.

Использование параметров в вашем скрипте

При первом обнаружении Elasticsearch нового скрипта он компилирует скрипт и сохраняет скомпилированную версию в кэше. Компиляция может быть ресурсоёмким процессом. Вместо жёсткого кодирования значений в скрипте передавайте их в качестве именованных params.

Например, в предыдущем скрипте мы могли бы просто жёстко закодировать значения и написать скрипт, который, казалось бы, менее сложный. Мы могли бы просто получить первое значение для my_field, а затем умножить его на 2:

"source": "return doc['my_field'].value * 2"

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

Вместо жёсткого кодирования значений используйте именованные params, чтобы сделать скрипты гибкими и уменьшить время компиляции при выполнении скрипта. Теперь вы можете изменять параметр multiplier, не перекомпилируя скрипт в Elasticsearch.

"source": "doc['my_field'].value * params['multiplier']",
"params": {
  "multiplier": 2
}

По умолчанию вы можете скомпилировать до 150 скриптов за 5 минут. Для контекстов приема данных скорость компиляции скриптов по умолчанию не ограничена.

script.context.field.max_compilations_rate=100/10m

Если вы скомпилируете слишком много уникальных скриптов за короткий промежуток времени, Elasticsearch отклонит новые динамические скрипты с ошибкой circuit_breaking_exception.

Упрощение скрипта

Используя синтаксические возможности, встроенные в Painless, вы можете уменьшить объём кода в своих скриптах и сделать их короче. Вот простой скрипт, который мы можем сделать короче:

GET my-index-000001/_search
{
  "script_fields": {
    "my_doubled_field": {
      "script": {
        "lang":   "painless",
        "source": "return doc['my_field'].value * params.get('multiplier');",
        "params": {
          "multiplier": 2
        }
      }
    }
  }
}

Давайте посмотрим на сокращённую версию скрипта, чтобы увидеть, какие улучшения она включает по сравнению с предыдущей итерацией:

GET my-index-000001/_search
{
  "script_fields": {
    "my_doubled_field": {
      "script": {
        "source": "doc['my_field'].value * params['multiplier']",
        "params": {
          "multiplier": 2
        }
      }
    }
  }
}

В этой версии скрипта удалены несколько компонентов и значительно упрощен синтаксис:

  • Объявление lang. Поскольку Painless является языком по умолчанию, вам не нужно указывать язык, если вы пишете скрипт Painless.
  • Ключевое слово return. Painless автоматически использует последнюю инструкцию в скрипте (где возможно), чтобы получить возвращаемое значение в контексте скрипта, который этого требует.
  • Метод get, который заменён на скобки []. Painless использует сокращение специально для типа Map, что позволяет использовать скобки вместо более громоздкого метода get.
  • Точка с запятой в конце инструкции source. Painless не требует точек с запятой для последней инструкции блока. Однако он требует их в других случаях, чтобы избежать неоднозначности.

Используйте этот сокращённый синтаксис везде, где Elasticsearch поддерживает скрипты, например, при создании динамических полей.

Хранение и извлечение скриптов

Вы можете хранить и извлекать скрипты из состояния кластера с помощью API хранимых скриптов. Хранимые скрипты уменьшают время компиляции и ускоряют поиск.

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

Для создания скрипта используйте API создания хранимого скрипта. Например, следующий запрос создаёт хранимый скрипт под названием calculate-score.

POST _scripts/calculate-score
{
  "script": {
    "lang": "painless",
    "source": "Math.log(_score * 2) + params['my_modifier']"
  }
}

Вы можете извлечь этот скрипт, используя API получения хранимого скрипта.

GET _scripts/calculate-score

Для использования хранимого скрипта в запросе включите скрипт id в объявление script:

GET my-index-000001/_search
{
  "query": {
    "script_score": {
      "query": {
        "match": {
            "message": "some message"
        }
      },
      "script": {
        "id": "calculate-score", 
        "params": {
          "my_modifier": 2
        }
      }
    }
  }
}

id хранимого скрипта

Для удаления хранимого скрипта отправьте запрос API удаления хранимого скрипта.

DELETE _scripts/calculate-score

Обновление документов с помощью скриптов

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

Сначала добавим простой документ:

PUT my-index-000001/_doc/1
{
  "counter" : 1,
  "tags" : ["red"]
}

Для инкремента счётчика вы можете отправить запрос на обновление со следующим скриптом:

POST my-index-000001/_update/1
{
  "script" : {
    "source": "ctx._source.counter += params.count",
    "lang": "painless",
    "params" : {
      "count" : 4
    }
  }
}

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

POST my-index-000001/_update/1
{
  "script": {
    "source": "ctx._source.tags.add(params['tag'])",
    "lang": "painless",
    "params": {
      "tag": "blue"
    }
  }
}

Вы также можете удалить метку из списка меток. Метод remove Java List доступен в Painless. Он принимает индекс элемента, который вы хотите удалить. Чтобы избежать возможной ошибки во время выполнения, необходимо сначала убедиться, что метка существует. Если список содержит дубликаты метки, этот скрипт удаляет только одно вхождение.

POST my-index-000001/_update/1
{
  "script": {
    "source": "if (ctx._source.tags.contains(params['tag'])) { ctx._source.tags.remove(ctx._source.tags.indexOf(params['tag'])) }",
    "lang": "painless",
    "params": {
      "tag": "blue"
    }
  }
}

Вы также можете добавлять и удалять поля из документа. Например, этот скрипт добавляет поле new_field:

POST my-index-000001/_update/1
{
  "script" : "ctx._source.new_field = 'value_of_new_field'"
}

В свою очередь, этот скрипт удаляет поле new_field:

POST my-index-000001/_update/1
{
  "script" : "ctx._source.remove('new_field')"
}

Вместо обновления документа, вы также можете изменить операцию, выполняемую в рамках скрипта. Например, этот запрос удаляет документ, если поле tags содержит green. В противном случае он ничего не делает (noop):

POST my-index-000001/_update/1
{
  "script": {
    "source": "if (ctx._source.tags.contains(params['tag'])) { ctx.op = 'delete' } else { ctx.op = 'none' }",
    "lang": "painless",
    "params": {
      "tag": "green"
    }
  }
}

© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/modules-scripting-using.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API