123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936 |
- <?php
- /**
- * Class representing a search index.
- */
- class SearchApiIndex extends Entity {
- // Cache values, set when the corresponding methods are called for the first
- // time.
- /**
- * Cached return value of datasource().
- *
- * @var SearchApiDataSourceControllerInterface
- */
- protected $datasource = NULL;
- /**
- * Cached return value of server().
- *
- * @var SearchApiServer
- */
- protected $server_object = NULL;
- /**
- * All enabled data alterations for this index.
- *
- * @var array
- */
- protected $callbacks = NULL;
- /**
- * All enabled processors for this index.
- *
- * @var array
- */
- protected $processors = NULL;
- /**
- * The properties added by data alterations on this index.
- *
- * @var array
- */
- protected $added_properties = NULL;
- /**
- * An array containing two arrays.
- *
- * At index 0, all fulltext fields of this index. At index 1, all indexed
- * fulltext fields of this index.
- *
- * @var array
- */
- protected $fulltext_fields = array();
- // Database values that will be set when object is loaded.
- /**
- * An integer identifying the index.
- * Immutable.
- *
- * @var integer
- */
- public $id;
- /**
- * A name to be displayed for the index.
- *
- * @var string
- */
- public $name;
- /**
- * The machine name of the index.
- * Immutable.
- *
- * @var string
- */
- public $machine_name;
- /**
- * A string describing the index' use to users.
- *
- * @var string
- */
- public $description;
- /**
- * The machine_name of the server with which data should be indexed.
- *
- * @var string
- */
- public $server;
- /**
- * The type of items stored in this index.
- * Immutable.
- *
- * @var string
- */
- public $item_type;
- /**
- * An array of options for configuring this index. The layout is as follows:
- * - cron_limit: The maximum number of items to be indexed per cron batch.
- * - index_directly: Boolean setting whether entities are indexed immediately
- * after they are created or updated.
- * - fields: An array of all indexed fields for this index. Keys are the field
- * identifiers, the values are arrays for specifying the field settings. The
- * structure of those arrays looks like this:
- * - type: The type set for this field. One of the types returned by
- * search_api_default_field_types().
- * - real_type: (optional) If a custom data type was selected for this
- * field, this type will be stored here, and "type" contain the fallback
- * default data type.
- * - boost: (optional) A boost value for terms found in this field during
- * searches. Usually only relevant for fulltext fields. Defaults to 1.0.
- * - entity_type (optional): If set, the type of this field is really an
- * entity. The "type" key will then just contain the primitive data type
- * of the ID field, meaning that servers will ignore this and merely index
- * the entity's ID. Components displaying this field, though, are advised
- * to use the entity label instead of the ID.
- * - additional fields: An associative array with keys and values being the
- * field identifiers of related entities whose fields should be displayed.
- * - data_alter_callbacks: An array of all data alterations available. Keys
- * are the alteration identifiers, the values are arrays containing the
- * settings for that data alteration. The inner structure looks like this:
- * - status: Boolean indicating whether the data alteration is enabled.
- * - weight: Used for sorting the data alterations.
- * - settings: Alteration-specific settings, configured via the alteration's
- * configuration form.
- * - processors: An array of all processors available for the index. The keys
- * are the processor identifiers, the values are arrays containing the
- * settings for that processor. The inner structure looks like this:
- * - status: Boolean indicating whether the processor is enabled.
- * - weight: Used for sorting the processors.
- * - settings: Processor-specific settings, configured via the processor's
- * configuration form.
- *
- * @var array
- */
- public $options = array();
- /**
- * A flag indicating whether this index is enabled.
- *
- * @var integer
- */
- public $enabled = 1;
- /**
- * A flag indicating whether to write to this index.
- *
- * @var integer
- */
- public $read_only = 0;
- /**
- * Constructor as a helper to the parent constructor.
- */
- public function __construct(array $values = array()) {
- parent::__construct($values, 'search_api_index');
- }
- /**
- * Execute necessary tasks for a newly created index.
- */
- public function postCreate() {
- if ($this->enabled) {
- $this->queueItems();
- }
- $server = $this->server();
- if ($server) {
- // Tell the server about the new index.
- if ($server->enabled) {
- $server->addIndex($this);
- }
- else {
- $tasks = variable_get('search_api_tasks', array());
- // When we add or remove an index, we can ignore all other tasks.
- $tasks[$server->machine_name][$this->machine_name] = array('add');
- variable_set('search_api_tasks', $tasks);
- }
- }
- }
- /**
- * Execute necessary tasks when the index is removed from the database.
- */
- public function postDelete() {
- if ($server = $this->server()) {
- if ($server->enabled) {
- $server->removeIndex($this);
- }
- else {
- $tasks = variable_get('search_api_tasks', array());
- $tasks[$server->machine_name][$this->machine_name] = array('remove');
- variable_set('search_api_tasks', $tasks);
- }
- }
- // Stop tracking entities for indexing.
- $this->dequeueItems();
- }
- /**
- * Record entities to index.
- */
- public function queueItems() {
- if (!$this->read_only) {
- $this->datasource()->startTracking(array($this));
- }
- }
- /**
- * Remove all records of entities to index.
- */
- public function dequeueItems() {
- $this->datasource()->stopTracking(array($this));
- _search_api_empty_cron_queue($this);
- }
- /**
- * Saves this index to the database, either creating a new record or updating
- * an existing one.
- *
- * @return
- * Failure to save the index will return FALSE. Otherwise, SAVED_NEW or
- * SAVED_UPDATED is returned depending on the operation performed. $this->id
- * will be set if a new index was inserted.
- */
- public function save() {
- if (empty($this->description)) {
- $this->description = NULL;
- }
- if (empty($this->server)) {
- $this->server = NULL;
- $this->enabled = FALSE;
- }
- // This will also throw an exception if the server doesn't exist – which is good.
- elseif (!$this->server(TRUE)->enabled) {
- $this->enabled = FALSE;
- }
- return parent::save();
- }
- /**
- * Helper method for updating entity properties.
- *
- * NOTE: You shouldn't change any properties of this object before calling
- * this method, as this might lead to the fields not being saved correctly.
- *
- * @param array $fields
- * The new field values.
- *
- * @return
- * SAVE_UPDATED on success, FALSE on failure, 0 if the fields already had
- * the specified values.
- */
- public function update(array $fields) {
- $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) {
- $this->$field = $value;
- $changed = TRUE;
- }
- }
- // If there are no new values, just return 0.
- if (!$changed) {
- return 0;
- }
- // Reset the index's internal property cache to correctly incorporate new
- // settings.
- $this->resetCaches();
- return $this->save();
- }
- /**
- * Schedules this search index for re-indexing.
- *
- * @return
- * TRUE on success, FALSE on failure.
- */
- public function reindex() {
- if (!$this->server || $this->read_only) {
- return TRUE;
- }
- _search_api_index_reindex($this);
- module_invoke_all('search_api_index_reindex', $this, FALSE);
- return TRUE;
- }
- /**
- * Clears this search index and schedules all of its items for re-indexing.
- *
- * @return
- * TRUE on success, FALSE on failure.
- */
- public function clear() {
- if (!$this->server || $this->read_only) {
- return TRUE;
- }
- $server = $this->server();
- if ($server->enabled) {
- $server->deleteItems('all', $this);
- }
- else {
- $tasks = variable_get('search_api_tasks', array());
- // If the index was cleared or newly added since the server was last enabled, we don't need to do anything.
- if (!isset($tasks[$server->machine_name][$this->machine_name])
- || (array_search('add', $tasks[$server->machine_name][$this->machine_name]) === FALSE
- && array_search('clear', $tasks[$server->machine_name][$this->machine_name]) === FALSE)) {
- $tasks[$server->machine_name][$this->machine_name][] = 'clear';
- variable_set('search_api_tasks', $tasks);
- }
- }
- _search_api_index_reindex($this);
- module_invoke_all('search_api_index_reindex', $this, TRUE);
- return TRUE;
- }
- /**
- * Magic method for determining which fields should be serialized.
- *
- * Don't serialize properties that are basically only caches.
- *
- * @return array
- * An array of properties to be serialized.
- */
- public function __sleep() {
- $ret = get_object_vars($this);
- unset($ret['server_object'], $ret['datasource'], $ret['processors'], $ret['added_properties'], $ret['fulltext_fields']);
- return array_keys($ret);
- }
- /**
- * Get the controller object of the data source used by this index.
- *
- * @throws SearchApiException
- * If the specified item type or data source doesn't exist or is invalid.
- *
- * @return SearchApiDataSourceControllerInterface
- * The data source controller for this index.
- */
- public function datasource() {
- if (!isset($this->datasource)) {
- $this->datasource = search_api_get_datasource_controller($this->item_type);
- }
- return $this->datasource;
- }
- /**
- * Get the server this index lies on.
- *
- * @param $reset
- * Whether to reset the internal cache. Set to TRUE when the index' $server
- * property has just changed.
- *
- * @throws SearchApiException
- * If $this->server is set, but no server with that machine name exists.
- *
- * @return SearchApiServer
- * The server associated with this index, or NULL if this index currently
- * doesn't lie on a server.
- */
- public function server($reset = FALSE) {
- if (!isset($this->server_object) || $reset) {
- $this->server_object = $this->server ? search_api_server_load($this->server) : FALSE;
- if ($this->server && !$this->server_object) {
- throw new SearchApiException(t('Unknown server @server specified for index @name.', array('@server' => $this->server, '@name' => $this->machine_name)));
- }
- }
- return $this->server_object ? $this->server_object : NULL;
- }
- /**
- * Create a query object for this index.
- *
- * @param $options
- * Associative array of options configuring this query. See
- * SearchApiQueryInterface::__construct().
- *
- * @throws SearchApiException
- * If the index is currently disabled.
- *
- * @return SearchApiQueryInterface
- * A query object for searching this index.
- */
- public function query($options = array()) {
- if (!$this->enabled) {
- throw new SearchApiException(t('Cannot search on a disabled index.'));
- }
- return $this->server()->query($this, $options);
- }
- /**
- * Indexes items on this index. Will return an array of IDs of items that
- * should be marked as indexed – i.e., items that were either rejected by a
- * data-alter callback or were successfully indexed.
- *
- * @param array $items
- * An array of items to index.
- *
- * @return array
- * 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)));
- }
- if (empty($this->options['fields'])) {
- throw new SearchApiException(t("Couldn't index values on '@name' index (no fields selected)", array('@name' => $this->name)));
- }
- $fields = $this->options['fields'];
- $custom_type_fields = array();
- foreach ($fields as $field => $info) {
- if (isset($info['real_type'])) {
- $custom_type = search_api_extract_inner_type($info['real_type']);
- if ($this->server()->supportsFeature('search_api_data_type_' . $custom_type)) {
- $fields[$field]['type'] = $info['real_type'];
- $custom_type_fields[$custom_type][$field] = search_api_list_nesting_level($info['real_type']);
- }
- }
- }
- if (empty($fields)) {
- throw new SearchApiException(t("Couldn't index values on '@name' index (no fields selected)", array('@name' => $this->name)));
- }
- // Mark all items that are rejected as indexed.
- $ret = array_keys($items);
- drupal_alter('search_api_index_items', $items, $this);
- if ($items) {
- $this->dataAlter($items);
- }
- $ret = array_diff($ret, array_keys($items));
- // Items that are rejected should also be deleted from the server.
- if ($ret) {
- $this->server()->deleteItems($ret, $this);
- }
- if (!$items) {
- return $ret;
- }
- $data = array();
- foreach ($items as $id => $item) {
- $data[$id] = search_api_extract_fields($this->entityWrapper($item), $fields);
- unset($items[$id]);
- foreach ($custom_type_fields as $type => $type_fields) {
- $info = search_api_get_data_type_info($type);
- if (isset($info['conversion callback']) && is_callable($info['conversion callback'])) {
- $callback = $info['conversion callback'];
- foreach ($type_fields as $field => $nesting_level) {
- if (isset($data[$id][$field]['value'])) {
- $value = $data[$id][$field]['value'];
- $original_type = $data[$id][$field]['original_type'];
- $data[$id][$field]['value'] = _search_api_convert_custom_type($callback, $value, $original_type, $type, $nesting_level);
- }
- }
- }
- }
- }
- $this->preprocessIndexItems($data);
- return array_merge($ret, $this->server()->indexItems($this, $data));
- }
- /**
- * Calls data alteration hooks for a set of items, according to the index
- * options.
- *
- * @param array $items
- * An array of items to be altered.
- *
- * @return SearchApiIndex
- * The called object.
- */
- public function dataAlter(array &$items) {
- // First, execute our own search_api_language data alteration.
- foreach ($items as &$item) {
- $item->search_api_language = isset($item->language) ? $item->language : LANGUAGE_NONE;
- }
- foreach ($this->getAlterCallbacks() as $callback) {
- $callback->alterItems($items);
- }
- return $this;
- }
- /**
- * Property info alter callback that adds the infos of the properties added by
- * data alter callbacks.
- *
- * @param EntityMetadataWrapper $wrapper
- * The wrapped data.
- * @param $property_info
- * The original property info.
- *
- * @return array
- * The altered property info.
- */
- public function propertyInfoAlter(EntityMetadataWrapper $wrapper, array $property_info) {
- if (entity_get_property_info($wrapper->type())) {
- // Overwrite the existing properties with the list of properties including
- // all fields regardless of the used bundle.
- $property_info['properties'] = entity_get_all_property_info($wrapper->type());
- }
- if (!isset($this->added_properties)) {
- $this->added_properties = array(
- 'search_api_language' => array(
- 'label' => t('Item language'),
- 'description' => t("A field added by the search framework to let components determine an item's language. Is always indexed."),
- 'type' => 'token',
- 'options list' => 'entity_metadata_language_list',
- ),
- );
- // We use the reverse order here so the hierarchy for overwriting property infos is the same
- // as for actually overwriting the properties.
- foreach (array_reverse($this->getAlterCallbacks()) as $callback) {
- $props = $callback->propertyInfo();
- if ($props) {
- $this->added_properties += $props;
- }
- }
- }
- // Let fields added by data-alter callbacks override default fields.
- $property_info['properties'] = array_merge($property_info['properties'], $this->added_properties);
- return $property_info;
- }
- /**
- * Fills the $processors array for use by the pre-/postprocessing functions.
- *
- * @return SearchApiIndex
- * The called object.
- * @return array
- * All enabled callbacks for this index, as SearchApiAlterCallbackInterface
- * objects.
- */
- protected function getAlterCallbacks() {
- if (isset($this->callbacks)) {
- return $this->callbacks;
- }
- $this->callbacks = array();
- if (empty($this->options['data_alter_callbacks'])) {
- return $this->callbacks;
- }
- $callback_settings = $this->options['data_alter_callbacks'];
- $infos = search_api_get_alter_callbacks();
- foreach ($callback_settings as $id => $settings) {
- if (empty($settings['status'])) {
- continue;
- }
- if (empty($infos[$id]) || !class_exists($infos[$id]['class'])) {
- watchdog('search_api', t('Undefined data alteration @class specified in index @name', array('@class' => $id, '@name' => $this->name)), NULL, WATCHDOG_WARNING);
- continue;
- }
- $class = $infos[$id]['class'];
- $callback = new $class($this, empty($settings['settings']) ? array() : $settings['settings']);
- if (!($callback instanceof SearchApiAlterCallbackInterface)) {
- watchdog('search_api', t('Unknown callback class @class specified for data alteration @name', array('@class' => $class, '@name' => $id)), NULL, WATCHDOG_WARNING);
- continue;
- }
- $this->callbacks[$id] = $callback;
- }
- return $this->callbacks;
- }
- /**
- * @return array
- * All enabled processors for this index, as SearchApiProcessorInterface
- * objects.
- */
- protected function getProcessors() {
- if (isset($this->processors)) {
- return $this->processors;
- }
- $this->processors = array();
- if (empty($this->options['processors'])) {
- return $this->processors;
- }
- $processor_settings = $this->options['processors'];
- $infos = search_api_get_processors();
- foreach ($processor_settings as $id => $settings) {
- if (empty($settings['status'])) {
- continue;
- }
- if (empty($infos[$id]) || !class_exists($infos[$id]['class'])) {
- watchdog('search_api', t('Undefined processor @class specified in index @name', array('@class' => $id, '@name' => $this->name)), NULL, WATCHDOG_WARNING);
- continue;
- }
- $class = $infos[$id]['class'];
- $processor = new $class($this, isset($settings['settings']) ? $settings['settings'] : array());
- if (!($processor instanceof SearchApiProcessorInterface)) {
- watchdog('search_api', t('Unknown processor class @class specified for processor @name', array('@class' => $class, '@name' => $id)), NULL, WATCHDOG_WARNING);
- continue;
- }
- $this->processors[$id] = $processor;
- }
- return $this->processors;
- }
- /**
- * Preprocess data items for indexing. Data added by data alter callbacks will
- * be available on the items.
- *
- * Typically, a preprocessor will execute its preprocessing (e.g. stemming,
- * n-grams, word splitting, stripping stop words, etc.) only on the items'
- * fulltext fields. Other fields should usually be left untouched.
- *
- * @param array $items
- * An array of items to be preprocessed for indexing.
- *
- * @return SearchApiIndex
- * The called object.
- */
- public function preprocessIndexItems(array &$items) {
- foreach ($this->getProcessors() as $processor) {
- $processor->preprocessIndexItems($items);
- }
- return $this;
- }
- /**
- * Preprocess a search query.
- *
- * The same applies as when preprocessing indexed items: typically, only the
- * fulltext search keys should be processed, queries on specific fields should
- * usually not be altered.
- *
- * @param SearchApiQuery $query
- * The object representing the query to be executed.
- *
- * @return SearchApiIndex
- * The called object.
- */
- public function preprocessSearchQuery(SearchApiQuery $query) {
- foreach ($this->getProcessors() as $processor) {
- $processor->preprocessSearchQuery($query);
- }
- return $this;
- }
- /**
- * Postprocess search results before display.
- *
- * If a class is used for both pre- and post-processing a search query, the
- * same object will be used for both calls (so preserving some data or state
- * locally is possible).
- *
- * @param array $response
- * An array containing the search results. See
- * SearchApiServiceInterface->search() for the detailed format.
- * @param SearchApiQuery $query
- * The object representing the executed query.
- *
- * @return SearchApiIndex
- * The called object.
- */
- public function postprocessSearchResults(array &$response, SearchApiQuery $query) {
- // Postprocessing is done in exactly the opposite direction than preprocessing.
- foreach (array_reverse($this->getProcessors()) as $processor) {
- $processor->postprocessSearchResults($response, $query);
- }
- return $this;
- }
- /**
- * Returns a list of all known fields for this index.
- *
- * @param $only_indexed (optional)
- * Return only indexed fields, not all known fields. Defaults to TRUE.
- * @param $get_additional (optional)
- * Return not only known/indexed fields, but also related entities whose
- * fields could additionally be added to the index.
- *
- * @return array
- * An array of all known fields for this index. Keys are the field
- * identifiers, the values are arrays for specifying the field settings. The
- * structure of those arrays looks like this:
- * - name: The human-readable name for the field.
- * - description: A description of the field, if available.
- * - indexed: Boolean indicating whether the field is indexed or not.
- * - type: The type set for this field. One of the types returned by
- * search_api_default_field_types().
- * - real_type: (optional) If a custom data type was selected for this
- * field, this type will be stored here, and "type" contain the fallback
- * default data type.
- * - boost: A boost value for terms found in this field during searches.
- * Usually only relevant for fulltext fields.
- * - entity_type (optional): If set, the type of this field is really an
- * entity. The "type" key will then contain "integer", meaning that
- * servers will ignore this and merely index the entity's ID. Components
- * displaying this field, though, are advised to use the entity label
- * instead of the ID.
- * If $get_additional is TRUE, this array is encapsulated in another
- * associative array, which contains the above array under the "fields" key,
- * and a list of related entities (field keys mapped to names) under the
- * "additional fields" key.
- */
- public function getFields($only_indexed = TRUE, $get_additional = FALSE) {
- $fields = empty($this->options['fields']) ? array() : $this->options['fields'];
- $wrapper = $this->entityWrapper();
- $additional = array();
- $entity_types = entity_get_info();
- // First we need all already added prefixes.
- $added = ($only_indexed || empty($this->options['additional fields'])) ? array() : $this->options['additional fields'];
- foreach (array_keys($fields) as $key) {
- $len = strlen($key) + 1;
- $pos = $len;
- // The third parameter ($offset) to strrpos has rather weird behaviour,
- // necessitating this rather awkward code. It will iterate over all
- // prefixes of each field, beginning with the longest, adding all of them
- // to $added until one is encountered that was already added (which means
- // all shorter ones will have already been added, too).
- while ($pos = strrpos($key, ':', $pos - $len)) {
- $prefix = substr($key, 0, $pos);
- if (isset($added[$prefix])) {
- break;
- }
- $added[$prefix] = $prefix;
- }
- }
- // Then we walk through all properties and look if they are already
- // contained in one of the arrays.
- // Since this uses an iterative instead of a recursive approach, it is a bit
- // complicated, with three arrays tracking the current depth.
- // A wrapper for a specific field name prefix, e.g. 'user:' mapped to the user wrapper
- $wrappers = array('' => $wrapper);
- // Display names for the prefixes
- $prefix_names = array('' => '');
- // The list nesting level for entities with a certain prefix
- $nesting_levels = array('' => 0);
- $types = search_api_default_field_types();
- $flat = array();
- while ($wrappers) {
- foreach ($wrappers as $prefix => $wrapper) {
- $prefix_name = $prefix_names[$prefix];
- // Deal with lists of entities.
- $nesting_level = $nesting_levels[$prefix];
- $type_prefix = str_repeat('list<', $nesting_level);
- $type_suffix = str_repeat('>', $nesting_level);
- if ($nesting_level) {
- $info = $wrapper->info();
- // The real nesting level of the wrapper, not the accumulated one.
- $level = search_api_list_nesting_level($info['type']);
- for ($i = 0; $i < $level; ++$i) {
- $wrapper = $wrapper[0];
- }
- }
- // Now look at all properties.
- foreach ($wrapper as $property => $value) {
- $info = $value->info();
- // We hide the complexity of multi-valued types from the user here.
- $type = search_api_extract_inner_type($info['type']);
- // Treat Entity API type "token" as our "string" type.
- // Also let text fields with limited options be of type "string" by default.
- if ($type == 'token' || ($type == 'text' && !empty($info['options list']))) {
- // Inner type is changed to "string".
- $type = 'string';
- // Set the field type accordingly.
- $info['type'] = search_api_nest_type('string', $info['type']);
- }
- $info['type'] = $type_prefix . $info['type'] . $type_suffix;
- $key = $prefix . $property;
- if ((isset($types[$type]) || isset($entity_types[$type])) && (!$only_indexed || !empty($fields[$key]))) {
- if (!empty($fields[$key])) {
- // This field is already known in the index configuration.
- $flat[$key] = $fields[$key] + array(
- 'name' => $prefix_name . $info['label'],
- 'description' => empty($info['description']) ? NULL : $info['description'],
- 'boost' => '1.0',
- 'indexed' => TRUE,
- );
- // Update the type and its nesting level for non-entity properties.
- if (!isset($entity_types[$type])) {
- $flat[$key]['type'] = search_api_nest_type(search_api_extract_inner_type($flat[$key]['type']), $info['type']);
- if (isset($flat[$key]['real_type'])) {
- $real_type = search_api_extract_inner_type($flat[$key]['real_type']);
- $flat[$key]['real_type'] = search_api_nest_type($real_type, $info['type']);
- }
- }
- }
- else {
- $flat[$key] = array(
- 'name' => $prefix_name . $info['label'],
- 'description' => empty($info['description']) ? NULL : $info['description'],
- 'type' => $info['type'],
- 'boost' => '1.0',
- 'indexed' => FALSE,
- );
- }
- if (isset($entity_types[$type])) {
- $base_type = isset($entity_types[$type]['entity keys']['name']) ? 'string' : 'integer';
- $flat[$key]['type'] = search_api_nest_type($base_type, $info['type']);
- $flat[$key]['entity_type'] = $type;
- }
- }
- if (empty($types[$type])) {
- if (isset($added[$key])) {
- // Visit this entity/struct in a later iteration.
- $wrappers[$key . ':'] = $value;
- $prefix_names[$key . ':'] = $prefix_name . $info['label'] . ' » ';
- $nesting_levels[$key . ':'] = search_api_list_nesting_level($info['type']);
- }
- else {
- $name = $prefix_name . $info['label'];
- // Add machine names to discern fields with identical labels.
- if (isset($used_names[$name])) {
- if ($used_names[$name] !== FALSE) {
- $additional[$used_names[$name]] .= ' [' . $used_names[$name] . ']';
- $used_names[$name] = FALSE;
- }
- $name .= ' [' . $key . ']';
- }
- $additional[$key] = $name;
- $used_names[$name] = $key;
- }
- }
- }
- unset($wrappers[$prefix]);
- }
- }
- if (!$get_additional) {
- return $flat;
- }
- $options = array();
- $options['fields'] = $flat;
- $options['additional fields'] = $additional;
- return $options;
- }
- /**
- * Convenience method for getting all of this index's fulltext fields.
- *
- * @param boolean $only_indexed
- * If set to TRUE, only the indexed fulltext fields will be returned.
- *
- * @return array
- * An array containing all (or all indexed) fulltext fields defined for this
- * index.
- */
- public function getFulltextFields($only_indexed = TRUE) {
- $i = $only_indexed ? 1 : 0;
- if (!isset($this->fulltext_fields[$i])) {
- $this->fulltext_fields[$i] = array();
- $fields = $only_indexed ? $this->options['fields'] : $this->getFields(FALSE);
- foreach ($fields as $key => $field) {
- if (search_api_is_text_type($field['type'])) {
- $this->fulltext_fields[$i][] = $key;
- }
- }
- }
- return $this->fulltext_fields[$i];
- }
- /**
- * Helper function for creating an entity metadata wrapper appropriate for
- * this index.
- *
- * @param $item
- * Unless NULL, an item of this index's item type which should be wrapped.
- * @param $alter
- * Whether to apply the index's active data alterations on the property
- * information used. To also apply the data alteration to the wrapped item,
- * execute SearchApiIndex::dataAlter() on it before calling this method.
- *
- * @return EntityMetadataWrapper
- * A wrapper for the item type of this index, optionally loaded with the
- * given data and having additional fields according to the data alterations
- * of this index.
- */
- public function entityWrapper($item = NULL, $alter = TRUE) {
- $info['property info alter'] = $alter ? array($this, 'propertyInfoAlter') : '_search_api_wrapper_add_all_properties';
- $info['property defaults']['property info alter'] = '_search_api_wrapper_add_all_properties';
- return $this->datasource()->getMetadataWrapper($item, $info);
- }
- /**
- * Helper method to load items from the type lying on this index.
- *
- * @param array $ids
- * The IDs of the items to load.
- *
- * @return array
- * The requested items, as loaded by the data source.
- *
- * @see SearchApiDataSourceControllerInterface::loadItems()
- */
- public function loadItems(array $ids) {
- return $this->datasource()->loadItems($ids);
- }
- /**
- * Reset internal static caches.
- *
- * Should be used when things like fields or data alterations change to avoid
- * using stale data.
- */
- public function resetCaches() {
- $this->datasource = NULL;
- $this->server_object = NULL;
- $this->callbacks = NULL;
- $this->processors = NULL;
- $this->added_properties = NULL;
- $this->fulltext_fields = array();
- }
- }
|