Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Indexer   F
last analyzed

Complexity

Total Complexity 98

Size/Duplication

Total Lines 724
Duplicated Lines 0 %

Importance

Changes 9
Bugs 0 Features 1
Metric Value
eloc 296
c 9
b 0
f 1
dl 0
loc 724
rs 2
wmc 98

17 Methods

Rating   Name   Duplication   Size   Complexity  
B addFaceting() 0 24 8
A handleException() 0 6 2
A getIndexFieldName() 0 16 5
F add() 0 79 15
B processMetadata() 0 24 7
A deleteDocument() 0 12 3
A addMessage() 0 8 1
A solrConnect() 0 14 3
B processPhysical() 0 43 9
F processLogical() 0 84 18
A delete() 0 23 4
A getSolrDocument() 0 14 1
A getFormattedDate() 0 17 6
A addErrorMessage() 0 6 1
B loadIndexConf() 0 53 11
A __construct() 0 2 1
A removeAppendsFromAuthor() 0 9 3

How to fix   Complexity   

Complex Class

Complex classes like Indexer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Indexer, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
5
 *
6
 * This file is part of the Kitodo and TYPO3 projects.
7
 *
8
 * @license GNU General Public License version 3 or later.
9
 * For the full copyright and license information, please read the
10
 * LICENSE.txt file that was distributed with this source code.
11
 */
12
13
namespace Kitodo\Dlf\Common;
14
15
use Kitodo\Dlf\Common\Solr\Solr;
16
use Kitodo\Dlf\Domain\Repository\DocumentRepository;
17
use Kitodo\Dlf\Domain\Model\Document;
18
use Kitodo\Dlf\Validation\DocumentValidator;
19
use Solarium\Core\Query\DocumentInterface;
20
use Solarium\QueryType\Update\Query\Query;
21
use Symfony\Component\Console\Input\InputInterface;
22
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
23
use TYPO3\CMS\Core\Database\ConnectionPool;
24
use TYPO3\CMS\Core\Messaging\FlashMessage;
25
use TYPO3\CMS\Core\Utility\GeneralUtility;
26
use TYPO3\CMS\Core\Utility\MathUtility;
27
use TYPO3\CMS\Core\Core\Environment;
28
29
/**
30
 * Indexer class for the 'dlf' extension
31
 *
32
 * @package TYPO3
33
 * @subpackage dlf
34
 *
35
 * @access public
36
 */
37
class Indexer
38
{
39
    /**
40
     * @access public
41
     * @static
42
     * @var string The extension key
43
     */
44
    public static string $extKey = 'dlf';
45
46
    /**
47
     * @access protected
48
     * @static
49
     * @var array Array of metadata fields' configuration
50
     *
51
     * @see loadIndexConf()
52
     */
53
    protected static array $fields = [
54
        'autocomplete' => [],
55
        'facets' => [],
56
        'sortables' => [],
57
        'indexed' => [],
58
        'stored' => [],
59
        'tokenized' => []
60
    ];
61
62
    /**
63
     * @access protected
64
     * @static
65
     * @var bool Is the index configuration loaded?
66
     *
67
     * @see $fields
68
     */
69
    protected static bool $fieldsLoaded = false;
70
71
    /**
72
     * @access protected
73
     * @static
74
     * @var array List of already processed documents
75
     */
76
    protected static array $processedDocs = [];
77
78
    /**
79
     * @access protected
80
     * @static
81
     * @var Solr Instance of Solr class
82
     */
83
    protected static Solr $solr;
84
85
    /**
86
     * Insert given document into Solr index
87
     *
88
     * @access public
89
     *
90
     * @static
91
     *
92
     * @param Document $document The document to add
93
     * @param DocumentRepository $documentRepository The document repository for search of parent
94
     * @param bool $softCommit If true, documents are just added by a soft commit to the index
95
     *
96
     * @return bool true on success or false on failure
97
     */
98
    public static function add(Document $document, DocumentRepository $documentRepository, bool $softCommit = false): bool
99
    {
100
        if (in_array($document->getUid(), self::$processedDocs)) {
101
            return true;
102
        } elseif (self::solrConnect($document->getSolrcore(), $document->getPid())) {
0 ignored issues
show
Bug introduced by
It seems like $document->getPid() can also be of type null; however, parameter $pid of Kitodo\Dlf\Common\Indexer::solrConnect() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

102
        } elseif (self::solrConnect($document->getSolrcore(), /** @scrutinizer ignore-type */ $document->getPid())) {
Loading history...
103
            $success = true;
104
            Helper::getLanguageService()->includeLLFile('EXT:dlf/Resources/Private/Language/locallang_be.xlf');
105
            // Handle multi-volume documents.
106
            $parentId = $document->getPartof();
107
            if ($parentId) {
108
                // get parent document
109
                $parent = $documentRepository->findByUid($parentId);
110
                if ($parent) {
111
                    // get XML document of parent
112
                    $doc = AbstractDocument::getInstance($parent->getLocation(), ['storagePid' => $parent->getPid()], true);
113
                    if ($doc !== null) {
114
                        $parent->setCurrentDocument($doc);
115
                        $success = self::add($parent, $documentRepository);
116
                    } else {
117
                        Helper::log('Could not load parent document with UID ' . $document->getCurrentDocument()->parentId, LOG_SEVERITY_ERROR);
118
                        return false;
119
                    }
120
                }
121
            }
122
            try {
123
                // Add document to list of processed documents.
124
                self::$processedDocs[] = $document->getUid();
125
                // Delete old Solr documents.
126
                self::deleteDocument('uid', (string) $document->getUid());
127
128
                // Index every logical unit as separate Solr document.
129
                foreach ($document->getCurrentDocument()->tableOfContents as $logicalUnit) {
130
                    if ($success) {
131
                        $success = self::processLogical($document, $logicalUnit);
132
                    } else {
133
                        break;
134
                    }
135
                }
136
                // Index full text files if available.
137
                if ($document->getCurrentDocument()->hasFulltext) {
138
                    foreach ($document->getCurrentDocument()->physicalStructure as $pageNumber => $xmlId) {
139
                        if ($success) {
140
                            $success = self::processPhysical($document, $pageNumber, $document->getCurrentDocument()->physicalStructureInfo[$xmlId]);
141
                        } else {
142
                            break;
143
                        }
144
                    }
145
                }
146
                // Commit all changes.
147
                $updateQuery = self::$solr->service->createUpdate();
148
                $updateQuery->addCommit($softCommit);
149
                self::$solr->service->update($updateQuery);
150
151
                if (!(Environment::isCli())) {
152
                    if ($success) {
153
                        self::addMessage(
154
                            sprintf(Helper::getLanguageService()->getLL('flash.documentIndexed'), $document->getTitle(), $document->getUid()),
155
                            'flash.done',
156
                            FlashMessage::OK
157
                        );
158
                    } else {
159
                        self::addErrorMessage(sprintf(Helper::getLanguageService()->getLL('flash.documentNotIndexed'), $document->getTitle(), $document->getUid()));
160
                    }
161
                }
162
                return $success;
163
            } catch (\Exception $e) {
164
                self::handleException($e->getMessage());
165
                return false;
166
            }
167
        } else {
168
            if (!(Environment::isCli())) {
169
                self::addMessage(
170
                    Helper::getLanguageService()->getLL('flash.solrNoConnection'),
171
                    'flash.warning',
172
                    FlashMessage::WARNING
173
                );
174
            }
175
            Helper::log('Could not connect to Apache Solr server', LOG_SEVERITY_ERROR);
176
            return false;
177
        }
178
    }
179
180
    /**
181
     * Delete document from Solr index
182
     *
183
     * @access public
184
     *
185
     * @static
186
     *
187
     * @param InputInterface $input The input parameters
188
     * @param string $field by which document should be removed
189
     * @param int $solrCoreUid UID of the SolrCore
190
     * @param bool $softCommit If true, documents are just deleted from the index by a soft commit
191
     *
192
     * @return bool true on success or false on failure
193
     */
194
    public static function delete(InputInterface $input, string $field, int $solrCoreUid, bool $softCommit = false): bool
195
    {
196
        if (self::solrConnect($solrCoreUid, $input->getOption('pid'))) {
197
            try {
198
                self::deleteDocument($field, $input->getOption('doc'), $softCommit);
199
                return true;
200
            } catch (\Exception $e) {
201
                if (!(Environment::isCli())) {
202
                    Helper::addMessage(
203
                        Helper::getLanguageService()->getLL('flash.solrException') . ' ' . htmlspecialchars($e->getMessage()),
204
                        Helper::getLanguageService()->getLL('flash.error'),
205
                        FlashMessage::ERROR,
206
                        true,
207
                        'core.template.flashMessages'
208
                    );
209
                }
210
                Helper::log('Apache Solr threw exception: "' . $e->getMessage() . '"', LOG_SEVERITY_ERROR);
211
                return false;
212
            }
213
        }
214
215
        Helper::log('Document not deleted from SOLR - problem with the connection to the SOLR core ' . $solrCoreUid, LOG_SEVERITY_ERROR);
216
        return false;
217
    }
218
219
    /**
220
     * Returns the dynamic index field name for the given metadata field.
221
     *
222
     * @access public
223
     *
224
     * @static
225
     *
226
     * @param string $indexName The metadata field's name in database
227
     * @param int $pid UID of the configuration page
228
     *
229
     * @return string The field's dynamic index name
230
     */
231
    public static function getIndexFieldName(string $indexName, int $pid = 0): string
232
    {
233
        // Sanitize input.
234
        $pid = max((int) $pid, 0);
235
        if (!$pid) {
236
            Helper::log('Invalid PID ' . $pid . ' for metadata configuration', LOG_SEVERITY_ERROR);
237
            return '';
238
        }
239
        // Load metadata configuration.
240
        self::loadIndexConf($pid);
241
        // Build field's suffix.
242
        $suffix = (in_array($indexName, self::$fields['tokenized']) ? 't' : 'u');
243
        $suffix .= (in_array($indexName, self::$fields['stored']) ? 's' : 'u');
244
        $suffix .= (in_array($indexName, self::$fields['indexed']) ? 'i' : 'u');
245
        $indexName .= '_' . $suffix;
246
        return $indexName;
247
    }
248
249
    /**
250
     * Load indexing configuration
251
     *
252
     * @access protected
253
     *
254
     * @static
255
     *
256
     * @param int $pid The configuration page's UID
257
     *
258
     * @return void
259
     */
260
    protected static function loadIndexConf(int $pid): void
261
    {
262
        if (!self::$fieldsLoaded) {
263
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
264
                ->getQueryBuilderForTable('tx_dlf_metadata');
265
266
            // Get the metadata indexing options.
267
            $result = $queryBuilder
268
                ->select(
269
                    'tx_dlf_metadata.index_name AS index_name',
270
                    'tx_dlf_metadata.index_tokenized AS index_tokenized',
271
                    'tx_dlf_metadata.index_stored AS index_stored',
272
                    'tx_dlf_metadata.index_indexed AS index_indexed',
273
                    'tx_dlf_metadata.is_sortable AS is_sortable',
274
                    'tx_dlf_metadata.is_facet AS is_facet',
275
                    'tx_dlf_metadata.is_listed AS is_listed',
276
                    'tx_dlf_metadata.index_autocomplete AS index_autocomplete',
277
                    'tx_dlf_metadata.index_boost AS index_boost'
278
                )
279
                ->from('tx_dlf_metadata')
280
                ->where(
281
                    $queryBuilder->expr()->eq('tx_dlf_metadata.pid', (int) $pid),
282
                    Helper::whereExpression('tx_dlf_metadata')
283
                )
284
                ->execute();
285
286
            while ($indexing = $result->fetchAssociative()) {
287
                if ($indexing['index_tokenized']) {
288
                    self::$fields['tokenized'][] = $indexing['index_name'];
289
                }
290
                if (
291
                    $indexing['index_stored']
292
                    || $indexing['is_listed']
293
                ) {
294
                    self::$fields['stored'][] = $indexing['index_name'];
295
                }
296
                if (
297
                    $indexing['index_indexed']
298
                    || $indexing['index_autocomplete']
299
                ) {
300
                    self::$fields['indexed'][] = $indexing['index_name'];
301
                }
302
                if ($indexing['is_sortable']) {
303
                    self::$fields['sortables'][] = $indexing['index_name'];
304
                }
305
                if ($indexing['is_facet']) {
306
                    self::$fields['facets'][] = $indexing['index_name'];
307
                }
308
                if ($indexing['index_autocomplete']) {
309
                    self::$fields['autocomplete'][] = $indexing['index_name'];
310
                }
311
            }
312
            self::$fieldsLoaded = true;
313
        }
314
    }
315
316
    /**
317
     * Processes a logical unit (and its children) for the Solr index
318
     *
319
     * @access protected
320
     *
321
     * @static
322
     *
323
     * @param Document $document The METS document
324
     * @param array $logicalUnit Array of the logical unit to process
325
     *
326
     * @return bool true on success or false on failure
327
     */
328
    protected static function processLogical(Document $document, array $logicalUnit): bool
329
    {
330
        $success = true;
331
        $doc = $document->getCurrentDocument();
332
        $doc->cPid = $document->getPid();
333
        // Get metadata for logical unit.
334
        $metadata = $doc->metadataArray[$logicalUnit['id']];
335
        if (!empty($metadata)) {
336
            $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'general');
337
            $validator = new DocumentValidator($metadata, explode(',', $extConf['requiredMetadataFields']));
338
339
            if ($validator->hasAllMandatoryMetadataFields()) {
340
                $metadata['author'] = self::removeAppendsFromAuthor($metadata['author']);
341
                // set Owner if available
342
                if ($document->getOwner()) {
343
                    $metadata['owner'][0] = $document->getOwner()->getIndexName();
344
                }
345
                // Create new Solr document.
346
                $updateQuery = self::$solr->service->createUpdate();
347
                $solrDoc = self::getSolrDocument($updateQuery, $document, $logicalUnit);
348
                if (MathUtility::canBeInterpretedAsInteger($logicalUnit['points'])) {
349
                    $solrDoc->setField('page', $logicalUnit['points']);
0 ignored issues
show
Bug introduced by
The method setField() does not exist on Solarium\Core\Query\DocumentInterface. It seems like you code against a sub-type of Solarium\Core\Query\DocumentInterface such as Solarium\Plugin\MinimumScoreFilter\Document or Solarium\QueryType\Update\Query\Document. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

349
                    $solrDoc->/** @scrutinizer ignore-call */ 
350
                              setField('page', $logicalUnit['points']);
Loading history...
350
                }
351
                if ($logicalUnit['id'] == $doc->toplevelId) {
352
                    $solrDoc->setField('thumbnail', $doc->thumbnail);
353
                } elseif (!empty($logicalUnit['thumbnailId'])) {
354
                    $solrDoc->setField('thumbnail', $doc->getFileLocation($logicalUnit['thumbnailId']));
355
                }
356
                // There can be only one toplevel unit per UID, independently of backend configuration
357
                $solrDoc->setField('toplevel', $logicalUnit['id'] == $doc->toplevelId ? true : false);
358
                $solrDoc->setField('title', $metadata['title'][0]);
359
                $solrDoc->setField('volume', $metadata['volume'][0]);
360
                // verify date formatting
361
                if(strtotime($metadata['date'][0])) {
362
                    $solrDoc->setField('date', self::getFormattedDate($metadata['date'][0]));
363
                }
364
                $solrDoc->setField('record_id', $metadata['record_id'][0]);
365
                $solrDoc->setField('purl', $metadata['purl'][0]);
366
                $solrDoc->setField('location', $document->getLocation());
367
                $solrDoc->setField('urn', $metadata['urn']);
368
                $solrDoc->setField('license', $metadata['license']);
369
                $solrDoc->setField('terms', $metadata['terms']);
370
                $solrDoc->setField('restrictions', $metadata['restrictions']);
371
                $coordinates = json_decode($metadata['coordinates'][0]);
372
                if (is_object($coordinates)) {
373
                    $solrDoc->setField('geom', json_encode($coordinates->features[0]));
374
                }
375
                $autocomplete = self::processMetadata($document, $metadata, $solrDoc);
376
                // Add autocomplete values to index.
377
                if (!empty($autocomplete)) {
378
                    $solrDoc->setField('autocomplete', $autocomplete);
379
                }
380
                // Add collection information to logical sub-elements if applicable.
381
                if (
382
                    in_array('collection', self::$fields['facets'])
383
                    && empty($metadata['collection'])
384
                    && !empty($doc->metadataArray[$doc->toplevelId]['collection'])
385
                ) {
386
                    $solrDoc->setField('collection_faceting', $doc->metadataArray[$doc->toplevelId]['collection']);
387
                }
388
                try {
389
                    $updateQuery->addDocument($solrDoc);
390
                    self::$solr->service->update($updateQuery);
391
                } catch (\Exception $e) {
392
                    self::handleException($e->getMessage());
393
                    return false;
394
                }
395
            } else {
396
                Helper::log('Tip: If "record_id" field is missing then there is possibility that METS file still contains it but with the wrong source type attribute in "recordIdentifier" element', LOG_SEVERITY_NOTICE);
397
                return false;
398
            }
399
        }
400
        // Check for child elements...
401
        if (!empty($logicalUnit['children'])) {
402
            foreach ($logicalUnit['children'] as $child) {
403
                if ($success) {
404
                    // ...and process them, too.
405
                    $success = self::processLogical($document, $child);
406
                } else {
407
                    break;
408
                }
409
            }
410
        }
411
        return $success;
412
    }
413
414
    /**
415
     * Processes a physical unit for the Solr index
416
     *
417
     * @access protected
418
     *
419
     * @static
420
     *
421
     * @param Document $document The METS document
422
     * @param int $page The page number
423
     * @param array $physicalUnit Array of the physical unit to process
424
     *
425
     * @return bool true on success or false on failure
426
     */
427
    protected static function processPhysical(Document $document, int $page, array $physicalUnit): bool
428
    {
429
        $doc = $document->getCurrentDocument();
430
        $doc->cPid = $document->getPid();
431
        if ($doc->hasFulltext && $fullText = $doc->getFullText($physicalUnit['id'])) {
432
            // Read extension configuration.
433
            $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::$extKey, 'files');
434
            // Create new Solr document.
435
            $updateQuery = self::$solr->service->createUpdate();
436
            $solrDoc = self::getSolrDocument($updateQuery, $document, $physicalUnit, $fullText);
437
            $solrDoc->setField('page', $page);
438
            $fileGrpsThumb = GeneralUtility::trimExplode(',', $extConf['fileGrpThumbs']);
439
            while ($fileGrpThumb = array_shift($fileGrpsThumb)) {
440
                if (!empty($physicalUnit['files'][$fileGrpThumb])) {
441
                    $solrDoc->setField('thumbnail', $doc->getFileLocation($physicalUnit['files'][$fileGrpThumb]));
442
                    break;
443
                }
444
            }
445
            $solrDoc->setField('toplevel', false);
446
            $solrDoc->setField('type', $physicalUnit['type']);
447
            $solrDoc->setField('collection', $doc->metadataArray[$doc->toplevelId]['collection']);
448
            $solrDoc->setField('location', $document->getLocation());
449
450
            $solrDoc->setField('fulltext', $fullText);
451
            if (is_array($doc->metadataArray[$doc->toplevelId])) {
452
                self::addFaceting($doc, $solrDoc);
453
            }
454
            // Add collection information to physical sub-elements if applicable.
455
            if (
456
                in_array('collection', self::$fields['facets'])
457
                && !empty($doc->metadataArray[$doc->toplevelId]['collection'])
458
            ) {
459
                $solrDoc->setField('collection_faceting', $doc->metadataArray[$doc->toplevelId]['collection']);
460
            }
461
            try {
462
                $updateQuery->addDocument($solrDoc);
463
                self::$solr->service->update($updateQuery);
464
            } catch (\Exception $e) {
465
                self::handleException($e->getMessage());
466
                return false;
467
            }
468
        }
469
        return true;
470
    }
471
472
    /**
473
     * Connects to Solr server.
474
     *
475
     * @access protected
476
     *
477
     * @static
478
     *
479
     * @param int $core UID of the Solr core
480
     * @param int $pid UID of the configuration page
481
     *
482
     * @return bool true on success or false on failure
483
     */
484
    protected static function solrConnect(int $core, int $pid = 0): bool
485
    {
486
        // Get Solr instance.
487
        $solr = Solr::getInstance($core);
488
        // Connect to Solr server.
489
        if ($solr->ready) {
490
            self::$solr = $solr;
491
            // Load indexing configuration if needed.
492
            if ($pid) {
493
                self::loadIndexConf($pid);
494
            }
495
            return true;
496
        }
497
        return false;
498
    }
499
500
    /**
501
     * Process metadata: add facets, sortable fields and create autocomplete array.
502
     *
503
     * @static
504
     *
505
     * @access private
506
     *
507
     * @param Document $document
508
     * @param array $metadata
509
     * @param DocumentInterface &$solrDoc
510
     *
511
     * @return array empty array or autocomplete values
512
     */
513
    private static function processMetadata($document, $metadata, &$solrDoc): array
514
    {
515
        $autocomplete = [];
516
        foreach ($metadata as $indexName => $data) {
517
            // TODO: Include also subentries if available.
518
            if (
519
                !empty($data)
520
                && substr($indexName, -8) !== '_sorting'
521
            ) {
522
                $solrDoc->setField(self::getIndexFieldName($indexName, $document->getPid()), $data);
0 ignored issues
show
Bug introduced by
It seems like $document->getPid() can also be of type null; however, parameter $pid of Kitodo\Dlf\Common\Indexer::getIndexFieldName() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

522
                $solrDoc->setField(self::getIndexFieldName($indexName, /** @scrutinizer ignore-type */ $document->getPid()), $data);
Loading history...
523
                if (in_array($indexName, self::$fields['sortables'])) {
524
                    // Add sortable fields to index.
525
                    $solrDoc->setField($indexName . '_sorting', $metadata[$indexName . '_sorting'][0]);
526
                }
527
                if (in_array($indexName, self::$fields['facets'])) {
528
                    // Add facets to index.
529
                    $solrDoc->setField($indexName . '_faceting', $data);
530
                }
531
                if (in_array($indexName, self::$fields['autocomplete'])) {
532
                    $autocomplete = array_merge($autocomplete, $data);
533
                }
534
            }
535
        }
536
        return $autocomplete;
537
    }
538
539
    /**
540
     * Add faceting information to physical sub-elements if applicable.
541
     *
542
     * @static
543
     *
544
     * @access private
545
     *
546
     * @param AbstractDocument $doc
547
     * @param DocumentInterface &$solrDoc
548
     *
549
     * @return void
550
     */
551
    private static function addFaceting($doc, &$solrDoc): void
552
    {
553
        // TODO: Include also subentries if available.
554
        foreach ($doc->metadataArray[$doc->toplevelId] as $indexName => $data) {
555
            if (
556
                !empty($data)
557
                && substr($indexName, -8) !== '_sorting'
558
            ) {
559
560
                if (in_array($indexName, self::$fields['facets'])) {
561
                    // Remove appended "valueURI" from authors' names for indexing.
562
                    if ($indexName == 'author') {
563
                        $data = self::removeAppendsFromAuthor($data);
564
                    }
565
                    // Add facets to index.
566
                    $solrDoc->setField($indexName . '_faceting', $data);
567
                }
568
            }
569
            // Add sorting information to physical sub-elements if applicable.
570
            if (
571
                !empty($data)
572
                && substr($indexName, -8) == '_sorting'
573
            ) {
574
                $solrDoc->setField($indexName, $doc->metadataArray[$doc->toplevelId][$indexName]);
575
            }
576
        }
577
    }
578
579
    /**
580
     * Delete document from SOLR by given field and value.
581
     *
582
     * @access private
583
     *
584
     * @static
585
     *
586
     * @param string $field by which document should be removed
587
     * @param string $value of the field by which document should be removed
588
     * @param bool $softCommit If true, documents are just deleted from the index by a soft commit
589
     *
590
     * @return void
591
     */
592
    private static function deleteDocument(string $field, string $value, bool $softCommit = false): void
593
    {
594
        $update = self::$solr->service->createUpdate();
595
        $query = "";
596
        if ($field == 'uid' || $field == 'partof') {
597
            $query = $field . ':' . $value;
598
        } else {
599
            $query = $field . ':"' . $value . '"';
600
        }
601
        $update->addDeleteQuery($query);
602
        $update->addCommit($softCommit);
603
        self::$solr->service->update($update);
604
    }
605
606
    /**
607
     * Get SOLR document with set standard fields (identical for logical and physical unit)
608
     *
609
     * @access private
610
     *
611
     * @static
612
     *
613
     * @param Query $updateQuery solarium query
614
     * @param Document $document The METS document
615
     * @param array $unit Array of the logical or physical unit to process
616
     * @param string $fullText Text containing full text for indexing
617
     *
618
     * @return DocumentInterface
619
     */
620
    private static function getSolrDocument(Query $updateQuery, Document $document, array $unit, string $fullText = ''): DocumentInterface
621
    {
622
        $solrDoc = $updateQuery->createDocument();
623
        // Create unique identifier from document's UID and unit's XML ID.
624
        $solrDoc->setField('id', $document->getUid() . $unit['id']);
625
        $solrDoc->setField('uid', $document->getUid());
626
        $solrDoc->setField('pid', $document->getPid());
627
        $solrDoc->setField('partof', $document->getPartof());
628
        $solrDoc->setField('root', $document->getCurrentDocument()->rootId);
629
        $solrDoc->setField('sid', $unit['id']);
630
        $solrDoc->setField('type', $unit['type']);
631
        $solrDoc->setField('collection', $document->getCurrentDocument()->metadataArray[$document->getCurrentDocument()->toplevelId]['collection']);
632
        $solrDoc->setField('fulltext', $fullText);
633
        return $solrDoc;
634
    }
635
636
    /**
637
     * Get formatted date without alteration.
638
     * Possible formats: YYYY or YYYY-MM or YYYY-MM-DD.
639
     *
640
     * @static
641
     *
642
     * @access private
643
     *
644
     * @param string $date
645
     *
646
     * @return string formatted date YYYY or YYYY-MM or YYYY-MM-DD or empty string
647
     */
648
    private static function getFormattedDate($date): string
649
    {
650
        if (
651
            preg_match("/^[\d]{4}$/", $date)
652
            || preg_match("/^[\d]{4}-[\d]{2}$/", $date)
653
            || preg_match("/^[\d]{4}-[\d]{2}-[\d]{2}$/", $date)
654
        ) {
655
            return $date;
656
        // change date YYYYMMDD to YYYY-MM-DD
657
        } elseif (preg_match("/^[\d]{8}$/", $date)) {
658
            return date("Y-m-d", strtotime($date));
659
        // convert any datetime to proper ISO extended datetime format and timezone for SOLR
660
        } elseif (preg_match("/^[0-9]{4}-[0-9]{2}-[0-9]{2}T.*$/", $date)) {
661
            return date('Y-m-d\TH:i:s\Z', strtotime($date));
662
        }
663
        // date doesn't match any standard
664
        return '';
665
    }
666
667
    /**
668
     * Remove appended "valueURI" from authors' names for indexing.
669
     *
670
     * @access private
671
     *
672
     * @static
673
     *
674
     * @param array|string $authors Array or string containing author/authors
675
     *
676
     * @return array|string
677
     */
678
    private static function removeAppendsFromAuthor($authors)
679
    {
680
        if (is_array($authors)) {
681
            foreach ($authors as $i => $author) {
682
                $splitName = explode(pack('C', 31), $author);
683
                $authors[$i] = $splitName[0];
684
            }
685
        }
686
        return $authors;
687
    }
688
689
    /**
690
     * Handle exception.
691
     *
692
     * @static
693
     *
694
     * @access private
695
     *
696
     * @param string $errorMessage
697
     *
698
     * @return void
699
     */
700
    private static function handleException(string $errorMessage): void
701
    {
702
        if (!(Environment::isCli())) {
703
            self::addErrorMessage(Helper::getLanguageService()->getLL('flash.solrException') . '<br />' . htmlspecialchars($errorMessage));
704
        }
705
        Helper::log('Apache Solr threw exception: "' . $errorMessage . '"', LOG_SEVERITY_ERROR);
706
    }
707
708
    /**
709
     * Add error message only with message content.
710
     *
711
     * @static
712
     *
713
     * @access private
714
     *
715
     * @param string $message
716
     *
717
     * @return void
718
     */
719
    private static function addErrorMessage(string $message): void
720
    {
721
        self::addMessage(
722
            $message,
723
            'flash.error',
724
            FlashMessage::ERROR
725
        );
726
    }
727
728
    /**
729
     * Add message only with changeable parameters.
730
     *
731
     * @static
732
     *
733
     * @access private
734
     *
735
     * @param string $message
736
     * @param string $type
737
     * @param int $status
738
     *
739
     * @return void
740
     */
741
    private static function addMessage(string $message, string $type, int $status): void
742
    {
743
        Helper::addMessage(
744
            $message,
745
            Helper::getLanguageService()->getLL($type),
746
            $status,
747
            true,
748
            'core.template.flashMessages'
749
        );
750
    }
751
752
    /**
753
     * Prevent instantiation by hiding the constructor
754
     *
755
     * @access private
756
     *
757
     * @return void
758
     */
759
    private function __construct()
760
    {
761
    }
762
}
763