diff --git a/includes/index_entity.inc b/includes/index_entity.inc
index 7042b82..a5e2c93 100644
--- includes/index_entity.inc
+++ includes/index_entity.inc
@@ -106,6 +106,11 @@ class SearchApiIndex extends Entity {
public $enabled;
/**
+ * @var integer
+ */
+ public $read_only;
+
+ /**
* Constructor as a helper to the parent constructor.
*/
public function __construct(array $values = array()) {
@@ -117,33 +122,35 @@ class SearchApiIndex extends Entity {
* database, or for the first time loaded from code).
*/
public function postCreate() {
- // Remember items to index.
- $entity_info = entity_get_info($this->entity_type);
- if (!empty($entity_info['base table'])) {
- // Use a subselect, which will probably be much faster than entity_load().
- // We just assume that no module/entity type will be stupid enough to use "base table" and
- // "entity_keys[id]" in a different way than the default controller.
- $id_field = $entity_info['entity keys']['id'];
- $table = $entity_info['base table'];
- $query = db_select($table, 't');
- $query->addField('t', $id_field, 'item_id');
- $query->addExpression(':index_id', 'index_id', array(':index_id' => $this->id));
- $query->addExpression('1', 'changed');
-
- db_insert('search_api_item')->from($query)->execute();
- }
- else {
- // We have to use the slow entity_load().
- $entities = entity_load($this->entity_type, FALSE);
- $query = db_insert('search_api_item')->fields(array('item_id', 'index_id', 'changed'));
- foreach ($entities as $item_id => $entity) {
- $query->values(array(
- 'item_id' => $item_id,
- 'index_id' => $this->id,
- 'changed' => 1,
- ));
+ if (!$this->read_only) {
+ // Remember items to index.
+ $entity_info = entity_get_info($this->entity_type);
+ if (!empty($entity_info['base table'])) {
+ // Use a subselect, which will probably be much faster than entity_load().
+ // We just assume that no module/entity type will be stupid enough to use "base table" and
+ // "entity_keys[id]" in a different way than the default controller.
+ $id_field = $entity_info['entity keys']['id'];
+ $table = $entity_info['base table'];
+ $query = db_select($table, 't');
+ $query->addField('t', $id_field, 'item_id');
+ $query->addExpression(':index_id', 'index_id', array(':index_id' => $this->id));
+ $query->addExpression('1', 'changed');
+
+ db_insert('search_api_item')->from($query)->execute();
+ }
+ else {
+ // We have to use the slow entity_load().
+ $entities = entity_load($this->entity_type, FALSE);
+ $query = db_insert('search_api_item')->fields(array('item_id', 'index_id', 'changed'));
+ foreach ($entities as $item_id => $entity) {
+ $query->values(array(
+ 'item_id' => $item_id,
+ 'index_id' => $this->id,
+ 'changed' => 1,
+ ));
+ }
+ $query->execute();
}
- $query->execute();
}
$server = $this->server();
@@ -166,7 +173,7 @@ class SearchApiIndex extends Entity {
* not defined in code anymore.
*/
public function postDelete() {
- if ($server = $this->server()) {
+ if (($server = $this->server()) && !$this->read_only) {
if ($server->enabled) {
$server->removeIndex($this);
}
@@ -177,6 +184,8 @@ class SearchApiIndex extends Entity {
}
}
+ // This should be executed even when the index is read only, in case at some
+ // point it was not read only.
db_delete('search_api_item')
->condition('index_id', $this->id)
->execute();
@@ -221,7 +230,7 @@ class SearchApiIndex extends Entity {
* the specified values.
*/
public function update(array $fields) {
- $changeable = array('name' => 1, 'enabled' => 1, 'description' => 1, 'server' => 1, 'options' => 1);
+ $changeable = array('name' => 1, 'enabled' => 1, 'description' => 1, 'server' => 1, 'options' => 1, 'read_only' => 1);
$changed = FALSE;
foreach ($fields as $field => $value) {
if (isset($changeable[$field]) && $value !== $this->$field) {
@@ -243,7 +252,7 @@ class SearchApiIndex extends Entity {
* TRUE on success, FALSE on failure.
*/
public function reindex() {
- if (!$this->server) {
+ if (!$this->server || $this->read_only) {
return TRUE;
}
$ret = _search_api_index_reindex($this->id);
@@ -260,7 +269,7 @@ class SearchApiIndex extends Entity {
* TRUE on success, FALSE on failure.
*/
public function clear() {
- if (!$this->server) {
+ if (!$this->server || $this->read_only) {
return TRUE;
}
@@ -359,6 +368,9 @@ class SearchApiIndex extends Entity {
* An array of the IDs of all items that should be marked as indexed.
*/
public function index(array $items) {
+ if ($this->read_only) {
+ return array();
+ }
if (!$this->enabled) {
throw new SearchApiException(t("Couldn't index values on '!name' index (index is disabled)", array('!name' => $this->name)));
}
@@ -624,6 +636,10 @@ class SearchApiIndex extends Entity {
* @return array
* An array containing all (or all indexed) fulltext fields defined for this
* index.
+ *
+ * @TODO What does this mean when we're using a read-only index? I suspect it
+ * will not return anything, and I suspect that will be just fine. But I'm not
+ * sure.
*/
public function getFulltextFields($only_indexed = TRUE) {
$i = $only_indexed ? 1 : 0;
diff --git a/search_api.admin.inc b/search_api.admin.inc
index 0f46bd8..6fc8429 100644
--- search_api.admin.inc
+++ search_api.admin.inc
@@ -661,6 +661,7 @@ function search_api_admin_index_view(SearchApiIndex $index = NULL, $action = NUL
'#server' => $index->server(),
'#options' => $index->options,
'#status' => $index->status,
+ '#read_only' => $index->read_only,
);
return $ret;
@@ -684,9 +685,11 @@ function search_api_admin_index_view(SearchApiIndex $index = NULL, $action = NUL
* - total_items: The total number of items that have to be indexed for this
* index.
* - status: The entity configuration status (in database, in code, etc.).
+ * - read_only: Boolean indicating whether this index is read only.
*/
function theme_search_api_index(array $variables) {
extract($variables);
+
$output = '';
$output .= '
' . check_plain($name) . '
' . "\n";
@@ -728,7 +731,7 @@ function theme_search_api_index(array $variables) {
$output .= '' . "\n";
}
- if (!empty($options)) {
+ if (!$read_only && !empty($options)) {
$output .= '' . t('Index options') . '' . "\n";
$output .= '' . "\n";
$output .= '- ' . t('Cron limit') . '
' . "\n";
@@ -762,6 +765,10 @@ function theme_search_api_index(array $variables) {
$output .= '
' . "\n";
}
+ elseif ($read_only) {
+ $output .= '' . t('Read only') . '' . "\n";
+ $output .= '' . t('This index is read-only.') . '' . "\n";
+ }
$output .= '' . t('Configuration status') . '' . "\n";
$output .= '' . "\n";
@@ -1002,6 +1009,12 @@ function search_api_admin_index_edit(array $form, array &$form_state, SearchApiI
$form['server']['#options'][$server->machine_name] = t('@server_name (disabled)', array('@server_name' => $server->name));
}
}
+ $form['read_only'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Read only'),
+ '#description' => t('Do not write to this index or track ids of entities in this index.'),
+ '#default_value' => $index->read_only,
+ );
$form['cron_limit'] = array(
'#type' => 'textfield',
'#title' => t('Cron limit'),
@@ -1010,6 +1023,10 @@ function search_api_admin_index_edit(array $form, array &$form_state, SearchApiI
'#default_value' => isset($index->options['cron_limit']) ? $index->options['cron_limit'] : SEARCH_API_DEFAULT_CRON_LIMIT,
'#size' => 4,
'#attributes' => array('class' => array('search-api-cron-limit')),
+ '#element_validate' => array('_element_validate_integer'),
+ '#states' => array(
+ 'invisible' => array(':input[name="read_only"]' => array('checked' => TRUE)),
+ ),
);
$form['submit'] = array(
@@ -1021,17 +1038,6 @@ function search_api_admin_index_edit(array $form, array &$form_state, SearchApiI
}
/**
- * Validation callback for search_api_admin_index_edit.
- */
-function search_api_admin_index_edit_validate(array $form, array &$form_state) {
- $cron_limit = $form_state['values']['cron_limit'];
- if ($cron_limit != '' . ((int) $cron_limit)) {
- // We don't enforce stricter rules and treat all negative values as -1.
- form_set_error('cron_limit', t('The cron limit must be a number.'));
- }
-}
-
-/**
* Submit callback for search_api_admin_index_edit.
*/
function search_api_admin_index_edit_submit(array $form, array &$form_state) {
diff --git a/search_api.install b/search_api.install
index 3b180ca..ef24bba 100644
--- search_api.install
+++ search_api.install
@@ -115,6 +115,13 @@ function search_api_schema() {
'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,
+ ),
) + entity_exportable_schema_fields(),
'indexes' => array(
'entity_type' => array('entity_type'),
@@ -678,3 +685,18 @@ function search_api_update_7106() {
}
}
}
+
+/**
+ * Add "read only" property to Search API index entities.
+ */
+function search_api_update_7107() {
+ $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.');
+}
diff --git a/search_api.module b/search_api.module
index cfda65e..2c8377a 100644
--- search_api.module
+++ search_api.module
@@ -98,7 +98,8 @@ function search_api_menu() {
'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'),
+ 'access callback' => '_search_api_access_index_indexer_config',
+ 'access arguments' => array(5),
'file' => 'search_api.admin.inc',
'weight' => -8,
'type' => MENU_LOCAL_TASK,
@@ -120,7 +121,8 @@ function search_api_menu() {
'description' => 'Select indexed fields.',
'page callback' => 'drupal_get_form',
'page arguments' => array('search_api_admin_index_fields', 5),
- 'access arguments' => array('administer search_api'),
+ 'access callback' => '_search_api_access_index_indexer_config',
+ 'access arguments' => array(5),
'file' => 'search_api.admin.inc',
'weight' => -4,
'type' => MENU_LOCAL_TASK,
@@ -131,7 +133,8 @@ function search_api_menu() {
'description' => 'Edit index workflow.',
'page callback' => 'drupal_get_form',
'page arguments' => array('search_api_admin_index_workflow', 5),
- 'access arguments' => array('administer search_api'),
+ 'access callback' => '_search_api_access_index_indexer_config',
+ 'access arguments' => array(5),
'file' => 'search_api.admin.inc',
'weight' => -2,
'type' => MENU_LOCAL_TASK,
@@ -154,6 +157,15 @@ function search_api_menu() {
}
/**
+ * Menu access callback for index configuration pages relating to indexing
+ * operations. If a Search API index is read only, the configuration tasks for
+ * indexing (write operations) should not be available.
+ */
+function _search_api_access_index_indexer_config($index) {
+ return user_access('administer search_api') && !$index->read_only;
+}
+
+/**
* Implements hook_theme().
*/
function search_api_theme() {
@@ -184,6 +196,7 @@ function search_api_theme() {
'indexed_items' => 0,
'total_items' => 0,
'status' => ENTITY_CUSTOM,
+ 'read_only' => 0,
),
'file' => 'search_api.admin.inc',
);
@@ -543,7 +556,7 @@ function search_api_entity_insert($entity, $type) {
$id = $info['entity keys']['id'];
$id = $entity->$id;
- foreach (search_api_index_load_multiple(FALSE, array('entity_type' => $type)) as $index) {
+ foreach (search_api_index_load_multiple(FALSE, array('entity_type' => $type, 'read_only' => 0)) as $index) {
db_insert('search_api_item')
->fields(array(
'index_id' => $index->id,
@@ -569,7 +582,17 @@ function search_api_entity_update($entity, $type) {
$id = $info['entity keys']['id'];
$id = $entity->$id;
- search_api_mark_dirty($type, array($id));
+ foreach (search_api_index_load_multiple(FALSE, array('entity_type' => $type, 'read_only' => 0)) as $index) {
+ // Mark index records as changed, but leave records that are already "dirty"
+ // untouched so that the indexing order doesn't change.
+ db_merge('search_api_item')
+ ->key(array(
+ 'item_id' => $id,
+ 'index_id' => $index->id,
+ ))
+ ->expression('changed', 'IF(changed = 0, :timestamp, changed)', array(':timestamp' => REQUEST_TIME))
+ ->execute();
+ }
}
/**
@@ -663,29 +686,6 @@ function search_api_search_api_processor_info() {
}
/**
- * Mark the entities with the specified IDs as "dirty", i.e., as needing to be
- * reindexed.
- *
- * @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) {
- $query = db_select('search_api_index', 'i')
- ->fields('i', array('id'))
- ->condition('entity_type' , $entity_type);
- db_update('search_api_item')
- ->fields(array(
- 'changed' => REQUEST_TIME,
- ))
- ->condition('item_id', $ids, 'IN')
- ->condition('index_id', $query, '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).
*
@@ -707,6 +707,11 @@ function search_api_index_items(SearchApiIndex $index, $limit = -1) {
throw new SearchApiException(t("Couldn't index values for '!name' index (unknown entity type '!type')", array('!name' => $index->name, '!type' => $index->entity_type)));
}
+ // Don't try to index read-only indexes.
+ if ($index->read_only) {
+ return 0;
+ }
+
$items = search_api_get_items_to_index($index, $limit);
if (!$items) {
return 0;