У нас есть большое количество документов в формате JSON, в которых можно найти закономерности и исторические тенденции. Elasticsearch кажется идеальным решением этой проблемы. Первый трюк заключается в том, что документы представляют собой наборы из десятков тысяч «вложенных» документов (с заголовком). Второй трюк заключается в том, что эти вложенные документы представляют данные разных типов.
Чтобы приспособиться к этому, все поля значений были «закодированы» как массив строк, поэтому одно целочисленное значение было сохранено в JSON как «[\"1\"]", а таблица с плавающей запятой сглажена. на "[\"123,45\",\"678,9\",...]" и так далее. (У нас также есть массивы строк, которые не нужно преобразовывать.) Хотя это неудобно, я бы подумал, что это будет хорошим компромиссом, учитывая, как работает все остальное, связанное с Elasticsearch.
Конкретная проблема здесь заключается в том, что эти сохраненные значения данных могут представлять собой битовое поле, из которого нам может потребоваться проверить состояние одного бита. Поскольку это поле будет храниться как одноэлементный строковый массив, например "[\"14657\"], нам нужно преобразовать его в одно целое число, а затем несколько раз сдвинуть его к нужному биту (или применить маску, если такая функция доступна).
С помощью Elasticsearch я вижу, что могу встраивать «безболезненные» скрипты, но примеры различаются, и я не смог найти тот, который показывает, как я могу преобразовать поле данных массива строк произвольной длины в соответствующие типы для дальнейшего сравнения. . Вот мой сценарий запроса в его нынешнем виде.
{
"_source" : false,
"from" : 0, "size" : 10,
"query": {
"nested": {
"path": "Variables",
"query": {
"bool": {
"must": {
"match": {"Variables.Designation": "Big_Long_Variable_Name"}
},
"must_not": {
"match": {"Variables.Data": "[0]"}
},
"filter": {
"script": {
"script": {
"source":
"
def vals = doc['Variables.Data'];
return vals[0] != params.setting;
",
"params": {
"setting": 3
}
}
}
}
}
},
"inner_hits": {
"_source": "Variables.Data"
}
}
}
}
Мне нужно каким-то образом преобразовать переменную vals в массив целых чисел, выбрать первое значение, выполнить некоторые битовые операции и выполнить сравнение, чтобы вернуть true или false. В этом примере я надеюсь, что смогу установить «настройку», равную битовой позиции, которую я хочу проверить на включение/выключение.
Я уже прошел через упражнение с Elasticsearch и обнаружил, что мне нужно сделать поле Variables.Data ключевым словом, чтобы я мог искать в нем определенные значения. Я понимаю, что это отходит от цели Elasticsearch, но я все еще думаю, что это может быть лучшим решением по другим причинам. Я создал новый индекс и повторно импортировал свои тестовые документы, и размер индекса увеличился примерно на 30%. Это компромисс, на который я готов пойти, если я смогу заставить это работать.
Какие инструменты у меня есть в Painless, чтобы заставить это работать? (Или я сумасшедший, чтобы попытаться сделать это с помощью этого инструмента?)
Я бы посоветовал вам кодировать ваши данные в типах, предоставляемых elasticsearch, где это возможно (и даже когда нет), чтобы максимально использовать безболезненно. Например, для битовых строк вы можете закодировать их как массив из 1 и 0 для упрощения работы с Painless.
Безболезненно, на мой взгляд, это еще примитивно. Трудно отлаживать. Это трудно читать. Это трудно поддерживать. И это ужасная идея иметь большие функции в Painless.
Чтобы ответить на ваш вопрос, вам в основном нужно безболезненно проанализировать строку массива и использовать ее в одном из доступных типов данных, чтобы выполнить желаемое сравнение. Например, для списка вы должны использовать что-то вроде функции расколоть, а затем вручную регистрировать каждый элемент в результатах как int, float, string и т. д.
Используйте API-интерфейс выполнения для проверки небольших битов, прежде чем добавлять это в поле сценария:
POST /_scripts/painless/_execute
{
"script": {
"source": """
ArrayList arr = []; //to start with
// use arr.add(INDEX, VALUE) to add after parsing
""",
"params": {
"foo": 100.0,
"bar": 1000.0
}
}
}
С другой стороны, если вы сохраните свои данные в типах данных, предоставленных ElasticSearch (обратите внимание, что ElasticSearch поддерживает сохранение списков внутри документов), то эту задачу будет намного проще выполнить в Painless.
Например, вместо того, чтобы иметь my_doc.foo = "[\"123.45\",\"678.9\",...]" в качестве строки для последующего анализа, почему бы не сохранить ее как собственный список с плавающей запятой, как my_doc .foo = [123,45, 678,9, ...]?
Таким образом, вы избегаете ненужного кода Painless, необходимого для разбора текстового документа.
Еще раз привет. Иногда просто интерфейс Painless-Kibana требует забавных escape-последовательностей... В Painless поля должны обрабатываться так же, как их интерпретирует ElasticSearch. Если это не так, то, возможно, вы можете перепроверить, интерпретирует ли ElasticSearch их так, как вы ожидаете.... Если бы вы могли сделать минимальный пример, который можно быстро протестировать, вы получите гораздо лучшую поддержку здесь, в SO, и определенно сэкономите вам много времени (и волос) специально для Painless. Удачного взлома
Вот странная часть: я изменил преобразованный JSON для вывода правильной, не строковой версии массива. (Теперь это выглядит как
"Data": ["one","two","etc"].) И когда Elasticsearch возвращает «документы», это выглядит как["3"], что мне и нужно, и я ожидаю, что Elastic интерпретирует как массив. Однако, когда Безболезненный имеет дело с этим полем — и вы правы, его трудно отлаживать и определять — он все еще видит"[\"3\"]", что упускает весь смысл изменения моего вывода и переиндексации моих данных. Я думаю, мне просто нужно иметь дело с этим трудным путем на данный момент.