Exponential 5.x Kernel 7.5.40 Exponential New Stack (Symfony-based) Kernel — PHP Class Reference

Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php
Go to the documentation of this file.
1 <?php
2
7 namespace eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway;
8
9 use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator;
10 use eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway;
11 use eZ\Publish\Core\Persistence\Database\DatabaseHandler;
12 use eZ\Publish\Core\Persistence\TransformationProcessor;
13 use eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Repository\SearchIndex;
14 use eZ\Publish\Core\Search\Legacy\Content\FullTextData;
15 use eZ\Publish\SPI\Persistence\Content;
16 use eZ\Publish\SPI\Persistence\Content\Type\Handler as SPITypeHandler;
17 use eZ\Publish\SPI\Search\Field;
18
22 class DoctrineDatabase extends Gateway
23 {
29 const DB_INT_MAX = 2147483647;
30
38 protected $dbHandler;
39
47 protected $typeHandler;
48
56 protected $transformationProcessor;
57
65 protected $searchIndex;
66
68 private $languageMaskGenerator;
69
75 protected $fullTextSearchConfiguration;
76
87 public function __construct(
88 DatabaseHandler $dbHandler,
89 SPITypeHandler $typeHandler,
90 TransformationProcessor $transformationProcessor,
91 SearchIndex $searchIndex,
92 MaskGenerator $languageMaskGenerator,
93 array $fullTextSearchConfiguration
94 ) {
95 $this->dbHandler = $dbHandler;
96 $this->typeHandler = $typeHandler;
97 $this->transformationProcessor = $transformationProcessor;
98 $this->searchIndex = $searchIndex;
99 $this->fullTextSearchConfiguration = $fullTextSearchConfiguration;
100 $this->languageMaskGenerator = $languageMaskGenerator;
101 }
102
112 public function index(FullTextData $fullTextData)
113 {
114 $indexArray = [];
115 $indexArrayOnlyWords = [];
116 $wordCount = 0;
117 $placement = 0;
118
119 // Remove previously indexed content if exists to avoid keeping in index removed field values
120 $this->remove($fullTextData->id);
121 foreach ($fullTextData->values as $fullTextValue) {
123 if (is_numeric(trim($fullTextValue->value))) {
124 $integerValue = (int)$fullTextValue->value;
125 if ($integerValue > self::DB_INT_MAX) {
126 $integerValue = 0;
127 }
128 } else {
129 $integerValue = 0;
130 }
131 $text = $this->transformationProcessor->transform(
132 $fullTextValue->value,
133 !empty($fullTextValue->transformationRules)
134 ? $fullTextValue->transformationRules
135 : $this->fullTextSearchConfiguration['commands']
136 );
137
138 // split by non-words
139 $wordArray = $fullTextValue->splitFlag ? preg_split('/\W/u', $text, -1, PREG_SPLIT_NO_EMPTY) : [$text];
140 foreach ($wordArray as $word) {
141 if (trim($word) === '') {
142 continue;
143 }
144 // words stored in search index are limited to 150 characters
145 if (mb_strlen($word) > 150) {
146 $word = mb_substr($word, 0, 150);
147 }
148 $indexArray[] = [
149 'Word' => $word,
150 'ContentClassAttributeID' => $fullTextValue->fieldDefinitionId,
151 'identifier' => $fullTextValue->fieldDefinitionIdentifier,
152 'integer_value' => $integerValue,
153 'language_code' => $fullTextValue->languageCode,
154 'is_main_and_always_available' => $fullTextValue->isMainAndAlwaysAvailable,
155 ];
156 $indexArrayOnlyWords[$word] = 1;
157 ++$wordCount;
158 // if we have "www." before word than
159 // treat it as url and add additional entry to the index
160 if (mb_strtolower(mb_substr($word, 0, 4)) === 'www.') {
161 $additionalUrlWord = substr($word, 4);
162 $indexArray[] = [
163 'Word' => $additionalUrlWord,
164 'ContentClassAttributeID' => $fullTextValue->fieldDefinitionId,
165 'identifier' => $fullTextValue->fieldDefinitionIdentifier,
166 'integer_value' => $integerValue,
167 'language_code' => $fullTextValue->languageCode,
168 'is_main_and_always_available' => $fullTextValue->isMainAndAlwaysAvailable,
169 ];
170 $indexArrayOnlyWords[$additionalUrlWord] = 1;
171 ++$wordCount;
172 }
173 }
174 }
175
176 $wordIDArray = $this->buildWordIDArray(array_keys($indexArrayOnlyWords));
177 $this->dbHandler->beginTransaction();
178 for ($arrayCount = 0; $arrayCount < $wordCount; $arrayCount += 1000) {
179 $placement = $this->indexWords(
180 $fullTextData,
181 array_slice($indexArray, $arrayCount, 1000),
182 $wordIDArray,
183 $placement
184 );
185 }
186 $this->dbHandler->commit();
187 }
188
198 public function bulkIndex(array $fullTextBulkData)
199 {
200 foreach ($fullTextBulkData as $fullTextData) {
201 $this->index($fullTextData);
202 }
203 }
204
217 public function remove($contentId, $versionId = null)
218 {
219 $doDelete = false;
220 $this->dbHandler->beginTransaction();
221 // fetch all the words and decrease the object count on all the words
222 $wordIDList = $this->searchIndex->getContentObjectWords($contentId);
223 if (count($wordIDList) > 0) {
224 $this->searchIndex->decrementWordObjectCount($wordIDList);
225 $doDelete = true;
226 }
227 if ($doDelete) {
228 $this->searchIndex->deleteWordsWithoutObjects();
229 $this->searchIndex->deleteObjectWordsLink($contentId);
230 }
231 $this->dbHandler->commit();
232
233 return true;
234 }
235
239 public function purgeIndex()
240 {
241 $this->searchIndex->purge();
242 }
243
258 private function indexWords(FullTextData $fullTextData, array $indexArray, array $wordIDArray, $placement = 0)
259 {
260 $contentId = $fullTextData->id;
261
262 $prevWordId = 0;
263
264 for ($i = 0; $i < count($indexArray); ++$i) {
265 $indexWord = $indexArray[$i]['Word'];
266 $indexWord = $this->transformationProcessor->transformByGroup($indexWord, 'lowercase');
267 $contentFieldId = $indexArray[$i]['ContentClassAttributeID'];
268 $identifier = $indexArray[$i]['identifier'];
269 $integerValue = $indexArray[$i]['integer_value'];
270 $languageCode = $indexArray[$i]['language_code'];
271 $wordId = $wordIDArray[$indexWord];
272 $isMainAndAlwaysAvailable = $indexArray[$i]['is_main_and_always_available'];
273 $languageMask = $this->languageMaskGenerator->generateLanguageMaskFromLanguageCodes(
274 [$languageCode],
275 $isMainAndAlwaysAvailable
276 );
277
278 if (isset($indexArray[$i + 1])) {
279 $nextIndexWord = $indexArray[$i + 1]['Word'];
280 $nextIndexWord = $this->transformationProcessor->transformByGroup($nextIndexWord, 'lowercase');
281 $nextWordId = $wordIDArray[$nextIndexWord];
282 } else {
283 $nextWordId = 0;
284 }
285 $frequency = 0;
286 $this->searchIndex->addObjectWordLink(
287 $wordId,
288 $contentId,
289 $frequency,
290 $placement,
291 $nextWordId,
292 $prevWordId,
293 $fullTextData->contentTypeId,
294 $contentFieldId,
295 $fullTextData->published,
296 $fullTextData->sectionId,
297 $identifier,
298 $integerValue,
299 $languageMask
300 );
301 $prevWordId = $wordId;
302 ++$placement;
303 }
304
305 return $placement;
306 }
307
319 private function buildWordIDArray(array $indexArrayOnlyWords)
320 {
321 $wordCount = count($indexArrayOnlyWords);
322 $wordIDArray = [];
323 $wordArray = [];
324
325 // store the words in the index and remember the ID
326 $this->dbHandler->beginTransaction();
327 for ($arrayCount = 0; $arrayCount < $wordCount; $arrayCount += 500) {
328 // Fetch already indexed words from database
329 $wordArrayChuck = array_slice($indexArrayOnlyWords, $arrayCount, 500);
330 $wordRes = $this->searchIndex->getWords($wordArrayChuck);
331
332 // Build a has of the existing words
333 $wordResCount = count($wordRes);
334 $existingWordArray = [];
335 for ($i = 0; $i < $wordResCount; ++$i) {
336 $wordIDArray[] = $wordRes[$i]['id'];
337 $existingWordArray[] = $wordRes[$i]['word'];
338 $wordArray[$wordRes[$i]['word']] = $wordRes[$i]['id'];
339 }
340
341 // Update the object count of existing words by one
342 if (count($wordIDArray) > 0) {
343 $this->searchIndex->incrementWordObjectCount($wordIDArray);
344 }
345
346 // Insert if there is any news words
347 $newWordArray = array_diff($wordArrayChuck, $existingWordArray);
348 if (count($newWordArray) > 0) {
349 $this->searchIndex->addWords($newWordArray);
350 $newWordRes = $this->searchIndex->getWords($newWordArray);
351 $newWordCount = count($newWordRes);
352 for ($i = 0; $i < $newWordCount; ++$i) {
353 $wordLowercase = $this->transformationProcessor->transformByGroup($newWordRes[$i]['word'], 'lowercase');
354 $wordArray[$wordLowercase] = $newWordRes[$i]['id'];
355 }
356 }
357 }
358 $this->dbHandler->commit();
359
360 return $wordArray;
361 }
362 }
eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator
Language MaskGenerator.
Definition: MaskGenerator.php:16
eZ\Publish\Core\Persistence\TransformationProcessor
Interface for processing a set of transformations on a string.
Definition: TransformationProcessor.php:15
eZ\Publish\Core\Search\Legacy\Content\FullTextData
Represents full text data of FullTextValue(s) for a Content object.
Definition: FullTextData.php:15
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase
WordIndexer gateway implementation using the Doctrine database.
Definition: Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php:23
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase\$languageMaskGenerator
$languageMaskGenerator
Definition: Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php:68
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase\bulkIndex
bulkIndex(array $fullTextBulkData)
Indexes an array of FullTextData objects.
Definition: Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php:198
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase\$transformationProcessor
$transformationProcessor
Definition: Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php:56
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase\$fullTextSearchConfiguration
$fullTextSearchConfiguration
Definition: Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php:75
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase\$dbHandler
$dbHandler
Definition: Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php:38
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase\$searchIndex
$searchIndex
Definition: Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php:65
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase\$typeHandler
$typeHandler
Definition: Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php:47
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase\__construct
__construct(DatabaseHandler $dbHandler, SPITypeHandler $typeHandler, TransformationProcessor $transformationProcessor, SearchIndex $searchIndex, MaskGenerator $languageMaskGenerator, array $fullTextSearchConfiguration)
Construct from handler handler.
Definition: Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php:87
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase\indexWords
indexWords(FullTextData $fullTextData, array $indexArray, array $wordIDArray, $placement=0)
Index wordIndex.
Definition: Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php:258
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase\purgeIndex
purgeIndex()
Remove entire search index.
Definition: Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php:239
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase\buildWordIDArray
buildWordIDArray(array $indexArrayOnlyWords)
Build WordIDArray and update ezsearch_word table.
Definition: Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php:319
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase\DB_INT_MAX
const DB_INT_MAX
Max acceptable by any DBMS INT value.
Definition: Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php:29
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway
The WordIndexer Gateway abstracts indexing of content full text data.
Definition: Search/Legacy/Content/WordIndexer/Gateway.php:16
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\index
index(FullTextData $fullTextValue)
Index search engine FullTextData objects corresponding to content object field values.
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Repository\SearchIndex
A service encapsulating database operations on ezsearch* tables.
Definition: SearchIndex.php:17
eZ\Publish\SPI\Search\Field
Definition: SPI/Search/Field.php:19
eZ\Publish\Core\Persistence\Database\DatabaseHandler
Interface for relational database abstractions supported by eZ.
Definition: DatabaseHandler.php:18
eZ\Publish\SPI\Persistence\Content\Type\Handler
Definition: SPI/Persistence/Content/Type/Handler.php:14
eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway
Definition: Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php:7
eZ\Publish\SPI\Persistence\Content
Definition: SPI/Persistence/Content/ContentInfo.php:7
Copyright © 1998 – 2026 7x. All rights reserved. Licensed under the GNU GPLv2+.