@@ -76,7 +76,7 @@ def merge_to_operations(data: Dict) -> List:
7676 nested_operations = merge_to_operations (value )
7777
7878 for nested_operation in nested_operations :
79- nested_operation .path = f"{ key } . { nested_operation .path } "
79+ nested_operation .path = f"{ key } / { nested_operation .path } "
8080 operations .append (nested_operation )
8181
8282 else :
@@ -90,6 +90,7 @@ def check_commands(
9090 op : str ,
9191 path : ElasticPath ,
9292 from_path : bool = False ,
93+ create_nest : bool = False ,
9394) -> None :
9495 """Add Elasticsearch checks to operation.
9596
@@ -101,25 +102,44 @@ def check_commands(
101102
102103 """
103104 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+ )
123143
124144
125145def remove_commands (commands : ESCommandSet , path : ElasticPath ) -> None :
@@ -130,15 +150,16 @@ def remove_commands(commands: ESCommandSet, path: ElasticPath) -> None:
130150 path (ElasticPath): Path to value to be removed
131151
132152 """
133- if path .index is not None :
153+ commands .add (f"def { path .variable_name } ;" )
154+ if isinstance (path .key , int ):
134155 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 "
136158 )
137159
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+ )
142163
143164
144165def add_commands (
@@ -160,21 +181,22 @@ def add_commands(
160181 value = (
161182 from_path .variable_name
162183 if operation .op == "move"
163- else f"ctx._source. { from_path .es_path } "
184+ else f"ctx._source{ from_path .es_path } "
164185 )
186+
165187 else :
166188 value = f"params.{ path .param_key } "
167189 params [path .param_key ] = operation .value
168190
169- if path .index is not None :
191+ if isinstance ( path .key , int ) :
170192 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 } ; "
174196 )
175197
176198 else :
177- commands .add (f"ctx._source. { path .es_path } = { value } ;" )
199+ commands .add (f"ctx._source{ path .es_path } = { value } ;" )
178200
179201
180202def test_commands (
@@ -190,14 +212,23 @@ def test_commands(
190212 value = f"params.{ path .param_key } "
191213 params [path .param_key ] = operation .value
192214
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+
193224 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 } );}}"
197228 )
198229
199230
200- def operations_to_script (operations : List ) -> Dict :
231+ def operations_to_script (operations : List , create_nest : bool = False ) -> Dict :
201232 """Convert list of operation to painless script.
202233
203234 Args:
@@ -215,10 +246,16 @@ def operations_to_script(operations: List) -> Dict:
215246 ElasticPath (path = operation .from_ ) if hasattr (operation , "from_" ) else None
216247 )
217248
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+ )
219252 if from_path is not None :
220253 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 ,
222259 )
223260
224261 if operation .op in ["remove" , "move" ]:
0 commit comments