Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Base class for search engines.
19
 *
20
 * All search engines must extend this class.
21
 *
22
 * @package   core_search
23
 * @copyright 2015 Daniel Neis
24
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25
 */
26
 
27
namespace core_search;
28
 
29
defined('MOODLE_INTERNAL') || die();
30
 
31
/**
32
 * Base class for search engines.
33
 *
34
 * All search engines must extend this class.
35
 *
36
 * @package   core_search
37
 * @copyright 2015 Daniel Neis
38
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39
 */
40
abstract class engine {
41
 
42
    /**
43
     * The search engine configuration.
44
     *
45
     * @var \stdClass
46
     */
47
    protected $config = null;
48
 
49
    /**
50
     * Last executed query error, if there was any.
51
     * @var string
52
     */
53
    protected $queryerror = null;
54
 
55
    /**
56
     * @var array Internal cache.
57
     */
58
    protected $cachedareas = array();
59
 
60
    /**
61
     * @var array Internal cache.
62
     */
63
    protected $cachedcourses = array();
64
 
65
    /**
66
     * User data required to show their fullnames. Indexed by userid.
67
     *
68
     * @var \stdClass[]
69
     */
70
    protected static $cachedusers = array();
71
 
72
    /**
73
     * @var string Frankenstyle plugin name.
74
     */
75
    protected $pluginname = null;
76
 
77
    /**
78
     * @var bool If true, should skip schema validity check when checking the search engine is ready
79
     */
80
    protected $skipschemacheck = false;
81
 
82
    /**
83
     * Initialises the search engine configuration.
84
     *
85
     * Search engine availability should be checked separately.
86
     *
87
     * The alternate configuration option is only used to construct a special second copy of the
88
     * search engine object, as described in {@see has_alternate_configuration}.
89
     *
90
     * @param bool $alternateconfiguration If true, use alternate configuration settings
91
     * @return void
92
     */
93
    public function __construct(bool $alternateconfiguration = false) {
94
 
95
        $classname = get_class($this);
96
        if (strpos($classname, '\\') === false) {
97
            throw new \coding_exception('"' . $classname . '" class should specify its component namespace and it should be named engine.');
98
        } else if (strpos($classname, '_') === false) {
99
            throw new \coding_exception('"' . $classname . '" class namespace should be its frankenstyle name');
100
        }
101
 
102
        // This is search_xxxx config.
103
        $this->pluginname = substr($classname, 0, strpos($classname, '\\'));
104
        if ($config = get_config($this->pluginname)) {
105
            $this->config = $config;
106
        } else {
107
            $this->config = new stdClass();
108
        }
109
 
110
        // For alternate configuration, automatically replace normal configuration values with
111
        // those beginning with 'alternate'.
112
        if ($alternateconfiguration) {
113
            foreach ((array)$this->config as $key => $value) {
114
                if (preg_match('~^alternate(.*)$~', $key, $matches)) {
115
                    $this->config->{$matches[1]} = $value;
116
                }
117
            }
118
        }
119
 
120
        // Flag just in case engine needs to know it is using the alternate configuration.
121
        $this->config->alternateconfiguration = $alternateconfiguration;
122
    }
123
 
124
    /**
125
     * Returns a course instance checking internal caching.
126
     *
127
     * @param int $courseid
128
     * @return stdClass
129
     */
130
    protected function get_course($courseid) {
131
        if (!empty($this->cachedcourses[$courseid])) {
132
            return $this->cachedcourses[$courseid];
133
        }
134
 
135
        // No need to clone, only read.
136
        $this->cachedcourses[$courseid] = get_course($courseid, false);
137
 
138
        return $this->cachedcourses[$courseid];
139
    }
140
 
141
    /**
142
     * Returns user data checking the internal static cache.
143
     *
144
     * Including here the minimum required user information as this may grow big.
145
     *
146
     * @param int $userid
147
     * @return stdClass
148
     */
149
    public function get_user($userid) {
150
        global $DB;
151
 
152
        if (empty(self::$cachedusers[$userid])) {
153
            $userfieldsapi = \core_user\fields::for_name();
154
            $fields = $userfieldsapi->get_sql('', false, '', '', false)->selects;
155
            self::$cachedusers[$userid] = $DB->get_record('user', array('id' => $userid), 'id, ' . $fields);
156
        }
157
        return self::$cachedusers[$userid];
158
    }
159
 
160
    /**
161
     * Clears the users cache.
162
     *
163
     * @return null
164
     */
165
    public static function clear_users_cache() {
166
        self::$cachedusers = [];
167
    }
168
 
169
    /**
170
     * Returns a search instance of the specified area checking internal caching.
171
     *
172
     * @param string $areaid Area id
173
     * @return \core_search\base
174
     */
175
    protected function get_search_area($areaid) {
176
 
177
        if (isset($this->cachedareas[$areaid]) && $this->cachedareas[$areaid] === false) {
178
            // We already checked that area and it is not available.
179
            return false;
180
        }
181
 
182
        if (!isset($this->cachedareas[$areaid])) {
183
            // First result that matches this area.
184
 
185
            $this->cachedareas[$areaid] = \core_search\manager::get_search_area($areaid);
186
            if ($this->cachedareas[$areaid] === false) {
187
                // The area does not exist or it is not available any more.
188
 
189
                $this->cachedareas[$areaid] = false;
190
                return false;
191
            }
192
 
193
            if (!$this->cachedareas[$areaid]->is_enabled()) {
194
                // We skip the area if it is not enabled.
195
 
196
                // Marking it as false so next time we don' need to check it again.
197
                $this->cachedareas[$areaid] = false;
198
 
199
                return false;
200
            }
201
        }
202
 
203
        return $this->cachedareas[$areaid];
204
    }
205
 
206
    /**
207
     * Returns a document instance prepared to be rendered.
208
     *
209
     * @param \core_search\base $searcharea
210
     * @param array $docdata
211
     * @return \core_search\document
212
     */
213
    protected function to_document(\core_search\base $searcharea, $docdata) {
214
 
215
        list($componentname, $areaname) = \core_search\manager::extract_areaid_parts($docdata['areaid']);
216
        $doc = \core_search\document_factory::instance($docdata['itemid'], $componentname, $areaname, $this);
217
        $doc->set_data_from_engine($docdata);
218
        $doc->set_doc_url($searcharea->get_doc_url($doc));
219
        $doc->set_context_url($searcharea->get_context_url($doc));
220
        $doc->set_doc_icon($searcharea->get_doc_icon($doc));
221
 
222
        // Uses the internal caches to get required data needed to render the document later.
223
        $course = $this->get_course($doc->get('courseid'));
224
        $doc->set_extra('coursefullname', $course->fullname);
225
 
226
        if ($doc->is_set('userid')) {
227
            $user = $this->get_user($doc->get('userid'));
228
            $doc->set_extra('userfullname', fullname($user));
229
        }
230
 
231
        return $doc;
232
    }
233
 
234
    /**
235
     * Loop through given iterator of search documents
236
     * and and have the search engine back end add them
237
     * to the index.
238
     *
239
     * @param \iterator $iterator the iterator of documents to index
240
     * @param base $searcharea the area for the documents to index
241
     * @param array $options document indexing options
242
     * @return array Processed document counts
243
     */
244
    public function add_documents($iterator, $searcharea, $options) {
245
        $numrecords = 0;
246
        $numdocs = 0;
247
        $numdocsignored = 0;
248
        $numbatches = 0;
249
        $lastindexeddoc = 0;
250
        $firstindexeddoc = 0;
251
        $partial = false;
252
        $lastprogress = manager::get_current_time();
253
 
254
        $batchmode = $this->supports_add_document_batch();
255
        $currentbatch = [];
256
 
257
        foreach ($iterator as $document) {
258
            // Stop if we have exceeded the time limit (and there are still more items). Always
259
            // do at least one second's worth of documents otherwise it will never make progress.
260
            if ($lastindexeddoc !== $firstindexeddoc &&
261
                    !empty($options['stopat']) && manager::get_current_time() >= $options['stopat']) {
262
                $partial = true;
263
                break;
264
            }
265
 
266
            if (!$document instanceof \core_search\document) {
267
                continue;
268
            }
269
 
270
            if (isset($options['lastindexedtime']) && $options['lastindexedtime'] == 0) {
271
                // If we have never indexed this area before, it must be new.
272
                $document->set_is_new(true);
273
            }
274
 
275
            if ($options['indexfiles']) {
276
                // Attach files if we are indexing.
277
                $searcharea->attach_files($document);
278
            }
279
 
280
            if ($batchmode && strlen($document->get('content')) <= $this->get_batch_max_content()) {
281
                $currentbatch[] = $document;
282
                if (count($currentbatch) >= $this->get_batch_max_documents()) {
283
                    [$processed, $failed, $batches] = $this->add_document_batch($currentbatch, $options['indexfiles']);
284
                    $numdocs += $processed;
285
                    $numdocsignored += $failed;
286
                    $numbatches += $batches;
287
                    $currentbatch = [];
288
                }
289
            } else {
290
                if ($this->add_document($document, $options['indexfiles'])) {
291
                    $numdocs++;
292
                } else {
293
                    $numdocsignored++;
294
                }
295
                $numbatches++;
296
            }
297
 
298
            $lastindexeddoc = $document->get('modified');
299
            if (!$firstindexeddoc) {
300
                $firstindexeddoc = $lastindexeddoc;
301
            }
302
            $numrecords++;
303
 
304
            // If indexing the area takes a long time, periodically output progress information.
305
            if (isset($options['progress'])) {
306
                $now = manager::get_current_time();
307
                if ($now - $lastprogress >= manager::DISPLAY_INDEXING_PROGRESS_EVERY) {
308
                    $lastprogress = $now;
309
                    // The first date format is the same used in \core\cron::trace_time_and_memory().
310
                    $options['progress']->output(date('H:i:s', (int)$now) . ': Done to ' . userdate(
311
                            $lastindexeddoc, get_string('strftimedatetimeshort', 'langconfig')), 1);
312
                }
313
            }
314
        }
315
 
316
        // Add remaining documents from batch.
317
        if ($batchmode && $currentbatch) {
318
            [$processed, $failed, $batches] = $this->add_document_batch($currentbatch, $options['indexfiles']);
319
            $numdocs += $processed;
320
            $numdocsignored += $failed;
321
            $numbatches += $batches;
322
        }
323
 
324
        return [$numrecords, $numdocs, $numdocsignored, $lastindexeddoc, $partial, $numbatches];
325
    }
326
 
327
    /**
328
     * Returns the plugin name.
329
     *
330
     * @return string Frankenstyle plugin name.
331
     */
332
    public function get_plugin_name() {
333
        return $this->pluginname;
334
    }
335
 
336
    /**
337
     * Gets the document class used by this search engine.
338
     *
339
     * Search engines can overwrite \core_search\document with \search_ENGINENAME\document class.
340
     *
341
     * Looks for a document class in the current search engine namespace, falling back to \core_search\document.
342
 
343
     * Publicly available because search areas do not have access to the engine details,
344
     * \core_search\document_factory accesses this function.
345
     *
346
     * @return string
347
     */
348
    public function get_document_classname() {
349
        $classname = $this->pluginname . '\\document';
350
        if (!class_exists($classname)) {
351
            $classname = '\\core_search\\document';
352
        }
353
        return $classname;
354
    }
355
 
356
    /**
357
     * Run any pre-indexing operations.
358
     *
359
     * Should be overwritten if the search engine needs to do any pre index preparation.
360
     *
361
     * @param bool $fullindex True if a full index will be performed
362
     * @return void
363
     */
364
    public function index_starting($fullindex = false) {
365
        // Nothing by default.
366
    }
367
 
368
    /**
369
     * Run any post indexing operations.
370
     *
371
     * Should be overwritten if the search engine needs to do any post index cleanup.
372
     *
373
     * @param int $numdocs The number of documents that were added to the index
374
     * @param bool $fullindex True if a full index was performed
375
     * @return void
376
     */
377
    public function index_complete($numdocs = 0, $fullindex = false) {
378
        // Nothing by default.
379
    }
380
 
381
    /**
382
     * Do anything that may need to be done before an area is indexed.
383
     *
384
     * @param \core_search\base $searcharea The search area that was complete
385
     * @param bool $fullindex True if a full index is being performed
386
     * @return void
387
     */
388
    public function area_index_starting($searcharea, $fullindex = false) {
389
        // Nothing by default.
390
    }
391
 
392
    /**
393
     * Do any area cleanup needed, and do anything to confirm contents.
394
     *
395
     * Return false to prevent the search area completed time and stats from being updated.
396
     *
397
     * @param \core_search\base $searcharea The search area that was complete
398
     * @param int $numdocs The number of documents that were added to the index
399
     * @param bool $fullindex True if a full index is being performed
400
     * @return bool True means that data is considered indexed
401
     */
402
    public function area_index_complete($searcharea, $numdocs = 0, $fullindex = false) {
403
        return true;
404
    }
405
 
406
    /**
407
     * Optimizes the search engine.
408
     *
409
     * Should be overwritten if the search engine can optimize its contents.
410
     *
411
     * @return void
412
     */
413
    public function optimize() {
414
        // Nothing by default.
415
        mtrace('The ' . get_string('pluginname', $this->get_plugin_name()) .
416
                ' search engine does not require automatic optimization.');
417
    }
418
 
419
    /**
420
     * Does the system satisfy all the requirements.
421
     *
422
     * Should be overwritten if the search engine has any system dependencies
423
     * that needs to be checked.
424
     *
425
     * @return bool
426
     */
427
    public function is_installed() {
428
        return true;
429
    }
430
 
431
    /**
432
     * Returns any error reported by the search engine when executing the provided query.
433
     *
434
     * It should be called from static::execute_query when an exception is triggered.
435
     *
436
     * @return string
437
     */
438
    public function get_query_error() {
439
        return $this->queryerror;
440
    }
441
 
442
    /**
443
     * Returns the total number of documents available for the most recent call to execute_query.
444
     *
445
     * This can be an estimate, but should get more accurate the higher the limited passed to execute_query is.
446
     * To do that, the engine can use (actual result returned count + count of unchecked documents), or
447
     * (total possible docs - docs that have been checked and rejected).
448
     *
449
     * Engine can limit to manager::MAX_RESULTS if there is cost to determining more.
450
     * If this cannot be computed in a reasonable way, manager::MAX_RESULTS may be returned.
451
     *
452
     * @return int
453
     */
454
    abstract public function get_query_total_count();
455
 
456
    /**
457
     * Return true if file indexing is supported and enabled. False otherwise.
458
     *
459
     * @return bool
460
     */
461
    public function file_indexing_enabled() {
462
        return false;
463
    }
464
 
465
    /**
466
     * Clears the current query error value.
467
     *
468
     * @return void
469
     */
470
    public function clear_query_error() {
471
        $this->queryerror = null;
472
    }
473
 
474
    /**
475
     * Is the server ready to use?
476
     *
477
     * This should also check that the search engine configuration is ok.
478
     *
479
     * If the function $this->should_skip_schema_check() returns true, then this function may leave
480
     * out time-consuming checks that the schema is valid. (This allows for improved performance on
481
     * critical pages such as the main search form.)
482
     *
483
     * @return true|string Returns true if all good or an error string.
484
     */
485
    abstract function is_server_ready();
486
 
487
    /**
488
     * Tells the search engine to skip any time-consuming checks that it might do as part of the
489
     * is_server_ready function, and only carry out a basic check that it can contact the server.
490
     *
491
     * This setting is not remembered and applies only to the current request.
492
     *
493
     * @since Moodle 3.5
494
     * @param bool $skip True to skip the checks, false to start checking again
495
     */
496
    public function skip_schema_check($skip = true) {
497
        $this->skipschemacheck = $skip;
498
    }
499
 
500
    /**
501
     * For use by subclasses. The engine can call this inside is_server_ready to check whether it
502
     * should skip time-consuming schema checks.
503
     *
504
     * @since Moodle 3.5
505
     * @return bool True if schema checks should be skipped
506
     */
507
    protected function should_skip_schema_check() {
508
        return $this->skipschemacheck;
509
    }
510
 
511
    /**
512
     * Adds a document to the search engine.
513
     *
514
     * @param document $document
515
     * @param bool     $fileindexing True if file indexing is to be used
516
     * @return bool    False if the file was skipped or failed, true on success
517
     */
518
    abstract function add_document($document, $fileindexing = false);
519
 
520
    /**
521
     * Adds multiple documents to the search engine.
522
     *
523
     * It should return the number successfully processed, and the number of batches they were
524
     * processed in (for example if you add 100 documents and there is an error processing one of
525
     * those documents, and it took 4 batches, it would return [99, 1, 4]).
526
     *
527
     * If the engine implements this, it should return true to {@see supports_add_document_batch}.
528
     *
529
     * The system will only call this function with up to {@see get_batch_max_documents} documents,
530
     * and each document in the batch will have content no larger than specified by
531
     * {@see get_batch_max_content}.
532
     *
533
     * @param document[] $documents Documents to add
534
     * @param bool $fileindexing True if file indexing is to be used
535
     * @return int[] Array of three elements, successfully processed, failed processed, batch count
536
     */
537
    public function add_document_batch(array $documents, bool $fileindexing = false): array {
538
        throw new \coding_exception('add_document_batch not supported by this engine');
539
    }
540
 
541
    /**
542
     * Executes the query on the engine.
543
     *
544
     * Implementations of this function should check user context array to limit the results to contexts where the
545
     * user have access. They should also limit the owneruserid field to manger::NO_OWNER_ID or the current user's id.
546
     * Engines must use area->check_access() to confirm user access.
547
     *
548
     * Engines should reasonably attempt to fill up to limit with valid results if they are available.
549
     *
550
     * The $filters object may include the following fields (optional except q):
551
     * - q: value of main search field; results should include this text
552
     * - title: if included, title must match this search
553
     * - areaids: array of search area id strings (only these areas will be searched)
554
     * - courseids: array of course ids (only these courses will be searched)
555
     * - groupids: array of group ids (only results specifically from these groupids will be
556
     *   searched) - this option will be ignored if the search engine doesn't support groups
557
     *
558
     * The $accessinfo parameter has two different values (for historical compatibility). If the
559
     * engine returns false to supports_group_filtering then it is an array of user contexts, or
560
     * true if the user can access all contexts. (This parameter used to be called $usercontexts.)
561
     * If the engine returns true to supports_group_filtering then it will be an object containing
562
     * these fields:
563
     * - everything (true if admin is searching with no restrictions)
564
     * - usercontexts (same as above)
565
     * - separategroupscontexts (array of context ids where separate groups are used)
566
     * - visiblegroupscontextsareas (array of subset of those where some areas use visible groups)
567
     * - usergroups (array of relevant group ids that user belongs to)
568
     *
569
     * The engine should apply group restrictions to those contexts listed in the
570
     * 'separategroupscontexts' array. In these contexts, it shouled only include results if the
571
     * groupid is not set, or if the groupid matches one of the values in USER_GROUPS array, or
572
     * if the search area is one of those listed in 'visiblegroupscontextsareas' for that context.
573
     *
574
     * @param \stdClass $filters Query and filters to apply.
575
     * @param \stdClass $accessinfo Information about the contexts the user can access
576
     * @param  int      $limit The maximum number of results to return. If empty, limit to manager::MAX_RESULTS.
577
     * @return \core_search\document[] Results or false if no results
578
     */
579
    abstract public function execute_query($filters, $accessinfo, $limit = 0);
580
 
581
    /**
582
     * Delete all documents.
583
     *
584
     * @param string $areaid To filter by area
585
     * @return void
586
     */
587
    abstract function delete($areaid = null);
588
 
589
    /**
590
     * Deletes information related to a specific context id. This should be used when the context
591
     * itself is deleted from Moodle.
592
     *
593
     * This only deletes information for the specified context - not for any child contexts.
594
     *
595
     * This function is optional; if not supported it will return false and the information will
596
     * not be deleted from the search index.
597
     *
598
     * If an engine implements this function it should also implement delete_index_for_course;
599
     * otherwise, nothing will be deleted when users delete an entire course at once.
600
     *
601
     * @param int $oldcontextid ID of context that has been deleted
602
     * @return bool True if implemented
603
     * @throws \core_search\engine_exception Engines may throw this exception for any problem
604
     */
605
    public function delete_index_for_context(int $oldcontextid) {
606
        return false;
607
    }
608
 
609
    /**
610
     * Deletes information related to a specific course id. This should be used when the course
611
     * itself is deleted from Moodle.
612
     *
613
     * This deletes all information relating to that course from the index, including all child
614
     * contexts.
615
     *
616
     * This function is optional; if not supported it will return false and the information will
617
     * not be deleted from the search index.
618
     *
619
     * If an engine implements this function then, ideally, it should also implement
620
     * delete_index_for_context so that deletion of single activities/blocks also works.
621
     *
622
     * @param int $oldcourseid ID of course that has been deleted
623
     * @return bool True if implemented
624
     * @throws \core_search\engine_exception Engines may throw this exception for any problem
625
     */
626
    public function delete_index_for_course(int $oldcourseid) {
627
        return false;
628
    }
629
 
630
    /**
631
     * Checks that the schema is the latest version. If the version stored in config does not match
632
     * the current, this function will attempt to upgrade the schema.
633
     *
634
     * @return bool|string True if schema is OK, a string if user needs to take action
635
     */
636
    public function check_latest_schema() {
637
        if (empty($this->config->schemaversion)) {
638
            $currentversion = 0;
639
        } else {
640
            $currentversion = $this->config->schemaversion;
641
        }
642
        if ($currentversion < document::SCHEMA_VERSION) {
643
            return $this->update_schema((int)$currentversion, (int)document::SCHEMA_VERSION);
644
        } else {
645
            return true;
646
        }
647
    }
648
 
649
    /**
650
     * Usually called by the engine; marks that the schema has been updated.
651
     *
652
     * @param int $version Records the schema version now applied
653
     */
654
    public function record_applied_schema_version($version) {
655
        set_config('schemaversion', $version, $this->pluginname);
656
    }
657
 
658
    /**
659
     * Requests the search engine to upgrade the schema. The engine should update the schema if
660
     * possible/necessary, and should ensure that record_applied_schema_version is called as a
661
     * result.
662
     *
663
     * If it is not possible to upgrade the schema at the moment, it can do nothing and return; the
664
     * function will be called again next time search is initialised.
665
     *
666
     * The default implementation just returns, with a DEBUG_DEVELOPER warning.
667
     *
668
     * @param int $oldversion Old schema version
669
     * @param int $newversion New schema version
670
     * @return bool|string True if schema is updated successfully, a string if it needs updating manually
671
     */
672
    protected function update_schema($oldversion, $newversion) {
673
        debugging('Unable to update search engine schema: ' . $this->pluginname, DEBUG_DEVELOPER);
674
        return get_string('schemanotupdated', 'search');
675
    }
676
 
677
    /**
678
     * Checks if this search engine supports groups.
679
     *
680
     * Note that returning true to this function causes the parameters to execute_query to be
681
     * passed differently!
682
     *
683
     * In order to implement groups and return true to this function, the search engine should:
684
     *
685
     * 1. Handle the fields ->separategroupscontexts and ->usergroups in the $accessinfo parameter
686
     *    to execute_query (ideally, using these to automatically restrict search results).
687
     * 2. Support the optional groupids parameter in the $filter parameter for execute_query to
688
     *    restrict results to only those where the stored groupid matches the given value.
689
     *
690
     * @return bool True if this engine supports searching by group id field
691
     */
692
    public function supports_group_filtering() {
693
        return false;
694
    }
695
 
696
    /**
697
     * Obtain a list of results orders (and names for them) that are supported by this
698
     * search engine in the given context.
699
     *
700
     * By default, engines sort by relevance only.
701
     *
702
     * @param \context $context Context that the user requested search from
703
     * @return array Array from order name => display text
704
     */
705
    public function get_supported_orders(\context $context) {
706
        return ['relevance' => get_string('order_relevance', 'search')];
707
    }
708
 
709
    /**
710
     * Checks if the search engine supports searching by user.
711
     *
712
     * If it returns true to this function, the search engine should support the 'userids' option
713
     * in the $filters value passed to execute_query(), returning only items where the userid in
714
     * the search document matches one of those user ids.
715
     *
716
     * @return bool True if the search engine supports searching by user
717
     */
718
    public function supports_users() {
719
        return false;
720
    }
721
 
722
    /**
723
     * Checks if the search engine supports adding documents in a batch.
724
     *
725
     * If it returns true to this function, the search engine must implement the add_document_batch
726
     * function.
727
     *
728
     * @return bool True if the search engine supports adding documents in a batch
729
     */
730
    public function supports_add_document_batch(): bool {
731
        return false;
732
    }
733
 
734
    /**
735
     * Gets the maximum number of documents to send together in batch mode.
736
     *
737
     * Only relevant if the engine returns true to {@see supports_add_document_batch}.
738
     *
739
     * Can be overridden by search engine if required.
740
     *
741
     * @var int Number of documents to send together in batch mode, default 100.
742
     */
743
    public function get_batch_max_documents(): int {
744
        return 100;
745
    }
746
 
747
    /**
748
     * Gets the maximum size of document content to be included in a shared batch (if the
749
     * document is bigger then it will be sent on its own; batching does not provide a performance
750
     * improvement for big documents anyway).
751
     *
752
     * Only relevant if the engine returns true to {@see supports_add_document_batch}.
753
     *
754
     * Can be overridden by search engine if required.
755
     *
756
     * @return int Max size in bytes, default 1MB
757
     */
758
    public function get_batch_max_content(): int {
759
        return 1024 * 1024;
760
    }
761
 
762
    /**
763
     * Checks if the search engine has an alternate configuration.
764
     *
765
     * This is used where the same search engine class supports two different configurations,
766
     * which are both shown on the settings screen. The alternate configuration is selected by
767
     * passing 'true' parameter to the constructor.
768
     *
769
     * The feature is used when a different connection is in use for indexing vs. querying
770
     * the search engine.
771
     *
772
     * This function should only return true if the engine supports an alternate configuration
773
     * and the user has filled in the settings. (We do not need to test they are valid, that will
774
     * happen as normal.)
775
     *
776
     * @return bool True if an alternate configuration is defined
777
     */
778
    public function has_alternate_configuration(): bool {
779
        return false;
780
    }
781
}