Newer
Older
Thomas Seidl
committed
<?php
/**
* Default number of items indexed at each cron run for each enabled index.
*/
define('SEARCH_API_DEFAULT_CRON_LIMIT', 50);
/**
* Implements hook_menu().
*/
function search_api_menu() {
$pre = 'admin/config/search/search_api';
$items[$pre] = array(
'title' => 'Search API',
Thomas Seidl
committed
'description' => 'Create and configure search engines.',
'page callback' => 'search_api_admin_overview',
'access arguments' => array('administer search_api'),
'file' => 'search_api.admin.inc',
);
$items[$pre . '/overview'] = array(
'title' => 'Overview',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items[$pre . '/add_server'] = array(
Thomas Seidl
committed
'title' => 'Add server',
'description' => 'Create a new search server.',
'page callback' => 'drupal_get_form',
'page arguments' => array('search_api_admin_add_server'),
Thomas Seidl
committed
'access arguments' => array('administer search_api'),
Thomas Seidl
committed
'weight' => -1,
Thomas Seidl
committed
'type' => MENU_LOCAL_ACTION,
Thomas Seidl
committed
'title' => 'Add index',
'description' => 'Create a new search index.',
'page callback' => 'drupal_get_form',
'page arguments' => array('search_api_admin_add_index'),
Thomas Seidl
committed
'access arguments' => array('administer search_api'),
Thomas Seidl
committed
'type' => MENU_LOCAL_ACTION,
);
$items[$pre . '/server/%search_api_server'] = array(
'title' => 'View server',
'title callback' => 'search_api_admin_item_title',
Thomas Seidl
committed
'title arguments' => array(5),
'description' => 'View server details.',
'page callback' => 'search_api_admin_server_view',
'page arguments' => array(5),
Thomas Seidl
committed
'access arguments' => array('administer search_api'),
'file' => 'search_api.admin.inc',
);
$items[$pre . '/server/%search_api_server/view'] = array(
'title' => 'View',
'weight' => -10,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items[$pre . '/server/%search_api_server/edit'] = array(
'title' => 'Edit',
'description' => 'Edit server details.',
'page callback' => 'drupal_get_form',
'page arguments' => array('search_api_admin_server_edit', 5),
Thomas Seidl
committed
'access arguments' => array('administer search_api'),
Thomas Seidl
committed
'weight' => -1,
'type' => MENU_LOCAL_TASK,
);
$items[$pre . '/server/%search_api_server/delete'] = array(
'title' => 'Delete',
'title callback' => 'search_api_title_delete_page',
'title arguments' => array(5),
'description' => 'Delete server.',
'page callback' => 'drupal_get_form',
Thomas Seidl
committed
'page arguments' => array('search_api_admin_confirm', 'server', 'delete', 5),
'access callback' => 'search_api_access_delete_page',
'access arguments' => array(5),
Thomas Seidl
committed
'file' => 'search_api.admin.inc',
'type' => MENU_LOCAL_TASK,
);
$items[$pre . '/index/%search_api_index'] = array(
'title' => 'View index',
'title callback' => 'search_api_admin_item_title',
Thomas Seidl
committed
'description' => 'View index details.',
'page callback' => 'search_api_admin_index_view',
'page arguments' => array(5),
'access arguments' => array('administer search_api'),
'file' => 'search_api.admin.inc',
);
$items[$pre . '/index/%search_api_index/view'] = array(
'title' => 'View',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
Thomas Seidl
committed
$items[$pre . '/index/%search_api_index/status'] = array(
'title' => 'Status',
'description' => 'Display and work on index status.',
'page callback' => 'drupal_get_form',
'page arguments' => array('search_api_admin_index_status_form', 5),
'access arguments' => array('administer search_api'),
'file' => 'search_api.admin.inc',
'weight' => -8,
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE | MENU_CONTEXT_PAGE,
Thomas Seidl
committed
);
Thomas Seidl
committed
$items[$pre . '/index/%search_api_index/edit'] = array(
Thomas Seidl
committed
'title' => 'Settings',
'description' => 'Edit index settings.',
Thomas Seidl
committed
'page callback' => 'drupal_get_form',
'page arguments' => array('search_api_admin_index_edit', 5),
'access arguments' => array('administer search_api'),
'file' => 'search_api.admin.inc',
Thomas Seidl
committed
'weight' => -6,
'context' => MENU_CONTEXT_INLINE | MENU_CONTEXT_PAGE,
$items[$pre . '/index/%search_api_index/fields'] = array(
'title' => 'Fields',
'description' => 'Select indexed fields.',
'page callback' => 'drupal_get_form',
'page arguments' => array('search_api_admin_index_fields', 5),
'access arguments' => array('administer search_api'),
'file' => 'search_api.admin.inc',
Thomas Seidl
committed
'weight' => -4,
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE | MENU_CONTEXT_PAGE,
Thomas Seidl
committed
);
$items[$pre . '/index/%search_api_index/workflow'] = array(
'title' => 'Workflow',
'description' => 'Edit index workflow.',
Thomas Seidl
committed
'page callback' => 'drupal_get_form',
'page arguments' => array('search_api_admin_index_workflow', 5),
Thomas Seidl
committed
'access arguments' => array('administer search_api'),
'file' => 'search_api.admin.inc',
'weight' => -2,
Thomas Seidl
committed
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE | MENU_CONTEXT_PAGE,
Thomas Seidl
committed
);
$items[$pre . '/index/%search_api_index/delete'] = array(
'title' => 'Delete',
'title callback' => 'search_api_title_delete_page',
'title arguments' => array(5),
Thomas Seidl
committed
'description' => 'Delete index.',
'page callback' => 'drupal_get_form',
Thomas Seidl
committed
'page arguments' => array('search_api_admin_confirm', 'index', 'delete', 5),
'access callback' => 'search_api_access_delete_page',
'access arguments' => array(5),
'file' => 'search_api.admin.inc',
'type' => MENU_LOCAL_TASK,
);
return $items;
}
Thomas Seidl
committed
/**
* Implements hook_theme().
*/
function search_api_theme() {
$themes['search_api_server'] = array(
Thomas Seidl
committed
'variables' => array(
'id' => NULL,
'name' => '',
Thomas Seidl
committed
'description' => NULL,
'enabled' => NULL,
'class_name' => NULL,
'class_description' => NULL,
'options' => array(),
'status' => ENTITY_CUSTOM,
Thomas Seidl
committed
),
Thomas Seidl
committed
'file' => 'search_api.admin.inc',
);
$themes['search_api_index'] = array(
'variables' => array(
'id' => NULL,
'name' => '',
'enabled' => NULL,
'server' => NULL,
'options' => array(),
'indexed_items' => 0,
'total_items' => 0,
'status' => ENTITY_CUSTOM,
Thomas Seidl
committed
'read_only' => 0,
),
'file' => 'search_api.admin.inc',
);
$themes['search_api_admin_item_order'] = array(
'render element' => 'element',
'file' => 'search_api.admin.inc',
);
$themes['search_api_admin_fields_table'] = array(
'render element' => 'element',
'file' => 'search_api.admin.inc',
);
Thomas Seidl
committed
return $themes;
}
/**
* Implements hook_permission().
*/
function search_api_permission() {
return array(
'administer search_api' => array(
'title' => t('Administer Search API'),
Thomas Seidl
committed
'description' => t('Create and configure Search API servers and indexes.'),
/**
* Implements hook_cron().
*
* Will index $options['cron-limit'] items for each enabled index.
*/
function search_api_cron() {
Thomas Seidl
committed
foreach (search_api_index_load_multiple(FALSE, array('enabled' => TRUE, 'read_only' => 0)) as $index) {
$limit = isset($index->options['cron_limit'])
? $index->options['cron_limit']
: SEARCH_API_DEFAULT_CRON_LIMIT;
if ($limit) {
Thomas Seidl
committed
try {
$num = search_api_index_items($index, $limit);
if ($num) {
watchdog('search_api', t('Indexed !num items for index !name', array('!num' => $num, '!name' => $index->name)), NULL, WATCHDOG_INFO);
}
}
catch (SearchApiException $e) {
watchdog('search_api', $e->getMessage(), NULL, WATCHDOG_WARNING);
Thomas Seidl
committed
}
Thomas Seidl
committed
/**
* Implements hook_entity_info().
*/
function search_api_entity_info() {
$info['search_api_server'] = array(
'label' => t('Search server'),
'controller class' => 'EntityAPIControllerExportable',
'metadata controller class' => FALSE,
Thomas Seidl
committed
'entity class' => 'SearchApiServer',
'base table' => 'search_api_server',
'uri callback' => 'search_api_server_url',
'module' => 'search_api',
'exportable' => TRUE,
Thomas Seidl
committed
'entity keys' => array(
'id' => 'id',
Thomas Seidl
committed
'label' => 'name',
'name' => 'machine_name',
Thomas Seidl
committed
),
);
$info['search_api_index'] = array(
'label' => t('Search index'),
'controller class' => 'EntityAPIControllerExportable',
'metadata controller class' => FALSE,
Thomas Seidl
committed
'entity class' => 'SearchApiIndex',
'base table' => 'search_api_index',
'uri callback' => 'search_api_index_url',
'module' => 'search_api',
'exportable' => TRUE,
Thomas Seidl
committed
'entity keys' => array(
'id' => 'id',
Thomas Seidl
committed
'label' => 'name',
'name' => 'machine_name',
Thomas Seidl
committed
),
);
return $info;
}
/**
* Implements hook_entity_property_info().
*/
function search_api_entity_property_info() {
$info['search_api_server']['properties'] = array(
'id' => array(
'label' => t('ID'),
'type' => 'integer',
'description' => t('The primary identifier for a server.'),
),
'name' => array(
'label' => t('Name'),
'type' => 'text',
'description' => t('The displayed name for a server.'),
'required' => TRUE,
),
'machine_name' => array(
'label' => t('Machine name'),
'type' => 'token',
'description' => t('The internally used machine name for a server.'),
'required' => TRUE,
),
'description' => array(
'label' => t('Description'),
'type' => 'text',
'description' => t('The displayed description for a server.'),
'sanitize' => 'filter_xss',
),
'class' => array(
'label' => t('Service class'),
'type' => 'text',
'description' => t('The ID of the service class to use for this server.'),
'required' => TRUE,
),
'enabled' => array(
'label' => t('Enabled'),
'type' => 'boolean',
'description' => t('A flag indicating whether the server is enabled.'),
),
);
$info['search_api_index']['properties'] = array(
'id' => array(
'label' => t('ID'),
'type' => 'integer',
'description' => t('An integer identifying the index.'),
),
'name' => array(
'label' => t('Name'),
'type' => 'text',
'description' => t('A name to be displayed for the index.'),
'required' => TRUE,
),
'machine_name' => array(
'label' => t('Machine name'),
'type' => 'token',
'description' => t('The internally used machine name for an index.'),
'required' => TRUE,
),
'description' => array(
'label' => t('Description'),
'type' => 'text',
'description' => t("A string describing the index' use to users."),
'sanitize' => 'filter_xss',
),
'server' => array(
'label' => t('Server ID'),
'type' => 'token',
'description' => t('The machine name of the search_api_server with which data should be indexed.'),
),
'server_entity' => array(
'label' => t('Server'),
'type' => 'search_api_server',
'description' => t('The search_api_server with which data should be indexed.'),
'getter callback' => 'search_api_index_get_server',
),
'entity_type' => array(
'label' => t('Entity type'),
'type' => 'text',
'description' => t('The entity type of items stored in this index.'),
'required' => TRUE,
),
'enabled' => array(
'label' => t('Enabled'),
'type' => 'boolean',
'description' => t('A flag indicating whether the index is enabled.'),
),
Thomas Seidl
committed
'read_only' => array(
'label' => t('Read only'),
'type' => 'boolean',
'description' => t('A flag indicating whether the index is read-only.'),
),
);
return $info;
}
Thomas Seidl
committed
* Implements hook_search_api_server_insert().
Thomas Seidl
committed
* Calls the postCreate() method for the server.
Thomas Seidl
committed
function search_api_search_api_server_insert(SearchApiServer $server) {
$server->postCreate();
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
}
/**
* Implements hook_search_api_server_update().
*
* Calls the server's postUpdate() method and marks all of this server's indexes
* for reindexing, if necessary.
*/
function search_api_search_api_server_update(SearchApiServer $server) {
if ($server->postUpdate()) {
foreach (search_api_index_load_multiple(FALSE, array('server' => $server->machine_name)) as $index) {
$index->reindex();
}
}
if ($server->enabled != $server->original->enabled) {
if ($server->enabled) {
// Were there any changes in the server's indexes while it was disabled?
$tasks = variable_get('search_api_tasks', array());
if (isset($tasks[$server->machine_name])) {
foreach ($tasks[$server->machine_name] as $index_id => $index_tasks) {
$index = search_api_index_load($index_id);
foreach ($index_tasks as $task) {
switch ($task) {
case 'add':
$server->addIndex($index);
break;
case 'clear':
$server->deleteItems('all', $index);
break;
case 'clear all':
// Would normally be used with a fake index ID of "", since it doesn't matter.
$server->deleteItems('all');
break;
case 'fields':
if ($server->fieldsUpdated($index)) {
_search_api_index_reindex($index->id);
}
break;
case 'remove':
$server->removeIndex($index ? $index : $index_id);
break;
default:
if (substr($task, 0, 7) == 'delete-') {
$id = substr($task, 7);
$server->deleteItems(array($id), $index);
}
else {
watchdog('search_api', t('Unknown task "!task" for server "!name".', array('!task' => $task, '!name' => $server->machine_name)), NULL, 'warning');
}
}
}
unset($tasks[$server->machine_name]);
variable_set('search_api_tasks', $tasks);
}
}
else {
foreach (search_api_index_load_multiple(FALSE, array('server' => $server->machine_name, 'enabled' => 1)) as $index) {
$index->update(array('enabled' => 0));
Thomas Seidl
committed
* Implements hook_search_api_server_delete().
Thomas Seidl
committed
* Calls the preDelete() method for the server.
Thomas Seidl
committed
function search_api_search_api_server_delete(SearchApiServer $server) {
$server->preDelete();
Thomas Seidl
committed
// Only react on real delete, not revert.
if ($server->status & ENTITY_IN_CODE == 0) {
foreach (search_api_index_load_multiple(FALSE, array('server' => $server->machine_name)) as $index) {
$index->update(array('server' => NULL, 'enabled' => FALSE));
}
Thomas Seidl
committed
$tasks = variable_get('search_api_tasks', array());
unset($tasks[$server->machine_name]);
variable_set('search_api_tasks', $tasks);
Thomas Seidl
committed
* Implements hook_search_api_index_insert().
* Populates {search_api_item} for new indexes.
Thomas Seidl
committed
function search_api_search_api_index_insert(SearchApiIndex $index) {
$index->postCreate();
}
/**
* Implements hook_search_api_index_update().
*/
function search_api_search_api_index_update(SearchApiIndex $index) {
if ($index->server != $index->original->server) {
// Server changed - inform old and new ones.
if ($index->original->server) {
$old_server = search_api_server_load($index->original->server);
Thomas Seidl
committed
// The server might have changed because the old one was deleted:
if ($old_server) {
if ($old_server->enabled) {
$old_server->removeIndex($index);
}
else {
$tasks = variable_get('search_api_tasks', array());
// When we add or remove an index, we can ignore all other tasks.
$tasks[$old_server->machine_name][$index->machine_name] = array('remove');
variable_set('search_api_tasks', $tasks);
}
if ($index->server) {
$new_server = $index->server(TRUE);
// If the server is enabled, we call addIndex(); otherwise, we save the task.
if ($new_server->enabled) {
$new_server->addIndex($index);
}
else {
$tasks = variable_get('search_api_tasks', array());
// When we add or remove an index, we can ignore all other tasks.
$tasks[$new_server->machine_name][$index->machine_name] = array('add');
variable_set('search_api_tasks', $tasks);
unset($new_server);
}
}
// We also have to re-index all content
_search_api_index_reindex($index->id);
$old_fields = $index->original->options + array('fields' => array());
$old_fields = $old_fields['fields'];
$new_fields = $index->options + array('fields' => array());
$new_fields = $new_fields['fields'];
if ($old_fields != $new_fields) {
if ($index->server && $index->server()->fieldsUpdated($index)) {
_search_api_index_reindex($index->id);
Thomas Seidl
committed
// If the index's enabled or read-only status is being changed, queue or
// dequeue entities for indexing.
if (!$index->read_only && $index->enabled != $index->original->enabled) {
Thomas Seidl
committed
if ($index->enabled) {
$index->queueItems();
}
else {
$index->dequeueItems();
}
}
elseif ($index->read_only != $index->original->read_only) {
if ($index->read_only) {
$index->dequeueItems();
}
else {
$index->queueItems();
}
}
Thomas Seidl
committed
* Implements hook_search_api_index_delete().
*
* Removes all data for indexes not available any more.
*/
Thomas Seidl
committed
function search_api_search_api_index_delete(SearchApiIndex $index) {
$index->postDelete();
Thomas Seidl
committed
/**
* Implements hook_entity_insert().
*
* Marks the new item as to-index for all indexes on entities of the specified
* type.
*
Thomas Seidl
committed
* @param $entity
* The new entity.
* @param $type
* The entity's type.
Thomas Seidl
committed
*/
Thomas Seidl
committed
function search_api_entity_insert($entity, $type) {
Thomas Seidl
committed
if ($type != 'search_api_index') {
// When inserting a new search index, the new index was already inserted into search_api_item.
// This would lead to a duplicate-key issue, if we would continue.
Thomas Seidl
committed
$info = entity_get_info($type);
$id = $info['entity keys']['id'];
$id = $entity->$id;
$query = db_select('search_api_index', 'i')
->condition('entity_type', $type)
->condition('enabled', 1)
->condition('read_only', 0);
$query->addField('i', 'id', 'index_id');
$query->addExpression(':item_id', 'item_id', array(':item_id' => $id));
$query->addExpression(':changed', 'changed', array(':changed' => 1));
db_insert('search_api_item')
->from($query)
->execute();
Thomas Seidl
committed
Thomas Seidl
committed
foreach (search_api_index_load_multiple(FALSE, array('enabled' => 1, 'entity_type' => $type, 'read_only' => 0)) as $index) {
if (!empty($index->options['index_directly'])) {
$item = clone $entity;
search_api_index_specific_items($index, array($id => $item));
}
}
Thomas Seidl
committed
}
/**
* Implements hook_entity_update().
*
* Marks the item as changed for all indexes on entities of the specified type.
*
Thomas Seidl
committed
* @param $entity
* The updated entity.
* @param $type
* The entity's type.
Thomas Seidl
committed
*/
Thomas Seidl
committed
function search_api_entity_update($entity, $type) {
$info = entity_get_info($type);
$id = $info['entity keys']['id'];
search_api_mark_dirty($type, array($id));
Thomas Seidl
committed
}
/**
* Implements hook_entity_delete().
*
* Removes the item from {search_api_item} and deletes it from all indexes.
*
* @param $entity
* The updated entity.
* @param $type
* The entity's type.
*/
function search_api_entity_delete($entity, $type) {
$info = entity_get_info($type);
$id_field = $info['entity keys']['id'];
$id = $entity->$id_field;
foreach (search_api_index_load_multiple(FALSE, array('entity_type' => $type, 'read_only' => 0)) as $index) {
Thomas Seidl
committed
db_delete('search_api_item')
->condition('item_id', $id)
->condition('index_id', $index->id)
Thomas Seidl
committed
->execute();
Thomas Seidl
committed
if ($index->server) {
$server = $index->server();
Thomas Seidl
committed
if ($server->enabled) {
$server->deleteItems(array($id), $index);
}
else {
Thomas Seidl
committed
$tasks = variable_get('search_api_tasks', array());
$tasks[$server->machine_name][$index->machine_name][] = 'delete-' . $id;
Thomas Seidl
committed
variable_set('search_api_tasks', $tasks);
Thomas Seidl
committed
}
}
Thomas Seidl
committed
}
}
/**
* Implements hook_search_api_alter_callback_info().
Thomas Seidl
committed
*/
function search_api_search_api_alter_callback_info() {
Thomas Seidl
committed
$callbacks['search_api_alter_add_url'] = array(
'name' => t('URL field'),
'description' => t("Adds the item's URL to the indexed data."),
Thomas Seidl
committed
'class' => 'SearchApiAlterAddUrl',
Thomas Seidl
committed
);
Thomas Seidl
committed
$callbacks['search_api_alter_add_aggregation'] = array(
'name' => t('Aggregated fields'),
'description' => t('Gives you the ability to define additional fields, containing data from one or more other fields.'),
'class' => 'SearchApiAlterAddAggregation',
Thomas Seidl
committed
);
Thomas Seidl
committed
$callbacks['search_api_alter_add_viewed_entity'] = array(
'name' => t('Complete entity view'),
'description' => t('Adds an additional field containing the whole HTML content of the entity when viewed.'),
'class' => 'SearchApiAlterAddViewedEntity',
);
$callbacks['search_api_alter_bundle_filter'] = array(
'name' => t('Bundle filter'),
'description' => t('Exclude items from indexing based on their bundle (content type, vocabulary, …).'),
'class' => 'SearchApiAlterBundleFilter',
);
Thomas Seidl
committed
return $callbacks;
}
/**
* Implements hook_search_api_processor_info().
*/
function search_api_search_api_processor_info() {
$processors['search_api_case_ignore'] = array(
'name' => t('Ignore case'),
'description' => t('This processor will make searches case-insensitive for all fulltext fields (and, optionally, also for filters on string fields).'),
$processors['search_api_html_filter'] = array(
'name' => t('HTML filter'),
'description' => t('Strips HTML tags from fulltext fields and decodes HTML entities. ' .
'Use this processor when indexing HTML data, e.g., node bodies for certain text formats.<br />' .
'The processor also allows to boost (or ignore) the contents of specific elements.'),
'class' => 'SearchApiHtmlFilter',
'weight' => 10,
);
$processors['search_api_tokenizer'] = array(
'name' => t('Tokenizer'),
'description' => t('Tokenizes fulltext data by stripping whitespace. ' .
'This processor allows you to specify which characters make up words and which characters should be ignored, using regular expression syntax. ' .
'Otherwise it is up to the search server implementation to decide how to split indexed fulltext data.'),
'class' => 'SearchApiTokenizer',
'weight' => 20,
$processors['search_api_stopwords'] = array(
'name' => t('Stopwords'),
'description' => t('This processor prevents certain words from being indexed and removes them from search terms. ' .
'For best results, it should only be executed after tokenizing.'),
'class' => 'SearchApiStopWords',
'weight' => 30,
);
Thomas Seidl
committed
* Mark the entities with the specified IDs as "dirty", i.e., as needing to be reindexed.
*
* For indexes for which items should be indexed immediately, the items are
* indexed directly, instead.
*
* @param $entity_type
* The type of entity, e.g., 'node'.
* @param array $ids
* The entity IDs of the entities to be marked dirty.
*/
function search_api_mark_dirty($entity_type, array $ids) {
Thomas Seidl
committed
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
$index_ids = array();
foreach (search_api_index_load_multiple(FALSE, array('enabled' => 1, 'entity_type' => $entity_type, 'read_only' => 0)) as $index) {
if (empty($index->options['index_directly'])) {
$index_ids[] = $index->id;
}
else {
// For indexes with the index_directly set, index the items right away.
$items = entity_load($entity_type, $ids, array(), TRUE);
$indexed = search_api_index_specific_items($index, $items);
if (count($indexed) < count($ids)) {
// If indexing failed for some items, mark those as dirty.
$diff = array_diff($ids, $indexed);
db_update('search_api_item')
->fields(array(
'changed' => REQUEST_TIME,
))
->condition('item_id', $ids, 'IN')
->condition('index_id', $index->id)
->condition('changed', 0)
->execute();
}
}
}
if ($index_ids) {
db_update('search_api_item')
->fields(array(
'changed' => REQUEST_TIME,
))
->condition('item_id', $ids, 'IN')
->condition('index_id', $index_ids, 'IN')
->condition('changed', 0)
->execute();
}
/**
* Indexes items for the specified index. Only items marked as changed are
* indexed, in their order of change (if known).
*
Thomas Seidl
committed
* @param SearchApiIndex $index
* The index on which items should be indexed.
* @param $limit
* The number of items which should be indexed at most. -1 means no limit.
* @throws SearchApiException
* If the index' entity type is unknown or another error occurs during
* indexing.
*
* @return
Thomas Seidl
committed
* Number of successfully indexed items.
Thomas Seidl
committed
function search_api_index_items(SearchApiIndex $index, $limit = -1) {
// Safety check if entity type is known (prevent failing of whole cron run/page request)
if (!entity_get_info($index->entity_type)) {
Thomas Seidl
committed
throw new SearchApiException(t("Couldn't index values for '!name' index (unknown entity type '!type')", array('!name' => $index->name, '!type' => $index->entity_type)));
Thomas Seidl
committed
// Don't try to index read-only indexes.
if ($index->read_only) {
return 0;
}
Thomas Seidl
committed
$items = search_api_get_items_to_index($index, $limit);
if (!$items) {
return 0;
}
Thomas Seidl
committed
return count(search_api_index_specific_items($index, $items));
}
Thomas Seidl
committed
Thomas Seidl
committed
/**
* Indexes the given items on the specified index.
*
* Items which were successfully indexed are marked as such afterwards.
*
* @param SearchApiIndex $index
* The index on which items should be indexed.
* @param array $items
* The items which should be indexed. Have to be entities of the appropriate
* type.
*
* @throws SearchApiException
* If the index' entity type is unknown or another error occurs during
* indexing.
*
* @return
* The IDs of all successfully indexed items.
*/
function search_api_index_specific_items(SearchApiIndex $index, array $items) {
$indexed = $index->index($items);
Thomas Seidl
committed
if (!empty($indexed)) {
search_api_set_items_indexed($index, $indexed);
Thomas Seidl
committed
return $indexed;
Thomas Seidl
committed
* Returns a list of at most $limit items that need to be indexed for the
Thomas Seidl
committed
* @param SearchApiIndex $index
* The index for which items should be retrieved.
* @param $limit
* The maximum number of items to retrieve. -1 means no limit.
* @return array
* An array of items (entities) that need to be indexed.
Thomas Seidl
committed
function search_api_get_items_to_index(SearchApiIndex $index, $limit = -1) {
if ($limit == 0) {
return array();
}
$select = db_select('search_api_item', 'i');
Thomas Seidl
committed
$select->addField('i', 'item_id');
$select->condition('index_id', $index->id);
$select->condition('changed', 0, '<>');
$select->orderBy('changed', 'ASC');
if ($limit > 0) {
$select->range(0, $limit);
}
$ids = $select->execute()->fetchCol();
Thomas Seidl
committed
return entity_load($index->entity_type, $ids, array(), TRUE);
Thomas Seidl
committed
/**
* Marks the items as successfully indexed for the specified index.
*
Thomas Seidl
committed
* @param SearchApiIndex $index
* The index on which items were indexed.
* @param array $ids
* The ids of the indexed items.
Thomas Seidl
committed
*
* @return
* The number of index entries changed.
Thomas Seidl
committed
*/
Thomas Seidl
committed
function search_api_set_items_indexed(SearchApiIndex $index, array $ids) {
Thomas Seidl
committed
return db_update('search_api_item')
Thomas Seidl
committed
->fields(array(
'changed' => 0,
))
->condition('index_id', $index->id)
->condition('item_id', $ids, 'IN')
->execute();
Thomas Seidl
committed
}
Thomas Seidl
committed
* Creates a search query on a specified search index.
* The ID or machine name of the index to execute the search on.
* @param $options
* An associative array of options. The following are recognized:
* - filters: Either a SearchApiQueryFilterInterface object or an array of
* filters used to filter the search.
* - sort: An array of sort directives of the form $field => $order, where
* $order is either 'ASC' or 'DESC'.
* - offset: The position of the first returned search results relative to the
* whole result in the index.
* - limit: The maximum number of search results to return. -1 means no limit.
* - 'query class': The query class to use. Must be a subtype of
* SearchApiQueryInterface.
* - conjunction: The type of conjunction to use for this query - either
* 'AND' or 'OR'. 'AND' by default.
* - 'parse mode': The mode with which to parse the $keys variable, if it
* is set and not already an array. See SearchApiQuery::parseModes() for
* parse modes recognized by the SearchApiQuery class.
* Subclasses might define additional modes.
* @return SearchApiQueryInterface
* An object for searching on the specified index.
*/
function search_api_query($id, array $options = array()) {
$index = search_api_index_load($id);
throw new SearchApiException(t('Unknown index with ID !id.', array('!id' => $id)));
}
Thomas Seidl
committed
return $index->query($options);
}
Thomas Seidl
committed
/**
* Static store for the searches executed on the current page. Can either be
* used to store an executed search, or to retrieve a previously stored
* search.
*
* @param $search_id
* For pages displaying multiple searches, an optional ID identifying the
* search in questions. When storing a search, this is filled automatically,
* unless it is manually set.
Thomas Seidl
committed
* @param SearchApiQuery $query
* When storing an executed search, the query that was executed. NULL
* otherwise.
* @param array $results
* When storing an executed search, the returned results as specified by
* SearchApiQueryInterface::execute(). An empty array, otherwise.
*
Thomas Seidl
committed
* If a search with the specified ID was executed, an array containing
* ($query, $results) as used in this function's parameters. If $search_id is
Thomas Seidl
committed
* NULL, an array of all executed searches will be returned, keyed by ID.
Thomas Seidl
committed
*/
function search_api_current_search($search_id = NULL, SearchApiQuery $query = NULL, array $results = array()) {
$searches = &drupal_static(__FUNCTION__, array());
if (isset($query)) {
if (!isset($search_id)) {
$search_id = $query->getOption('search id');
}
Thomas Seidl
committed
$base = $search_id;
$i = 0;
while (isset($searches[$search_id])) {
$search_id = $base . '-' . ++$i;
}
Thomas Seidl
committed
$searches[$search_id] = array($query, $results);
}
if (isset($search_id)) {
return isset($searches[$search_id]) ? $searches[$search_id] : NULL;
Thomas Seidl
committed
}
Thomas Seidl
committed
return $searches;
Thomas Seidl
committed
}
/**
* Returns all field types recognized by the Search API framework.
*
* @return array
* An associative array with all recognized types as keys, mapped to their
* translated display names.
*/
function search_api_field_types() {
return array(
'text' => t('Fulltext'),
'string' => t('String'),
'integer' => t('Integer'),
'decimal' => t('Decimal'),
'date' => t('Date'),
'duration' => t('Duration'),
'boolean' => t('Boolean'),
'uri' => t('URI'),
);
}
/**
* Returns either a list of all available service infos, or a specific one.
*
* @see hook_search_api_service_info()
* @param $id
*
* @return array
* If $id was not specified, an array of all available service classes.
* Otherwise, either the service info with the specified id (if it exists),
function search_api_get_service_info($id = NULL) {
$services = &drupal_static(__FUNCTION__);
if (!isset($services)) {
$services = module_invoke_all('search_api_service_info');
// Allow other modules to alter definitions
drupal_alter('search_api_service_info', $services);
if (isset($id)) {
return isset($services[$id]) ? $services[$id] : NULL;
/**
* Returns a list of all available data alter callbacks.
*
* @see hook_search_api_alter_callback_info()
*
* @return array
* An array of all available data alter callbacks, keyed by function name.
*/
function search_api_get_alter_callbacks() {