@@ -166,6 +166,47 @@ def merge_to_operations(data: Dict) -> List:
166166 return operations
167167
168168
169+ def split_json_path (path : str ) -> OperationPath :
170+ """Split a JSON path into it's components.
171+
172+ Args:
173+ path: JSON path.
174+
175+ Returns:
176+ Tuple: nest, partition, key.
177+ """
178+ path = (
179+ path [1 :].replace ("/" , "." ) if path .startswith ("/" ) else path .replace ("/" , "." )
180+ )
181+ nest , partition , key = path .rpartition ("." )
182+
183+ try :
184+ index = int (key )
185+ path = f"{ nest } [{ index } ]"
186+ nest , partition , key = nest .rpartition ("." )
187+
188+ except ValueError :
189+ index = None
190+
191+ return {
192+ "path" : path ,
193+ "nest" : nest ,
194+ "partition" : partition ,
195+ "key" : key ,
196+ "index" : index ,
197+ }
198+
199+
200+ def script_checks (source , op , path ) -> Dict :
201+ if path ["nest" ]:
202+ source += f"if (!ctx._source.containsKey('{ path ['nest' ]} ')){{Debug.explain('{ path ['nest' ]} does not exist');}}"
203+
204+ if path ["index" ] or op != "add" :
205+ source += f"if (!ctx._source.{ path ['nest' ] + path ['partition' ]} containsKey('{ path ['key' ]} ')){{Debug.explain('{ path ['path' ]} does not exist');}}"
206+
207+ return source
208+
209+
169210def operations_to_script (operations : List ) -> Dict :
170211 """Convert list of operation to painless script.
171212
@@ -177,21 +218,55 @@ def operations_to_script(operations: List) -> Dict:
177218 """
178219 source = ""
179220 for operation in operations :
180- nest , partition , key = operation .path .rpartition ("." )
181- if nest :
182- source += f"if (!ctx._source.containsKey('{ nest } ')){{Debug.explain('{ nest } does not exist');}}"
221+ op_path = split_json_path (operation .path )
222+
223+ if hasattr (operation , "from" ):
224+ from_path = split_json_path (getattr (operation , "from" ))
225+ source = script_checks (source , operation .op , op_path )
226+ if from_path ["index" ]:
227+ from_key = from_path ["nest" ] + from_path ["partition" ] + from_path ["key" ]
228+ source += (
229+ f"if ((ctx._source.{ from_key } instanceof ArrayList && ctx._source.{ from_key } .size() < { from_path ['index' ]} )"
230+ f"|| (!ctx._source.{ from_key + from_path ['partition' ]} containsKey('{ from_path ['index' ]} '))"
231+ f"{{Debug.explain('{ from_path ['path' ]} does not exist');}}"
232+ )
183233
184- if operation .op != "add" :
185- source += f"if (!ctx._source.{ nest + partition } containsKey('{ key } ')){{Debug.explain('{ operation .path } does not exist');}}"
234+ source = script_checks (source , operation .op , op_path )
186235
187236 if operation .op in ["copy" , "move" ]:
188- source += f"ctx._source.{ operation .path } = ctx._source.{ getattr (operation , 'from' )} ;"
237+ if op_path ["index" ]:
238+ source += (
239+ f"if (ctx._source.{ op_path ['nest' ] + op_path ['partition' ] + op_path ['key' ]} instanceof ArrayList)"
240+ f"{{ctx._source.{ op_path ['nest' ] + op_path ['partition' ] + op_path ['key' ] + op_path ['partition' ]} add({ op_path ['index' ]} , { from_path ['path' ]} )}}"
241+ f"else{{ctx._source.{ op_path ['path' ]} = { from_path ['path' ]} }}"
242+ )
243+
244+ else :
245+ source += (
246+ f"ctx._source.{ op_path ['path' ]} = ctx._source.{ from_path ['path' ]} ;"
247+ )
189248
190249 if operation .op in ["remove" , "move" ]:
191- source += f"ctx._source.{ nest + partition } remove('{ key } ');"
250+ remove_path = from_path if operation .op == "move" else op_path
251+
252+ if remove_path ["index" ]:
253+ source += f"ctx._source.{ remove_path ['nest' ] + remove_path ['partition' ] + remove_path ['key' ] + remove_path ['partition' ]} remove('{ remove_path ['index' ]} ');"
254+
255+ else :
256+ source += f"ctx._source.{ remove_path ['nest' ] + remove_path ['partition' ]} remove('{ remove_path ['key' ]} ');"
192257
193258 if operation .op in ["add" , "replace" ]:
194- source += f"ctx._source.{ operation .path } = { json .dumps (operation .value )} ;"
259+ if op_path ["index" ]:
260+ source += (
261+ f"if (ctx._source.{ op_path ['nest' ] + op_path ['partition' ] + op_path ['key' ]} instanceof ArrayList)"
262+ f"{{ctx._source.{ op_path ['nest' ] + op_path ['partition' ] + op_path ['key' ] + op_path ['partition' ]} add({ op_path ['index' ]} , { json .dumps (operation .value )} )}}"
263+ f"else{{ctx._source.{ op_path ['path' ]} = { json .dumps (operation .value )} }}"
264+ )
265+
266+ else :
267+ source += (
268+ f"ctx._source.{ op_path ['path' ]} = { json .dumps (operation .value )} ;"
269+ )
195270
196271 return {
197272 "source" : source ,
0 commit comments