Skip to content

Commit f0876e4

Browse files
committed
Add batch document handling to solr
1 parent 4aef85a commit f0876e4

File tree

3 files changed

+172
-15
lines changed

3 files changed

+172
-15
lines changed

Diff for: lib/task/search/arSolrPopulateTask.class.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public function execute($arguments = [], $options = [])
3535

3636
new sfDatabaseManager($this->configuration);
3737

38-
$solr = new arSolrPlugin($options);
38+
$solr = QubitSearch::getSolrInstance();
3939

4040
// Index by slug, if specified, or all indexable resources except those with an excluded type
4141
//if ($options['slug']) {

Diff for: plugins/arSolrPlugin/lib/arSolrPlugin.class.php

+133-14
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ class arSolrPlugin extends QubitSearchEngine
4242
*/
4343
protected $enabled = true;
4444

45+
/**
46+
* This array will be used to store documents to add in a batch.
47+
*
48+
* @var array
49+
*/
50+
private $batchAddDocs = [];
51+
52+
/**
53+
* This array will be used to store documents to delete in a batch.
54+
*
55+
* @var array
56+
*/
57+
private $batchDeleteDocs = [];
58+
4559
/**
4660
* Constructor.
4761
*/
@@ -53,6 +67,10 @@ public function __construct(array $options = [])
5367

5468
$this->client = new arSolrClient($this->config['solr']);
5569

70+
// Load batch mode configuration
71+
$this->batchMode = true === $this->config['batch_mode'];
72+
$this->batchSize = $this->config['batch_size'];
73+
5674
$this->initialize();
5775
}
5876

@@ -113,6 +131,55 @@ public function flush()
113131
$this->initialize();
114132
}
115133

134+
/*
135+
* Flush batch of documents if we're in batch mode.
136+
*
137+
* We process additions before deletions to avoid an error due to deleting a
138+
* document that hasn't been created yet.
139+
*/
140+
public function flushBatch()
141+
{
142+
if ($this->batchMode) {
143+
// Batch add documents, if any
144+
if (count($this->batchAddDocs) > 0) {
145+
try {
146+
$response = $this->client->addDocuments($this->batchAddDocs);
147+
148+
if ($response->error) {
149+
$this->log(var_export($response->error, true));
150+
$this->log(json_encode($this->batchAddDocs));
151+
}
152+
} catch (Exception $e) {
153+
// Clear batchAddDocs if something went wrong too
154+
$this->batchAddDocs = [];
155+
156+
throw $e;
157+
}
158+
159+
$this->batchAddDocs = [];
160+
}
161+
162+
// Batch delete documents, if any
163+
if (count($this->batchDeleteDocs) > 0) {
164+
try {
165+
$response = $this->client->deleteDocuments($this->batchDeleteDocs);
166+
167+
if ($response->error) {
168+
$this->log(var_export($response->error, true));
169+
$this->log(json_encode($this->batchDeleteDocs));
170+
}
171+
} catch (Exception $e) {
172+
// Clear batchDeleteDocs if something went wrong too
173+
$this->batchDeleteDocs = [];
174+
175+
throw $e;
176+
}
177+
178+
$this->batchDeleteDocs = [];
179+
}
180+
}
181+
}
182+
116183
/**
117184
* Populate index.
118185
*
@@ -185,6 +252,9 @@ public function populate($options = [])
185252
}
186253
}
187254

255+
// Add the last batch of documents
256+
$this->flushBatch();
257+
188258
$this->addAutoCompleteConfigs();
189259
$this->setAnalyzers();
190260

@@ -229,11 +299,56 @@ public function addDocument($data, $type)
229299
throw new sfException('Failed to parse id field.');
230300
}
231301

232-
$response = $this->client->addDocument([$type => $data]);
302+
if ($this->batchMode) {
303+
// Add this document to the batch add queue
304+
$document = [
305+
$type => $data,
306+
];
307+
array_push($this->batchAddDocs, $document);
308+
309+
// If we have a full batch, send additions and deletions in bulk
310+
if (count($this->batchAddDocs) >= $this->batchSize) {
311+
$this->flushBatch();
312+
}
313+
} else {
314+
$response = $this->client->addDocument([$type => $data]);
315+
316+
if ($response->error) {
317+
$this->log(var_export($response->error, true));
318+
$this->log(json_encode([$type => $data]));
319+
}
320+
}
321+
}
322+
323+
public function delete($object)
324+
{
325+
if (!$this->enabled) {
326+
return;
327+
}
328+
329+
if ($object instanceof QubitUser) {
330+
return;
331+
}
332+
333+
if ($this->batchMode) {
334+
// The document being deleted may not have been added to the index yet (if it's
335+
// still queued up in $this->batchAddDocs) so create a document object representing
336+
// the document to be deleted and add this document object to the batch delete
337+
// queue.
338+
$document = $this->client->createDocumentWithId($object->id, get_class($object));
339+
340+
$this->batchDeleteDocs[] = $document;
233341

234-
if ($response->error) {
235-
$this->log(var_export($response->error, true));
236-
$this->log(json_encode([$type => $data]));
342+
// If we have a full batch, send additions and deletions in bulk
343+
if (count($this->batchDeleteDocs) >= $this->batchSize) {
344+
$this->flushBatch();
345+
}
346+
} else {
347+
try {
348+
$this->client->deleteById($object->id, get_class($object));
349+
} catch (Exception $e) {
350+
// Ignore
351+
}
237352
}
238353
}
239354

@@ -292,32 +407,36 @@ private function addAutoCompleteFields()
292407
'QubitAip.type.i18n.%s%.name',
293408
];
294409

410+
$fields = [];
411+
$copyFields = [];
412+
295413
foreach ($this->langs as $lang) {
296-
$addFieldArr = [
414+
$langField = [
297415
'name' => "autocomplete_{$lang}",
298416
'type' => "text_{$lang}",
299417
'stored' => 'true',
300418
'multiValued' => 'true',
301419
];
302420

303-
$copyFieldsArr = [
304-
[
305-
'source' => 'QubitInformationObject.referenceCode',
306-
'dest' => "autocomplete_{$lang}",
307-
],
421+
$refField = [
422+
'source' => 'QubitInformationObject.referenceCode',
423+
'dest' => "autocomplete_{$lang}",
308424
];
309-
$this->client->addFields($addFieldArr);
425+
426+
array_push($fields, $langField);
427+
array_push($copyFields, $refField);
310428

311429
foreach ($autocompleteFields as $field) {
312430
$field = str_replace('%s%', $lang, $field);
313-
array_push($copyFieldsArr, [
431+
array_push($copyFields, [
314432
'source' => $field,
315433
'dest' => "autocomplete_{$lang}",
316434
]);
317435
}
318-
319-
$this->client->addCopyFields($copyFieldsArr);
320436
}
437+
438+
$this->client->addFields($fields);
439+
$this->client->addCopyFields($copyFields);
321440
}
322441

323442
private function addAutoCompleteConfigs()

Diff for: plugins/arSolrPlugin/lib/client/arSolrClient.class.php

+38
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,44 @@ public function addDocument($document)
7171
return makeHttpRequest($url, 'POST', json_encode($document));
7272
}
7373

74+
public function addDocuments($documents)
75+
{
76+
$url = "{$this->config['api_url']}/solr/{$this->config['collection']}/update/json/docs";
77+
78+
return makeHttpRequest($url, 'POST', json_encode($documents));
79+
}
80+
81+
public function deleteDocuments($documents)
82+
{
83+
$url = "{$this->config['api_url']}/solr/{$this->config['collection']}/update";
84+
85+
return makeHttpRequest($url, 'POST', json_encode([
86+
'delete' => $documents,
87+
]));
88+
}
89+
90+
public function deleteById($id, $type)
91+
{
92+
$document = $this->createDocumentWithId($id, $type);
93+
94+
return $this->deleteDocuments($document);
95+
}
96+
97+
public function deleteByQuery($query)
98+
{
99+
$queryParams = $query->getQueryParams();
100+
101+
// Ignore offset, size, and additional params when deleting by query
102+
return $this->deleteDocuments([
103+
'query' => $queryParams['query'],
104+
]);
105+
}
106+
107+
public function createDocumentWithId($id, $type)
108+
{
109+
return ["{$type}.id" => $id];
110+
}
111+
74112
public function getCollections()
75113
{
76114
$url = "{$this->config['api_url']}/solr/admin/collections?action=LIST";

0 commit comments

Comments
 (0)