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.