MDL-60981 core_search: Add get_contexts_to_reindex API

This new API returns a list of contexts for each search area. This
allows the areas to be reindexed in a sensible order (roughly
speaking, newest first) and also allows this to be controlled by
each area.

An implementation in the forum module means that forums are ordered
by the date of the most recent discussion, so that active forums
will be reindexed early even if they were created a long time ago.
This commit is contained in:
sam marshall 2017-12-07 12:26:55 +00:00
parent 9993c1d02c
commit 25564a784b
9 changed files with 302 additions and 0 deletions

View file

@ -285,6 +285,14 @@ abstract class base {
* The default implementation returns false, indicating that this facility is not supported and
* the older get_recordset_by_timestamp function should be used.
*
* This function must accept all possible values for the $context parameter. For example, if
* you are implementing this function for the forum module, it should still operate correctly
* if called with the context for a glossary module, or for the HTML block. (In these cases
* where it will not return any data, it may return null.)
*
* The $context parameter can also be null or the system context; both of these indicate that
* all data, without context restriction, should be returned.
*
* @param int $modifiedfrom Return only records modified after this date
* @param \context|null $context Context (null means no context restriction)
* @return \moodle_recordset|null|false Recordset / null if no results / false if not supported
@ -474,4 +482,18 @@ abstract class base {
return [$sql, $params];
}
/**
* Gets a list of all contexts to reindex when reindexing this search area. The list should be
* returned in an order that is likely to be suitable when reindexing, for example with newer
* contexts first.
*
* The default implementation simply returns the system context, which will result in
* reindexing everything in normal date order (oldest first).
*
* @return \Iterator Iterator of contexts to reindex
*/
public function get_contexts_to_reindex() {
return new \ArrayIterator([\context_system::instance()]);
}
}

View file

@ -343,4 +343,59 @@ abstract class base_block extends base {
return [$sql, $params];
}
/**
* This can be used in subclasses to change ordering within the get_contexts_to_reindex
* function.
*
* It returns 2 values:
* - Extra SQL joins (tables block_instances 'bi' and context 'x' already exist).
* - An ORDER BY value which must use aggregate functions, by default 'MAX(bi.timemodified) DESC'.
*
* Note the query already includes a GROUP BY on the context fields, so if your joins result
* in multiple rows, you can use aggregate functions in the ORDER BY. See forum for an example.
*
* @return string[] Array with 2 elements; extra joins for the query, and ORDER BY value
*/
protected function get_contexts_to_reindex_extra_sql() {
return ['', 'MAX(bi.timemodified) DESC'];
}
/**
* Gets a list of all contexts to reindex when reindexing this search area.
*
* For blocks, the default is to return all contexts for blocks of that type, that are on a
* course page, in order of time added (most recent first).
*
* @return \Iterator Iterator of contexts to reindex
* @throws \moodle_exception If any DB error
*/
public function get_contexts_to_reindex() {
global $DB;
list ($extrajoins, $dborder) = $this->get_contexts_to_reindex_extra_sql();
$contexts = [];
$selectcolumns = \context_helper::get_preload_record_columns_sql('x');
$groupbycolumns = '';
foreach (\context_helper::get_preload_record_columns('x') as $column => $thing) {
if ($groupbycolumns !== '') {
$groupbycolumns .= ',';
}
$groupbycolumns .= $column;
}
$rs = $DB->get_recordset_sql("
SELECT $selectcolumns
FROM {block_instances} bi
JOIN {context} x ON x.instanceid = bi.id AND x.contextlevel = ?
JOIN {context} parent ON parent.id = bi.parentcontextid
$extrajoins
WHERE bi.blockname = ? AND parent.contextlevel = ?
GROUP BY $groupbycolumns
ORDER BY $dborder", [CONTEXT_BLOCK, $this->get_block_name(), CONTEXT_COURSE]);
return new \core\dml\recordset_walk($rs, function($rec) {
$id = $rec->ctxid;
\context_helper::preload_from_record($rec);
return \context::instance_by_id($id);
});
}
}

View file

@ -192,4 +192,57 @@ abstract class base_mod extends base {
return [$sql, $params];
}
/**
* This can be used in subclasses to change ordering within the get_contexts_to_reindex
* function.
*
* It returns 2 values:
* - Extra SQL joins (tables course_modules 'cm' and context 'x' already exist).
* - An ORDER BY value which must use aggregate functions, by default 'MAX(cm.added) DESC'.
*
* Note the query already includes a GROUP BY on the context fields, so if your joins result
* in multiple rows, you can use aggregate functions in the ORDER BY. See forum for an example.
*
* @return string[] Array with 2 elements; extra joins for the query, and ORDER BY value
*/
protected function get_contexts_to_reindex_extra_sql() {
return ['', 'MAX(cm.added) DESC'];
}
/**
* Gets a list of all contexts to reindex when reindexing this search area.
*
* For modules, the default is to return all contexts for modules of that type, in order of
* time added (most recent first).
*
* @return \Iterator Iterator of contexts to reindex
* @throws \moodle_exception If any DB error
*/
public function get_contexts_to_reindex() {
global $DB;
list ($extrajoins, $dborder) = $this->get_contexts_to_reindex_extra_sql();
$contexts = [];
$selectcolumns = \context_helper::get_preload_record_columns_sql('x');
$groupbycolumns = '';
foreach (\context_helper::get_preload_record_columns('x') as $column => $thing) {
if ($groupbycolumns !== '') {
$groupbycolumns .= ',';
}
$groupbycolumns .= $column;
}
$rs = $DB->get_recordset_sql("
SELECT $selectcolumns
FROM {course_modules} cm
JOIN {context} x ON x.instanceid = cm.id AND x.contextlevel = ?
$extrajoins
WHERE cm.module = (SELECT id FROM {modules} WHERE name = ?)
GROUP BY $groupbycolumns
ORDER BY $dborder", [CONTEXT_MODULE, $this->get_module_name()]);
return new \core\dml\recordset_walk($rs, function($rec) {
$id = $rec->ctxid;
\context_helper::preload_from_record($rec);
return \context::instance_by_id($id);
});
}
}