9
9
use Algolia \AlgoliaSearch \Exceptions \ExceededRetriesException ;
10
10
use Algolia \AlgoliaSearch \Helper \AlgoliaHelper ;
11
11
use Algolia \AlgoliaSearch \Helper \ConfigHelper ;
12
+ use Algolia \AlgoliaSearch \Helper \Logger ;
12
13
use Algolia \AlgoliaSearch \Registry \ReplicaState ;
13
14
use Magento \Framework \Exception \LocalizedException ;
14
15
use Magento \Framework \Exception \NoSuchEntityException ;
34
35
class ReplicaManager implements ReplicaManagerInterface
35
36
{
36
37
public const REPLICA_TRANSFORM_MODE_STANDARD = 1 ;
37
- public const REPLICA_TRANSFORM_MODE_VIRTUAL = 2 ;
38
- public const REPLICA_TRANSFORM_MODE_ACTUAL = 3 ;
38
+ public const REPLICA_TRANSFORM_MODE_VIRTUAL = 2 ;
39
+ public const REPLICA_TRANSFORM_MODE_ACTUAL = 3 ;
40
+
41
+ protected const _DEBUG = true ;
39
42
40
43
protected array $ _algoliaReplicaConfig = [];
41
44
protected array $ _magentoReplicaPossibleConfig = [];
42
45
43
46
public function __construct (
44
- protected ConfigHelper $ configHelper ,
47
+ protected ConfigHelper $ configHelper ,
45
48
protected AlgoliaHelper $ algoliaHelper ,
46
- protected ReplicaState $ replicaState
47
- ) {}
49
+ protected ReplicaState $ replicaState ,
50
+ protected Logger $ logger
51
+ )
52
+ {
53
+ }
48
54
49
55
/**
50
56
* Evaluate the replica state of the index for a given store and determine
@@ -54,61 +60,76 @@ public function __construct(
54
60
* @throws NoSuchEntityException
55
61
* @throws LocalizedException
56
62
*/
57
- protected function hasReplicaConfigurationChanged (string $ indexName , int $ storeId ): bool {
58
- $ old = $ this ->getMagentoReplicaConfigurationFromAlgolia ($ indexName , $ storeId );
59
- $ new = $ this ->transformSortingIndicesToReplicaSetting ($ this ->configHelper ->getSortingIndices ($ indexName , $ storeId ));
63
+ protected function hasReplicaConfigurationChanged (string $ primaryIndexName , int $ storeId ): bool
64
+ {
65
+ $ old = $ this ->getMagentoReplicaConfigurationFromAlgolia ($ primaryIndexName , $ storeId );
66
+ $ new = $ this ->transformSortingIndicesToReplicaSetting ($ this ->configHelper ->getSortingIndices ($ primaryIndexName , $ storeId ));
60
67
sort ($ old );
61
68
sort ($ new );
62
69
return $ old !== $ new ;
63
70
}
64
71
65
- protected function getReplicaConfigurationFromAlgolia ($ indexName , bool $ refreshCache = false )
72
+ protected function getReplicaConfigurationFromAlgolia ($ primaryIndexName , bool $ refreshCache = false )
66
73
{
67
- if ($ refreshCache || !isset ($ this ->_algoliaReplicaConfig [$ indexName ])) {
68
- $ currentSettings = $ this ->algoliaHelper ->getSettings ($ indexName );
69
- $ this ->_algoliaReplicaConfig [$ indexName ] = array_key_exists ('replicas ' , $ currentSettings )
74
+ if ($ refreshCache || !isset ($ this ->_algoliaReplicaConfig [$ primaryIndexName ])) {
75
+ $ currentSettings = $ this ->algoliaHelper ->getSettings ($ primaryIndexName );
76
+ $ this ->_algoliaReplicaConfig [$ primaryIndexName ] = array_key_exists ('replicas ' , $ currentSettings )
70
77
? $ currentSettings ['replicas ' ]
71
78
: [];
72
79
}
73
- return $ this ->_algoliaReplicaConfig [$ indexName ];
80
+ return $ this ->_algoliaReplicaConfig [$ primaryIndexName ];
74
81
}
75
82
76
- protected function clearAlgoliaReplicaSettingCache ($ indexName = null ): void
83
+ protected function clearAlgoliaReplicaSettingCache ($ primaryIndexName = null ): void
77
84
{
78
- if (is_null ($ indexName )) {
85
+ if (is_null ($ primaryIndexName )) {
79
86
$ this ->_algoliaReplicaConfig = [];
80
- }
81
- else
82
- {
83
- unset($ this ->_algoliaReplicaConfig [$ indexName ]);
87
+ } else {
88
+ unset($ this ->_algoliaReplicaConfig [$ primaryIndexName ]);
84
89
}
85
90
}
86
91
87
92
/**
88
93
* Obtain the replica configuration from Algolia but only those indices that are
89
94
* relevant to the Magento integration
90
95
*
91
- * @param string $indexName
96
+ * @param string $primaryIndexName
92
97
* @param int $storeId
93
98
* @return string[]
94
99
* @throws LocalizedException
95
100
* @throws NoSuchEntityException
96
101
*/
97
- protected function getMagentoReplicaConfigurationFromAlgolia (string $ indexName , int $ storeId ): array
102
+ protected function getMagentoReplicaConfigurationFromAlgolia (string $ primaryIndexName , int $ storeId ): array
98
103
{
99
- $ algoliaReplicas = $ this ->getReplicaConfigurationFromAlgolia ($ indexName );
100
- $ magentoReplicas = $ this ->getPossibleMagentoReplicaSettings ($ indexName , $ storeId );
104
+ $ algoliaReplicas = $ this ->getReplicaConfigurationFromAlgolia ($ primaryIndexName );
105
+ $ magentoReplicas = $ this ->getPossibleMagentoReplicaSettings ($ primaryIndexName , $ algoliaReplicas );
101
106
return array_values (array_intersect ($ magentoReplicas , $ algoliaReplicas ));
102
107
}
103
108
109
+ /**
110
+ * Replicas will be considered Magento managed if they are prefixed with the primary index name
111
+ * @param string $baseIndexName
112
+ * @param string[] $algoliaReplicas
113
+ * @return string[]
114
+ */
115
+ protected function getPossibleMagentoReplicaSettings (string $ baseIndexName , array $ algoliaReplicas ): array
116
+ {
117
+ return array_filter (
118
+ $ algoliaReplicas ,
119
+ function ($ algoliaReplicaSetting ) use ($ baseIndexName ) {
120
+ return str_starts_with ($ this ->getBareIndexNameFromReplicaSetting ($ algoliaReplicaSetting ), $ baseIndexName );
121
+ }
122
+ );
123
+ }
124
+
104
125
/**
105
126
* @throws NoSuchEntityException
106
127
* @throws LocalizedException
107
128
*/
108
- protected function getNonMagentoReplicaConfigurationFromAlgolia (string $ indexName , int $ storeId ): array
129
+ protected function getNonMagentoReplicaConfigurationFromAlgolia (string $ primaryIndexName , int $ storeId ): array
109
130
{
110
- $ algoliaReplicas = $ this ->getReplicaConfigurationFromAlgolia ($ indexName );
111
- $ magentoReplicas = $ this ->getPossibleMagentoReplicaSettings ($ indexName , $ storeId );
131
+ $ algoliaReplicas = $ this ->getReplicaConfigurationFromAlgolia ($ primaryIndexName );
132
+ $ magentoReplicas = $ this ->getPossibleMagentoReplicaSettings ($ primaryIndexName , $ algoliaReplicas );
112
133
return array_diff ($ algoliaReplicas , $ magentoReplicas );
113
134
}
114
135
@@ -119,17 +140,17 @@ protected function getNonMagentoReplicaConfigurationFromAlgolia(string $indexNam
119
140
*/
120
141
protected function transformSortingIndicesToReplicaSetting (
121
142
array $ sortingIndices ,
122
- int $ mode = self ::REPLICA_TRANSFORM_MODE_ACTUAL
143
+ int $ mode = self ::REPLICA_TRANSFORM_MODE_ACTUAL
123
144
): array
124
145
{
125
146
return array_map (
126
- function ($ sort ) use ($ mode ) {
147
+ function ($ sort ) use ($ mode ) {
127
148
$ replica = $ sort ['name ' ];
128
149
if (
129
150
$ mode === self ::REPLICA_TRANSFORM_MODE_VIRTUAL
130
151
|| array_key_exists ('virtualReplica ' , $ sort )
131
- && $ sort ['virtualReplica ' ]
132
- && $ mode === self ::REPLICA_TRANSFORM_MODE_ACTUAL
152
+ && $ sort ['virtualReplica ' ]
153
+ && $ mode === self ::REPLICA_TRANSFORM_MODE_ACTUAL
133
154
) {
134
155
$ replica = "virtual( $ replica) " ;
135
156
}
@@ -143,21 +164,21 @@ function($sort) use ($mode) {
143
164
* In order to avoid interfering with replicas configured directly in the Algolia dashboard,
144
165
* we must know which replica indices are Magento managed and which are not.
145
166
*
146
- * @param string $indexName
167
+ * @param string $primaryIndexName
147
168
* @param int $storeId
148
169
* @param bool $refreshCache
149
170
* @return array
150
171
* @throws LocalizedException
151
172
* @throws NoSuchEntityException
152
173
*/
153
- protected function getPossibleMagentoReplicaSettings (string $ indexName , int $ storeId , bool $ refreshCache = false ): array
174
+ protected function getPossibleMagentoReplicaSettingsFromConfig (string $ primaryIndexName , int $ storeId , bool $ refreshCache = false ): array
154
175
{
155
176
if ($ refreshCache || !isset ($ this ->_magentoReplicaPossibleConfig [$ storeId ])) {
156
177
//TODO: Determine whether it is necessary to merge the new configuration on an update when checking against Algolia
157
178
$ sortConfig = $ this ->replicaState ->isStateChanged ()
158
179
? array_merge ($ this ->replicaState ->getOriginalSortConfiguration (), $ this ->replicaState ->getUpdatedSortConfiguration ())
159
180
: null ;
160
- $ sortingIndices = $ this ->configHelper ->getSortingIndices ($ indexName , $ storeId , null , $ sortConfig );
181
+ $ sortingIndices = $ this ->configHelper ->getSortingIndices ($ primaryIndexName , $ storeId , null , $ sortConfig );
161
182
$ this ->_magentoReplicaPossibleConfig [$ storeId ] = array_merge (
162
183
$ this ->transformSortingIndicesToReplicaSetting ($ sortingIndices , self ::REPLICA_TRANSFORM_MODE_STANDARD ),
163
184
$ this ->transformSortingIndicesToReplicaSetting ($ sortingIndices , self ::REPLICA_TRANSFORM_MODE_VIRTUAL )
@@ -169,55 +190,80 @@ protected function getPossibleMagentoReplicaSettings(string $indexName, int $sto
169
190
/**
170
191
* @inheritDoc
171
192
*/
172
- public function handleReplicas (string $ indexName , int $ storeId , array $ primaryIndexSettings ): void
193
+ public function handleReplicas (string $ primaryIndexName , int $ storeId , array $ primaryIndexSettings ): void
173
194
{
174
195
// TODO: Determine if InstantSearch is a hard requirement (i.e. headless implementations may still need replicas)
175
196
if ($ this ->configHelper ->isInstantEnabled ($ storeId )
176
- && $ this ->hasReplicaConfigurationChanged ($ indexName , $ storeId ))
177
- {
197
+ && $ this ->hasReplicaConfigurationChanged ($ primaryIndexName , $ storeId )) {
178
198
// TODO: Handle ranking adjustments when toggling virtual vs standard replicas
179
- $ addedReplicas = $ this ->setReplicasOnPrimaryIndex ($ indexName , $ storeId );
180
- $ this ->configureRanking ($ indexName , $ storeId , $ addedReplicas , $ primaryIndexSettings );
199
+ $ addedReplicas = $ this ->setReplicasOnPrimaryIndex ($ primaryIndexName , $ storeId );
200
+ $ this ->configureRanking ($ primaryIndexName , $ storeId , $ addedReplicas , $ primaryIndexSettings );
181
201
}
182
202
}
183
203
184
204
/**
185
- * @param $indexName
205
+ * @param $primaryIndexName
186
206
* @param int $storeId
187
- * @return string[] Replicas added by this operation
207
+ * @return string[] Replicas added or modified by this operation
188
208
* @throws LocalizedException
189
209
* @throws NoSuchEntityException
190
210
* @throws AlgoliaException
191
211
*/
192
212
protected function setReplicasOnPrimaryIndex ($ indexName , int $ storeId ): array
193
213
{
194
214
$ sortingIndices = $ this ->configHelper ->getSortingIndices ($ indexName , $ storeId );
195
- $ newMagentoReplicas = $ this ->transformSortingIndicesToReplicaSetting ($ sortingIndices );
196
- $ oldMagentoReplicas = $ this ->getMagentoReplicaConfigurationFromAlgolia ($ indexName , $ storeId );
197
- $ nonMagentoReplicas = $ this ->getNonMagentoReplicaConfigurationFromAlgolia ($ indexName , $ storeId );
198
- $ oldMagentoReplicaIndices = $ this ->getBareIndexNamesFromReplicaSetting ($ oldMagentoReplicas );
199
- $ newMagentoReplicaIndices = $ this ->getBareIndexNamesFromReplicaSetting ($ newMagentoReplicas );
215
+ $ newMagentoReplicasSetting = $ this ->transformSortingIndicesToReplicaSetting ($ sortingIndices );
216
+ $ oldMagentoReplicasSetting = $ this ->getMagentoReplicaConfigurationFromAlgolia ($ indexName , $ storeId );
217
+ $ nonMagentoReplicasSetting = $ this ->getNonMagentoReplicaConfigurationFromAlgolia ($ indexName , $ storeId );
218
+ $ oldMagentoReplicaIndices = $ this ->getBareIndexNamesFromReplicaSetting ($ oldMagentoReplicasSetting );
219
+ $ newMagentoReplicaIndices = $ this ->getBareIndexNamesFromReplicaSetting ($ newMagentoReplicasSetting );
220
+
200
221
$ replicasToDelete = array_diff ($ oldMagentoReplicaIndices , $ newMagentoReplicaIndices );
201
- // TODO: Refactor for virtual / standard toggle - not just added replica indices require ranking config
202
222
$ replicasToAdd = array_diff ($ newMagentoReplicaIndices , $ oldMagentoReplicaIndices );
203
- $ this ->algoliaHelper ->setSettings ($ indexName , ['replicas ' => array_merge ($ newMagentoReplicas , $ nonMagentoReplicas )]);
223
+ $ replicasToRank = $ this ->getBareIndexNamesFromReplicaSetting (array_diff ($ newMagentoReplicasSetting , $ oldMagentoReplicasSetting ));
224
+ $ replicasToUpdate = array_diff ($ replicasToRank , $ replicasToAdd );
225
+
226
+ $ this ->algoliaHelper ->setSettings (
227
+ $ indexName ,
228
+ ['replicas ' => array_merge ($ newMagentoReplicasSetting , $ nonMagentoReplicasSetting )]
229
+ );
204
230
$ setReplicasTaskId = $ this ->algoliaHelper ->getLastTaskId ();
205
231
$ this ->algoliaHelper ->waitLastTask ($ indexName , $ setReplicasTaskId );
206
232
$ this ->clearAlgoliaReplicaSettingCache ($ indexName );
207
233
$ this ->deleteReplicas ($ replicasToDelete );
208
- return $ replicasToAdd ;
234
+
235
+ if (self ::_DEBUG ) {
236
+ $ this ->logger ->log (
237
+ "Replicas configured on $ indexName for store $ storeId: "
238
+ . count ($ replicasToAdd ) . ' added, '
239
+ . count ($ replicasToUpdate ) . ' updated, '
240
+ . count ($ replicasToDelete ) . ' deleted '
241
+ );
242
+ }
243
+
244
+ // include both added and updated replica indices
245
+ return $ replicasToRank ;
209
246
}
210
247
211
- function getBareIndexNamesFromReplicaSetting (array $ replicas ): array
248
+ /**
249
+ * @param string[] $replicas
250
+ * @return string[]
251
+ */
252
+ protected function getBareIndexNamesFromReplicaSetting (array $ replicas ): array
212
253
{
213
254
return array_map (
214
- function ($ str ) {
215
- return preg_replace ( ' /.*\((.*)\).*/ ' , ' $1 ' , $ str );
255
+ function ($ str ) {
256
+ return $ this -> getBareIndexNameFromReplicaSetting ( $ str );
216
257
},
217
258
$ replicas
218
259
);
219
260
}
220
261
262
+ protected function getBareIndexNameFromReplicaSetting (string $ replicaSetting ): string
263
+ {
264
+ return preg_replace ('/.*\((.*)\).*/ ' , '$1 ' , $ replicaSetting );
265
+ }
266
+
221
267
protected function deleteReplicas (array $ replicasToDelete ): void
222
268
{
223
269
foreach ($ replicasToDelete as $ deletedReplica ) {
@@ -227,7 +273,7 @@ protected function deleteReplicas(array $replicasToDelete): void
227
273
228
274
/**
229
275
* Apply ranking settings to the added replica indices
230
- * @param string $indexName
276
+ * @param string $primaryIndexName
231
277
* @param int $storeId
232
278
* @param string[] $replicas
233
279
* @param array<string, mixed> $primaryIndexSettings
@@ -236,9 +282,9 @@ protected function deleteReplicas(array $replicasToDelete): void
236
282
* @throws LocalizedException
237
283
* @throws NoSuchEntityException
238
284
*/
239
- protected function configureRanking (string $ indexName , int $ storeId , array $ replicas , array $ primaryIndexSettings ): void
285
+ protected function configureRanking (string $ primaryIndexName , int $ storeId , array $ replicas , array $ primaryIndexSettings ): void
240
286
{
241
- $ sortingIndices = $ this ->configHelper ->getSortingIndices ($ indexName , $ storeId );
287
+ $ sortingIndices = $ this ->configHelper ->getSortingIndices ($ primaryIndexName , $ storeId );
242
288
$ replicaDetails = array_filter (
243
289
$ sortingIndices ,
244
290
function ($ replica ) use ($ replicas ) {
0 commit comments