/** * Creates a new processor object for use in the tests. */ protected function setUp() { parent::setUp(); $this->processor = new RoleFilter(array(), 'role_filter', array()); /** @var \Drupal\search_api\IndexInterface $index */ $index = $this->getMock('Drupal\\search_api\\IndexInterface'); $node_datasource = $this->getMock('Drupal\\search_api\\Datasource\\DatasourceInterface'); $node_datasource->expects($this->any())->method('getEntityTypeId')->will($this->returnValue('node')); /** @var \Drupal\search_api\Datasource\DatasourceInterface $node_datasource */ $user_datasource = $this->getMock('Drupal\\search_api\\Datasource\\DatasourceInterface'); $user_datasource->expects($this->any())->method('getEntityTypeId')->will($this->returnValue('user')); /** @var \Drupal\search_api\Datasource\DatasourceInterface $user_datasource */ $item = Utility::createItem($index, Utility::createCombinedId('entity:node', '1:en'), $node_datasource); $node = $this->getMockBuilder('Drupal\\Tests\\search_api\\TestNodeInterface')->disableOriginalConstructor()->getMock(); /** @var \Drupal\node\NodeInterface $node */ $item->setOriginalObject(EntityAdapter::createFromEntity($node)); $this->items[$item->getId()] = $item; $item = Utility::createItem($index, Utility::createCombinedId('entity:user', '1:en'), $user_datasource); $account1 = $this->getMockBuilder('Drupal\\Tests\\search_api\\TestUserInterface')->disableOriginalConstructor()->getMock(); $account1->expects($this->any())->method('getRoles')->will($this->returnValue(array('authenticated' => 'authenticated', 'editor' => 'editor'))); /** @var \Drupal\user\UserInterface $account1 */ $item->setOriginalObject(EntityAdapter::createFromEntity($account1)); $this->items[$item->getId()] = $item; $item = Utility::createItem($index, Utility::createCombinedId('entity:user', '2:en'), $user_datasource); $account2 = $this->getMockBuilder('Drupal\\Tests\\search_api\\TestUserInterface')->disableOriginalConstructor()->getMock(); $account2->expects($this->any())->method('getRoles')->will($this->returnValue(array('authenticated' => 'authenticated'))); /** @var \Drupal\user\UserInterface $account2 */ $item->setOriginalObject(EntityAdapter::createFromEntity($account2)); $this->items[$item->getId()] = $item; }
/** * Creates a certain number of test items. * * @param \Drupal\search_api\IndexInterface $index * The index that should be used for the items. * @param int $count * The number of items to create. * @param array[] $fields * The fields to create on the items, with keys being combined property * paths and values being arrays with properties to set on the field. * @param \Drupal\Core\TypedData\ComplexDataInterface|null $object * (optional) The object to set on each item as the "original object". * @param array|null $datasource_ids * (optional) An array of datasource IDs to use for the items, in that order * (starting again from the front if necessary). * * @return \Drupal\search_api\Item\ItemInterface[] * An array containing the requested test items. */ public function createItems(IndexInterface $index, $count, array $fields, ComplexDataInterface $object = NULL, array $datasource_ids = array('entity:node')) { $datasource_count = count($datasource_ids); $items = array(); for ($i = 0; $i < $count; ++$i) { $datasource_id = $datasource_ids[$i % $datasource_count]; $this->itemIds[$i] = $item_id = Utility::createCombinedId($datasource_id, $i + 1 . ':en'); $item = Utility::createItem($index, $item_id); if (isset($object)) { $item->setOriginalObject($object); } foreach ($fields as $combined_property_path => $field_info) { list($field_info['datasource_id'], $field_info['property_path']) = Utility::splitCombinedId($combined_property_path); // Only add fields of the right datasource. if (isset($field_info['datasource_id']) && $field_info['datasource_id'] != $datasource_id) { continue; } $field_id = Utility::getNewFieldId($index, $field_info['property_path']); $field = Utility::createField($index, $field_id, $field_info); $item->setField($field_id, $field); } $item->setFieldsExtracted(TRUE); $items[$item_id] = $item; } return $items; }
/** * Creates a new processor object for use in the tests. */ protected function setUp() { parent::setUp(); $this->processor = new NodeStatus(array(), 'node_status', array()); $datasource = $this->getMock('Drupal\\search_api\\Datasource\\DatasourceInterface'); $datasource->expects($this->any())->method('getEntityTypeId')->will($this->returnValue('node')); /** @var \Drupal\search_api\Datasource\DatasourceInterface $datasource */ $index = $this->getMock('Drupal\\search_api\\IndexInterface'); $index->expects($this->any())->method('getDatasources')->will($this->returnValue(array($datasource))); /** @var \Drupal\search_api\IndexInterface $index */ $item = Utility::createItem($index, Utility::createCombinedId('entity:node', '1:en'), $datasource); $unpublished_node = $this->getMockBuilder('Drupal\\Tests\\search_api\\TestNodeInterface')->disableOriginalConstructor()->getMock(); $unpublished_node->expects($this->any())->method('isPublished')->will($this->returnValue(FALSE)); /** @var \Drupal\node\NodeInterface $unpublished_node */ $item->setOriginalObject(EntityAdapter::createFromEntity($unpublished_node)); $this->items[$item->getId()] = $item; $item = Utility::createItem($index, Utility::createCombinedId('entity:node', '2:en'), $datasource); $published_node = $this->getMockBuilder('Drupal\\Tests\\search_api\\TestNodeInterface')->disableOriginalConstructor()->getMock(); $published_node->expects($this->any())->method('isPublished')->will($this->returnValue(TRUE)); /** @var \Drupal\node\NodeInterface $published_node */ $item->setOriginalObject(EntityAdapter::createFromEntity($published_node)); $this->items[$item->getId()] = $item; }
/** * {@inheritdoc} */ public function search(QueryInterface $query) { $this->checkError(__FUNCTION__); $results = Utility::createSearchResultSet($query); $result_items = array(); $datasources = $query->getIndex()->getDatasources(); /** @var \Drupal\search_api\Datasource\DatasourceInterface $datasource */ $datasource = reset($datasources); $datasource_id = $datasource->getPluginId(); if ($query->getKeys() && $query->getKeys()[0] == 'test') { $item_id = Utility::createCombinedId($datasource_id, '1'); $item = Utility::createItem($query->getIndex(), $item_id, $datasource); $item->setScore(2); $item->setExcerpt('test'); $result_items[$item_id] = $item; } elseif ($query->getOption('search_api_mlt')) { $item_id = Utility::createCombinedId($datasource_id, '2'); $item = Utility::createItem($query->getIndex(), $item_id, $datasource); $item->setScore(2); $item->setExcerpt('test test'); $result_items[$item_id] = $item; } else { $item_id = Utility::createCombinedId($datasource_id, '1'); $item = Utility::createItem($query->getIndex(), $item_id, $datasource); $item->setScore(1); $result_items[$item_id] = $item; $item_id = Utility::createCombinedId($datasource_id, '2'); $item = Utility::createItem($query->getIndex(), $item_id, $datasource); $item->setScore(1); $result_items[$item_id] = $item; } $results->setResultCount(count($result_items)); return $results; }
/** * Creates a certain number of test items. * * @param \Drupal\search_api\IndexInterface $index * The index that should be used for the items. * @param int $count * The number of items to create. * @param array[] $fields * The fields to create on the items, with keys being field IDs and values * being arrays with the following information: * - type: The type to set for the field. * - values: (optional) The values to set for the field. * @param \Drupal\Core\TypedData\ComplexDataInterface|null $object * (optional) The object to set on each item as the "original object". * @param array|null $datasource_ids * (optional) An array of datasource IDs to use for the items, in that order * (starting again from the front if necessary). Defaults to using * "entity:node" for all items. * * @return \Drupal\search_api\Item\ItemInterface[] * An array containing the requested test items. */ public function createItems(IndexInterface $index, $count, array $fields, ComplexDataInterface $object = NULL, array $datasource_ids = NULL) { if (!isset($datasource_ids)) { $datasource_ids = array('entity:node'); } $datasource_count = count($datasource_ids); $items = array(); for ($i = 0; $i < $count; ++$i) { $datasource_id = $datasource_ids[$i % $datasource_count]; $this->itemIds[$i] = $item_id = Utility::createCombinedId($datasource_id, $i + 1 . ':en'); $item = Utility::createItem($index, $item_id); if (isset($object)) { $item->setOriginalObject($object); } foreach ($fields as $field_id => $field_info) { // Only add fields of the right datasource. list($field_datasource_id) = Utility::splitCombinedId($field_id); if (isset($field_datasource_id) && $field_datasource_id != $datasource_id) { continue; } $field = Utility::createField($index, $field_id)->setType($field_info['type']); if (isset($field_info['values'])) { $field->setValues($field_info['values']); } $item->setField($field_id, $field); } $item->setFieldsExtracted(TRUE); $items[$item_id] = $item; } return $items; }
/** * {@inheritdoc} */ public function search(QueryInterface $query) { $this->ignored = $this->warnings = array(); $index = $query->getIndex(); $db_info = $this->getIndexDbInfo($index); if (!isset($db_info['field_tables'])) { throw new SearchApiException(new FormattableMarkup('Unknown index @id.', array('@id' => $index->id()))); } $fields = $this->getFieldInfo($index); $db_query = $this->createDbQuery($query, $fields); $results = Utility::createSearchResultSet($query); $skip_count = $query->getOption('skip result count'); if (!$skip_count) { $count_query = $db_query->countQuery(); $results->setResultCount($count_query->execute()->fetchField()); } if ($skip_count || $results->getResultCount()) { if ($query->getOption('search_api_facets')) { $results->setExtraData('search_api_facets', $this->getFacets($query, clone $db_query)); } $query_options = $query->getOptions(); if (isset($query_options['offset']) || isset($query_options['limit'])) { $offset = isset($query_options['offset']) ? $query_options['offset'] : 0; $limit = isset($query_options['limit']) ? $query_options['limit'] : 1000000; $db_query->range($offset, $limit); } $sort = $query->getSorts(); if ($sort) { foreach ($sort as $field_name => $order) { if ($order != 'ASC' && $order != 'DESC') { $msg = $this->t('Unknown sort order @order. Assuming "ASC".', array('@order' => $order)); $this->warnings[(string) $msg] = 1; $order = 'ASC'; } if ($field_name == 'search_api_relevance') { $db_query->orderBy('score', $order); continue; } if ($field_name == 'search_api_id') { $db_query->orderBy('item_id', $order); continue; } if (!isset($fields[$field_name])) { throw new SearchApiException(new FormattableMarkup('Trying to sort on unknown field @field.', array('@field' => $field_name))); } $alias = $this->getTableAlias(array('table' => $db_info['index_table']), $db_query); $db_query->orderBy($alias . '.' . $fields[$field_name]['column'], $order); // PostgreSQL automatically adds a field to the SELECT list when // sorting on it. Therefore, if we have aggregations present we also // have to add the field to the GROUP BY (since Drupal won't do it for // us). However, if no aggregations are present, a GROUP BY would lead // to another error. Therefore, we only add it if there is already a // GROUP BY. if ($db_query->getGroupBy()) { $db_query->groupBy($alias . '.' . $fields[$field_name]['column']); } } } else { $db_query->orderBy('score', 'DESC'); } $result = $db_query->execute(); foreach ($result as $row) { $item = Utility::createItem($index, $row->item_id); $item->setScore($row->score / self::SCORE_MULTIPLIER); $results->addResultItem($item); } if ($skip_count && !empty($item)) { $results->setResultCount(1); } } $results->setWarnings(array_keys($this->warnings)); $results->setIgnoredSearchKeys(array_keys($this->ignored)); return $results; }
/** * Extract results from a Solr response. * * @param \Drupal\search_api\Query\QueryInterface $query * The Search API query object. * @param \Solarium\Core\Query\Result\ResultInterface $result * A Solarium select response object. * * @return \Drupal\search_api\Query\ResultSetInterface * A result set object. */ protected function extractResults(QueryInterface $query, ResultInterface $result) { $index = $query->getIndex(); $field_names = $this->getFieldNames($index); $fields = $index->getFields(); $site_hash = SearchApiSolrUtility::getSiteHash(); // We can find the item ID and the score in the special 'search_api_*' // properties. Mappings are provided for these properties in // SearchApiSolrBackend::getFieldNames(). $id_field = $field_names['search_api_id']; $score_field = $field_names['search_api_relevance']; // Set up the results array. $result_set = SearchApiUtility::createSearchResultSet($query); $result_set->setExtraData('search_api_solr_response', $result->getData()); // In some rare cases (e.g., MLT query with nonexistent ID) the response // will be NULL. $is_grouping = $result instanceof Result && $result->getGrouping(); if (!$result->getResponse() && !$is_grouping) { $result_set->setResultCount(0); return $result_set; } // If field collapsing has been enabled for this query, we need to process // the results differently. $grouping = $query->getOption('search_api_grouping'); $docs = array(); if (!empty($grouping['use_grouping']) && $is_grouping) { // $docs = array(); // $result_set['result count'] = 0; // foreach ($grouping['fields'] as $field) { // if (!empty($response->grouped->{$fields[$field]})) { // $result_set['result count'] += $response->grouped->{$fields[$field]}->ngroups; // foreach ($response->grouped->{$fields[$field]}->groups as $group) { // foreach ($group->doclist->docs as $doc) { // $docs[] = $doc; // } // } // } // } } else { $result_set->setResultCount($result->getNumFound()); $docs = $result->getDocuments(); } // Add each search result to the results array. /** @var \Solarium\QueryType\Select\Result\Document $doc */ foreach ($docs as $doc) { $doc_fields = $doc->getFields(); $item_id = $doc_fields[$id_field]; // For items coming from a different site, we need to adapt the item ID. if (!$this->configuration['site_hash'] && $doc_fields['hash'] != $site_hash) { $item_id = $doc_fields['hash'] . '--' . $item_id; } $result_item = SearchApiUtility::createItem($index, $item_id); $result_item->setScore($doc_fields[$score_field]); unset($doc_fields[$id_field], $doc_fields[$score_field]); // Extract properties from the Solr document, translating from Solr to // Search API property names. This reverses the mapping in // SearchApiSolrBackend::getFieldNames(). foreach ($field_names as $search_api_property => $solr_property) { if (isset($doc_fields[$solr_property])) { // Date fields need some special treatment to become valid date values // (i.e., timestamps) again. if (isset($fields[$search_api_property]) && $fields[$search_api_property]->getType() == 'date' && preg_match('/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$/', $doc_fields[$solr_property][0])) { $doc_fields[$solr_property][0] = strtotime($doc_fields[$solr_property][0]); } $field = SearchApiUtility::createField($index, $search_api_property); $field->setValues($doc_fields[$solr_property]); $result_item->setField($search_api_property, $field); } } $index_id = $this->getIndexId($index->id()); $solr_id = $this->createId($index_id, $result_item->getId()); $item_fields = $result_item->getFields(); $excerpt = $this->getSolrHelper()->getExcerpt($result->getData(), $solr_id, $item_fields, $field_names); if ($excerpt) { $result_item->setExcerpt($excerpt); } $result_set->addResultItem($result_item); } // Check for spellcheck suggestions. /*if (module_exists('search_api_spellcheck') && $query->getOption('search_api_spellcheck')) { $result_set->setExtraData('search_api_spellcheck', new SearchApiSpellcheckSolr($result)); }*/ return $result_set; }