Esempio n. 1
0
 /**
  * Indexes a single item on the specified index.
  *
  * Used as a helper method in indexItems().
  *
  * @param \Drupal\search_api\IndexInterface $index
  *   The index for which the item is being indexed.
  * @param \Drupal\search_api\Item\ItemInterface $item
  *   The item to index.
  *
  * @throws \Exception
  *   Any encountered database (or other) exceptions are passed on, out of this
  *   method.
  */
 protected function indexItem(IndexInterface $index, ItemInterface $item)
 {
     $fields = $this->getFieldInfo($index);
     $fields_updated = FALSE;
     $field_errors = array();
     $db_info = $this->getIndexDbInfo($index);
     $denormalized_table = $db_info['index_table'];
     $item_id = $item->getId();
     $transaction = $this->database->startTransaction('search_api_db_indexing');
     try {
         // Remove the item from the denormalized table.
         $this->database->delete($denormalized_table)->condition('item_id', $item_id)->execute();
         $denormalized_values = array();
         $text_inserts = array();
         foreach ($item->getFields() as $field_id => $field) {
             // Sometimes index changes are not triggering the update hooks
             // correctly. Therefore, to avoid DB errors, we re-check the tables
             // here before indexing.
             if (empty($fields[$field_id]['table']) && !$fields_updated) {
                 unset($db_info['field_tables'][$field_id]);
                 $this->fieldsUpdated($index);
                 $fields_updated = TRUE;
                 $fields = $db_info['field_tables'];
             }
             if (empty($fields[$field_id]['table']) && empty($field_errors[$field_id])) {
                 // Log an error, but only once per field. Since a superfluous field is
                 // not too serious, we just index the rest of the item normally.
                 $field_errors[$field_id] = TRUE;
                 $this->getLogger()->warning("Unknown field @field: please check (and re-save) the index's fields settings.", array('@field' => $field_id));
                 continue;
             }
             $field_info = $fields[$field_id];
             $table = $field_info['table'];
             $column = $field_info['column'];
             $this->database->delete($table)->condition('item_id', $item_id)->execute();
             $type = $field->getType();
             $values = array();
             foreach ($field->getValues() as $field_value) {
                 $converted_value = $this->convert($field_value, $type, $field->getOriginalType(), $index);
                 // Don't add NULL values to the return array. Also, adding an empty
                 // array is, of course, a waste of time.
                 if (isset($converted_value) && $converted_value !== array()) {
                     $values = array_merge($values, is_array($converted_value) ? $converted_value : array($converted_value));
                 }
             }
             if (!$values) {
                 // SQLite sometimes has problems letting columns not present in an
                 // INSERT statement default to NULL, so we set NULL values for the
                 // denormalized table explicitly.
                 $denormalized_values[$column] = NULL;
                 continue;
             }
             // If the field contains more than one value, we remember that the field
             // can be multi-valued.
             if (count($values) > 1) {
                 $db_info['field_tables'][$field_id]['multi-valued'] = TRUE;
             }
             if (Utility::isTextType($type, array('text', 'tokenized_text'))) {
                 // Remember the text table the first time we encounter it.
                 if (!isset($text_table)) {
                     $text_table = $table;
                 }
                 $unique_tokens = array();
                 $denormalized_value = '';
                 foreach ($values as $token) {
                     $word = $token['value'];
                     $score = $token['score'];
                     // Store the first 30 characters of the string as the denormalized
                     // value.
                     if (strlen($denormalized_value) < 30) {
                         $denormalized_value .= $word . ' ';
                     }
                     // Skip words that are too short, except for numbers.
                     if (is_numeric($word)) {
                         $word = ltrim($word, '-0');
                     } elseif (Unicode::strlen($word) < $this->configuration['min_chars']) {
                         continue;
                     }
                     // Taken from core search to reflect less importance of words later
                     // in the text.
                     // Focus is a decaying value in terms of the amount of unique words
                     // up to this point. From 100 words and more, it decays, to e.g. 0.5
                     // at 500 words and 0.3 at 1000 words.
                     $score *= min(1, 0.01 + 3.5 / (2 + count($unique_tokens) * 0.015));
                     // Only insert each canonical base form of a word once.
                     $word_base_form = $this->dbmsCompatibility->preprocessIndexValue($word);
                     if (!isset($unique_tokens[$word_base_form])) {
                         $unique_tokens[$word_base_form] = array('value' => $word, 'score' => $score);
                     } else {
                         $unique_tokens[$word_base_form]['score'] += $score;
                     }
                 }
                 $denormalized_values[$column] = Unicode::truncateBytes(trim($denormalized_value), 30);
                 if ($unique_tokens) {
                     $field_name = self::getTextFieldName($field_id);
                     $boost = $field_info['boost'];
                     foreach ($unique_tokens as $token) {
                         $text_inserts[] = array('item_id' => $item_id, 'field_name' => $field_name, 'word' => $token['value'], 'score' => (int) round($token['score'] * $boost * self::SCORE_MULTIPLIER));
                     }
                 }
             } else {
                 $denormalized_values[$column] = reset($values);
                 // Make sure no duplicate values are inserted (which would lead to a
                 // database exception).
                 // Use the canonical base form of the value for the comparison to
                 // avoid not catching different values that are duplicates under the
                 // database table's collation.
                 $case_insensitive_unique_values = array();
                 foreach ($values as $value) {
                     $value_base_form = $this->dbmsCompatibility->preprocessIndexValue("{$value}", 'field');
                     // We still insert the value in its original case.
                     $case_insensitive_unique_values[$value_base_form] = $value;
                 }
                 $values = array_values($case_insensitive_unique_values);
                 $insert = $this->database->insert($table)->fields(array('item_id', 'value'));
                 foreach ($values as $value) {
                     $insert->values(array('item_id' => $item_id, 'value' => $value));
                 }
                 $insert->execute();
             }
         }
         $this->database->insert($denormalized_table)->fields(array_merge($denormalized_values, array('item_id' => $item_id)))->execute();
         if ($text_inserts && isset($text_table)) {
             $query = $this->database->insert($text_table)->fields(array('item_id', 'field_name', 'word', 'score'));
             foreach ($text_inserts as $row) {
                 $query->values($row);
             }
             $query->execute();
         }
         // In case any new fields were detected as multi-valued, we re-save the
         // index's DB info.
         $this->getKeyValueStore()->set($index->id(), $db_info);
     } catch (\Exception $e) {
         $transaction->rollback();
         throw $e;
     }
 }
 /**
  * Prepares a single item for indexing.
  *
  * Used as a helper method in indexItem()/indexItems().
  *
  * @param \Drupal\search_api\Item\ItemInterface $item
  *   The item to index.
  */
 protected function prepareItem(IndexInterface $index, ItemInterface $item)
 {
     $item_id = $item->getId();
     $item_to_index = ['objectID' => $item_id];
     /** @var \Drupal\search_api\Item\FieldInterface $field */
     $item_fields = $item->getFields();
     $item_fields += $this->getSpecialFields($index, $item);
     foreach ($item_fields as $field_id => $field) {
         $type = $field->getType();
         $values = NULL;
         foreach ($field->getValues() as $field_value) {
             if (!$field_value) {
                 continue;
             }
             switch ($type) {
                 case 'text':
                 case 'string':
                 case 'uri':
                     $field_value .= '';
                     if (Unicode::strlen($field_value) > 10000) {
                         $field_value = Unicode::substr(trim($field_value), 0, 10000);
                     }
                     $values[] = $field_value;
                     break;
                 case 'integer':
                 case 'duration':
                 case 'decimal':
                     $values[] = 0 + $field_value;
                     break;
                 case 'boolean':
                     $values[] = $field_value ? TRUE : FALSE;
                     break;
                 case 'date':
                     if (is_numeric($field_value) || !$field_value) {
                         $values[] = 0 + $field_value;
                         break;
                     }
                     $values[] = strtotime($field_value);
                     break;
                 default:
                     $values[] = $field_value;
             }
         }
         if (count($values) <= 1) {
             $values = reset($values);
         }
         $item_to_index[$field->getFieldIdentifier()] = $values;
     }
     return $item_to_index;
 }
 /**
  * Indexes a single item on the specified index.
  *
  * Used as a helper method in indexItems().
  *
  * @param \Drupal\search_api\IndexInterface $index
  *   The index for which the item is being indexed.
  * @param \Drupal\search_api\Item\ItemInterface $item
  *   The item to index.
  */
 protected function indexItem(IndexInterface $index, ItemInterface $item)
 {
     $item_id = $item->getId();
     $item_to_index = array('objectID' => $item_id);
     /** @var \Drupal\search_api\Item\FieldInterface $field */
     foreach ($item as $key => $field) {
         $item_to_index[$field->getFieldIdentifier()] = $field->getValues();
     }
     return $item_to_index;
 }
Esempio n. 4
0
 /**
  * {@inheritdoc}
  */
 public function addResultItem(ItemInterface $result_item)
 {
     $this->resultItems[$result_item->getId()] = $result_item;
     return $this;
 }