#elasticsearch #hashmap #pipeline #ingest
#elasticsearch #hashmap #конвейер #ввод
Вопрос:
Используя конвейер ввода, я хочу выполнить итерацию по хэш-карте и удалить подчеркивания из всех строковых значений (где существуют подчеркивания), оставив подчеркивания в ключах нетронутыми. Некоторые значения представляют собой массивы, которые необходимо дополнительно повторить, чтобы выполнить ту же модификацию.
В конвейере я использую функцию для обхода и изменения значений представления коллекции хэш-карты.
PUT /_ingest/pipeline/samples
{
"description": "preprocessing of samples.json",
"processors": [
{
"script": {
"tag": "remove underscore from sample_tags values",
"source": """
void findReplace(Collection collection) {
collection.forEach(element -> {
if (element instanceof String) {
element.replace('_',' ');
} else {
findReplace(element);
}
return true;
})
}
Collection samples = ctx.samples;
samples.forEach(sample -> { //sample.sample_tags is a HashMap
Collection sample_tags = sample.sample_tags.values();
findReplace(sample_tags);
return true;
})
"""
}
}
]
}
Когда я имитирую прием конвейера, я обнаруживаю, что строковые значения не изменяются. Где я ошибаюсь?
POST /_ingest/pipeline/samples/_simulate
{
"docs": [
{
"_index": "samples",
"_id": "xUSU_3UB5CXFr25x7DcC",
"_source": {
"samples": [
{
"sample_tags": {
"Entry_A": [
"A_hyphentated-sample",
"sample1"
],
"Entry_B": "A_multiple_underscore_example",
"Entry_C": [
"sample2",
"another_example_with_underscores"
],
"Entry_E": "last_example"
}
}
]
}
}
]
}
\Result
{
"docs" : [
{
"doc" : {
"_index" : "samples",
"_type" : "_doc",
"_id" : "xUSU_3UB5CXFr25x7DcC",
"_source" : {
"samples" : [
{
"sample_tags" : {
"Entry_E" : "last_example",
"Entry_C" : [
"sample2",
"another_example_with_underscores"
],
"Entry_B" : "A_multiple_underscore_example",
"Entry_A" : [
"A_hyphentated-sample",
"sample1"
]
}
}
]
},
"_ingest" : {
"timestamp" : "2020-12-01T17:29:52.3917165Z"
}
}
}
]
}
Ответ №1:
Вот модифицированная версия вашего скрипта, которая будет работать с предоставленными вами данными:
PUT /_ingest/pipeline/samples
{
"description": "preprocessing of samples.json",
"processors": [
{
"script": {
"tag": "remove underscore from sample_tags values",
"source": """
String replaceString(String value) {
return value.replace('_',' ');
}
void findReplace(Map map) {
map.keySet().forEach(key -> {
if (map[key] instanceof String) {
map[key] = replaceString(map[key]);
} else {
map[key] = map[key].stream().map(this::replaceString).collect(Collectors.toList());
}
});
}
ctx.samples.forEach(sample -> {
findReplace(sample.sample_tags);
return true;
});
"""
}
}
]
}
Результат выглядит следующим образом:
{
"samples" : [
{
"sample_tags" : {
"Entry_E" : "last example",
"Entry_C" : [
"sample2",
"another example with underscores"
],
"Entry_B" : "A multiple underscore example",
"Entry_A" : [
"A hyphentated-sample",
"sample1"
]
}
}
]
}
Комментарии:
1. Хорошо, я не знал, что вы можете заменить на месте. 1
2. Потрясающе, рад, что это помогло!
3. @JoeSorocin обратите внимание, что решение само по себе не заменяет «на месте»,
map[key]
оно по-прежнему переназначается для каждого ключа, как в обычном операторе assign .4. Верно, но findReplace по-прежнему недействителен, не так ли?
5. @JoeSorocin да, я думаю, это зависит от определения «на месте» 🙂 Для меня на месте означает, что вы повторяете сами значения и изменяете их, как если бы у вас был указатель на него, что невозможно в Java (слава богу)
Ответ №2:
Вы были на правильном пути, но работали с копиями значений и не устанавливали измененные значения обратно в контекст документа ctx
, который в конечном итоге возвращается из конвейера. Это означает, что вам нужно будет отслеживать текущие индексы итерации — то есть для списков массивов, а также для хэш-карт и всего, что между ними, — чтобы затем вы могли ориентироваться на позиции полей в глубоко вложенном контексте.
Вот пример, в котором рассматриваются строки и списки массивов (только для строк). Вам нужно будет расширить его для обработки хэш-карт (и других типов), а затем, возможно, извлечь весь процесс в отдельную функцию. Но AFAIK вы не можете возвращать несколько типов данных в Java, поэтому это может быть сложно…
PUT /_ingest/pipeline/samples
{
"description": "preprocessing of samples.json",
"processors": [
{
"script": {
"tag": "remove underscore from sample_tags values",
"source": """
ArrayList samples = ctx.samples;
for (int i = 0; i < samples.size(); i ) {
def sample = samples.get(i).sample_tags;
for (def entry : sample.entrySet()) {
def key = entry.getKey();
def val = entry.getValue();
def replaced_val;
if (val instanceof String) {
replaced_val = val.replace('_',' ');
} else if (val instanceof ArrayList) {
replaced_val = new ArrayList();
for (int j = 0; j < val.length; j ) {
replaced_val.add(val[j].replace('_',' '));
}
}
// else if (val instanceof HashMap) {
// do your thing
// }
// crucial part
ctx.samples[i][key] = replaced_val;
}
}
"""
}
}
]
}