/** * Extracts value and original type from a single piece of data. * * @param \Drupal\Core\TypedData\TypedDataInterface $data * The piece of data from which to extract information. * @param \Drupal\search_api\Item\FieldInterface $field * The field into which to put the extracted data. */ public static function extractField(TypedDataInterface $data, FieldInterface $field) { $values = static::extractFieldValues($data); // If the data type of the field is a custom one, then the value can be // altered by the data type plugin. $data_type_manager = \Drupal::service('plugin.manager.search_api.data_type'); if ($data_type_manager->hasDefinition($field->getType())) { /** @var \Drupal\search_api\DataType\DataTypeInterface $data_type_plugin */ $data_type_plugin = $data_type_manager->createInstance($field->getType()); foreach ($values as $i => $value) { $values[$i] = $data_type_plugin->getValue($value); } } $field->setValues($values); $field->setOriginalType($data->getDataDefinition()->getDataType()); }
/** * {@inheritdoc} */ public function setField($field_id, FieldInterface $field = NULL) { if ($field) { if ($field->getFieldIdentifier() !== $field_id) { throw new \InvalidArgumentException('The field identifier passed must be consistent with the identifier set on the field object.'); } // Make sure that the field has the same index object set as we. This // might otherwise cause impossibly hard-to-detect bugs. $field->setIndex($this->index); $this->fields[$field_id] = $field; } else { unset($this->fields[$field_id]); } return $this; }
/** * {@inheritdoc} */ public function addField(FieldInterface $field) { $field_id = $field->getFieldIdentifier(); if (Utility::isFieldIdReserved($field_id)) { $args['%field_id'] = $field_id; throw new SearchApiException(new FormattableMarkup('%field_id is a reserved value and cannot be used as the machine name of a normal field.', $args)); } $old_field = $this->getField($field_id); if ($old_field && $old_field != $field) { $args['%field_id'] = $field_id; throw new SearchApiException(new FormattableMarkup('Cannot add field with machine name %field_id: machine name is already taken.', $args)); } $this->fieldInstances[$field_id] = $field; return $this; }
protected function createFieldTable(FieldInterface $field = NULL, $db) { $new_table = !$this->database->schema()->tableExists($db['table']); if ($new_table) { $table = array( 'name' => $db['table'], 'module' => 'search_api_db', 'fields' => array( 'item_id' => array( 'type' => 'varchar', 'length' => 50, 'description' => 'The primary identifier of the item', 'not null' => TRUE, ), ), ); $this->database->schema()->createTable($db['table'], $table); // Some DBMSs will need a character encoding and collation set. switch ($this->database->databaseType()) { case 'mysql': $this->database->query("ALTER TABLE {{$db['table']}} CONVERT TO CHARACTER SET 'utf8' COLLATE 'utf8_bin'"); break; // @todo Add fixes for other DBMSs. case 'oracle': case 'pgsql': case 'sqlite': case 'sqlsrv': break; } } // Stop here if we want to create a table with just the 'item_id' column. if (!isset($field)) { return; } $column = isset($db['column']) ? $db['column'] : 'value'; $db_field = $this->sqlType($field->getType()); $db_field += array( 'description' => "The field's value for this item", ); if ($new_table) { $db_field['not null'] = TRUE; } $this->database->schema()->addField($db['table'], $column, $db_field); if ($db_field['type'] === 'varchar') { $index_spec = array(array($column, 10)); } else { $index_spec = array($column); } // Create a table specification skeleton to pass to addIndex(). $table_spec = array( 'fields' => array( $column => $db_field, ), 'indexes' => array( $column => $index_spec, ), ); $this->database->schema()->addIndex($db['table'], $column, $index_spec, $table_spec); if ($new_table) { // Add a covering index for fields with multiple values. if (!isset($db['column'])) { $this->database->schema()->addPrimaryKey($db['table'], array('item_id', $column)); } // This is a denormalized table with many columns, where we can't predict // the best covering index. else { $this->database->schema()->addPrimaryKey($db['table'], array('item_id')); } } }
/** * Tests whether a certain field should be processed. * * @param string $name * The field's ID. * @param \Drupal\search_api\Item\FieldInterface $field * The field's information. * * @return bool * TRUE if the field should be processed, FALSE otherwise. */ protected function testField($name, FieldInterface $field) { if (!isset($this->configuration['fields'])) { return $this->testType($field->getType()); } return !empty($this->configuration['fields'][$name]); }
protected function createFieldTable(FieldInterface $field = NULL, $db, $type = 'field') { $new_table = !$this->database->schema()->tableExists($db['table']); if ($new_table) { $table = array('name' => $db['table'], 'module' => 'search_api_db', 'fields' => array('item_id' => array('type' => 'varchar', 'length' => 50, 'description' => 'The primary identifier of the item', 'not null' => TRUE))); $this->database->schema()->createTable($db['table'], $table); $this->dbmsCompatibility->alterNewTable($db['table'], $type); } // Stop here if we want to create a table with just the 'item_id' column. if (!isset($field)) { return; } $column = isset($db['column']) ? $db['column'] : 'value'; $db_field = $this->sqlType($field->getType()); $db_field += array('description' => "The field's value for this item"); if ($new_table) { $db_field['not null'] = TRUE; } $this->database->schema()->addField($db['table'], $column, $db_field); if ($db_field['type'] === 'varchar') { $index_spec = array(array($column, 10)); } else { $index_spec = array($column); } // Create a table specification skeleton to pass to addIndex(). $table_spec = array('fields' => array($column => $db_field), 'indexes' => array($column => $index_spec)); // This is a quick fix for a core bug, so we can run the tests with SQLite // until this is fixed. // // In SQLite, indexes and tables can't have the same name, which is // the case for Search API DB. We have following situation: // - a table named search_api_db_default_index_search_api_language // - a table named search_api_db_default_index // // The last table has an index on the search_api_language column, // which results in an index with the same as the first table, which // conflicts in SQLite. // // The core issue addressing this (https://www.drupal.org/node/1008128) was // closed as it fixed the PostgresSQL part. The SQLite fix is added in // https://www.drupal.org/node/2625664 // We prevent this by adding an extra underscore (which is also the proposed // solution in the original core issue). // // @todo: Remove when #2625664 lands in Core. See #2625722 for a patch that // implements this. try { $this->database->schema()->addIndex($db['table'], '_' . $column, $index_spec, $table_spec); } catch (\PDOException $e) { $variables['%column'] = $column; $variables['%table'] = $db['table']; watchdog_exception('search_api_db', $e, '%type while trying to add a database index for column %column to table %table: @message in %function (line %line of %file).', $variables, RfcLogLevel::WARNING); } if ($new_table) { // Add a covering index for fields with multiple values. if (!isset($db['column'])) { $this->database->schema()->addPrimaryKey($db['table'], array('item_id', $column)); } else { $this->database->schema()->addPrimaryKey($db['table'], array('item_id')); } } }
/** * Tests whether a certain field should be processed. * * @param string $name * The field's ID. * @param \Drupal\search_api\Item\FieldInterface $field * The field's information. * * @return bool * TRUE if the field should be processed, FALSE otherwise. */ protected function testField($name, FieldInterface $field) { if (!isset($this->configuration['fields'])) { return $this->testType($field->getType()); } return in_array($name, $this->configuration['fields'], TRUE); }
/** * Create a single search query string according to the given field, value * and operator. */ protected function createFilterQuery($field, $value, $operator, FieldInterface $index_field) { $field = SearchApiSolrUtility::escapeFieldName($field); if ($value === NULL) { return ($operator == '=' ? '*:* AND -' : '') . "{$field}:[* TO *]"; } $value = trim($value); $value = $this->formatFilterValue($value, $index_field->getType()); switch ($operator) { case '<>': return "*:* AND -({$field}:{$value})"; case '<': return "{$field}:{* TO {$value}}"; case '<=': return "{$field}:[* TO {$value}]"; case '>=': return "{$field}:[{$value} TO *]"; case '>': return "{$field}:{{$value} TO *}"; default: return "{$field}:{$value}"; } }