'Stores all search servers created through the Search API.', 'fields' => array( 'id' => array( 'description' => 'The primary identifier for a server.', 'type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE, ), 'name' => array( 'description' => 'The displayed name for a server.', 'type' => 'varchar', 'length' => 50, 'not null' => TRUE, ), 'machine_name' => array( 'description' => 'The machine name for a server.', 'type' => 'varchar', 'length' => 50, 'not null' => TRUE, ), 'description' => array( 'description' => 'The displayed description for a server.', 'type' => 'text', 'not null' => FALSE, ), 'class' => array( 'description' => 'The id of the service class to use for this server.', 'type' => 'varchar', 'length' => 50, 'not null' => TRUE, ), 'options' => array( 'description' => 'The options used to configure the service object.', 'type' => 'text', 'serialize' => TRUE, 'not null' => TRUE, ), 'enabled' => array( 'description' => 'A flag indicating whether the server is enabled.', 'type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1, ), 'status' => array( 'description' => 'The exportable status of the entity.', 'type' => 'int', 'not null' => TRUE, 'default' => 0x01, 'size' => 'tiny', ), 'module' => array( 'description' => 'The name of the providing module if the entity has been defined in code.', 'type' => 'varchar', 'length' => 255, 'not null' => FALSE, ), ), 'indexes' => array( 'enabled' => array('enabled'), ), 'unique keys' => array( 'machine_name' => array('machine_name'), ), 'primary key' => array('id'), ); $schema['search_api_index'] = array( 'description' => 'Stores all search indexes on a {search_api_server}.', 'fields' => array( 'id' => array( 'description' => 'An integer identifying the index.', 'type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE, ), 'name' => array( 'description' => 'A name to be displayed for the index.', 'type' => 'varchar', 'length' => 50, 'not null' => TRUE, ), 'machine_name' => array( 'description' => 'The machine name of the index.', 'type' => 'varchar', 'length' => 50, 'not null' => TRUE, ), 'description' => array( 'description' => "A string describing the index' use to users.", 'type' => 'text', 'not null' => FALSE, ), 'server' => array( 'description' => 'The {search_api_server}.machine_name with which data should be indexed.', 'type' => 'varchar', 'length' => 50, 'not null' => FALSE, ), 'entity_type' => array( 'description' => 'The entity type of items stored in this index.', 'type' => 'varchar', 'length' => 50, 'not null' => TRUE, ), 'options' => array( 'description' => 'An array of additional arguments configuring this index.', 'type' => 'text', 'serialize' => TRUE, 'not null' => TRUE, ), 'enabled' => array( 'description' => 'A flag indicating whether this index is enabled.', 'type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1, ), 'read_only' => array( 'description' => 'A flag indicating whether to write to this index.', 'type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0, ), 'status' => array( 'description' => 'The exportable status of the entity.', 'type' => 'int', 'not null' => TRUE, 'default' => 0x01, 'size' => 'tiny', ), 'module' => array( 'description' => 'The name of the providing module if the entity has been defined in code.', 'type' => 'varchar', 'length' => 255, 'not null' => FALSE, ), ), 'indexes' => array( 'entity_type' => array('entity_type'), 'server' => array('server'), 'enabled' => array('enabled'), ), 'unique keys' => array( 'machine_name' => array('machine_name'), ), 'primary key' => array('id'), ); $schema['search_api_item'] = array( 'description' => 'Stores the items which should be indexed for each index, and their status.', 'fields' => array( 'item_id' => array( 'description' => "The item's entity id (e.g. {node}.nid for nodes).", 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, ), 'index_id' => array( 'description' => 'The {search_api_index}.id this item belongs to.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, ), 'changed' => array( 'description' => 'Either a flag or a timestamp to indicate if or when the item was changed since it was last indexed.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 1, ), ), 'indexes' => array( 'indexing' => array('index_id', 'changed'), ), 'primary key' => array('item_id', 'index_id'), ); return $schema; } /** * Implements hook_install(). * * Creates a default node index if the module is installed manually. */ function search_api_install() { // In case the module is installed via an installation profile, a batch is // active and we skip that. if (batch_get()) { return; } $name = t('Default node index'); $values = array( 'name' => $name, 'machine_name' => preg_replace('/[^a-z0-9]+/', '_', drupal_strtolower($name)), 'description' => t('An automatically created search index for indexing node data. Might be configured to specific needs.'), 'server' => NULL, 'entity_type' => 'node', 'options' => array( 'cron_limit' => '50', 'data_alter_callbacks' => array( 'search_api_alter_add_fulltext' => array( 'status' => 0, 'weight' => '0', 'settings' => array( 'fields' => array(), ), ), 'search_api_alter_add_url' => array( 'status' => 0, 'weight' => '0', ), ), 'processors' => array( 'search_api_case_ignore' => array( 'status' => 1, 'weight' => '0', 'settings' => array( 'strings' => 0, ), ), 'search_api_html_filter' => array( 'status' => 1, 'weight' => '10', 'settings' => array( 'title' => 0, 'alt' => 1, 'tags' => "h1 = 5\n" . "h2 = 3\n" . "h3 = 2\n" . "strong = 2\n" . "b = 2\n" . "em = 1.5\n" . "u = 1.5", ), ), 'search_api_tokenizer' => array( 'status' => 1, 'weight' => '20', 'settings' => array( 'spaces' => '[^\\p{L}\\p{N}]', 'ignorable' => '[-]', ), ), ), 'fields' => array( 'nid' => array( 'name' => 'Node ID', 'type' => 'integer', 'boost' => '1.0', 'indexed' => 0, ), 'vid' => array( 'name' => 'Revision ID', 'type' => 'integer', 'boost' => '1.0', 'indexed' => 0, ), 'tnid' => array( 'name' => 'Translation set ID', 'type' => 'integer', 'boost' => '1.0', 'indexed' => 0, ), 'uid' => array( 'name' => 'User ID', 'type' => 'integer', 'boost' => '1.0', 'indexed' => 0, ), 'is_new' => array( 'name' => 'Is new', 'type' => 'boolean', 'boost' => '1.0', 'indexed' => 0, ), 'type' => array( 'name' => 'Content type', 'type' => 'string', 'boost' => '1.0', 'indexed' => 1, ), 'type_name' => array( 'name' => 'Content type name', 'type' => 'string', 'boost' => '1.0', 'indexed' => 0, ), 'title' => array( 'name' => 'Title', 'type' => 'text', 'boost' => '5.0', 'indexed' => 1, ), 'language' => array( 'name' => 'Language', 'type' => 'string', 'boost' => '1.0', 'indexed' => 0, ), 'url' => array( 'name' => 'URL', 'type' => 'uri', 'boost' => '1.0', 'indexed' => 0, ), 'edit_url' => array( 'name' => 'Edit URL', 'type' => 'uri', 'boost' => '1.0', 'indexed' => 0, ), 'status' => array( 'name' => 'Published', 'type' => 'boolean', 'boost' => '1.0', 'indexed' => 1, ), 'promote' => array( 'name' => 'Promoted to frontpage', 'type' => 'boolean', 'boost' => '1.0', 'indexed' => 1, ), 'sticky' => array( 'name' => 'Sticky in lists', 'type' => 'boolean', 'boost' => '1.0', 'indexed' => 1, ), 'created' => array( 'name' => 'Date created', 'type' => 'date', 'boost' => '1.0', 'indexed' => 1, ), 'changed' => array( 'name' => 'Date changed', 'type' => 'date', 'boost' => '1.0', 'indexed' => 1, ), 'author' => array( 'name' => 'Author', 'type' => 'integer', 'entity_type' => 'user', 'boost' => '1.0', 'indexed' => 1, ), 'log' => array( 'name' => 'Revision log message', 'type' => 'text', 'boost' => '1.0', 'indexed' => 0, ), 'revision' => array( 'name' => 'Creates revision', 'type' => 'boolean', 'boost' => '1.0', 'indexed' => 0, ), 'comment' => array( 'name' => 'Comments allowed', 'type' => 'integer', 'boost' => '1.0', 'indexed' => 0, ), 'comment_count' => array( 'name' => 'Comment count', 'type' => 'integer', 'boost' => '1.0', 'indexed' => 1, ), 'comment_count_new' => array( 'name' => 'New comment count', 'type' => 'integer', 'boost' => '1.0', 'indexed' => 0, ), 'search_api_language' => array( 'name' => 'Item language', 'type' => 'string', 'boost' => '1.0', 'indexed' => 1, ), 'search_api_fulltext' => array( 'name' => 'Fulltext', 'type' => 'text', 'boost' => '1.0', 'indexed' => 1, ), 'body:value' => array( 'name' => 'Body » Text', 'type' => 'text', 'boost' => '1.0', 'indexed' => 1, ), 'body:format' => array( 'name' => 'Body » Text format', 'type' => 'integer', 'boost' => '1.0', 'indexed' => 0, ), ), ), ); search_api_index_insert($values); drupal_set_message('The Search API module was installed. A new default node index was created.'); } /** * Implements hook_enable(). */ function search_api_enable() { // Mark all items as "dirty", since we can't know whether they are. db_delete('search_api_item') ->execute(); $types = array(); foreach (search_api_index_load_multiple(FALSE) as $index) { $types[$index->entity_type][] = $index->id; } if (!$types) { return; } $insert = db_insert('search_api_item') ->fields(array('item_id', 'index_id', 'changed')); foreach ($types as $type => $indexes) { // Partly copied from SearchApiIndex::queueItems(). $entity_info = entity_get_info($type); // If the entity info specifies a base table, use that to add all items // directly in an INSERT ... SELECT. Otherwise, use entity_load() and add // the values to the outer $insert query. if (!empty($entity_info['base table'])) { // Use a subselect, which will probably be much faster than entity_load(). // Assumes that all entities use the "base table" property and the // "entity keys[id]" in the same way as the default controller. $id_field = $entity_info['entity keys']['id']; $table = $entity_info['base table']; // Select all entity ids and all relevant indexes. $query = db_select($table, 't'); $query->join('search_api_index', 'i', 'i.entity_type = :type AND i.read_only = :read_only AND i.enabled = :enabled', array(':type' => $type, ':read_only' => 0, ':enabled' => 1)); $query->addField('t', $id_field, 'item_id'); $query->addField('i', 'id', 'index_id'); $query->addExpression('1', 'changed'); // INSERT ... SELECT ... db_insert('search_api_item') ->from($query) ->execute(); } else { foreach (entity_load($type) as $id => $entity) { foreach ($indexes as $index_id) { $insert->values(array( 'item_id' => $id, 'index_id' => $index_id, 'changed' => 1, )); } } } } // Only execute this query if we added any values to it. if (isset($index_id)) { $insert->execute(); } } /** * Implements hook_disable(). */ function search_api_disable() { db_delete('search_api_item') ->execute(); } /** * Implements hook_uninstall(). */ function search_api_uninstall() { variable_del('search_api_tasks'); } /** * Update function that adds the machine names for servers and indexes. */ function search_api_update_7101() { $tx = db_transaction(); try { // Servers $spec = array( 'description' => 'The machine name for a server.', 'type' => 'varchar', 'length' => 50, 'not null' => TRUE, 'default' => '', ); db_add_field('search_api_server', 'machine_name', $spec); $names = array(); $servers = db_select('search_api_server', 's') ->fields('s') ->execute(); foreach ($servers as $server) { $base = $name = drupal_strtolower(preg_replace('/[^a-z0-9]+/i', '_', $server->name)); $i = 0; while (isset($names[$name])) { $name = $base . '_' . ++$i; } $names[$name] = TRUE; db_update('search_api_server') ->fields(array('machine_name' => $name)) ->condition('id', $server->id) ->execute(); } db_add_unique_key('search_api_server', 'machine_name', array('machine_name')); //Indexes $spec = array( 'description' => 'The machine name of the index.', 'type' => 'varchar', 'length' => 50, 'not null' => TRUE, 'default' => '', ); db_add_field('search_api_index', 'machine_name', $spec); $names = array(); $indexes = db_select('search_api_index', 'i') ->fields('i') ->execute(); foreach ($indexes as $index) { $base = $name = drupal_strtolower(preg_replace('/[^a-z0-9]+/i', '_', $index->name)); $i = 0; while (isset($names[$name])) { $name = $base . '_' . ++$i; } $names[$name] = TRUE; db_update('search_api_index') ->fields(array('machine_name' => $name)) ->condition('id', $index->id) ->execute(); } db_add_unique_key('search_api_index', 'machine_name', array('machine_name')); } catch (Exception $e) { $tx->rollback(); try { db_drop_field('search_api_server', 'machine_name'); db_drop_field('search_api_index', 'machine_name'); } catch (Exception $e1) { // Ignore. } throw new DrupalUpdateException(t('An exception occurred during the update: !msg.', array('!msg' => $e->getMessage()))); } } /** * Update replacing IDs with machine names for foreign keys. * {search_api_index}.server and {search_api_item}.index_id are altered. */ function search_api_update_7102() { // Update of search_api_index: $indexes = array(); $select = db_select('search_api_index', 'i')->fields('i'); foreach ($select->execute() as $index) { $indexes[$index->id] = $index; } $servers = db_select('search_api_server', 's')->fields('s', array('id', 'machine_name'))->execute()->fetchAllKeyed(); db_drop_index('search_api_index', 'server'); db_drop_field('search_api_index', 'server'); $spec = array( 'description' => 'The {search_api_server}.machine_name with which data should be indexed.', 'type' => 'varchar', 'length' => 50, 'not null' => FALSE, ); db_add_field('search_api_index', 'server', $spec); foreach ($indexes as $index) { db_update('search_api_index') ->fields(array('server' => $servers[$index->server])) ->condition('id', $index->id) ->execute(); } db_add_index('search_api_index', 'server', array('server')); // Update of search_api_item: db_drop_index('search_api_item', 'indexing'); db_drop_primary_key('search_api_item'); $spec = array( 'description' => 'The {search_api_index}.machine_name this item belongs to.', 'type' => 'varchar', 'length' => 50, 'not null' => TRUE, ); $keys_new = array( 'indexes' => array( 'indexing' => array('index_id', 'changed'), ), 'primary key' => array('item_id', 'index_id'), ); db_change_field('search_api_item', 'index_id', 'index_id', $spec, $keys_new); foreach ($indexes as $index) { // We explicitly forbid numeric machine names, therefore we don't have to // worry about conflicts here. db_update('search_api_item') ->fields(array( 'index_id' => $index->machine_name, )) ->condition('index_id', $index->id) ->execute(); } } /** * Add the database fields newly required for entities by the Entity API. */ function search_api_update_7103() { if (!function_exists('entity_exportable_schema_fields')) { throw new DrupalUpdateException(t('Please update the Entity API module first.')); } foreach (array('search_api_server', 'search_api_index') as $table) { foreach (entity_exportable_schema_fields() as $field => $spec) { db_add_field($table, $field, $spec); } } } /** * Change {search_api_item}.index_id back to the index' numeric ID. */ function search_api_update_7104() { $select = db_select('search_api_index', 'i')->fields('i'); foreach ($select->execute() as $index) { // We explicitly forbid numeric machine names, therefore we don't have to // worry about conflicts here. db_update('search_api_item') ->fields(array( 'index_id' => $index->id, )) ->condition('index_id', $index->machine_name) ->execute(); } // Update primary key and index. db_drop_index('search_api_item', 'indexing'); db_drop_primary_key('search_api_item'); $spec = array( 'description' => 'The {search_api_index}.id this item belongs to.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, ); $keys_new = array( 'indexes' => array( 'indexing' => array('index_id', 'changed'), ), 'primary key' => array('item_id', 'index_id'), ); db_change_field('search_api_item', 'index_id', 'index_id', $spec, $keys_new); } /** * Remove all empty aggregated fields for the search_api_alter_add_fulltext data * alterations. */ function search_api_update_7105() { $rows = db_select('search_api_index', 'i') ->fields('i', array('id', 'options')) ->execute() ->fetchAllKeyed(); foreach ($rows as $id => $options) { $opt = unserialize($options); if (isset($opt['data_alter_callbacks']['search_api_alter_add_fulltext']['settings']['fields'])) { foreach ($opt['data_alter_callbacks']['search_api_alter_add_fulltext']['settings']['fields'] as $name => $field) { if (empty($field['name']) || empty($field['fields'])) { unset($opt['data_alter_callbacks']['search_api_alter_add_fulltext']['settings']['fields'][$name]); } } } $opt = serialize($opt); if ($opt != $options) { db_update('search_api_index') ->fields(array( 'options' => $opt, )) ->condition('id', $id) ->execute(); } } } /** * Update the settings for the "Aggregated fields" data alteration. */ function search_api_update_7106() { $rows = db_select('search_api_index', 'i') ->fields('i') ->execute() ->fetchAll(); foreach ($rows as $row) { $opt = unserialize($row->options); $callbacks = &$opt['data_alter_callbacks']; if (isset($callbacks['search_api_alter_add_fulltext'])) { $callbacks['search_api_alter_add_aggregation'] = $callbacks['search_api_alter_add_fulltext']; unset($callbacks['search_api_alter_add_fulltext']); if (!empty($callbacks['search_api_alter_add_aggregation']['settings']['fields'])) { foreach ($callbacks['search_api_alter_add_aggregation']['settings']['fields'] as $field => &$info) { if (!isset($info['type'])) { $info['type'] = 'fulltext'; } } } } $opt = serialize($opt); if ($opt != $row->options) { // Mark the entity as overridden, in case it has been defined in code // only. $row->status |= 0x01; db_update('search_api_index') ->fields(array( 'options' => $opt, 'status' => $row->status, )) ->condition('id', $row->id) ->execute(); } } } /** * Add "read only" property to Search API index entities. */ function search_api_update_7108() { $db_field = array( 'description' => 'A flag indicating whether to write to this index.', 'type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0, ); db_add_field('search_api_index', 'read_only', $db_field); return t('Added a "read only" property to index entities.'); }