1
+ <?php
2
+
3
+ namespace ScoutElastic \Console ;
4
+
5
+ use Exception ;
6
+ use Illuminate \Console \Command ;
7
+ use Illuminate \Support \Facades \Artisan ;
8
+ use ScoutElastic \Console \Features \requiresModelArgument ;
9
+ use ScoutElastic \Facades \ElasticClient ;
10
+ use ScoutElastic \Migratable ;
11
+ use ScoutElastic \Payloads \IndexPayload ;
12
+ use ScoutElastic \Payloads \RawPayload ;
13
+ use Symfony \Component \Console \Input \InputArgument ;
14
+
15
+ class ElasticMigrateCommand extends Command
16
+ {
17
+ use requiresModelArgument {
18
+ requiresModelArgument::getArguments as private modelArgument;
19
+ }
20
+
21
+ protected $ name = 'elastic:migrate ' ;
22
+
23
+ protected $ description = 'Migrate model to another index ' ;
24
+
25
+ protected function getArguments ()
26
+ {
27
+ $ arguments = $ this ->modelArgument ();
28
+
29
+ $ arguments [] = ['target-index ' , InputArgument::REQUIRED , 'The index name to migrate ' ];
30
+
31
+ return $ arguments ;
32
+ }
33
+
34
+ protected function isTargetIndexExists ()
35
+ {
36
+ $ targetIndex = $ this ->argument ('target-index ' );
37
+
38
+ $ payload = (new RawPayload ())
39
+ ->set ('index ' , $ targetIndex )
40
+ ->get ();
41
+
42
+ return ElasticClient::indices ()
43
+ ->exists ($ payload );
44
+ }
45
+
46
+ protected function createTargetIndex ()
47
+ {
48
+ $ targetIndex = $ this ->argument ('target-index ' );
49
+
50
+ $ sourceIndexConfigurator = $ this ->getModel ()
51
+ ->getIndexConfigurator ();
52
+
53
+ $ payload = (new RawPayload ())
54
+ ->set ('index ' , $ targetIndex )
55
+ ->setIfNotEmpty ('body.settings ' , $ sourceIndexConfigurator ->getSettings ())
56
+ ->setIfNotEmpty ('body.mappings._default_ ' , $ sourceIndexConfigurator ->getDefaultMapping ())
57
+ ->get ();
58
+
59
+ ElasticClient::indices ()
60
+ ->create ($ payload );
61
+
62
+ $ this ->info (sprintf (
63
+ 'The index %s was created! ' ,
64
+ $ targetIndex
65
+ ));
66
+ }
67
+
68
+ protected function updateTargetIndex ()
69
+ {
70
+ $ targetIndex = $ this ->argument ('target-index ' );
71
+
72
+ $ sourceIndexConfigurator = $ this ->getModel ()
73
+ ->getIndexConfigurator ();
74
+
75
+ $ targetIndexPayload = (new RawPayload ())
76
+ ->set ('index ' , $ targetIndex )
77
+ ->get ();
78
+
79
+ $ indices = ElasticClient::indices ();
80
+
81
+ try {
82
+ $ indices ->close ($ targetIndexPayload );
83
+
84
+ if ($ settings = $ sourceIndexConfigurator ->getSettings ()) {
85
+ $ targetIndexSettingsPayload = (new RawPayload ())
86
+ ->set ('index ' , $ targetIndex )
87
+ ->set ('body.settings ' , $ settings )
88
+ ->get ();
89
+
90
+ $ indices ->putSettings ($ targetIndexSettingsPayload );
91
+ }
92
+
93
+ if ($ defaultMapping = $ sourceIndexConfigurator ->getDefaultMapping ()) {
94
+ $ targetIndexMappingPayload = (new RawPayload ())
95
+ ->set ('index ' , $ targetIndex )
96
+ ->set ('type ' , '_default_ ' )
97
+ ->set ('body._default_ ' , $ defaultMapping )
98
+ ->get ();
99
+
100
+ $ indices ->putMapping ($ targetIndexMappingPayload );
101
+ }
102
+
103
+ $ indices ->open ($ targetIndexPayload );
104
+ } catch (Exception $ exception ) {
105
+ $ indices ->open ($ targetIndexPayload );
106
+
107
+ throw $ exception ;
108
+ }
109
+
110
+ $ this ->info (sprintf (
111
+ 'The index %s was updated! ' ,
112
+ $ targetIndex
113
+ ));
114
+ }
115
+
116
+ protected function updateTargetIndexMapping ()
117
+ {
118
+ $ sourceModel = $ this ->getModel ();
119
+ $ sourceIndexConfigurator = $ sourceModel ->getIndexConfigurator ();
120
+
121
+ $ targetIndex = $ this ->argument ('target-index ' );
122
+ $ targetType = $ sourceModel ->searchableAs ();
123
+
124
+ $ mapping = array_merge_recursive (
125
+ $ sourceIndexConfigurator ->getDefaultMapping (),
126
+ $ sourceModel ->getMapping ()
127
+ );
128
+
129
+ if (empty ($ mapping )) {
130
+ $ this ->warn (sprintf (
131
+ 'The %s mapping is empty. ' ,
132
+ get_class ($ sourceModel )
133
+ ));
134
+
135
+ return ;
136
+ }
137
+
138
+ $ payload = (new RawPayload ())
139
+ ->set ('index ' , $ targetIndex )
140
+ ->set ('type ' , $ targetType )
141
+ ->set ('body. ' .$ targetType , $ mapping )
142
+ ->get ();
143
+
144
+ ElasticClient::indices ()
145
+ ->putMapping ($ payload );
146
+
147
+ $ this ->info (sprintf (
148
+ 'The %s mapping was updated! ' ,
149
+ $ targetIndex
150
+ ));
151
+ }
152
+
153
+ protected function isAliasExists ($ name )
154
+ {
155
+ $ payload = (new RawPayload ())
156
+ ->set ('name ' , $ name )
157
+ ->get ();
158
+
159
+ return ElasticClient::indices ()
160
+ ->existsAlias ($ payload );
161
+ }
162
+
163
+ protected function deleteAlias ($ name )
164
+ {
165
+ $ indices = ElasticClient::indices ();
166
+
167
+ $ getPayload = (new RawPayload ())
168
+ ->set ('name ' , $ name )
169
+ ->get ();
170
+
171
+ $ aliases = $ indices ->getAlias ($ getPayload );
172
+
173
+ if (empty ($ aliases )) {
174
+ return ;
175
+ }
176
+
177
+ foreach ($ aliases as $ index => $ alias ) {
178
+ $ deletePayload = (new RawPayload ())
179
+ ->set ('index ' , $ index )
180
+ ->set ('name ' , $ name )
181
+ ->get ();
182
+
183
+ $ indices ->deleteAlias ($ deletePayload );
184
+
185
+ $ this ->info (sprintf (
186
+ 'The %s alias for the %s index was deleted. ' ,
187
+ $ name ,
188
+ $ index
189
+ ));
190
+ }
191
+ }
192
+
193
+ protected function createAliasForTargetIndex ($ name )
194
+ {
195
+ $ targetIndex = $ this ->argument ('target-index ' );
196
+
197
+ if ($ this ->isAliasExists ($ name )) {
198
+ $ this ->deleteAlias ($ name );
199
+ }
200
+
201
+ $ payload = (new RawPayload ())
202
+ ->set ('index ' , $ targetIndex )
203
+ ->set ('name ' , $ name )
204
+ ->get ();
205
+
206
+ ElasticClient::indices ()
207
+ ->putAlias ($ payload );
208
+
209
+ $ this ->info (sprintf (
210
+ 'The %s alias for the %s index was created. ' ,
211
+ $ name ,
212
+ $ targetIndex
213
+ ));
214
+ }
215
+
216
+ protected function importDocumentsToTargetIndex ()
217
+ {
218
+ $ sourceModel = $ this ->getModel ();
219
+
220
+ Artisan::call (
221
+ 'scout:import ' ,
222
+ ['model ' => get_class ($ sourceModel )]
223
+ );
224
+ }
225
+
226
+ protected function deleteSourceIndex ()
227
+ {
228
+ $ sourceIndexConfigurator = $ this ->getModel ()
229
+ ->getIndexConfigurator ();
230
+
231
+ $ payload = (new IndexPayload ($ sourceIndexConfigurator ))
232
+ ->get ();
233
+
234
+ ElasticClient::indices ()
235
+ ->delete ($ payload );
236
+
237
+ $ this ->info (sprintf (
238
+ 'The %s index was removed. ' ,
239
+ $ sourceIndexConfigurator ->getName ()
240
+ ));
241
+ }
242
+
243
+ public function handle ()
244
+ {
245
+ $ sourceModel = $ this ->getModel ();
246
+ $ sourceIndexConfigurator = $ sourceModel ->getIndexConfigurator ();
247
+
248
+ if (!in_array (Migratable::class, class_uses_recursive ($ sourceIndexConfigurator ))) {
249
+ $ this ->error (sprintf (
250
+ 'The %s index configurator must use the %s trait. ' ,
251
+ get_class ($ sourceIndexConfigurator ),
252
+ Migratable::class
253
+ ));
254
+
255
+ return ;
256
+ }
257
+
258
+ $ this ->isTargetIndexExists () ? $ this ->updateTargetIndex () : $ this ->createTargetIndex ();
259
+
260
+ $ this ->updateTargetIndexMapping ();
261
+
262
+ $ this ->createAliasForTargetIndex ($ sourceIndexConfigurator ->getWriteAlias ());
263
+
264
+ $ this ->importDocumentsToTargetIndex ();
265
+
266
+ $ this ->deleteSourceIndex ();
267
+
268
+ $ this ->createAliasForTargetIndex ($ sourceIndexConfigurator ->getName ());
269
+
270
+ $ this ->info (sprintf (
271
+ 'The %s model successfully migrated to the %s index. ' ,
272
+ get_class ($ sourceModel ),
273
+ $ this ->argument ('target-index ' )
274
+ ));
275
+ }
276
+ }
0 commit comments