Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>./*** Solr schema manipulation manager.** @package search_solr* @copyright 2015 David Monllao {@link http://www.davidmonllao.com}* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/namespace search_solr;defined('MOODLE_INTERNAL') || die();require_once($CFG->dirroot . '/lib/filelib.php');/*** Schema class to interact with Solr schema.** At the moment it only implements create which should be enough for a basic* moodle configuration in Solr.** @package search_solr* @copyright 2015 David Monllao {@link http://www.davidmonllao.com}* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class schema {/*** @var stdClass*/protected $config = null;/*** cUrl instance.* @var \curl*/protected $curl = null;/*** An engine instance.* @var engine*/protected $engine = null;/*** Constructor.** @param engine $engine Optional engine parameter, if not specified then one will be created* @throws \moodle_exception* @return void*/public function __construct(engine $engine = null) {if (!$this->config = get_config('search_solr')) {throw new \moodle_exception('missingconfig', 'search_solr');}if (empty($this->config->server_hostname) || empty($this->config->indexname)) {throw new \moodle_exception('missingconfig', 'search_solr');}$this->engine = $engine ?? new engine();$this->curl = $this->engine->get_curl_object();// HTTP headers.$this->curl->setHeader('Content-type: application/json');}/*** Can setup be executed against the configured server.** @return true|string True or error message.*/public function can_setup_server() {$status = $this->engine->is_server_configured();if ($status !== true) {return $status;}// At this stage we know that the server is properly configured with a valid host:port and indexname.// We're not too concerned about repeating the SolrClient::system() call (already called in// is_server_configured) because this is just a setup script.if ($this->engine->get_solr_major_version() < 5) {// Schema setup script only available for 5.0 onwards.return get_string('schemasetupfromsolr5', 'search_solr');}return true;}/*** Setup solr stuff required by moodle.** @param bool $checkexisting Whether to check if the fields already exist or not* @return bool*/public function setup($checkexisting = true) {$fields = \search_solr\document::get_default_fields_definition();// Field id is already there.unset($fields['id']);$this->check_index();$return = $this->add_fields($fields, $checkexisting);// Tell the engine we are now using the latest schema version.$this->engine->record_applied_schema_version(document::SCHEMA_VERSION);return $return;}/*** Checks the schema is properly set up.** @throws \moodle_exception* @return void*/public function validate_setup() {$fields = \search_solr\document::get_default_fields_definition();// Field id is already there.unset($fields['id']);$this->check_index();$this->validate_fields($fields, true);}/*** Checks if the index is ready, triggers an exception otherwise.** @throws \moodle_exception* @return void*/protected function check_index() {// Check that the server is available and the index exists.$url = $this->engine->get_connection_url('/select?wt=json');$result = $this->curl->get($url);if ($this->curl->error) {throw new \moodle_exception('connectionerror', 'search_solr');}if ($this->curl->info['http_code'] === 404) {throw new \moodle_exception('connectionerror', 'search_solr');}}/*** Adds the provided fields to Solr schema.** Intentionally separated from create(), it can be called to add extra fields.* fields separately.** @throws \coding_exception* @throws \moodle_exception* @param array $fields \core_search\document::$requiredfields format* @param bool $checkexisting Whether to check if the fields already exist or not* @return bool*/protected function add_fields($fields, $checkexisting = true) {if ($checkexisting) {// Check that non of them exists.$this->validate_fields($fields, false);}$url = $this->engine->get_connection_url('/schema');// Add all fields.foreach ($fields as $fieldname => $data) {if (!isset($data['type']) || !isset($data['stored']) || !isset($data['indexed'])) {throw new \coding_exception($fieldname . ' does not define all required field params: type, stored and indexed.');}$type = $this->doc_field_to_solr_field($data['type']);// Changing default multiValued value to false as we want to match values easily.$params = array('add-field' => array('name' => $fieldname,'type' => $type,'stored' => $data['stored'],'multiValued' => false,'indexed' => $data['indexed']));$results = $this->curl->post($url, json_encode($params));// We only validate if we are interested on it.if ($checkexisting) {if ($this->curl->error) {throw new \moodle_exception('errorcreatingschema', 'search_solr', '', $this->curl->error);}$this->validate_add_field_result($results);}}return true;}/*** Checks if the schema existing fields are properly set, triggers an exception otherwise.** @throws \moodle_exception* @param array $fields* @param bool $requireexisting Require the fields to exist, otherwise exception.* @return void*/protected function validate_fields(&$fields, $requireexisting = false) {global $CFG;foreach ($fields as $fieldname => $data) {$url = $this->engine->get_connection_url('/schema/fields/' . $fieldname);$results = $this->curl->get($url);if ($this->curl->error) {throw new \moodle_exception('errorcreatingschema', 'search_solr', '', $this->curl->error);}if (!$results) {throw new \moodle_exception('errorcreatingschema', 'search_solr', '', get_string('nodatafromserver', 'search_solr'));}$results = json_decode($results);if ($requireexisting && !empty($results->error) && $results->error->code === 404) {$a = new \stdClass();$a->fieldname = $fieldname;$a->setupurl = $CFG->wwwroot . '/search/engine/solr/setup_schema.php';throw new \moodle_exception('errorvalidatingschema', 'search_solr', '', $a);}// The field should not exist so we only accept 404 errors.if (empty($results->error) || (!empty($results->error) && $results->error->code !== 404)) {if (!empty($results->error)) {throw new \moodle_exception('errorcreatingschema', 'search_solr', '', $results->error->msg);} else {// All these field attributes are set when fields are added through this script and should// be returned and match the defined field's values.$expectedsolrfield = $this->doc_field_to_solr_field($data['type']);if (empty($results->field) || !isset($results->field->type) ||!isset($results->field->multiValued) || !isset($results->field->indexed) ||!isset($results->field->stored)) {throw new \moodle_exception('errorcreatingschema', 'search_solr', '',get_string('schemafieldautocreated', 'search_solr', $fieldname));} else if ($results->field->type !== $expectedsolrfield ||$results->field->multiValued !== false ||$results->field->indexed !== $data['indexed'] ||$results->field->stored !== $data['stored']) {throw new \moodle_exception('errorcreatingschema', 'search_solr', '',get_string('schemafieldautocreated', 'search_solr', $fieldname));} else {// The field already exists and it is properly defined, no need to create it.unset($fields[$fieldname]);}}}}}/*** Checks that the field results do not contain errors.** @throws \moodle_exception* @param string $results curl response body* @return void*/protected function validate_add_field_result($result) {if (!$result) {throw new \moodle_exception('errorcreatingschema', 'search_solr', '', get_string('nodatafromserver', 'search_solr'));}$results = json_decode($result);if (!$results) {if (is_scalar($result)) {$errormsg = $result;} else {$errormsg = json_encode($result);}throw new \moodle_exception('errorcreatingschema', 'search_solr', '', $errormsg);}// It comes as error when fetching fields data.if (!empty($results->error)) {throw new \moodle_exception('errorcreatingschema', 'search_solr', '', $results->error);}// It comes as errors when adding fields.if (!empty($results->errors)) {// We treat this error separately.$errorstr = '';foreach ($results->errors as $error) {$errorstr .= implode(', ', $error->errorMessages);}throw new \moodle_exception('errorcreatingschema', 'search_solr', '', $errorstr);}}/*** Returns the solr field type from the document field type string.** @param string $datatype* @return string*/private function doc_field_to_solr_field($datatype) {$type = $datatype;$solrversion = $this->engine->get_solr_major_version();switch($datatype) {case 'text':$type = 'text_general';break;case 'int':if ($solrversion >= 7) {$type = 'pint';}break;case 'tdate':if ($solrversion >= 7) {$type = 'pdate';}break;}return $type;}}