@@ -76,7 +76,7 @@ def merge_to_operations(data: Dict) -> List:
76
76
nested_operations = merge_to_operations (value )
77
77
78
78
for nested_operation in nested_operations :
79
- nested_operation .path = f"{ key } . { nested_operation .path } "
79
+ nested_operation .path = f"{ key } / { nested_operation .path } "
80
80
operations .append (nested_operation )
81
81
82
82
else :
@@ -90,6 +90,7 @@ def check_commands(
90
90
op : str ,
91
91
path : ElasticPath ,
92
92
from_path : bool = False ,
93
+ create_nest : bool = False ,
93
94
) -> None :
94
95
"""Add Elasticsearch checks to operation.
95
96
@@ -101,25 +102,44 @@ def check_commands(
101
102
102
103
"""
103
104
if path .nest :
104
- commands .add (
105
- f"if (!ctx._source.containsKey('{ path .nest } '))"
106
- f"{{Debug.explain('{ path .nest } does not exist');}}"
107
- )
108
-
109
- if path .index or op in ["remove" , "replace" , "test" ] or from_path :
110
- commands .add (
111
- f"if (!ctx._source{ path .es_nest } .containsKey('{ path .key } '))"
112
- f"{{Debug.explain('{ path .key } does not exist in { path .nest } ');}}"
113
- )
114
-
115
- if from_path and path .index is not None :
116
- commands .add (
117
- f"if ((ctx._source{ path .es_location } instanceof ArrayList"
118
- f" && ctx._source{ path .es_location } .size() < { path .index } )"
119
- f" || (!(ctx._source{ path .es_location } instanceof ArrayList)"
120
- f" && !ctx._source{ path .es_location } .containsKey('{ path .index } ')))"
121
- f"{{Debug.explain('{ path .path } does not exist');}}"
122
- )
105
+ part_nest = ""
106
+ for index , path_part in enumerate (path .parts ):
107
+
108
+ # Create nested dictionaries if not present for merge operations
109
+ if create_nest and not from_path :
110
+ value = "[:]"
111
+ for sub_part in reversed (path .parts [index + 1 :]):
112
+ value = f"['{ sub_part } ': { value } ]"
113
+
114
+ commands .add (
115
+ f"if (!ctx._source{ part_nest } .containsKey('{ path_part } '))"
116
+ f"{{ctx._source{ part_nest } ['{ path_part } '] = { value } ;}}"
117
+ f"{ '' if index == len (path .parts ) - 1 else ' else ' } "
118
+ )
119
+
120
+ else :
121
+ commands .add (
122
+ f"if (!ctx._source{ part_nest } .containsKey('{ path_part } '))"
123
+ f"{{Debug.explain('{ path_part } in { path .path } does not exist');}}"
124
+ )
125
+
126
+ part_nest += f"['{ path_part } ']"
127
+
128
+ if from_path or op in ["remove" , "replace" , "test" ]:
129
+
130
+ if isinstance (path .key , int ):
131
+ commands .add (
132
+ f"if ((ctx._source{ path .es_nest } instanceof ArrayList"
133
+ f" && ctx._source{ path .es_nest } .size() < { abs (path .key )} )"
134
+ f" || (!(ctx._source{ path .es_nest } instanceof ArrayList)"
135
+ f" && !ctx._source{ path .es_nest } .containsKey('{ path .key } ')))"
136
+ f"{{Debug.explain('{ path .key } does not exist in { path .nest } ');}}"
137
+ )
138
+ else :
139
+ commands .add (
140
+ f"if (!ctx._source{ path .es_nest } .containsKey('{ path .key } '))"
141
+ f"{{Debug.explain('{ path .key } does not exist in { path .nest } ');}}"
142
+ )
123
143
124
144
125
145
def remove_commands (commands : ESCommandSet , path : ElasticPath ) -> None :
@@ -130,15 +150,16 @@ def remove_commands(commands: ESCommandSet, path: ElasticPath) -> None:
130
150
path (ElasticPath): Path to value to be removed
131
151
132
152
"""
133
- if path .index is not None :
153
+ commands .add (f"def { path .variable_name } ;" )
154
+ if isinstance (path .key , int ):
134
155
commands .add (
135
- f"def { path .variable_name } = ctx._source{ path .es_location } .remove({ path .index } );"
156
+ f"if (ctx._source{ path .es_nest } instanceof ArrayList)"
157
+ f"{{{ path .variable_name } = ctx._source{ path .es_nest } .remove({ path .es_key } );}} else "
136
158
)
137
159
138
- else :
139
- commands .add (
140
- f"def { path .variable_name } = ctx._source{ path .es_nest } .remove('{ path .key } ');"
141
- )
160
+ commands .add (
161
+ f"{ path .variable_name } = ctx._source{ path .es_nest } .remove('{ path .key } ');"
162
+ )
142
163
143
164
144
165
def add_commands (
@@ -160,21 +181,22 @@ def add_commands(
160
181
value = (
161
182
from_path .variable_name
162
183
if operation .op == "move"
163
- else f"ctx._source. { from_path .es_path } "
184
+ else f"ctx._source{ from_path .es_path } "
164
185
)
186
+
165
187
else :
166
188
value = f"params.{ path .param_key } "
167
189
params [path .param_key ] = operation .value
168
190
169
- if path .index is not None :
191
+ if isinstance ( path .key , int ) :
170
192
commands .add (
171
- f"if (ctx._source{ path .es_location } instanceof ArrayList)"
172
- f"{{ctx._source{ path .es_location } .{ 'add' if operation .op in ['add' , 'move' ] else 'set' } ({ path .index } , { value } )}}"
173
- f"else{{ ctx._source. { path .es_path } = { value } }} "
193
+ f"if (ctx._source{ path .es_nest } instanceof ArrayList)"
194
+ f"{{ctx._source{ path .es_nest } .{ 'add' if operation .op in ['add' , 'move' ] else 'set' } ({ path .es_key } , { value } ); }}"
195
+ f" else ctx._source{ path . es_nest } [' { path .es_key } '] = { value } ; "
174
196
)
175
197
176
198
else :
177
- commands .add (f"ctx._source. { path .es_path } = { value } ;" )
199
+ commands .add (f"ctx._source{ path .es_path } = { value } ;" )
178
200
179
201
180
202
def test_commands (
@@ -190,14 +212,23 @@ def test_commands(
190
212
value = f"params.{ path .param_key } "
191
213
params [path .param_key ] = operation .value
192
214
215
+ if isinstance (path .key , int ):
216
+ commands .add (
217
+ f"if (ctx._source{ path .es_nest } instanceof ArrayList)"
218
+ f"{{if (ctx._source{ path .es_nest } [{ path .es_key } ] != { value } )"
219
+ f"{{Debug.explain('Test failed `{ path .path } `"
220
+ f" != ' + ctx._source{ path .es_path } );}}"
221
+ f"}} else "
222
+ )
223
+
193
224
commands .add (
194
- f"if (ctx._source. { path .es_path } != { value } )"
195
- f"{{Debug.explain('Test failed `{ path .path } ` | "
196
- f"{ operation . json_value } != ' + ctx._source. { path .es_path } );}}"
225
+ f"if (ctx._source{ path .es_path } != { value } )"
226
+ f"{{Debug.explain('Test failed `{ path .path } `"
227
+ f" != ' + ctx._source{ path .es_path } );}}"
197
228
)
198
229
199
230
200
- def operations_to_script (operations : List ) -> Dict :
231
+ def operations_to_script (operations : List , create_nest : bool = False ) -> Dict :
201
232
"""Convert list of operation to painless script.
202
233
203
234
Args:
@@ -215,10 +246,16 @@ def operations_to_script(operations: List) -> Dict:
215
246
ElasticPath (path = operation .from_ ) if hasattr (operation , "from_" ) else None
216
247
)
217
248
218
- check_commands (commands = commands , op = operation .op , path = path )
249
+ check_commands (
250
+ commands = commands , op = operation .op , path = path , create_nest = create_nest
251
+ )
219
252
if from_path is not None :
220
253
check_commands (
221
- commands = commands , op = operation .op , path = from_path , from_path = True
254
+ commands = commands ,
255
+ op = operation .op ,
256
+ path = from_path ,
257
+ from_path = True ,
258
+ create_nest = create_nest ,
222
259
)
223
260
224
261
if operation .op in ["remove" , "move" ]:
0 commit comments