Merge branch 'wip-MDL-58697-33-assigngrading' of git://github.com/adamann2/moodle...
[moodle.git] / search / classes / engine.php
blob52c4bb371d6876c67ced0988753c691199a08e12
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.
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/>.
17 /**
18 * Base class for search engines.
20 * All search engines must extend this class.
22 * @package core_search
23 * @copyright 2015 Daniel Neis
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 namespace core_search;
29 defined('MOODLE_INTERNAL') || die();
31 /**
32 * Base class for search engines.
34 * All search engines must extend this class.
36 * @package core_search
37 * @copyright 2015 Daniel Neis
38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40 abstract class engine {
42 /**
43 * The search engine configuration.
45 * @var stdClass
47 protected $config = null;
49 /**
50 * Last executed query error, if there was any.
51 * @var string
53 protected $queryerror = null;
55 /**
56 * @var array Internal cache.
58 protected $cachedareas = array();
60 /**
61 * @var array Internal cache.
63 protected $cachedcourses = array();
65 /**
66 * User data required to show their fullnames. Indexed by userid.
68 * @var stdClass[]
70 protected static $cachedusers = array();
72 /**
73 * @var string Frankenstyle plugin name.
75 protected $pluginname = null;
77 /**
78 * Initialises the search engine configuration.
80 * Search engine availability should be checked separately.
82 * @return void
84 public function __construct() {
86 $classname = get_class($this);
87 if (strpos($classname, '\\') === false) {
88 throw new \coding_exception('"' . $classname . '" class should specify its component namespace and it should be named engine.');
89 } else if (strpos($classname, '_') === false) {
90 throw new \coding_exception('"' . $classname . '" class namespace should be its frankenstyle name');
93 // This is search_xxxx config.
94 $this->pluginname = substr($classname, 0, strpos($classname, '\\'));
95 if ($config = get_config($this->pluginname)) {
96 $this->config = $config;
97 } else {
98 $this->config = new stdClass();
103 * Returns a course instance checking internal caching.
105 * @param int $courseid
106 * @return stdClass
108 protected function get_course($courseid) {
109 if (!empty($this->cachedcourses[$courseid])) {
110 return $this->cachedcourses[$courseid];
113 // No need to clone, only read.
114 $this->cachedcourses[$courseid] = get_course($courseid, false);
116 return $this->cachedcourses[$courseid];
120 * Returns user data checking the internal static cache.
122 * Including here the minimum required user information as this may grow big.
124 * @param int $userid
125 * @return stdClass
127 public function get_user($userid) {
128 global $DB;
130 if (empty(self::$cachedusers[$userid])) {
131 $fields = get_all_user_name_fields(true);
132 self::$cachedusers[$userid] = $DB->get_record('user', array('id' => $userid), 'id, ' . $fields);
134 return self::$cachedusers[$userid];
138 * Returns a search instance of the specified area checking internal caching.
140 * @param string $areaid Area id
141 * @return \core_search\base
143 protected function get_search_area($areaid) {
145 if (isset($this->cachedareas[$areaid]) && $this->cachedareas[$areaid] === false) {
146 // We already checked that area and it is not available.
147 return false;
150 if (!isset($this->cachedareas[$areaid])) {
151 // First result that matches this area.
153 $this->cachedareas[$areaid] = \core_search\manager::get_search_area($areaid);
154 if ($this->cachedareas[$areaid] === false) {
155 // The area does not exist or it is not available any more.
157 $this->cachedareas[$areaid] = false;
158 return false;
161 if (!$this->cachedareas[$areaid]->is_enabled()) {
162 // We skip the area if it is not enabled.
164 // Marking it as false so next time we don' need to check it again.
165 $this->cachedareas[$areaid] = false;
167 return false;
171 return $this->cachedareas[$areaid];
175 * Returns a document instance prepared to be rendered.
177 * @param \core_search\base $searcharea
178 * @param array $docdata
179 * @return \core_search\document
181 protected function to_document(\core_search\base $searcharea, $docdata) {
183 list($componentname, $areaname) = \core_search\manager::extract_areaid_parts($docdata['areaid']);
184 $doc = \core_search\document_factory::instance($docdata['itemid'], $componentname, $areaname, $this);
185 $doc->set_data_from_engine($docdata);
186 $doc->set_doc_url($searcharea->get_doc_url($doc));
187 $doc->set_context_url($searcharea->get_context_url($doc));
189 // Uses the internal caches to get required data needed to render the document later.
190 $course = $this->get_course($doc->get('courseid'));
191 $doc->set_extra('coursefullname', $course->fullname);
193 if ($doc->is_set('userid')) {
194 $user = $this->get_user($doc->get('userid'));
195 $doc->set_extra('userfullname', fullname($user));
198 return $doc;
202 * Returns the plugin name.
204 * @return string Frankenstyle plugin name.
206 public function get_plugin_name() {
207 return $this->pluginname;
211 * Gets the document class used by this search engine.
213 * Search engines can overwrite \core_search\document with \search_ENGINENAME\document class.
215 * Looks for a document class in the current search engine namespace, falling back to \core_search\document.
217 * Publicly available because search areas do not have access to the engine details,
218 * \core_search\document_factory accesses this function.
220 * @return string
222 public function get_document_classname() {
223 $classname = $this->pluginname . '\\document';
224 if (!class_exists($classname)) {
225 $classname = '\\core_search\\document';
227 return $classname;
231 * Run any pre-indexing operations.
233 * Should be overwritten if the search engine needs to do any pre index preparation.
235 * @param bool $fullindex True if a full index will be performed
236 * @return void
238 public function index_starting($fullindex = false) {
239 // Nothing by default.
243 * Run any post indexing operations.
245 * Should be overwritten if the search engine needs to do any post index cleanup.
247 * @param int $numdocs The number of documents that were added to the index
248 * @param bool $fullindex True if a full index was performed
249 * @return void
251 public function index_complete($numdocs = 0, $fullindex = false) {
252 // Nothing by default.
256 * Do anything that may need to be done before an area is indexed.
258 * @param \core_search\base $searcharea The search area that was complete
259 * @param bool $fullindex True if a full index is being performed
260 * @return void
262 public function area_index_starting($searcharea, $fullindex = false) {
263 // Nothing by default.
267 * Do any area cleanup needed, and do anything to confirm contents.
269 * Return false to prevent the search area completed time and stats from being updated.
271 * @param \core_search\base $searcharea The search area that was complete
272 * @param int $numdocs The number of documents that were added to the index
273 * @param bool $fullindex True if a full index is being performed
274 * @return bool True means that data is considered indexed
276 public function area_index_complete($searcharea, $numdocs = 0, $fullindex = false) {
277 return true;
281 * Optimizes the search engine.
283 * Should be overwritten if the search engine can optimize its contents.
285 * @return void
287 public function optimize() {
288 // Nothing by default.
292 * Does the system satisfy all the requirements.
294 * Should be overwritten if the search engine has any system dependencies
295 * that needs to be checked.
297 * @return bool
299 public function is_installed() {
300 return true;
304 * Returns any error reported by the search engine when executing the provided query.
306 * It should be called from static::execute_query when an exception is triggered.
308 * @return string
310 public function get_query_error() {
311 return $this->queryerror;
315 * Returns the total number of documents available for the most recent call to execute_query.
317 * This can be an estimate, but should get more accurate the higher the limited passed to execute_query is.
318 * To do that, the engine can use (actual result returned count + count of unchecked documents), or
319 * (total possible docs - docs that have been checked and rejected).
321 * Engine can limit to manager::MAX_RESULTS if there is cost to determining more.
322 * If this cannot be computed in a reasonable way, manager::MAX_RESULTS may be returned.
324 * @return int
326 abstract public function get_query_total_count();
329 * Return true if file indexing is supported and enabled. False otherwise.
331 * @return bool
333 public function file_indexing_enabled() {
334 return false;
338 * Clears the current query error value.
340 * @return void
342 public function clear_query_error() {
343 $this->queryerror = null;
347 * Is the server ready to use?
349 * This should also check that the search engine configuration is ok.
351 * @return true|string Returns true if all good or an error string.
353 abstract function is_server_ready();
356 * Adds a document to the search engine.
358 * @param document $document
359 * @param bool $fileindexing True if file indexing is to be used
360 * @return bool False if the file was skipped or failed, true on success
362 abstract function add_document($document, $fileindexing = false);
365 * Executes the query on the engine.
367 * Implementations of this function should check user context array to limit the results to contexts where the
368 * user have access. They should also limit the owneruserid field to manger::NO_OWNER_ID or the current user's id.
369 * Engines must use area->check_access() to confirm user access.
371 * Engines should reasonably attempt to fill up to limit with valid results if they are available.
373 * @param stdClass $filters Query and filters to apply.
374 * @param array $usercontexts Contexts where the user has access. True if the user can access all contexts.
375 * @param int $limit The maximum number of results to return. If empty, limit to manager::MAX_RESULTS.
376 * @return \core_search\document[] Results or false if no results
378 abstract function execute_query($filters, $usercontexts, $limit = 0);
381 * Delete all documents.
383 * @param string $areaid To filter by area
384 * @return void
386 abstract function delete($areaid = null);