diff --git a/lib/db/install.xml b/lib/db/install.xml
index bd36ac76690..e3c9f7cdb6f 100644
--- a/lib/db/install.xml
+++ b/lib/db/install.xml
@@ -3720,5 +3720,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php
index d0fb99f05b2..8bf5154041a 100644
--- a/lib/db/upgrade.php
+++ b/lib/db/upgrade.php
@@ -2601,5 +2601,31 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2017092900.00);
}
+ if ($oldversion < 2017100600.01) {
+
+ // Define table search_index_requests to be created.
+ $table = new xmldb_table('search_index_requests');
+
+ // Adding fields to table search_index_requests.
+ $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+ $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('searcharea', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('timerequested', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('partialarea', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('partialtime', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+
+ // Adding keys to table search_index_requests.
+ $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+ $table->add_key('contextid', XMLDB_KEY_FOREIGN, array('contextid'), 'context', array('id'));
+
+ // Conditionally launch create table for search_index_requests.
+ if (!$dbman->table_exists($table)) {
+ $dbman->create_table($table);
+ }
+
+ // Main savepoint reached.
+ upgrade_main_savepoint(true, 2017100600.01);
+ }
+
return true;
}
diff --git a/search/classes/manager.php b/search/classes/manager.php
index c8425a157c5..47b4b23406a 100644
--- a/search/classes/manager.php
+++ b/search/classes/manager.php
@@ -637,6 +637,8 @@ class manager {
* @return bool Whether there was any updated document or not.
*/
public function index($fullindex = false, $timelimit = 0, \progress_trace $progress = null) {
+ global $DB;
+
// Cannot combine time limit with reindex.
if ($timelimit && $fullindex) {
throw new \coding_exception('Cannot apply time limit when reindexing');
@@ -687,6 +689,10 @@ class manager {
if ($fullindex === true) {
$referencestarttime = 0;
+
+ // For full index, we delete any queued context index requests, as those will
+ // obviously be met by the full index.
+ $DB->delete_records('search_index_requests');
} else {
$partial = get_config($componentconfigname, $varname . '_partial');
if ($partial) {
@@ -905,4 +911,36 @@ class manager {
return false;
}
+
+ /**
+ * Requests that a specific context is indexed by the scheduled task. The context will be
+ * added to a queue which is processed by the task.
+ *
+ * This is used after a restore to ensure that restored items are indexed, even though their
+ * modified time will be older than the latest indexed.
+ *
+ * @param \context $context Context to index within
+ * @param string $areaid Area to index, '' = all areas
+ */
+ public static function request_index(\context $context, $areaid = '') {
+ global $DB;
+
+ // Check through existing requests for this context or any parent context.
+ list ($contextsql, $contextparams) = $DB->get_in_or_equal(
+ $context->get_parent_context_ids(true));
+ $existing = $DB->get_records_select('search_index_requests',
+ 'contextid ' . $contextsql, $contextparams, '', 'id, searcharea, partialarea');
+ foreach ($existing as $rec) {
+ // If we haven't started processing the existing request yet, and it covers the same
+ // area (or all areas) then that will be sufficient so don't add anything else.
+ if ($rec->partialarea === '' && ($rec->searcharea === $areaid || $rec->searcharea === '')) {
+ return;
+ }
+ }
+
+ // No suitable existing request, so add a new one.
+ $newrecord = [ 'contextid' => $context->id, 'searcharea' => $areaid,
+ 'timerequested' => time(), 'partialarea' => '', 'partialtime' => 0 ];
+ $DB->insert_record('search_index_requests', $newrecord);
+ }
}
diff --git a/search/tests/manager_test.php b/search/tests/manager_test.php
index 9ae2a4dab37..0c1984cca1a 100644
--- a/search/tests/manager_test.php
+++ b/search/tests/manager_test.php
@@ -560,4 +560,82 @@ class search_manager_testcase extends advanced_testcase {
$this->assertTrue(testable_core_search::is_search_area('\\mod_forum\\search\\post'));
$this->assertTrue(testable_core_search::is_search_area('mod_forum\\search\\post'));
}
+
+ /**
+ * Tests the request_index function used for reindexing certain contexts. This only tests
+ * adding things to the request list, it doesn't test that they are actually indexed by the
+ * scheduled task.
+ */
+ public function test_request_index() {
+ global $DB;
+
+ $this->resetAfterTest();
+
+ $course1 = $this->getDataGenerator()->create_course();
+ $course1ctx = context_course::instance($course1->id);
+ $course2 = $this->getDataGenerator()->create_course();
+ $course2ctx = context_course::instance($course2->id);
+ $forum1 = $this->getDataGenerator()->create_module('forum', ['course' => $course1->id]);
+ $forum1ctx = context_module::instance($forum1->cmid);
+ $forum2 = $this->getDataGenerator()->create_module('forum', ['course' => $course2->id]);
+ $forum2ctx = context_module::instance($forum2->cmid);
+
+ // Initially no requests.
+ $this->assertEquals(0, $DB->count_records('search_index_requests'));
+
+ // Request update for course 1, all areas.
+ \core_search\manager::request_index($course1ctx);
+
+ // Check all details of entry.
+ $results = array_values($DB->get_records('search_index_requests'));
+ $this->assertCount(1, $results);
+ $this->assertEquals($course1ctx->id, $results[0]->contextid);
+ $this->assertEquals('', $results[0]->searcharea);
+ $now = time();
+ $this->assertLessThanOrEqual($now, $results[0]->timerequested);
+ $this->assertGreaterThan($now - 10, $results[0]->timerequested);
+ $this->assertEquals('', $results[0]->partialarea);
+ $this->assertEquals(0, $results[0]->partialtime);
+
+ // Request forum 1, all areas; not added as covered by course 1.
+ \core_search\manager::request_index($forum1ctx);
+ $this->assertEquals(1, $DB->count_records('search_index_requests'));
+
+ // Request forum 1, specific area; not added as covered by course 1 all areas.
+ \core_search\manager::request_index($forum1ctx, 'forum-post');
+ $this->assertEquals(1, $DB->count_records('search_index_requests'));
+
+ // Request course 1 again, specific area; not added as covered by all areas.
+ \core_search\manager::request_index($course1ctx, 'forum-post');
+ $this->assertEquals(1, $DB->count_records('search_index_requests'));
+
+ // Request course 1 again, all areas; not needed as covered already.
+ \core_search\manager::request_index($course1ctx);
+ $this->assertEquals(1, $DB->count_records('search_index_requests'));
+
+ // Request course 2, specific area.
+ \core_search\manager::request_index($course2ctx, 'label-activity');
+ // Note: I'm ordering by ID for convenience - this is dangerous in real code (see MDL-43447)
+ // but in a unit test it shouldn't matter as nobody is using clustered databases for unit
+ // test.
+ $results = array_values($DB->get_records('search_index_requests', null, 'id'));
+ $this->assertCount(2, $results);
+ $this->assertEquals($course1ctx->id, $results[0]->contextid);
+ $this->assertEquals($course2ctx->id, $results[1]->contextid);
+ $this->assertEquals('label-activity', $results[1]->searcharea);
+
+ // Request forum 2, same specific area; not added.
+ \core_search\manager::request_index($forum2ctx, 'label-activity');
+ $this->assertEquals(2, $DB->count_records('search_index_requests'));
+
+ // Request forum 2, different specific area; added.
+ \core_search\manager::request_index($forum2ctx, 'forum-post');
+ $this->assertEquals(3, $DB->count_records('search_index_requests'));
+
+ // Request forum 2, all areas; also added. (Note: This could obviously remove the previous
+ // one, but for simplicity, I didn't make it do that; also it could perhaps cause problems
+ // if we had already begun processing the previous entry.)
+ \core_search\manager::request_index($forum2ctx);
+ $this->assertEquals(4, $DB->count_records('search_index_requests'));
+ }
}
diff --git a/version.php b/version.php
index a8c58ddd6fb..0a6f2d6f3f8 100644
--- a/version.php
+++ b/version.php
@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
-$version = 2017100600.00; // YYYYMMDD = weekly release date of this DEV branch.
+$version = 2017100600.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.