コード例 #1
0
  /**
   * Tests custom data types integration.
   */
  public function testCustomDataTypes() {
    $original_value = $this->entities[1]->get('name')->value;
    $original_type = $this->index->getFields()['entity:entity_test/name']->getType();

    $item = $this->index->loadItem('entity:entity_test/1:en');
    $item = Utility::createItemFromObject($this->index, $item, 'entity:entity_test/1:en');
    $name_field = $item->getField('entity:entity_test/name');

    $processed_value = $name_field->getValues()[0];
    $processed_type = $name_field->getType();

    $this->assertEqual($processed_value, $original_value, 'The processed value matches the original value');
    $this->assertEqual($processed_type, $original_type, 'The processed type matches the original type.');

    // Reset the fields on the item and change to the supported data type.
    $item->setFieldsExtracted(FALSE);
    $item->setFields(array());
    $this->index->getFields()['entity:entity_test/name']->setType('search_api_test_data_type');
    $name_field = $item->getField('entity:entity_test/name');

    $processed_value = $name_field->getValues()[0];
    $processed_type = $name_field->getType();

    $this->assertEqual($processed_value, $original_value, 'The processed value matches the original value');
    $this->assertEqual($processed_type, 'search_api_test_data_type', 'The processed type matches the new type.');

    // Reset the fields on the item and change to the non-supported data type.
    $item->setFieldsExtracted(FALSE);
    $item->setFields(array());
    $this->index->getFields()['entity:entity_test/name']->setType('search_api_unsupported_test_data_type');
    $name_field = $item->getField('entity:entity_test/name');

    $processed_value = $name_field->getValues()[0];
    $processed_type = $name_field->getType();

    $this->assertEqual($processed_value, $original_value, 'The processed value matches the original value');
    $this->assertEqual($processed_type, 'integer', 'The processed type matches the fallback type.');

    // Reset the fields on the item and change to the data altering data type.
    $item->setFieldsExtracted(FALSE);
    $item->setFields(array());
    $this->index->getFields()['entity:entity_test/name']->setType('search_api_altering_test_data_type');
    $name_field = $item->getField('entity:entity_test/name');

    $processed_value = $name_field->getValues()[0];
    $processed_type = $name_field->getType();

    $this->assertEqual($processed_value, strlen($original_value), 'The processed value matches the altered original value');
    $this->assertEqual($processed_type, 'search_api_altering_test_data_type', 'The processed type matches the defined type.');
  }
コード例 #2
0
 /**
  * {@inheritdoc}
  */
 public function getQueryTypesForFacet(FacetInterface $facet)
 {
     // Get our Facets Field Identifier, which is equal to the Search API Field
     // identifier.
     $field_id = $facet->getFieldIdentifier();
     // Get the Search API Server.
     $server = $this->index->getServerInstance();
     // Get the Search API Backend.
     $backend = $server->getBackend();
     $fields = $this->index->getFields();
     foreach ($fields as $field) {
         if ($field->getFieldIdentifier() == $field_id) {
             return $this->getQueryTypesForDataType($backend, $field->getType());
         }
     }
     throw new InvalidQueryTypeException($this->t("No available query types were found for facet @facet", ['@facet' => $facet->getName()]));
 }
コード例 #3
0
 /**
  * Removes a field from a search index.
  *
  * @param \Drupal\search_api\IndexInterface $search_api_index
  *   The search index.
  * @param string $field_id
  *   The ID of the field to remove.
  *
  * @return \Symfony\Component\HttpFoundation\Response
  *   The response to send to the browser.
  *
  * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
  *   Thrown when the field was not found.
  */
 public function removeField(IndexInterface $search_api_index, $field_id)
 {
     $fields = $search_api_index->getFields();
     if (isset($fields[$field_id])) {
         try {
             $search_api_index->removeField($field_id);
             $search_api_index->save();
         } catch (SearchApiException $e) {
             $args['%field'] = $fields[$field_id]->getLabel();
             drupal_set_message($this->t('The field %field is locked and cannot be removed.', $args), 'error');
         }
     } else {
         throw new NotFoundHttpException();
     }
     // Redirect to the index's "View" page.
     $url = $search_api_index->toUrl('fields');
     return $this->redirect($url->getRouteName(), $url->getRouteParameters());
 }
コード例 #4
0
ファイル: Database.php プロジェクト: jkyto/agolf
  /**
   * {@inheritdoc}
   */
  public function addIndex(IndexInterface $index) {
    try {
      // Create the denormalized table now.
      $index_table = $this->findFreeTable('search_api_db_', $index->id());
      $this->createFieldTable(NULL, array('table' => $index_table));

      $db_info = array();
      $db_info['field_tables'] = array();
      $db_info['index_table'] = $index_table;
      $this->getKeyValueStore()->set($index->id(), $db_info);

      // If there are no fields, we are done now.
      if (!$index->getFields()) {
        return;
      }
    }
      // The database operations might throw PDO or other exceptions, so we catch
      // them all and re-wrap them appropriately.
    catch (\Exception $e) {
      throw new SearchApiException($e->getMessage(), $e->getCode(), $e);
    }

    // If dealing with features or stale data or whatever, we might already have
    // settings stored for this index. If we have, we should take care to only
    // change what is needed, so we don't save the server (potentially setting
    // it to "Overridden") unnecessarily.
    // The easiest way to do this is by just pretending the index was already
    // present, but its fields were updated.
    $this->fieldsUpdated($index);
  }
コード例 #5
0
 /**
  * Updates the storage tables when the field configuration changes.
  *
  * @param \Drupal\search_api\IndexInterface $index
  *   The search index whose fields (might) have changed.
  *
  * @return bool
  *   TRUE if the data needs to be reindexed, FALSE otherwise.
  *
  * @throws \Drupal\search_api\SearchApiException
  *   Thrown if any exceptions occur internally, e.g., in the database
  *   layer.
  */
 protected function fieldsUpdated(IndexInterface $index)
 {
     try {
         $fields =& $this->configuration['field_tables'][$index->id()];
         $new_fields = $index->getFields();
         $reindex = FALSE;
         $cleared = FALSE;
         $change = FALSE;
         $text_table = NULL;
         $denormalized_table = $this->configuration['index_tables'][$index->id()];
         foreach ($fields as $field_id => $field) {
             if (!isset($text_table) && Utility::isTextType($field['type'])) {
                 // Stash the shared text table name for the index.
                 $text_table = $field['table'];
             }
             if (!isset($new_fields[$field_id])) {
                 // The field is no longer in the index, drop the data.
                 $this->removeFieldStorage($field_id, $field, $denormalized_table);
                 unset($fields[$field_id]);
                 $change = TRUE;
                 continue;
             }
             $old_type = $field['type'];
             $new_type = $new_fields[$field_id]->getType();
             $fields[$field_id]['type'] = $new_type;
             $fields[$field_id]['boost'] = $new_fields[$field_id]->getBoost();
             if ($old_type != $new_type) {
                 $change = TRUE;
                 if ($old_type == 'text' || $new_type == 'text') {
                     // A change in fulltext status necessitates completely clearing the
                     // index.
                     $reindex = TRUE;
                     if (!$cleared) {
                         $cleared = TRUE;
                         $this->deleteAllIndexItems($index);
                     }
                     $this->removeFieldStorage($field_id, $field, $denormalized_table);
                     // Keep the table in $new_fields to create the new storage.
                     continue;
                 } elseif ($this->sqlType($old_type) != $this->sqlType($new_type)) {
                     // There is a change in SQL type. We don't have to clear the index,
                     // since types can be converted.
                     $this->database->schema()->changeField($field['table'], 'value', 'value', $this->sqlType($new_type) + array('description' => "The field's value for this item"));
                     $this->database->schema()->changeField($denormalized_table, $field['column'], $field['column'], $this->sqlType($new_type) + array('description' => "The field's value for this item"));
                     $reindex = TRUE;
                 } elseif ($old_type == 'date' || $new_type == 'date') {
                     // Even though the SQL type stays the same, we have to reindex since
                     // conversion rules change.
                     $reindex = TRUE;
                 }
             } elseif ($new_type == 'text' && $field['boost'] != $new_fields[$field_id]->getBoost()) {
                 $change = TRUE;
                 if (!$reindex) {
                     $multiplier = $new_fields[$field_id]->getBoost() / $field['boost'];
                     $this->database->update($text_table)->expression('score', 'score * :mult', array(':mult' => $multiplier))->condition('field_name', self::getTextFieldName($field_id))->execute();
                 }
             }
             // Make sure the table and column now exist. (Especially important when
             // we actually add the index for the first time.)
             $storage_exists = $this->database->schema()->tableExists($field['table']) && $this->database->schema()->fieldExists($field['table'], 'value');
             $denormalized_storage_exists = $this->database->schema()->tableExists($denormalized_table) && $this->database->schema()->fieldExists($denormalized_table, $field['column']);
             if (!Utility::isTextType($field['type']) && !$storage_exists) {
                 $db = array('table' => $field['table'], 'column' => 'value');
                 $this->createFieldTable($new_fields[$field_id], $db);
             }
             // Ensure that a column is created in the denormalized storage even for
             // 'text' fields.
             if (!$denormalized_storage_exists) {
                 $db = array('table' => $denormalized_table, 'column' => $field['column']);
                 $this->createFieldTable($new_fields[$field_id], $db);
             }
             unset($new_fields[$field_id]);
         }
         $prefix = 'search_api_db_' . $index->id();
         // These are new fields that were previously not indexed.
         foreach ($new_fields as $field_id => $field) {
             $reindex = TRUE;
             if (Utility::isTextType($field->getType())) {
                 if (!isset($text_table)) {
                     // If we have not encountered a text table, assign a name for it.
                     $text_table = $this->findFreeTable($prefix . '_', 'text');
                 }
                 $fields[$field_id]['table'] = $text_table;
             } else {
                 $fields[$field_id]['table'] = $this->findFreeTable($prefix . '_', $field_id);
                 $this->createFieldTable($field, $fields[$field_id]);
             }
             // Always add a column in the denormalized table.
             $fields[$field_id]['column'] = $this->findFreeColumn($denormalized_table, $field_id);
             $this->createFieldTable($field, array('table' => $denormalized_table, 'column' => $fields[$field_id]['column']));
             $fields[$field_id]['type'] = $field->getType();
             $fields[$field_id]['boost'] = $field->getBoost();
             $change = TRUE;
         }
         // If needed, make sure the text table exists.
         if (isset($text_table) && !$this->database->schema()->tableExists($text_table)) {
             $table = array('name' => $text_table, 'module' => 'search_api_db', 'fields' => array('item_id' => array('type' => 'varchar', 'length' => 50, 'description' => 'The primary identifier of the item', 'not null' => TRUE), 'field_name' => array('description' => "The name of the field in which the token appears, or an MD5 hash of the field", 'not null' => TRUE, 'type' => 'varchar', 'length' => 191), 'word' => array('description' => 'The text of the indexed token', 'type' => 'varchar', 'length' => 50, 'not null' => TRUE), 'score' => array('description' => 'The score associated with this token', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0)), 'indexes' => array('word_field' => array(array('word', 20), 'field_name')), 'primary key' => array('item_id', 'field_name', 'word'));
             $this->database->schema()->createTable($text_table, $table);
             // Some DBMSs will need a character encoding and collation set. Since
             // this largely circumvents Drupal's database layer, but isn't integral
             // enough to fail completely when it doesn't work, we wrap it in a
             // try/catch, to be on the safe side.
             try {
                 switch ($this->database->databaseType()) {
                     case 'mysql':
                         $this->database->query("ALTER TABLE {{$text_table}} CONVERT TO CHARACTER SET 'utf8' COLLATE 'utf8_bin'");
                         break;
                     case 'pgsql':
                         $this->database->query("ALTER TABLE {{$text_table}} ALTER COLUMN word SET DATA TYPE character varying(50) COLLATE \"C\"");
                         break;
                         // @todo Add fixes for other DBMSs.
                     // @todo Add fixes for other DBMSs.
                     case 'oracle':
                     case 'sqlite':
                     case 'sqlsrv':
                         break;
                 }
             } catch (\PDOException $e) {
                 $vars['%index'] = $index->label();
                 watchdog_exception('search_api_db', $e, '%type while trying to change collation for the fulltext table of index %index: !message in %function (line %line of %file).', $vars);
             }
         }
         if ($change) {
             $this->server->save();
         }
         return $reindex;
     } catch (\Exception $e) {
         throw new SearchApiException($e->getMessage(), $e->getCode(), $e);
     }
 }
コード例 #6
0
 /**
  * {@inheritdoc}
  */
 public function getFields()
 {
     return $this->entity->getFields();
 }
コード例 #7
0
 /**
  * Creates a list of all indexed field names mapped to their Solr field names.
  *
  * @param \Drupal\search_api\IndexInterface $index
  *   The Search Api index.
  * @param bool $single_value_name
  *   (optional) Whether to return names for fields which store only the first
  *   value of the field. Defaults to FALSE.
  * @param bool $reset
  *   (optional) Whether to reset the static cache.
  *
  * The special fields "search_api_id" and "search_api_relevance" are also
  * included. Any Solr fields that exist on search results are mapped back to
  * to their local field names in the final result set.
  *
  * @see SearchApiSolrBackend::search()
  */
 public function getFieldNames(IndexInterface $index, $single_value_name = FALSE, $reset = FALSE)
 {
     // @todo The field name mapping should be cached per index because custom
     // queries needs to access it on every query.
     $subkey = (int) $single_value_name;
     if (!isset($this->fieldNames[$index->id()][$subkey]) || $reset) {
         // This array maps "local property name" => "solr doc property name".
         $ret = array('search_api_id' => 'item_id', 'search_api_relevance' => 'score');
         // Add the names of any fields configured on the index.
         $fields = $index->getFields();
         foreach ($fields as $key => $field) {
             // Generate a field name; this corresponds with naming conventions in
             // our schema.xml
             $type = $field->getType();
             $type_info = SearchApiSolrUtility::getDataTypeInfo($type);
             $pref = isset($type_info['prefix']) ? $type_info['prefix'] : '';
             $pref .= $single_value_name ? 's' : 'm';
             $name = $pref . '_' . $key;
             $ret[$key] = SearchApiSolrUtility::encodeSolrDynamicFieldName($name);
         }
         // Let modules adjust the field mappings.
         $hook_name = $single_value_name ? 'search_api_solr_single_value_field_mapping' : 'search_api_solr_field_mapping';
         $this->moduleHandler->alter($hook_name, $index, $ret);
         $this->fieldNames[$index->id()][$subkey] = $ret;
     }
     return $this->fieldNames[$index->id()][$subkey];
 }