updated and repatched search_api module

please check this thread : Decide on strategy for language aware search https://www.drupal.org/node/1393058
This commit is contained in:
Bachir Soussi Chiadmi 2015-04-20 19:18:35 +02:00
parent d3dcf44a1e
commit ca9413af4d
34 changed files with 1272 additions and 377 deletions

View File

@ -0,0 +1,28 @@
From 6402fc7ab8f6343defbee7111dee7dd16a5082fc Mon Sep 17 00:00:00 2001
From: Bachir Soussi Chiadmi <bachir@g-u-i.net>
Date: Fri, 7 Feb 2014 10:10:15 +0100
Subject: [PATCH 1/4] re-added own boosts
---
search_api.admin.inc | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/search_api.admin.inc b/search_api.admin.inc
index f4210f4..b2269d6 100644
--- a/search_api.admin.inc
+++ b/search_api.admin.inc
@@ -1658,8 +1658,9 @@ function search_api_admin_index_fields(array $form, array &$form_state, SearchAp
// An array of option arrays for types, keyed by nesting level.
$types = array(0 => search_api_field_types());
$entity_types = entity_get_info();
- $boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0'));
-
+ //$boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0'));
+ $boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0', '100', '1000', '1010', '1020', '1030', '1040', '1050', '1060'));
+
$fulltext_types = array(0 => array('text'));
// Add all custom data types with fallback "text" to fulltext types as well.
foreach (search_api_get_data_type_info() as $id => $type) {
--
2.3.5

View File

@ -0,0 +1,28 @@
From 54ee5c7b3a05850e15067d77a182cb8fe723d8e0 Mon Sep 17 00:00:00 2001
From: Bachir Soussi Chiadmi <bachir@g-u-i.net>
Date: Fri, 7 Feb 2014 10:29:08 +0100
Subject: [PATCH 2/4] taxo term translation bug : added reference to bug fix in
comment
---
search_api.module | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/search_api.module b/search_api.module
index 000cadb..55bd54a 100644
--- a/search_api.module
+++ b/search_api.module
@@ -2221,6 +2221,10 @@ function search_api_extract_fields(EntityMetadataWrapper $wrapper, array $fields
foreach ($nested as $prefix => $nested_fields) {
if (isset($wrapper->$prefix)) {
$nested_fields = search_api_extract_fields($wrapper->$prefix, $nested_fields, $value_options);
+ # http://drupal.org/node/1873910#comment-6876200
+ // $subwrapper = $wrapper->$prefix;
+ // $subwrapper->language( $wrapper->language->value() );
+ // $nested_fields = search_api_extract_fields($subwrapper, $nested_fields, $value_options);
foreach ($nested_fields as $field => $info) {
$fields["$prefix:$field"] = $info;
}
--
2.3.5

View File

@ -0,0 +1,25 @@
From 85251183d6ad5fadaa65154e33e1e8ac8ca7f9b0 Mon Sep 17 00:00:00 2001
From: Bachir Soussi Chiadmi <bachir@g-u-i.net>
Date: Fri, 7 Feb 2014 10:32:26 +0100
Subject: [PATCH 3/4] NODE_PUBLISED : in previous patches i commented the next
line, why ? maybe will have to do it again
---
search_api.module | 1 +
1 file changed, 1 insertion(+)
diff --git a/search_api.module b/search_api.module
index 55bd54a..17f611a 100644
--- a/search_api.module
+++ b/search_api.module
@@ -1994,6 +1994,7 @@ function _search_api_query_add_node_access($account, SearchApiQueryInterface $qu
$query->filter($filter);
}
else {
+ // /!\ in previous patches i commented the next line, why ? maybe will have to do it again
$query->condition('status', $published);
}
--
2.3.5

View File

@ -0,0 +1,50 @@
From c06be9a44ed0be31859a1800cf7f0ae6e8ae492a Mon Sep 17 00:00:00 2001
From: Bachir Soussi Chiadmi <bachir@g-u-i.net>
Date: Fri, 21 Feb 2014 19:49:45 +0100
Subject: [PATCH 4/4] Icons re-added icons, why they didn't be here, i don't
know ...
---
disabled.png | Bin 0 -> 384 bytes
enabled.png | Bin 0 -> 383 bytes
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 disabled.png
create mode 100644 enabled.png
diff --git a/disabled.png b/disabled.png
new file mode 100644
index 0000000000000000000000000000000000000000..224776502046765ef7c083ffa3229fd206b9c975
GIT binary patch
literal 384
zcmV-`0e}99P)<h;3K|Lk000e1NJLTq000aC000aK1^@s6R&`wG0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzB1uF+RCwBik-tg<K@i4gmNYiGco6Uz
z1Rp_+BwY$iC2}^tfuaNqN)E0PY=wY@f3Z~1&O-&?K=37m)3r_F_{}jFE*3iQyZL7J
zn_c#nMT9h-L*7E#2LVlo2k}xSM_RBBJcfJ*9ns%$zMRR1dkA@F3^O3`V!2Gwi`45z
zLR~;$(8^>Hxo5S~v);h!F5lHi?8tY}Y=6k>{VeZk13H0TfJ{L>zr#&187PJtE1&YF
z#chq}k)8^(h8y8iq7K@{qH60+Je8qL{fT(Z%i(p9?@Xp=Ap7MLyiFg&aBu-LbgHOE
zFV;2lcsCYG0D;xhDo4mEm@`uJI=W__B!9S*DqmuQ&OZ-#wfQCMPL+ypp>5y+{X%=Y
e>QV2H00RI@_ptZRUTXUQ0000<MNUMnLSTXuN2F!|
literal 0
HcmV?d00001
diff --git a/enabled.png b/enabled.png
new file mode 100644
index 0000000000000000000000000000000000000000..95f8730e6955f1de7d244817db5ed7678bce0f72
GIT binary patch
literal 383
zcmV-_0f7FAP)<h;3K|Lk000e1NJLTq000aC000aK1^@s6R&`wG0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzAxT6*RCwBA{Qv(y0|*Fj@h<{WbwJF|
zfC@eWanoC$jeQ^vBS?eLCf`Lsb}R#au=t(d<~T-yb)Ka_P8S1lpp21kmFrs|LkN$e
zvp{SB#LPhaj_LoOKSDsv0L0&c_%)Ob!<&HE3Wy_s_%BE;(?6gD5Pt+>Hz0Nf;@42I
zO+Xy_DRSR0DE}{rX8QO0Hv<r}0WtHJ*h80rv@;M-2jWm5{}<Oh%P1gw1yl_KBl}E~
z|4_Gn2V&13X{Qgu9M3V!GyzD>fw~_IKsJ1Y+QJFM+u5cX*n=d1bS#iR2V^r;9)v$K
zvP{rM4_1&(vw%1sYp{YMj=5K3DUcIIAP$!OExr-W1Y&_0|Ns6i2I7xE%z%bLVr3vT
dAhiGi1_1Nf-q(XP%8~#8002ovPDHLkV1gxRmaYH*
literal 0
HcmV?d00001
--
2.3.5

View File

@ -1,3 +1,70 @@
Search API 1.14 (12/26/2014):
-----------------------------
- #2382385 by illusionuk, drunken monkey: Fixed error handling when using
invalid fulltext or sort field in Views.
- #2371099 by drunken monkey: Fixed display of active "Exclude" facets.
- #1861134 by Cyberwolf, jackbravo, drunken monkey: Fixed indexing on multiple
indexes with Drush.
- #2347367 by drunken monkey, das-peter: Fixed forgotten usages of
$index->item_type.
- #2359201 by drunken monkey: Added a "List" option to "Aggregated fields".
- #2364247 by drunken monkey: Fixed documentation for
SearchApiQueryFilterInterface::getFilters().
- #2364875 by Xano: Fixed Views argument handler for fulltext fields.
- #2174163 by drunken monkey: Fixed detection of field type changes by data
alterations.
- #2305755 by drunken monkey, pfrenssen: Fixed invalidation of the stored index
fields cache.
- #2334727 by Alex Bukach, drunken monkey: Fixed Views caching does not take
items_per_page into account.
- #1372092 by drunken monkey: Added an error message when no service class is
available when creating a server.
- #2305627 by drunken monkey, cpliakas: Fixed date facets not displayed when
the configured granularity is larger than the calculated granularity.
- #2319263 by solotandem: Added easier way to subclass entity classes.
- #2278737 by drunken monkey: Fixed use of multiple Views fulltext search
filters.
Search API 1.13 (07/23/2014):
-----------------------------
- #2281535 by areynolds, nicola85: Adapted to latest changes in Views cache
plugins.
- #2145547 by aaronbauman: Fixed duplicated sorts (one exposed) in Views.
- #2146435 by alanmackenzie: Fixed Views paging with custom pager add-ons.
- #2278791 by drunken monkey | tksmd: Fixed excerpt when searching single CJK
word.
- #2272983 by idflood, drunken monkey: Fixed Highlighting processor for queries
without returned results.
- #2216345 by bacardi55, fabianderijk, drunken monkey: Fixed array to string
conversion in Highlighting processor.
Search API 1.12 (05/23/2014):
-----------------------------
- #2265349 by drunken monkey: Marked _search_api_settings_equals() as
deprecated.
- #2256891 by justanothermark: Fixed "0" entity labels.
- #2233749 by rjacobs, drunken monkey: Added drush support to change the server
used by an index.
- #2219553 by drunken monkey: Fixed Views fulltext filter operators.
- #2135697 by drunken monkey: Fixed handling of HTML attributes in the
Highlighting processor.
- #2179755 by drunken monkey, fago: Fixed whitespaces after HTML filter.
- #2204847 by drunken monkey, alanmackenzie: Fixed Views caching issues with
pagination.
- #2198791 by drunken monkey: Fixed empty Views entity filters.
- #2195469 by freakalis, drunken monkey: Added "Exclude fields" options to
Highlighting processor.
- #2169455 by drunken monkey: Fixed "undefined index" in
search_api_update_7116().
- #2219563 by drunken monkey: Added __toString() methods for queries and
filters.
- #1888174 by drunken monkey, ipallian: Fixed problems with date facets.
- #2187487 by drunken monkey: Fixed admin summary of language filter.
- #2198261 by drunken monkey: Fixed fatal error on view editing.
- #2168713 by idebr: Fixed highlighting of keys containing slashes.
- #2150779 by hefox: Fixed "Overridden" detection for index features.
- #1227702 by drunken monkey: Improved error handling.
Search API 1.11 (12/25/2013):
-----------------------------
- #1879196 by drunken monkey: Fixed invalid old indexes causing errors.

View File

@ -1,39 +0,0 @@
diff --git a/search_api.admin.inc b/search_api.admin.inc
index 5fbc8d8..9a5122e 100644
--- a/search_api.admin.inc
+++ b/search_api.admin.inc
@@ -1480,8 +1480,8 @@ function search_api_admin_index_fields(array $form, array &$form_state, SearchAp
$fulltext_type = array(0 => 'text');
$entity_types = entity_get_info();
$default_types = search_api_default_field_types();
- $boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0'));
-
+ // $boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0', '5000', '10000', '20000', '40000', '80000', '160000', '320000'));
+ $boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0', '100', '1000', '1010', '1020', '1030', '1040', '1050', '1060'));
$form_state['index'] = $index;
$form['#theme'] = 'search_api_admin_fields_table';
$form['#tree'] = TRUE;
diff --git a/search_api.module b/search_api.module
index bba0681..ba27465 100644
--- a/search_api.module
+++ b/search_api.module
@@ -1444,7 +1444,7 @@ function _search_api_query_add_node_access($account, SearchApiQueryInterface $qu
$query->filter($filter);
}
else {
- $query->condition('status', NODE_PUBLISHED);
+ // $query->condition('status', NODE_PUBLISHED);
}
// Filter by node access grants.
$filter = $query->createFilter('OR');
@@ -1636,6 +1636,10 @@ function search_api_extract_fields(EntityMetadataWrapper $wrapper, array $fields
foreach ($nested as $prefix => $nested_fields) {
if (isset($wrapper->$prefix)) {
$nested_fields = search_api_extract_fields($wrapper->$prefix, $nested_fields, $value_options);
+ # http://drupal.org/node/1873910#comment-6876200
+ // $subwrapper = $wrapper->$prefix;
+ // $subwrapper->language( $wrapper->language->value() );
+ // $nested_fields = search_api_extract_fields($subwrapper, $nested_fields, $value_options);
foreach ($nested_fields as $field => $info) {
$fields["$prefix:$field"] = $info;
}

View File

@ -57,14 +57,94 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
if ($active = $this->adapter->getActiveItems($this->facet)) {
$item = end($active);
$field = $this->facet['field'];
$regex = str_replace(array('^', '$'), '', FACETAPI_REGEX_DATE);
$filter = preg_replace_callback($regex, array($this, 'replaceDateString'), $item['value']);
$filter = $this->createRangeFilter($item['value']);
$this->addFacetFilter($query, $field, $filter);
}
}
/**
* Rewrites the handler-specific date range syntax to the normal facet syntax.
*
* @param $value
* The user-facing facet value.
*
* @return string
* A facet to add as a filter, in the format used internally in this module.
*/
protected function createRangeFilter($value) {
// Gets the granularity. Ignore any filters passed directly from the server
// (range or missing). We always create filters starting with a year.
if (!$value || !ctype_digit($value[0])) {
return $value;
}
$parts = explode('-', $value);
$date = new DateTime();
switch (count($parts)) {
case 1:
$date->setDate($parts[0], 1, 1);
$date->setTime(0, 0, 0);
$lower = $date->format('U');
$date->setDate($parts[0] + 1, 1, 1);
$date->setTime(0, 0, -1);
$upper = $date->format('U');
break;
case 2:
// Luckily, $month = 13 is treated as January of next year. (The same
// goes for all other parameters.) We use the inverse trick for the
// seconds of the upper bound, since that's inclusive and we want to
// stop at a second before the next segment starts.
$date->setDate($parts[0], $parts[1], 1);
$date->setTime(0, 0, 0);
$lower = $date->format('U');
$date->setDate($parts[0], $parts[1] + 1, 1);
$date->setTime(0, 0, -1);
$upper = $date->format('U');
break;
case 3:
$date->setDate($parts[0], $parts[1], $parts[2]);
$date->setTime(0, 0, 0);
$lower = $date->format('U');
$date->setDate($parts[0], $parts[1], $parts[2] + 1);
$date->setTime(0, 0, -1);
$upper = $date->format('U');
break;
case 4:
$date->setDate($parts[0], $parts[1], $parts[2]);
$date->setTime($parts[3], 0, 0);
$lower = $date->format('U');
$date->setTime($parts[3] + 1, 0, -1);
$upper = $date->format('U');
break;
case 5:
$date->setDate($parts[0], $parts[1], $parts[2]);
$date->setTime($parts[3], $parts[4], 0);
$lower = $date->format('U');
$date->setTime($parts[3], $parts[4] + 1, -1);
$upper = $date->format('U');
break;
case 6:
$date->setDate($parts[0], $parts[1], $parts[2]);
$date->setTime($parts[3], $parts[4], $parts[5]);
return $date->format('U');
default:
return $value;
}
return "[$lower TO $upper]";
}
/**
* Replacement callback for replacing ISO dates with timestamps.
*
* Not used anymore, but kept for backwards compatibility with potential
* subclasses.
*/
public function replaceDateString($matches) {
return strtotime($matches[0]);
@ -86,15 +166,9 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
$build = array();
$search = search_api_current_search($search_id);
$results = $search[1];
if (!$results['result count']) {
return array();
}
// Gets total number of documents matched in search.
$total = $results['result count'];
// Most of the code below is copied from search_facetapi's implementation of
// this method.
// Executes query, iterates over results.
if (isset($results['search_api_facets']) && isset($results['search_api_facets'][$this->facet['name']])) {
$values = $results['search_api_facets'][$this->facet['name']];
@ -113,13 +187,6 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
}
}
else {
$filter = substr($value['filter'], 1, -1);
$pos = strpos($filter, ' ');
if ($pos !== FALSE) {
$lower = facetapi_isodate(substr($filter, 0, $pos), FACETAPI_DATE_DAY);
$upper = facetapi_isodate(substr($filter, $pos + 1), FACETAPI_DATE_DAY);
$filter = '[' . $lower . ' TO ' . $upper . ']';
}
$build[$filter]['#count'] = $value['count'];
}
}
@ -128,23 +195,28 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
// Get the finest level of detail we're allowed to drill down to.
$settings = $facet->getSettings()->settings;
$granularity = isset($settings['date_granularity']) ? $settings['date_granularity'] : FACETAPI_DATE_MINUTE;
$max_granularity = isset($settings['date_granularity']) ? $settings['date_granularity'] : FACETAPI_DATE_MINUTE;
// Gets active facets, starts building hierarchy.
$parent = $gap = NULL;
$parent = $granularity = NULL;
$active_items = $this->adapter->getActiveItems($this->facet);
foreach ($active_items as $value => $item) {
// If the item is active, the count is the result set count.
$build[$value] = array('#count' => $total);
// Gets next "gap" increment.
if ($value[0] != '[' || $value[strlen($value) - 1] != ']' || !($pos = strpos($value, ' TO '))) {
// Gets next "gap" increment. Ignore any filters passed directly from the
// server (range or missing). We always create filters starting with a
// year.
$value = "$value";
if (!$value || !ctype_digit($value[0])) {
continue;
}
$start = substr($value, 1, $pos);
$end = substr($value, $pos + 4, -1);
$date_gap = facetapi_get_date_gap($start, $end);
$gap = facetapi_get_next_date_gap($date_gap, $granularity);
$granularity = search_api_facetapi_date_get_granularity($value);
if (!$granularity) {
continue;
}
$granularity = facetapi_get_next_date_gap($granularity, $max_granularity);
// If there is a previous item, there is a parent, uses a reference so the
// arrays are populated when they are updated.
@ -156,6 +228,7 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
// Stores the last value iterated over.
$parent = $value;
}
if (empty($raw_values)) {
return $build;
}
@ -165,7 +238,7 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
$timestamps = array_keys($raw_values);
if (NULL === $parent) {
if (count($raw_values) > 1) {
$gap = facetapi_get_timestamp_gap(min($timestamps), max($timestamps));
$granularity = facetapi_get_timestamp_gap(min($timestamps), max($timestamps), $max_granularity);
// Array of numbers used to determine whether the next gap is smaller than
// the minimum gap allowed in the drilldown.
$gap_numbers = array(
@ -178,36 +251,20 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
);
// Gets gap numbers for both the gap and minimum gap, checks if the gap
// is within the limit set by the $granularity parameter.
if ($gap_numbers[$gap] < $gap_numbers[$granularity]) {
$gap = $granularity;
if ($gap_numbers[$granularity] < $gap_numbers[$max_granularity]) {
$granularity = $max_granularity;
}
}
else {
$gap = $granularity;
$granularity = $max_granularity;
}
}
// Converts all timestamps to dates in ISO 8601 format.
$dates = array();
foreach ($timestamps as $timestamp) {
$dates[$timestamp] = facetapi_isodate($timestamp, $gap);
}
// Treat each date as the range start and next date as the range end.
$range_end = array();
$previous = NULL;
foreach (array_unique($dates) as $date) {
if (NULL !== $previous) {
$range_end[$previous] = facetapi_get_next_date_increment($previous, $gap);
}
$previous = $date;
}
$range_end[$previous] = facetapi_get_next_date_increment($previous, $gap);
// Groups dates by the range they belong to, builds the $build array
// with the facet counts and formatted range values.
// Groups dates by the range they belong to, builds the $build array with
// the facet counts and formatted range values.
$format = search_api_facetapi_date_get_granularity_format($granularity);
foreach ($raw_values as $value => $count) {
$new_value = '[' . $dates[$value] . ' TO ' . $range_end[$dates[$value]] . ']';
$new_value = date($format, $value);
if (!isset($build[$new_value])) {
$build[$new_value] = array('#count' => $count);
}
@ -226,4 +283,5 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
return $build;
}
}

View File

@ -30,7 +30,7 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy
// Return terms for this facet.
$this->adapter->addFacet($this->facet, $query);
$settings = $this->adapter->getFacet($this->facet)->getSettings()->settings;
$settings = $this->getSettings()->settings;
// First check if the facet is enabled for this search.
$default_true = isset($settings['default_true']) ? $settings['default_true'] : TRUE;
@ -56,7 +56,12 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy
$conjunction = 'OR';
}
else {
throw new SearchApiException(t('Unknown facet operator %operator.', array('%operator' => $operator)));
$vars = array(
'%operator' => $operator,
'%facet' => !empty($this->facet['label']) ? $this->facet['label'] : $this->facet['name'],
);
watchdog('search_api_facetapi', 'Unknown facet operator %operator used for facet %facet.', $vars, WATCHDOG_WARNING);
return;
}
$tags = array('facet:' . $this->facet['field']);
$facet_filter = $query->createFilter($conjunction, $tags);
@ -77,7 +82,7 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy
// Test if this filter should be negated.
$settings = $this->adapter->getFacet($this->facet)->getSettings();
$exclude = !empty($settings->settings['exclude']);
// Integer (or other nun-string) filters might mess up some of the following
// Integer (or other non-string) filters might mess up some of the following
// comparison expressions.
$filter = (string) $filter;
if ($filter == '!') {
@ -143,9 +148,15 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy
return array();
}
$search_id = $search_ids[$facet['name']];
$search = search_api_current_search($search_id);
list(, $results) = search_api_current_search($search_id);
$build = array();
$results = $search[1];
// Always include the active facet items.
foreach ($this->adapter->getActiveItems($this->facet) as $filter) {
$build[$filter['value']]['#count'] = $results['result count'];
}
// Then, add the facets returned by the server.
if (isset($results['search_api_facets']) && isset($results['search_api_facets'][$this->facet['name']])) {
$values = $results['search_api_facets'][$this->facet['name']];
foreach ($values as $value) {

View File

@ -9,9 +9,9 @@ files[] = plugins/facetapi/adapter.inc
files[] = plugins/facetapi/query_type_term.inc
files[] = plugins/facetapi/query_type_date.inc
; Information added by Drupal.org packaging script on 2013-12-25
version = "7.x-1.11"
; Information added by Drupal.org packaging script on 2014-12-26
version = "7.x-1.14"
core = "7.x"
project = "search_api"
datestamp = "1387965506"
datestamp = "1419580682"

View File

@ -53,7 +53,7 @@ function search_api_facetapi_facetapi_searcher_info() {
$info = array();
$indexes = search_api_index_load_multiple(FALSE);
foreach ($indexes as $index) {
if ($index->enabled && $index->server()->supportsFeature('search_api_facets')) {
if (_search_api_facetapi_index_support_feature($index)) {
$searcher_name = 'search_api@' . $index->machine_name;
$info[$searcher_name] = array(
'label' => t('Search service: @name', array('@name' => $index->name)),
@ -97,7 +97,7 @@ function search_api_facetapi_facetapi_facet_info(array $searcher_info) {
'date' => array(
'query type' => 'date',
'map options' => array(
'map callback' => 'facetapi_map_date',
'map callback' => 'search_api_facetapi_map_date',
),
),
);
@ -116,7 +116,7 @@ function search_api_facetapi_facetapi_facet_info(array $searcher_info) {
'description' => t('Filter by @type.', array('@type' => $field['name'])),
'allowed operators' => array(
FACETAPI_OPERATOR_AND => TRUE,
FACETAPI_OPERATOR_OR => $index->server()->supportsFeature('search_api_facets_operator_or'),
FACETAPI_OPERATOR_OR => _search_api_facetapi_index_support_feature($index, 'search_api_facets_operator_or'),
),
'dependency plugins' => array('role'),
'facet missing allowed' => TRUE,
@ -218,7 +218,7 @@ function search_api_facetapi_settings($realm_name, SearchApiIndex $index) {
if (!$index->enabled) {
return array('#markup' => t('Since this index is at the moment disabled, no facets can be activated.'));
}
if (!$index->server()->supportsFeature('search_api_facets')) {
if (!_search_api_facetapi_index_support_feature($index)) {
return array('#markup' => t('This index uses a server that does not support facet functionality.'));
}
$searcher_name = 'search_api@' . $index->machine_name;
@ -226,6 +226,28 @@ function search_api_facetapi_settings($realm_name, SearchApiIndex $index) {
return drupal_get_form('facetapi_realm_settings_form', $searcher_name, $realm_name);
}
/**
* Checks whether a certain feature is supported for an index.
*
* @param SearchApiIndex $index
* The search index which should be checked.
* @param string $feature
* (optional) The feature to check for. Defaults to "search_api_facets".
*
* @return bool
* TRUE if the feature is supported by the index's server (and the index is
* currently enabled), FALSE otherwise.
*/
function _search_api_facetapi_index_support_feature(SearchApiIndex $index, $feature = 'search_api_facets') {
try {
$server = $index->server();
return $server && $server->supportsFeature($feature);
}
catch (SearchApiException $e) {
return FALSE;
}
}
/**
* Gets hierarchy information for taxonomy terms.
*
@ -366,7 +388,7 @@ function _search_api_facetapi_facet_create_label(array $values, array $options)
$entities = entity_load($type, $values);
foreach ($entities as $id => $entity) {
$label = entity_label($type, $entity);
if ($label) {
if ($label !== FALSE) {
$map[$id] = $label;
}
}
@ -432,3 +454,131 @@ function search_api_facetapi_search_api_admin_index_fields_submit($form, &$form_
$cid = 'facetapi:facet_info:search_api@' . $form_state['index']->machine_name . ':';
cache_clear_all($cid, 'cache', TRUE);
}
/**
* Computes the granularity of a date facet filter.
*
* @param $filter
* The filter value to examine.
*
* @return string|null
* Either one of the FACETAPI_DATE_* constants corresponding to the
* granularity of the filter, or NULL if it couldn't be computed.
*/
function search_api_facetapi_date_get_granularity($filter) {
// Granularity corresponds to number of dashes in filter value.
$units = array(
FACETAPI_DATE_YEAR,
FACETAPI_DATE_MONTH,
FACETAPI_DATE_DAY,
FACETAPI_DATE_HOUR,
FACETAPI_DATE_MINUTE,
FACETAPI_DATE_SECOND,
);
$count = substr_count($filter, '-');
return isset($units[$count]) ? $units[$count] : NULL;
}
/**
* Returns the date format used for a given granularity.
*
* @param $granularity
* One of the FACETAPI_DATE_* constants.
*
* @return string
* The date format used for the given granularity.
*/
function search_api_facetapi_date_get_granularity_format($granularity) {
$formats = array(
FACETAPI_DATE_YEAR => 'Y',
FACETAPI_DATE_MONTH => 'Y-m',
FACETAPI_DATE_DAY => 'Y-m-d',
FACETAPI_DATE_HOUR => 'Y-m-d-H',
FACETAPI_DATE_MINUTE => 'Y-m-d-H-i',
FACETAPI_DATE_SECOND => 'Y-m-d-H-i-s',
);
return $formats[$granularity];
}
/**
* Constructs labels for date facet filter values.
*
* @param array $values
* The date facet filter values, as used in URL parameters.
* @param array $options
* (optional) Options for creating the mapping. The following options are
* recognized:
* - format callback: A callback for creating a label for a timestamp. The
* function signature is like search_api_facetapi_format_timestamp(),
* receiving a timestamp and one of the FACETAPI_DATE_* constants as the
* parameters and returning a human-readable label.
*
* @return array
* An array of labels for the given facet filters.
*/
function search_api_facetapi_map_date(array $values, array $options = array()) {
$map = array();
foreach ($values as $value) {
// Ignore any filters passed directly from the server (range or missing). We
// always create filters starting with a year.
$value = "$value";
if (!$value || !ctype_digit($value[0])) {
continue;
}
// Get the granularity of the filter.
$granularity = search_api_facetapi_date_get_granularity($value);
if (!$granularity) {
continue;
}
// For years, the URL value is already the label.
if ($granularity == FACETAPI_DATE_YEAR) {
$map[$value] = $value;
continue;
}
// Otherwise, parse the timestamp from the known format and format it as a
// label.
$format = search_api_facetapi_date_get_granularity_format($granularity);
$date = DateTime::createFromFormat($format, $value);
if (!$date) {
continue;
}
$format_callback = 'search_api_facetapi_format_timestamp';
if (!empty($options['format callback']) && is_callable($options['format callback'])) {
$format_callback = $options['format callback'];
}
$map[$value] = call_user_func($format_callback, $date->format('U'), $granularity);
}
return $map;
}
/**
* Format a date according to the default timezone and the given precision.
*
* @param int $timestamp
* An integer containing the Unix timestamp being formated.
* @param string $precision
* A string containing the formatting precision. See the FACETAPI_DATE_*
* constants for valid values.
*
* @return string
* A human-readable representation of the timestamp.
*/
function search_api_facetapi_format_timestamp($timestamp, $precision = FACETAPI_DATE_YEAR) {
$formats = array(
FACETAPI_DATE_YEAR => 'Y',
FACETAPI_DATE_MONTH => 'F Y',
FACETAPI_DATE_DAY => 'F j, Y',
FACETAPI_DATE_HOUR => 'H:__',
FACETAPI_DATE_MINUTE => 'H:i',
FACETAPI_DATE_SECOND => 'H:i:s',
);
if (!isset($formats[$precision])) {
$precision = FACETAPI_DATE_YEAR;
}
return format_date($timestamp, 'custom', $formats[$precision]);
}

View File

@ -66,7 +66,13 @@ class SearchApiViewsHandlerArgumentFulltext extends SearchApiViewsHandlerArgumen
*/
public function query($group_by = FALSE) {
if ($this->options['fields']) {
$this->query->fields($this->options['fields']);
try {
$this->query->fields($this->options['fields']);
}
catch (SearchApiException $e) {
$this->query->abort($e->getMessage());
return;
}
}
if ($this->options['conjunction'] != 'AND') {
$this->query->setOption('conjunction', $this->options['conjunction']);

View File

@ -62,24 +62,30 @@ class SearchApiViewsHandlerArgumentMoreLikeThis extends SearchApiViewsHandlerArg
* The argument sent may be found at $this->argument.
*/
public function query($group_by = FALSE) {
$server = $this->query->getIndex()->server();
if (!$server->supportsFeature('search_api_mlt')) {
$class = search_api_get_service_info($server->class);
watchdog('search_api_views', 'The search service "@class" does not offer "More like this" functionality.',
try {
$server = $this->query->getIndex()->server();
if (!$server->supportsFeature('search_api_mlt')) {
$class = search_api_get_service_info($server->class);
watchdog('search_api_views', 'The search service "@class" does not offer "More like this" functionality.',
array('@class' => $class['name']), WATCHDOG_ERROR);
$this->query->abort();
return;
}
$fields = $this->options['fields'] ? $this->options['fields'] : array();
if (empty($fields)) {
foreach ($this->query->getIndex()->options['fields'] as $key => $field) {
$fields[] = $key;
$this->query->abort();
return;
}
$fields = $this->options['fields'] ? $this->options['fields'] : array();
if (empty($fields)) {
foreach ($this->query->getIndex()->options['fields'] as $key => $field) {
$fields[] = $key;
}
}
$mlt = array(
'id' => $this->argument,
'fields' => $fields,
);
$this->query->getSearchApiQuery()->setOption('search_api_mlt', $mlt);
}
catch (SearchApiException $e) {
$this->query->abort($e->getMessage());
}
$mlt = array(
'id' => $this->argument,
'fields' => $fields,
);
$this->query->getSearchApiQuery()->setOption('search_api_mlt', $mlt);
}
}

View File

@ -90,7 +90,8 @@ abstract class SearchApiViewsHandlerFilterEntity extends SearchApiViewsHandlerFi
// Set the correct default value in case the admin-set value is used (and a
// value is present). The value is used if the form is either not exposed,
// or the exposed form wasn't submitted yet (there is
// or the exposed form wasn't submitted yet. (There doesn't seem to be an
// easier way to check for that.)
if ($this->value && (empty($form_state['input']) || !empty($form_state['input']['live_preview']))) {
$form['value']['#default_value'] = $this->ids_to_strings($this->value);
}
@ -102,11 +103,13 @@ abstract class SearchApiViewsHandlerFilterEntity extends SearchApiViewsHandlerFi
public function value_validate($form, &$form_state) {
if (!empty($form['value'])) {
$value = &$form_state['values']['options']['value'];
$values = $this->isMultiValued($form_state['values']['options']) ? drupal_explode_tags($value) : array($value);
$ids = $this->validate_entity_strings($form['value'], $values);
if (strlen($value)) {
$values = $this->isMultiValued($form_state['values']['options']) ? drupal_explode_tags($value) : array($value);
$ids = $this->validate_entity_strings($form['value'], $values);
if ($ids) {
$value = $ids;
if ($ids) {
$value = $ids;
}
}
}
}
@ -135,6 +138,7 @@ abstract class SearchApiViewsHandlerFilterEntity extends SearchApiViewsHandlerFi
return;
}
$this->validated_exposed_input = FALSE;
$identifier = $this->options['expose']['identifier'];
$input = $form_state['values'][$identifier];
@ -143,14 +147,14 @@ abstract class SearchApiViewsHandlerFilterEntity extends SearchApiViewsHandlerFi
$input = $this->options['group_info']['group_items'][$input]['value'];
}
if (!strlen($input)) {
return;
}
$values = $this->isMultiValued() ? drupal_explode_tags($input) : array($input);
if (!$this->options['is_grouped'] || ($this->options['is_grouped'] && ($input != 'All'))) {
$this->validated_exposed_input = $this->validate_entity_strings($form[$identifier], $values);
}
else {
$this->validated_exposed_input = FALSE;
}
}
/**
@ -175,6 +179,9 @@ abstract class SearchApiViewsHandlerFilterEntity extends SearchApiViewsHandlerFi
* {@inheritdoc}
*/
public function admin_summary() {
if (!is_array($this->value)) {
$this->value = $this->value ? array($this->value) : array();
}
$value = $this->value;
$this->value = empty($value) ? '' : $this->ids_to_strings($value);
$ret = parent::admin_summary();

View File

@ -156,8 +156,9 @@ class SearchApiViewsHandlerFilterFulltext extends SearchApiViewsHandlerFilterTex
if ($filter) {
$filter = $this->query->createFilter('OR');
$op = $this->operator === 'NOT' ? '<>' : '=';
foreach ($fields as $field) {
$filter->condition($field, $this->value, $this->operator);
$filter->condition($field, $this->value, $op);
}
$this->query->filter($filter);
return;
@ -166,11 +167,18 @@ class SearchApiViewsHandlerFilterFulltext extends SearchApiViewsHandlerFilterTex
// If the operator was set to OR or NOT, set OR as the conjunction. (It is
// also set for NOT since otherwise it would be "not all of these words".)
if ($this->operator != 'AND') {
$this->query->setOption('conjunction', $this->operator);
$this->query->setOption('conjunction', 'OR');
}
$this->query->fields($fields);
$old = $this->query->getOriginalKeys();
try {
$this->query->fields($fields);
}
catch (SearchApiException $e) {
$this->query->abort($e->getMessage());
return;
}
$old = $this->query->getKeys();
$old_original = $this->query->getOriginalKeys();
$this->query->keys($this->value);
if ($this->operator == 'NOT') {
$keys = &$this->query->getKeys();
@ -181,16 +189,44 @@ class SearchApiViewsHandlerFilterFulltext extends SearchApiViewsHandlerFilterTex
// We can't know how negation is expressed in the server's syntax.
}
}
// If there were fulltext keys set, we take care to combine them in a
// meaningful way (especially with negated keys).
if ($old) {
$keys = &$this->query->getKeys();
// Array-valued keys are combined.
if (is_array($keys)) {
$keys[] = $old;
// If the old keys weren't parsed into an array, we instead have to
// combine the original keys.
if (is_scalar($old)) {
$keys = "($old) ({$this->value})";
}
else {
// If the conjunction or negation settings aren't the same, we have to
// nest both old and new keys array.
if (!empty($keys['#negation']) != !empty($old['#negation']) || $keys['#conjunction'] != $old['#conjunction']) {
$keys = array(
'#conjunction' => 'AND',
$old,
$keys,
);
}
// Otherwise, just add all individual words from the old keys to the
// new ones.
else {
foreach (element_children($old) as $i) {
$keys[] = $old[$i];
}
}
}
}
elseif (is_array($old)) {
// We don't support such nonsense.
}
else {
$keys = "($old) ($keys)";
// If the parse mode was "direct" for both old and new keys, we
// concatenate them and set them both via method and reference (to also
// update the originalKeys property.
elseif (is_scalar($old_original)) {
$combined_keys = "($old_original) ($keys)";
$this->query->keys($combined_keys);
$keys = $combined_keys;
}
}
}

View File

@ -14,26 +14,14 @@
class SearchApiViewsHandlerFilterLanguage extends SearchApiViewsHandlerFilterOptions {
/**
* Provide a form for setting options.
* {@inheritdoc}
*/
public function value_form(&$form, &$form_state) {
parent::value_form($form, $form_state);
$form['value']['#options'] = array(
protected function get_value_options() {
parent::get_value_options();
$this->value_options = array(
'current' => t("Current user's language"),
'default' => t('Default site language'),
) + $form['value']['#options'];
}
/**
* Provides a summary of this filter's value for the admin UI.
*/
public function admin_summary() {
$tmp = $this->definition['options'];
$this->definition['options']['current'] = t('current');
$this->definition['options']['default'] = t('default');
$ret = parent::admin_summary();
$this->definition['options'] = $tmp;
return $ret;
) + $this->value_options;
}
/**

View File

@ -28,8 +28,23 @@ class SearchApiViewsHandlerSort extends views_handler_sort {
unset($this->query->orderby);
$sort = &$this->query->getSort();
$sort = array();
unset($sort);
}
// If two of the same fields are used for sort, ignore the latter in order
// for the prior to take precedence. (Temporary workaround until
// https://www.drupal.org/node/2145547 is fixed in Views.)
$alreadySorted = $this->query->getSort();
if (is_array($alreadySorted) && isset($alreadySorted[$this->real_field])) {
return;
}
try {
$this->query->sort($this->real_field, $this->options['order']);
}
catch (SearchApiException $e) {
$this->query->abort($e->getMessage());
}
$this->query->sort($this->real_field, $this->options['order']);
}
}

View File

@ -77,22 +77,24 @@ class SearchApiViewsCache extends views_plugin_cache_time {
}
/**
* Overrides views_plugin_cache::get_results_key().
* Overrides views_plugin_cache::get_cache_key().
*
* Use the Search API query as the main source for the key.
* Use the Search API query as the main source for the key. Note that in
* Views < 3.8, this function does not exist.
*/
public function get_results_key() {
public function get_cache_key($key_data = array()) {
global $user;
if (!isset($this->_results_key)) {
$query = $this->getSearchApiQuery();
$query->preExecute();
$key_data = array(
$key_data += array(
'query' => $query,
'roles' => array_keys($user->roles),
'super-user' => $user->uid == 1, // special caching for super user.
'language' => $GLOBALS['language']->language,
'base_url' => $GLOBALS['base_url'],
'offset' => $this->view->get_current_page() . '*' . $this->view->get_items_per_page() . '+' . $this->view->get_offset(),
);
// Not sure what gets passed in exposed_info, so better include it. All
// other parameters used in the parent method are already reflected in the
@ -100,8 +102,19 @@ class SearchApiViewsCache extends views_plugin_cache_time {
if (isset($_GET['exposed_info'])) {
$key_data['exposed_info'] = $_GET['exposed_info'];
}
}
$key = md5(serialize($key_data));
return $key;
}
$this->_results_key = $this->view->name . ':' . $this->display->id . ':results:' . md5(serialize($key_data));
/**
* Overrides views_plugin_cache::get_results_key().
*
* This is unnecessary for Views >= 3.8.
*/
public function get_results_key() {
if (!isset($this->_results_key)) {
$this->_results_key = $this->view->name . ':' . $this->display->id . ':results:' . $this->get_cache_key();
}
return $this->_results_key;

View File

@ -169,7 +169,7 @@ class SearchApiViewsQuery extends views_plugin_query {
'#default_value' => $this->options['search_api_bypass_access'],
);
if (entity_get_info($this->index->item_type)) {
if ($this->index->getEntityType()) {
$form['entity_access'] = array(
'#type' => 'checkbox',
'#title' => t('Additional access checks on result entities'),
@ -342,7 +342,7 @@ class SearchApiViewsQuery extends views_plugin_query {
catch (Exception $e) {
$this->errors[] = $e->getMessage();
// Recursion to get the same error behaviour as above.
return $this->execute($view);
$this->execute($view);
}
}
@ -374,9 +374,9 @@ class SearchApiViewsQuery extends views_plugin_query {
// First off, we try to gather as much field values as possible without
// loading any items.
foreach ($results as $id => $result) {
if (!empty($this->options['entity_access'])) {
$entity = entity_load($this->index->item_type, array($id));
if (!entity_access('view', $this->index->item_type, $entity[$id])) {
if (!empty($this->options['entity_access']) && ($entity_type = $this->index->getEntityType())) {
$entity = entity_load($entity_type, array($id));
if (!entity_access('view', $entity_type, $entity[$id])) {
continue;
}
}
@ -660,16 +660,18 @@ class SearchApiViewsQuery extends views_plugin_query {
return $ret;
}
public function getOption($name) {
public function getOption($name, $default = NULL) {
if (!$this->errors) {
return $this->query->getOption($name);
return $this->query->getOption($name, $default);
}
return $default;
}
public function setOption($name, $value) {
if (!$this->errors) {
return $this->query->setOption($name, $value);
}
return NULL;
}
public function &getOptions() {

View File

@ -27,9 +27,9 @@ files[] = includes/handler_sort.inc
files[] = includes/plugin_cache.inc
files[] = includes/query.inc
; Information added by Drupal.org packaging script on 2013-12-25
version = "7.x-1.11"
; Information added by Drupal.org packaging script on 2014-12-26
version = "7.x-1.14"
core = "7.x"
project = "search_api"
datestamp = "1387965506"
datestamp = "1419580682"

View File

@ -134,8 +134,8 @@ function search_api_views_views_data() {
if (isset($field['entity_type']) && $field['entity_type'] === 'taxonomy_term') {
$field_id = ($pos = strrpos($key, ':')) ? substr($key, $pos + 1) : $key;
$field_info = field_info_field($field_id);
if (isset($field_info['settings']['allowed_values'][0]['vocabulary'])) {
$vocabulary_fields[$field_info['settings']['allowed_values'][0]['vocabulary']][] = $key;
if ($vocabulary = _search_api_views_get_field_vocabulary($field_info)) {
$vocabulary_fields[$vocabulary][] = $key;
}
else {
$vocabulary_fields[''][] = $key;
@ -184,7 +184,7 @@ function _search_api_views_add_handlers($id, array $field, EntityMetadataWrapper
if ($inner_type == 'text') {
$table[$id] += array(
'argument' => array(
'handler' => 'SearchApiViewsHandlerArgument',
'handler' => 'SearchApiViewsHandlerArgumentString',
),
'filter' => array(
'handler' => 'SearchApiViewsHandlerFilterText',
@ -209,7 +209,6 @@ function _search_api_views_add_handlers($id, array $field, EntityMetadataWrapper
}
elseif (isset($field['entity_type']) && $field['entity_type'] === 'taxonomy_term') {
$table[$id]['filter']['handler'] = 'SearchApiViewsHandlerFilterTaxonomyTerm';
$info = $wrapper->info();
$field_info = field_info_field($info['name']);
// For the "Parent terms" and "All parent terms" properties, we can
// extrapolate the vocabulary from the parent in the selector. (E.g.,
@ -221,8 +220,8 @@ function _search_api_views_add_handlers($id, array $field, EntityMetadataWrapper
$field_info = field_info_field($parts[count($parts) - 2]);
}
}
if (isset($field_info['settings']['allowed_values'][0]['vocabulary'])) {
$table[$id]['filter']['vocabulary'] = $field_info['settings']['allowed_values'][0]['vocabulary'];
if ($vocabulary = _search_api_views_get_field_vocabulary($field_info)) {
$table[$id]['filter']['vocabulary'] = $vocabulary;
}
}
else {
@ -293,3 +292,31 @@ function search_api_views_views_plugins() {
return $ret;
}
/**
* Returns the vocabulary machine name of a term field.
*
* @param array|null $field_info
* The field's field info array, or NULL if the field is not provided by the
* Field API. See the return value of field_info_field().
*
* @return string|null
* If the field contains taxonomy terms of a single vocabulary (which could be
* determined), that vocabulary's machine name; NULL otherwise.
*/
function _search_api_views_get_field_vocabulary($field_info) {
// Test for "Term reference" fields.
if (isset($field_info['settings']['allowed_values'][0]['vocabulary'])) {
return $field_info['settings']['allowed_values'][0]['vocabulary'];
}
// Test for "Entity reference" fields.
elseif (isset($field_info['settings']['handler']) && $field_info['settings']['handler'] === 'base') {
if (!empty($field_info['settings']['handler_settings']['target_bundles'])) {
$bundles = $field_info['settings']['handler_settings']['target_bundles'];
if (count($bundles) == 1) {
return key($bundles);
}
}
}
return NULL;
}

View File

@ -193,6 +193,12 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback {
return isset($a) ? min($a, $b) : $b;
case 'first':
return isset($a) ? $a : $b;
case 'list':
if (!isset($a)) {
$a = array();
}
$a[] = $b;
return $a;
}
}
@ -261,6 +267,7 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback {
'max' => t('Maximum'),
'min' => t('Minimum'),
'first' => t('First'),
'list' => t('List'),
);
case 'type':
return array(
@ -270,6 +277,7 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback {
'max' => 'integer',
'min' => 'integer',
'first' => 'string',
'list' => 'list<string>',
);
case 'description':
return array(
@ -279,6 +287,7 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback {
'max' => t('The Maximum aggregation computes the numerically largest contained field value.'),
'min' => t('The Minimum aggregation computes the numerically smallest contained field value.'),
'first' => t('The First aggregation will simply keep the first encountered field value. This is helpful foremost when you know that a list field will only have a single value.'),
'list' => t('The List aggregation collects all field values into a multi-valued field containing all values.'),
);
}
}
@ -289,6 +298,8 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback {
public function formButtonSubmit(array $form, array &$form_state) {
$button_name = $form_state['triggering_element']['#name'];
if ($button_name == 'op') {
// Increment $i until the corresponding field is not set, then create the
// field with that number as suffix.
for ($i = 1; isset($this->options['fields']['search_api_aggregation_' . $i]); ++$i) {
}
$this->options['fields']['search_api_aggregation_' . $i] = array(

View File

@ -172,20 +172,25 @@ class SearchApiIndex extends Entity {
/**
* Constructor as a helper to the parent constructor.
*/
public function __construct(array $values = array()) {
parent::__construct($values, 'search_api_index');
public function __construct(array $values = array(), $entity_type = 'search_api_index') {
parent::__construct($values, $entity_type);
}
/**
* Execute necessary tasks for a newly created index.
*/
public function postCreate() {
if ($this->enabled) {
$this->queueItems();
try {
if ($server = $this->server()) {
// Tell the server about the new index.
$server->addIndex($this);
if ($this->enabled) {
$this->queueItems();
}
}
}
if ($server = $this->server()) {
// Tell the server about the new index.
$server->addIndex($this);
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
}
}
@ -193,8 +198,13 @@ class SearchApiIndex extends Entity {
* Execute necessary tasks when the index is removed from the database.
*/
public function postDelete() {
if ($server = $this->server()) {
$server->removeIndex($this);
try {
if ($server = $this->server()) {
$server->removeIndex($this);
}
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
}
// Stop tracking entities for indexing.
@ -206,7 +216,12 @@ class SearchApiIndex extends Entity {
*/
public function queueItems() {
if (!$this->read_only) {
$this->datasource()->startTracking(array($this));
try {
$this->datasource()->startTracking(array($this));
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
}
}
}
@ -214,7 +229,12 @@ class SearchApiIndex extends Entity {
* Remove all records of entities to index.
*/
public function dequeueItems() {
$this->datasource()->stopTracking(array($this));
try {
$this->datasource()->stopTracking(array($this));
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
}
}
/**
@ -231,16 +251,25 @@ class SearchApiIndex extends Entity {
if (empty($this->description)) {
$this->description = NULL;
}
if (empty($this->server)) {
$server = FALSE;
if (!empty($this->server)) {
$server = search_api_server_load($this->server);
if (!$server) {
$vars['%server'] = $this->server;
$vars['%index'] = $this->name;
watchdog('search_api', 'Unknown server %server specified for index %index.', $vars, WATCHDOG_ERROR);
}
}
if (!$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;
$this->server = NULL;
if (!empty($this->options['fields'])) {
ksort($this->options['fields']);
}
$this->resetCaches();
return parent::save();
}
@ -305,7 +334,12 @@ class SearchApiIndex extends Entity {
return TRUE;
}
$this->server()->deleteItems('all', $this);
try {
$this->server()->deleteItems('all', $this);
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
}
_search_api_index_reindex($this);
module_invoke_all('search_api_index_reindex', $this, TRUE);
@ -350,7 +384,12 @@ class SearchApiIndex extends Entity {
* otherwise.
*/
public function getEntityType() {
return $this->datasource()->getEntityType();
try {
return $this->datasource()->getEntityType();
}
catch (SearchApiException $e) {
return NULL;
}
}
/**
@ -385,7 +424,7 @@ class SearchApiIndex extends Entity {
* SearchApiQueryInterface::__construct().
*
* @throws SearchApiException
* If the index is currently disabled.
* If the index is currently disabled or its server doesn't exist.
*
* @return SearchApiQueryInterface
* A query object for searching this index.
@ -399,15 +438,20 @@ class SearchApiIndex extends Entity {
/**
* 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.
* 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.
* An array of items to index, of this index's item type.
*
* @return array
* An array of the IDs of all items that should be marked as indexed.
*
* @throws SearchApiException
* If an error occurred during indexing.
*/
public function index(array $items) {
if ($this->read_only) {
@ -925,12 +969,18 @@ class SearchApiIndex extends Entity {
* @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.
* of this index (if $alter wasn't set to FALSE).
*/
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);
try {
$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);
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
return entity_metadata_wrapper($this->item_type);
}
}
/**
@ -945,16 +995,24 @@ class SearchApiIndex extends Entity {
* @see SearchApiDataSourceControllerInterface::loadItems()
*/
public function loadItems(array $ids) {
return $this->datasource()->loadItems($ids);
try {
return $this->datasource()->loadItems($ids);
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
return array();
}
}
/**
* Reset internal static caches.
* Reset internal caches.
*
* Should be used when things like fields or data alterations change to avoid
* using stale data.
*/
public function resetCaches() {
cache_clear_all($this->getCacheId(''), 'cache', TRUE);
$this->datasource = NULL;
$this->server_object = NULL;
$this->callbacks = NULL;

View File

@ -22,8 +22,6 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
/**
* PREG regular expression for splitting words.
*
* We highlight around non-indexable or CJK characters.
*
* @var string
*/
protected static $split;
@ -40,7 +38,7 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
'\x{F900}-\x{FAFF}\x{FF21}-\x{FF3A}\x{FF41}-\x{FF5A}\x{FF66}-\x{FFDC}' .
'\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}';
self::$boundary = '(?:(?<=[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . $cjk . '])|(?=[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . $cjk . ']))';
self::$split = '/[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . $cjk . ']+/iu';
self::$split = '/[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . ']+/iu';
}
/**
@ -53,6 +51,7 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
'excerpt' => TRUE,
'excerpt_length' => 256,
'highlight' => 'always',
'exclude_fields' => array(),
);
$form['prefix'] = array(
@ -87,6 +86,22 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
),
),
);
// Exclude certain fulltextfields
$fields = $this->index->getFields();
$fulltext_fields = array();
foreach ($this->index->getFulltextFields() as $field) {
if (isset($fields[$field])) {
$fulltext_fields[$field] = $fields[$field]['name'] . ' (' . $field . ')';
}
}
$form['exclude_fields'] = array(
'#type' => 'checkboxes',
'#title' => t('Exclude fields from excerpt'),
'#description' => t('Exclude certain fulltext fields from being displayed in the excerpt.'),
'#options' => $fulltext_fields,
'#default_value' => $this->options['exclude_fields'],
'#attributes' => array('class' => array('search-api-checkboxes-list')),
);
$form['highlight'] = array(
'#type' => 'select',
'#title' => t('Highlight returned field data'),
@ -106,21 +121,29 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
* {@inheritdoc}
*/
public function configurationFormValidate(array $form, array &$values, array &$form_state) {
// Overridden so $form['fields'] is not checked.
$values['exclude_fields'] = array_filter($values['exclude_fields']);
}
/**
* {@inheritdoc}
*/
public function postprocessSearchResults(array &$response, SearchApiQuery $query) {
if (!$response['result count'] || !($keys = $this->getKeywords($query))) {
if (empty($response['results']) || !($keys = $this->getKeywords($query))) {
return;
}
$fulltext_fields = $this->index->getFulltextFields();
if (!empty($this->options['exclude_fields'])) {
$fulltext_fields = drupal_map_assoc($fulltext_fields);
foreach ($this->options['exclude_fields'] as $field) {
unset($fulltext_fields[$field]);
}
}
foreach ($response['results'] as $id => &$result) {
if ($this->options['excerpt']) {
$text = array();
$fields = $this->getFulltextFields($response['results'], $id);
$fields = $this->getFulltextFields($response['results'], $id, $fulltext_fields);
foreach ($fields as $data) {
if (is_array($data)) {
$text = array_merge($text, $data);
@ -129,10 +152,11 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
$text[] = $data;
}
}
$result['excerpt'] = $this->createExcerpt(implode("\n\n", $text), $keys);
$result['excerpt'] = $this->createExcerpt($this->flattenArrayValues($text), $keys);
}
if ($this->options['highlight'] != 'never') {
$fields = $this->getFulltextFields($response['results'], $id, $this->options['highlight'] == 'always');
$fields = $this->getFulltextFields($response['results'], $id, $fulltext_fields, $this->options['highlight'] == 'always');
foreach ($fields as $field => $data) {
if (is_array($data)) {
foreach ($data as $i => $text) {
@ -155,6 +179,8 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
* @param int|string $i
* The index in the results array of the result whose data should be
* returned.
* @param array $fulltext_fields
* The fulltext fields from which the excerpt should be created.
* @param bool $load
* TRUE if the item should be loaded if necessary, FALSE if only fields
* already returned in the results should be used.
@ -163,7 +189,7 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
* An array containing fulltext field names mapped to the text data
* contained in them for the given result.
*/
protected function getFulltextFields(array &$results, $i, $load = TRUE) {
protected function getFulltextFields(array &$results, $i, array $fulltext_fields, $load = TRUE) {
global $language;
$data = array();
@ -171,7 +197,6 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
// Act as if $load is TRUE if we have a loaded item.
$load |= !empty($result['entity']);
$result += array('fields' => array());
$fulltext_fields = $this->index->getFulltextFields();
// We only need detailed fields data if $load is TRUE.
$fields = $load ? $this->index->getFields() : array();
$needs_extraction = array();
@ -309,7 +334,7 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
// Locate a keyword (position $p, always >0 because $text starts with a
// space).
$p = 0;
if (preg_match('/' . self::$boundary . $key . self::$boundary . '/iu', $text, $match, PREG_OFFSET_CAPTURE, $included[$key])) {
if (preg_match('/' . self::$boundary . preg_quote($key, '/') . self::$boundary . '/iu', $text, $match, PREG_OFFSET_CAPTURE, $included[$key])) {
$p = $match[0][1];
}
// Now locate a space in front (position $q) and behind it (position $s),
@ -379,7 +404,9 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
$text = (isset($newranges[0]) ? '' : $dots[0]) . implode($dots[1], $out) . $dots[2];
$text = check_plain($text);
return $this->highlightField($text, $keys);
// Since we stripped the tags at the beginning, highlighting doesn't need to
// handle HTML anymore.
return $this->highlightField($text, $keys, FALSE);
}
/**
@ -389,15 +416,55 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
* The text of the field.
* @param array $keys
* Search keywords entered by the user.
* @param bool $html
* Whether the text can contain HTML tags or not. In the former case, text
* inside tags (i.e., tag names and attributes) won't be highlighted.
*
* @return string
* The field's text with all occurrences of search keywords highlighted.
*/
protected function highlightField($text, array $keys) {
protected function highlightField($text, array $keys, $html = TRUE) {
if (is_array($text)) {
$text = $this->flattenArrayValues($text);
}
if ($html) {
$texts = preg_split('#((?:</?[[:alpha:]](?:[^>"\']*|"[^"]*"|\'[^\']\')*>)+)#i', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
for ($i = 0; $i < count($texts); $i += 2) {
$texts[$i] = $this->highlightField($texts[$i], $keys, FALSE);
}
return implode('', $texts);
}
$replace = $this->options['prefix'] . '\0' . $this->options['suffix'];
$keys = implode('|', array_map('preg_quote', $keys));
$keys = implode('|', array_map('preg_quote', $keys, array_fill(0, count($keys), '/')));
$text = preg_replace('/' . self::$boundary . '(' . $keys . ')' . self::$boundary . '/iu', $replace, ' ' . $text . ' ');
return substr($text, 1, -1);
}
/**
* Flattens a (possibly multidimensional) array into a string.
*
* @param array $array
* The array to flatten.
* @param string $glue
* The separator to insert between individual array items.
*
* @return string
* The glued string.
*/
protected function flattenArrayValues(array $array, $glue = "\n\n") {
$ret = array();
foreach ($array as $item) {
if (is_array($item)) {
$ret[] = $this->flattenArrayValues($item, $glue);
}
else {
$ret[] = $item;
}
}
return implode($glue, $ret);
}
}

View File

@ -102,6 +102,8 @@ class SearchApiHtmlFilter extends SearchApiAbstractProcessor {
}
else {
$value = strip_tags($text);
// Remove any multiple or leading/trailing spaces we might have introduced.
$value = preg_replace('/\s\s+/', ' ', trim($value));
}
}
@ -109,8 +111,11 @@ class SearchApiHtmlFilter extends SearchApiAbstractProcessor {
$ret = array();
while (($pos = strpos($text, '<')) !== FALSE) {
if ($boost && $pos > 0) {
$token = html_entity_decode(substr($text, 0, $pos), ENT_QUOTES, 'UTF-8');
// Remove any multiple or leading/trailing spaces we might have introduced.
$token = preg_replace('/\s\s+/', ' ', trim($token));
$ret[] = array(
'value' => html_entity_decode(substr($text, 0, $pos), ENT_QUOTES, 'UTF-8'),
'value' => $token,
'score' => $boost,
);
}
@ -130,8 +135,11 @@ class SearchApiHtmlFilter extends SearchApiAbstractProcessor {
}
}
if ($text) {
$token = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
// Remove any multiple or leading/trailing spaces we might have introduced.
$token = preg_replace('/\s\s+/', ' ', trim($token));
$ret[] = array(
'value' => html_entity_decode($text, ENT_QUOTES, 'UTF-8'),
'value' => $token,
'score' => $boost,
);
$text = '';

View File

@ -226,6 +226,9 @@ interface SearchApiQueryInterface {
*
* This method should always be called by execute() and contain all necessary
* operations before the query is passed to the server's search() method.
*
* @throws SearchApiException
* If any error occurred during the preparation of the query.
*/
public function preExecute();
@ -366,7 +369,7 @@ class SearchApiQuery implements SearchApiQueryInterface {
/**
* The index's machine name.
*
* used during serialization to avoid serializing the whole index object.
* Used during serialization to avoid serializing the whole index object.
*
* @var string
*/
@ -811,6 +814,31 @@ class SearchApiQuery implements SearchApiQueryInterface {
$this->filter = clone $this->filter;
}
/**
* Implements the magic __toString() method to simplify debugging.
*/
public function __toString() {
$ret = 'Index: ' . $this->index->machine_name . "\n";
$ret .= 'Keys: ' . str_replace("\n", "\n ", var_export($this->orig_keys, TRUE)) . "\n";
if (isset($this->keys)) {
$ret .= 'Parsed keys: ' . str_replace("\n", "\n ", var_export($this->keys, TRUE)) . "\n";
$ret .= 'Searched fields: ' . (isset($this->fields) ? implode(', ', $this->fields) : '[ALL]') . "\n";
}
if ($filter = (string) $this->filter) {
$filter = str_replace("\n", "\n ", $filter);
$ret .= "Filters:\n $filter\n";
}
if ($this->sort) {
$sort = array();
foreach ($this->sort as $field => $order) {
$sort[] = "$field $order";
}
$ret .= 'Sorting: ' . implode(', ', $sort) . "\n";
}
$ret .= 'Options: ' . str_replace("\n", "\n ", var_export($this->options, TRUE)) . "\n";
return $ret;
}
}
/**
@ -890,8 +918,11 @@ interface SearchApiQueryFilterInterface {
* Return all conditions and nested filters contained in this filter.
*
* @return array
* An array containing this filter's subfilters. Each of these is either an
* array (field, value, operator), or another SearchApiFilter object.
* An array containing this filter's subfilters. Each of these is either a
* condition, represented as a numerically indexed array with the arguments
* of a previous SearchApiQueryFilterInterface::condition() call (field,
* value, operator); or a nested filter, represented by a
* SearchApiQueryFilterInterface filter object.
*/
public function &getFilters();
@ -1010,4 +1041,24 @@ class SearchApiQueryFilter implements SearchApiQueryFilterInterface {
}
}
/**
* Implements the magic __toString() method to simplify debugging.
*/
public function __toString() {
// Special case for a single, nested filter:
if (count($this->filters) == 1 && is_object($this->filters[0])) {
return (string) $this->filters[0];
}
$ret = array();
foreach ($this->filters as $filter) {
if (is_object($filter)) {
$ret[] = "[\n " . str_replace("\n", "\n ", (string) $filter) . "\n ]";
}
else {
$ret[] = "$filter[0] $filter[2] " . str_replace("\n", "\n ", var_export($filter[1], TRUE));
}
}
return $ret ? ' ' . implode("\n{$this->conjunction}\n ", $ret) : '';
}
}

View File

@ -74,8 +74,8 @@ class SearchApiServer extends Entity {
/**
* Constructor as a helper to the parent constructor.
*/
public function __construct(array $values = array()) {
parent::__construct($values, 'search_api_server');
public function __construct(array $values = array(), $entity_type = 'search_api_server') {
parent::__construct($values, $entity_type);
}
/**

View File

@ -252,6 +252,13 @@ function search_api_admin_add_server(array $form, array &$form_state) {
$form['options']['#prefix'] = '<div id="search-api-class-options">';
$form['options']['#suffix'] = '</div>';
// If $info is not set, there are no service classes. Display an error message
// telling the user how to change that and return an empty form.
if (!isset($info)) {
drupal_set_message(t('There are no service classes available for the Search API. Please install a <a href="@url">module that provides a service class</a> to proceed.', array('@url' => url('https://www.drupal.org/node/1254698'))), 'error');
return array();
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Create server'),
@ -834,6 +841,15 @@ function search_api_admin_index_view(SearchApiIndex $index, $action = NULL) {
}
$status = search_api_index_status($index);
try {
$server = $index->server();
}
catch (SearchApiException $e) {
$server = NULL;
$vars['%server'] = $index->server;
$message = t('The index has an unknown server (ID: %server) set. Please check the index settings.', $vars);
drupal_set_message($message, 'error');
}
$ret['view'] = array(
'#theme' => 'search_api_index',
'#id' => $index->id,
@ -842,15 +858,21 @@ function search_api_admin_index_view(SearchApiIndex $index, $action = NULL) {
'#description' => $index->description,
'#item_type' => $index->item_type,
'#enabled' => $index->enabled,
'#server' => $index->server(),
'#server' => $server,
'#options' => $index->options,
'#fields' => $index->getFields(),
'#indexed_items' => $status['indexed'],
'#on_server' => _search_api_get_items_on_server($index),
'#on_server' => NULL,
'#total_items' => $status['total'],
'#status' => $index->status,
'#read_only' => $index->read_only,
);
try{
$ret['view']['#on_server'] = _search_api_get_items_on_server($index);
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
}
if ($index->enabled && !$index->read_only) {
$ret['form'] = drupal_get_form('search_api_admin_index_status_form', $index, $status);
}
@ -873,7 +895,8 @@ function search_api_admin_index_view(SearchApiIndex $index, $action = NULL) {
* - fields: All indexed fields of the index.
* - indexed_items: The number of items already indexed in their latest
* version on this index.
* - on_server: The number of items actually indexed on the server.
* - on_server: The number of items actually indexed on the server. NULL if
* the search for finding out the item count failed.
* - 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.).
@ -963,15 +986,21 @@ function theme_search_api_index(array $variables) {
$rows[] = _search_api_deep_copy($row);
$theme = array(
'percent' => (int) (100 * $indexed_items / $total_items),
'percent' => $total_items ? (int) (100 * $indexed_items / $total_items) : 100,
'message' => t('@indexed/@total indexed', array('@indexed' => $indexed_items, '@total' => $total_items)),
);
$output .= '<h3>' . t('Index status') . '</h3>';
$output .= '<div class="search-api-index-status">' . theme('progress_bar', $theme) . '</div>';
$vars['@url'] = url('https://drupal.org/node/2009804#server-index-status');
$info = format_plural($on_server, 'There is 1 item indexed on the server for this index. (<a href="@url">More information</a>)', 'There are @count items indexed on the server for this index. (<a href="@url">More information</a>)', $vars);
$class = '';
if (!isset($on_server)) {
$info = t('An error occurred while trying to determine the server index status. Please check the logs for details.');
$class = 'error';
}
else {
$vars['@url'] = url('https://drupal.org/node/2009804#server-index-status');
$info = format_plural($on_server, 'There is 1 item indexed on the server for this index. (<a href="@url">More information</a>)', 'There are @count items indexed on the server for this index. (<a href="@url">More information</a>)', $vars);
$class = '';
}
$label = t('Server index status');
$rows[] = _search_api_deep_copy($row);
}
@ -1178,12 +1207,21 @@ function search_api_admin_index_edit(array $form, array &$form_state, SearchApiI
'#default_value' => $index->name,
'#required' => TRUE,
);
try {
$enabled_fixed = !$index->enabled && !$index->server();
}
catch (Exception $e) {
watchdog_exception('search_api', $e);
// The exception only occurs if the index is disabled, and for an unknown
// server we of course want do prevent the index from being enabled.
$enabled_fixed = TRUE;
}
$form['enabled'] = array(
'#type' => 'checkbox',
'#title' => t('Enabled'),
'#default_value' => $index->enabled,
// Can't enable an index lying on a disabled server, or no server at all.
'#disabled' => !$index->enabled && (!$index->server() || !$index->server()->enabled),
'#disabled' => $enabled_fixed,
);
$form['description'] = array(
'#type' => 'textarea',
@ -1565,6 +1603,7 @@ function search_api_admin_index_workflow_submit(array $form, array &$form_state)
foreach ($form_state['callbacks'] as $name => $callback) {
// Check whether callback status has changed.
if ($values['callbacks'][$name]['status'] == empty($options['data_alter_callbacks'][$name]['status'])) {
$callbacks_changed = TRUE;
if ($values['callbacks'][$name]['status']) {
// Callback was just enabled, add its fields.
$properties = $callback->propertyInfo();
@ -1591,16 +1630,6 @@ function search_api_admin_index_workflow_submit(array $form, array &$form_state)
}
}
}
else {
// Callback was just disabled, remove its fields.
$properties = $callback->propertyInfo();
if ($properties) {
foreach ($properties as $key => $field) {
unset($index->options['fields'][$key]);
}
}
}
}
}
@ -1614,9 +1643,9 @@ function search_api_admin_index_workflow_submit(array $form, array &$form_state)
uasort($index->options['data_alter_callbacks'], 'search_api_admin_element_compare');
uasort($index->options['processors'], 'search_api_admin_element_compare');
// Reset the index's internal property cache to correctly incorporate the
// new data alterations.
$index->resetCaches();
// Re-calculate the fields, since they might have changed in hard-to-predict
// ways.
search_api_index_recalculate_fields(array($index));
$index->save();
$index->reindex();
@ -1658,9 +1687,11 @@ function search_api_admin_index_fields(array $form, array &$form_state, SearchAp
// An array of option arrays for types, keyed by nesting level.
$types = array(0 => search_api_field_types());
$entity_types = entity_get_info();
//$boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0'));
// $boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0'));
$boosts = drupal_map_assoc(array('0.1', '0.2', '0.3', '0.5', '0.8', '1.0', '2.0', '3.0', '5.0', '8.0', '13.0', '21.0', '100', '1000', '1010', '1020', '1030', '1040', '1050', '1060'));
$fulltext_types = array(0 => array('text'));
// Add all custom data types with fallback "text" to fulltext types as well.
foreach (search_api_get_data_type_info() as $id => $type) {
@ -2210,8 +2241,16 @@ function search_api_admin_confirm_submit(array $form, array &$form_state) {
$action = $values['action'];
$id = $values['id'];
$success = FALSE;
$function = "search_api_{$type}_{$action}";
if ($function($id)) {
try {
// Some actions, like disabling, can actually throw an exception.
$success = $function($id);
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
}
if ($success) {
drupal_set_message($values['message']);
}
else {

View File

@ -335,7 +335,7 @@ function hook_search_api_items_indexed(SearchApiIndex $index, array $item_ids) {
* Lets modules alter a search query before executing it.
*
* @param SearchApiQueryInterface $query
* The SearchApiQueryInterface object representing the search query.
* The search query being executed.
*/
function hook_search_api_query_alter(SearchApiQueryInterface $query) {
// Exclude entities with ID 0. (Assume the ID field is always indexed.)

View File

@ -108,6 +108,19 @@ function search_api_drush_command() {
'aliases' => array('sapi-c'),
);
$items['search-api-set-index-server'] = array(
'description' => 'Set the search server used by a given index.',
'examples' => array(
'drush search-api-set-index-server default_node_index my_solr_server' => dt('Set the !index index to use the !server server.', array('!index' => 'default_node_index', '!server' => 'my_solr_server')),
'drush sapi-sis default_node_index my_solr_server' => dt('Alias to set the !index index to use the !server server.', array('!index' => 'default_node_index', '!server' => 'my_solr_server')),
),
'arguments' => array(
'index_id' => dt('The numeric ID or machine name of an index.'),
'server_id' => dt('The numeric ID or machine name of a server to set on the index.'),
),
'aliases' => array('sapi-sis'),
);
return $items;
}
@ -137,15 +150,21 @@ function drush_search_api_list() {
foreach ($indexes as $index) {
$type = search_api_get_item_type_info($index->item_type);
$type = isset($type['name']) ? $type['name'] : $index->item_type;
$server = $index->server();
$server = $server ? $server->name : '(' . t('none') . ')';
try {
$server = $index->server();
$server = $server ? $server->name : '(' . dt('none') . ')';
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
$server = '(' . dt('unknown: !server', array('server' => $index->server)) . ')';
}
$row = array(
$index->id,
$index->name,
$index->machine_name,
$server,
$type,
$index->enabled ? t('enabled') : t('disabled'),
$index->enabled ? dt('enabled') : dt('disabled'),
$index->options['cron_limit'],
);
$rows[] = $row;
@ -168,17 +187,24 @@ function drush_search_api_enable($index_id = NULL) {
return;
}
foreach ($indexes as $index) {
$vars = array('!index' => $index->name);
if (!$index->enabled) {
drush_log(dt("Enabling index !index and queueing items for indexing.", array('!index' => $index->name)), 'notice');
if (search_api_index_enable($index->id)) {
drush_log(dt("The index !index was successfully enabled.", array('!index' => $index->name)), 'ok');
drush_log(dt("Enabling index !index and queueing items for indexing.", $vars), 'notice');
$success = FALSE;
try {
if ($success = search_api_index_enable($index->id)) {
drush_log(dt("The index !index was successfully enabled.", $vars), 'ok');
}
}
else {
drush_log(dt("Error enabling index !index.", array('!index' => $index->name)), 'error');
catch (SearchApiException $e) {
drush_log($e->getMessage(), 'error');
}
if (!$success) {
drush_log(dt("Error enabling index !index.", $vars), 'error');
}
}
else {
drush_log(dt("The index !index is already enabled.", array('!index' => $index->name)), 'error');
drush_log(dt("The index !index is already enabled.", $vars), 'error');
}
}
}
@ -198,16 +224,23 @@ function drush_search_api_disable($index_id = NULL) {
return;
}
foreach ($indexes as $index) {
$vars = array('!index' => $index->name);
if ($index->enabled) {
if (search_api_index_disable($index->id)) {
drush_log(dt("The index !index was successfully disabled.", array('!index' => $index->name)), 'ok');
$success = FALSE;
try {
if ($success = search_api_index_disable($index->id)) {
drush_log(dt("The index !index was successfully disabled.", $vars), 'ok');
}
}
else {
drush_log(dt("Error disabling index !index.", array('!index' => $index->name)), 'error');
catch (SearchApiException $e) {
drush_log($e->getMessage(), 'error');
}
if (!$success) {
drush_log(dt("Error disabling index !index.", $vars), 'error');
}
}
else {
drush_log(dt("The index !index is already disabled.", array('!index' => $index->name)), 'error');
drush_log(dt("The index !index is already disabled.", $vars), 'error');
}
}
}
@ -264,40 +297,72 @@ function drush_search_api_index($index_id = NULL, $limit = NULL, $batch_size = N
if (empty($indexes)) {
return;
}
$process_batch = FALSE;
foreach ($indexes as $index) {
// Get the number of remaing items to index.
$datasource = $index->datasource();
$index_status = $datasource->getIndexStatus($index);
$remaining = $index_status['total'] - $index_status['indexed'];
if ($remaining <= 0) {
drush_log(dt("The index !index is up to date.", array('!index' => $index->name)), 'ok');
continue;
}
// Get the number of items to index per batch run.
if (!isset($batch_size)) {
$batch_size = empty($index->options['cron_limit']) ? SEARCH_API_DEFAULT_CRON_LIMIT : $index->options['cron_limit'];
}
elseif ($batch_size <= 0) {
$batch_size = $remaining;
}
// Get the number items to index.
if (!isset($limit) || !is_int($limit += 0) || $limit <= 0) {
$limit = $remaining;
}
drush_log(dt("Indexing a maximum number of !limit items (!batch_size items per batch run) for the index !index.", array('!index' => $index->name, '!limit' => $limit, '!batch_size' => $batch_size)), 'ok');
// Create the batch.
if (!_search_api_batch_indexing_create($index, $batch_size, $limit, $remaining, TRUE)) {
drush_log(dt("Couldn't create a batch, please check the batch size and limit parameters."), 'error');
}
else {
// Launch the batch process.
drush_backend_batch_process();
if (_drush_search_api_batch_indexing_create($index, $limit, $batch_size)) {
$process_batch = TRUE;
}
}
if ($process_batch) {
drush_backend_batch_process();
}
}
/**
* Creates and sets a batch for indexing items for a particular index.
*
* @param SearchApiIndex $index
* The index for which items should be indexed.
* @param int $limit
* (optional) The maximum number of items to index, or NULL to index all
* items.
* @param int $batch_size
* (optional) The number of items to index per batch, or NULL to index all
* items at once.
*
* @return bool
* TRUE if batch was created, FALSE otherwise.
*/
function _drush_search_api_batch_indexing_create(SearchApiIndex $index, $limit = NULL, $batch_size = NULL) {
// Get the number of remaining items to index.
try {
$datasource = $index->datasource();
}
catch (SearchApiException $e) {
drush_log($e->getMessage(), 'error');
return FALSE;
}
$index_status = $datasource->getIndexStatus($index);
$remaining = $index_status['total'] - $index_status['indexed'];
if ($remaining <= 0) {
drush_log(dt("The index !index is up to date.", array('!index' => $index->name)), 'ok');
return FALSE;
}
// Get the number of items to index per batch run.
if (!isset($batch_size)) {
$batch_size = empty($index->options['cron_limit']) ? SEARCH_API_DEFAULT_CRON_LIMIT : $index->options['cron_limit'];
}
elseif ($batch_size <= 0) {
$batch_size = $remaining;
}
// Get the total number of items to index.
if (!isset($limit) || !is_int($limit += 0) || $limit <= 0) {
$limit = $remaining;
}
drush_log(dt("Indexing a maximum number of !limit items (!batch_size items per batch run) for the index !index.", array('!index' => $index->name, '!limit' => $limit, '!batch_size' => $batch_size)), 'ok');
// Create the batch.
if (!_search_api_batch_indexing_create($index, $batch_size, $limit, $remaining, TRUE)) {
drush_log(dt("Couldn't create a batch, please check the batch size and limit parameters."), 'error');
return FALSE;
}
return TRUE;
}
/**
@ -367,12 +432,54 @@ function drush_search_api_clear($index_id = NULL) {
}
/**
* Helper function to return an index or all indexes as an array.
* Set the server for a given index.
*/
function drush_search_api_set_index_server($index_id = NULL, $server_id = NULL) {
if (search_api_drush_static(__FUNCTION__)) {
return;
}
// Make sure we have parameters to work with.
if (empty($index_id) || empty($server_id)) {
drush_log(dt('You must specify both an index and server.'), 'error');
return;
}
// Fetch current index and server data.
$indexes = search_api_drush_get_index($index_id);
$servers = search_api_drush_get_server($server_id);
if (empty($indexes) || empty($servers)) {
// If the specified index or server can't be found, just return. An
// appropriate error message should have been printed already.
return;
}
// Set the new server on the index.
$success = FALSE;
$index = reset($indexes);
$server = reset($servers);
try {
$success = $index->update(array('server' => $server->machine_name));
}
catch (SearchApiException $e) {
drush_log($e->getMessage(), 'error');
}
if ($success === FALSE) {
drush_log(dt('There was an error setting index !index to use server !server.', array('!index' => $index->name, '!server' => $server->name)), 'error');
}
elseif (!$success) {
drush_log(dt('Index !index was already using server !server.', array('!index' => $index->name, '!server' => $server->name)), 'ok');
}
else {
drush_log(dt('Index !index has been set to use server !server and items have been queued for indexing.', array('!index' => $index->name, '!server' => $server->name)), 'ok');
}
}
/**
* Returns an index or all indexes as an array.
*
* @param $index_id
* (optional) The provided index id.
* @param string|int|null $index_id
* (optional) The ID or machine name of the index to load. Defaults to
* loading all available indexes.
*
* @return
* @return SearchApiIndex[]
* An array of indexes.
*/
function search_api_drush_get_index($index_id = NULL) {
@ -387,7 +494,34 @@ function search_api_drush_get_index($index_id = NULL) {
}
/**
* Static lookup to prevent Drush 4 from running twice.
* Returns a server or all servers as an array.
*
* @param string|int|null $server_id
* (optional) The ID or machine name of the server to load. Defaults to
* loading all available servers.
*
* @return SearchApiServer[]
* An array of servers.
*/
function search_api_drush_get_server($server_id = NULL) {
$ids = isset($server_id) ? array($server_id) : FALSE;
$servers = search_api_server_load_multiple($ids);
if (empty($servers)) {
drush_set_error(dt('Invalid server_id or no servers present.'));
// @todo: Maybe add logic to print table of all servers.
}
return $servers;
}
/**
* Does a static lookup to prevent Drush 4 from running twice.
*
* @param string $function
* The Drush function being called.
*
* @return bool
* TRUE if the function was already called in this Drush execution, FALSE
* otherwise.
*
* @see http://drupal.org/node/704848
*/
@ -397,4 +531,5 @@ function search_api_drush_static($function) {
return TRUE;
}
$index[$function] = TRUE;
return FALSE;
}

View File

@ -34,9 +34,9 @@ files[] = includes/service.inc
configure = admin/config/search/search_api
; Information added by Drupal.org packaging script on 2013-12-25
version = "7.x-1.11"
; Information added by Drupal.org packaging script on 2014-12-26
version = "7.x-1.14"
core = "7.x"
project = "search_api"
datestamp = "1387965506"
datestamp = "1419580682"

View File

@ -349,8 +349,13 @@ function search_api_enable() {
}
}
foreach ($types as $type => $indexes) {
$controller = search_api_get_datasource_controller($type);
$controller->startTracking($indexes);
try {
$controller = search_api_get_datasource_controller($type);
$controller->startTracking($indexes);
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
}
}
}
@ -961,6 +966,10 @@ function search_api_update_7116() {
$insert = db_insert('search_api_task')
->fields(array('server_id', 'type', 'index_id', 'data'));
foreach ($tasks as $task) {
$task += array(
'index_id' => NULL,
'data' => NULL,
);
$insert->values($task);
}
$insert->execute();

View File

@ -275,7 +275,7 @@ function search_api_theme() {
'options' => array(),
'fields' => array(),
'indexed_items' => 0,
'on_server' => 0,
'on_server' => NULL,
'total_items' => 0,
'status' => ENTITY_CUSTOM,
'read_only' => 0,
@ -662,9 +662,21 @@ function search_api_search_api_index_update(SearchApiIndex $index) {
}
if ($index->server) {
$new_server = $index->server(TRUE);
// If the server is enabled, we call addIndex(); otherwise, we save the task.
$new_server->addIndex($index);
try {
$new_server = $index->server(TRUE);
// If the server is enabled, we call addIndex(); otherwise, we save the task.
$new_server->addIndex($index);
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
// If the new server doesn't exist, we remove the index from all
// servers. Note that saving an entity in its own update hook is usually
// a recipe for disaster, but since we are only doing this if a server
// is set and remove the server here before saving, it should be safe
// enough.
$index->server = NULL;
$index->save();
}
}
// We also have to re-index all content.
@ -672,28 +684,17 @@ function search_api_search_api_index_update(SearchApiIndex $index) {
}
// If the fields were changed, call the appropriate service class hook method
// and re-index the content, if necessary. Also, clear the fields cache.
// and re-index the content, if necessary.
$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) {
cache_clear_all($index->getCacheId(), 'cache', TRUE);
if ($index->server) {
$index->server()->fieldsUpdated($index);
}
}
// If additional fields changed, clear the index's specific cache which
// includes them.
$old_additional = $index->original->options + array('additional fields' => array());
$old_additional = $old_additional['additional fields'];
$new_additional = $index->options + array('additional fields' => array());
$new_additional = $new_additional['additional fields'];
if ($old_additional != $new_additional) {
cache_clear_all($index->getCacheId() . '-0-1', 'cache');
}
// If the index's enabled or read-only status is being changed, queue or
// dequeue items for indexing.
if (!$index->read_only && $index->enabled != $index->original->enabled) {
@ -1098,7 +1099,14 @@ function search_api_track_item_insert($type, array $item_ids) {
return;
}
search_api_get_datasource_controller($type)->trackItemInsert($item_ids, $indexes);
try {
search_api_get_datasource_controller($type)->trackItemInsert($item_ids, $indexes);
}
catch (SearchApiException $e) {
$vars['%item_type'] = $type;
watchdog_exception('search_api', $e, '%type while inserting items of type %item_type: !message in %function (line %line of %file).', $vars);
return;
}
foreach ($indexes as $index) {
if (!empty($index->options['index_directly'])) {
@ -1128,19 +1136,26 @@ function search_api_track_item_change($type, array $item_ids) {
if (!$indexes) {
return;
}
search_api_get_datasource_controller($type)->trackItemChange($item_ids, $indexes);
foreach ($indexes as $index) {
if (!empty($index->options['index_directly'])) {
// For indexes with the index_directly option set, queue the items to be
// indexed at the end of the request.
try {
search_api_index_specific_items_delayed($index, $item_ids);
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
try {
search_api_get_datasource_controller($type)->trackItemChange($item_ids, $indexes);
foreach ($indexes as $index) {
if (!empty($index->options['index_directly'])) {
// For indexes with the index_directly option set, queue the items to be
// indexed at the end of the request.
try {
search_api_index_specific_items_delayed($index, $item_ids);
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
}
}
}
}
catch (SearchApiException $e) {
$vars['%item_type'] = $type;
watchdog_exception('search_api', $e, '%type while updating items of type %item_type: !message in %function (line %line of %file).', $vars);
return;
}
}
/**
@ -1158,7 +1173,12 @@ function search_api_track_item_change($type, array $item_ids) {
* the Drupal 8 version of this module.
*/
function search_api_track_item_queued(SearchApiIndex $index, array $item_ids) {
$index->datasource()->trackItemQueued($item_ids, $index);
try {
$index->datasource()->trackItemQueued($item_ids, $index);
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
}
}
/**
@ -1191,16 +1211,27 @@ function search_api_track_item_delete($type, array $item_ids) {
);
$indexes = search_api_index_load_multiple(FALSE, $conditions);
if ($indexes) {
search_api_get_datasource_controller($type)->trackItemDelete($item_ids, $indexes);
try {
search_api_get_datasource_controller($type)->trackItemDelete($item_ids, $indexes);
}
catch (SearchApiException $e) {
$vars['%item_type'] = $type;
watchdog_exception('search_api', $e, '%type while deleting items of type %item_type: !message in %function (line %line of %file).', $vars);
}
}
// Then, delete it from all servers. Servers of disabled indexes have to be
// considered, too!
unset($conditions['enabled']);
foreach (search_api_index_load_multiple(FALSE, $conditions) as $index) {
if ($index->server) {
$server = $index->server();
$server->deleteItems($item_ids, $index);
try {
if ($server = $index->server()) {
$server->deleteItems($item_ids, $index);
}
}
catch (Exception $e) {
$vars['%item_type'] = $type;
watchdog_exception('search_api', $e, '%type while deleting items of type %item_type: !message in %function (line %line of %file).', $vars);
}
}
}
@ -1389,7 +1420,7 @@ function search_api_index_recalculate_fields($indexes = FALSE) {
}
// Use a more accurate method of determining if the fields settings are
// equal to avoid needlessly re-indexing the whole index.
if (!_search_api_settings_equals($fields, $index->options['fields'])) {
if ($fields != $index->options['fields']) {
$options = $index->options;
$options['fields'] = $fields;
$index->update(array('options' => $options));
@ -1400,9 +1431,6 @@ function search_api_index_recalculate_fields($indexes = FALSE) {
/**
* Test two setting arrays (or individual settings) for equality.
*
* While a simple == also works in some cases, this function takes into account
* that the order of keys (usually) doesn't matter in settings arrays.
*
* @param mixed $setting1
* The first setting (array).
* @param mixed $setting2
@ -1410,6 +1438,8 @@ function search_api_index_recalculate_fields($indexes = FALSE) {
*
* @return bool
* TRUE if both settings are identical, FALSE otherwise.
*
* @deprecated The simple "==" operator will achieve the same.
*/
function _search_api_settings_equals($setting1, $setting2) {
if (!is_array($setting1) || !is_array($setting2)) {
@ -1495,10 +1525,7 @@ function search_api_index_specific_items(SearchApiIndex $index, array $ids) {
// some specific setups.
$type = search_api_get_item_type_info($index->item_type);
$type = $type ? $type['name'] : $index->item_type;
watchdog('search_api',
"Error during indexing: invalid item loaded for @type with ID @id.",
array('@id' => $id, '@type' => $type),
WATCHDOG_WARNING);
watchdog('search_api', "Error during indexing: invalid item loaded for @type with ID @id.", array('@id' => $id, '@type' => $type), WATCHDOG_WARNING);
}
}
$indexed = $items ? $index->index($cloned_items) : array();
@ -1578,25 +1605,14 @@ function search_api_get_items_to_index(SearchApiIndex $index, $limit = -1) {
* @param $id
* 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.
* An associative array of options to be passed to
* SearchApiQueryInterface::__construct().
*
* @return SearchApiQueryInterface
* An object for searching on the specified index.
*
* @throws SearchApiException
* If the index is unknown or disabled, or some other error was encountered.
*/
function search_api_query($id, array $options = array()) {
$index = search_api_index_load($id);
@ -1607,9 +1623,10 @@ function search_api_query($id, array $options = array()) {
}
/**
* 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.
* Stores or retrieves a search executed in this page request.
*
* Static storage for the searches executed during the current page request. Can
* 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
@ -1935,7 +1952,14 @@ function search_api_search_api_query_alter(SearchApiQueryInterface $query) {
}
}
else {
watchdog('search_api', 'An illegal user UID was given for node access: @uid.', array('@uid' => $query->getOption('search_api_access_account', $user)), WATCHDOG_WARNING);
$account = $query->getOption('search_api_access_account', '(' . t('none') . ')');
if (is_object($account)) {
$account = $account->uid;
}
if (!is_scalar($account)) {
$account = var_export($account, TRUE);
}
watchdog('search_api', 'An illegal user UID was given for node access: @uid.', array('@uid' => $account), WATCHDOG_WARNING);
}
}
}
@ -1994,7 +2018,6 @@ function _search_api_query_add_node_access($account, SearchApiQueryInterface $qu
$query->filter($filter);
}
else {
// /!\ in previous patches i commented the next line, why ? maybe will have to do it again
$query->condition('status', $published);
}
@ -2225,7 +2248,7 @@ function search_api_extract_fields(EntityMetadataWrapper $wrapper, array $fields
# http://drupal.org/node/1873910#comment-6876200
// $subwrapper = $wrapper->$prefix;
// $subwrapper->language( $wrapper->language->value() );
// $nested_fields = search_api_extract_fields($subwrapper, $nested_fields, $value_options);
// $nested_fields = search_api_extract_fields($subwrapper, $nested_fields, $value_options);
foreach ($nested_fields as $field => $info) {
$fields["$prefix:$field"] = $info;
}
@ -2561,12 +2584,18 @@ function search_api_index_url(SearchApiIndex $index) {
* @param SearchApiIndex $index
* The index whose server should be returned.
*
* @return SearchApiServer
* @return SearchApiServer|null
* The server this index currently resides on, or NULL if the index is
* currently unassigned.
*/
function search_api_index_get_server(SearchApiIndex $index) {
return $index->server();
try {
return $index->server();
}
catch (SearchApiException $e) {
watchdog_exception('search_api', $e);
return NULL;
}
}
/**
@ -2643,14 +2672,14 @@ function search_api_index_edit_fields($id, array $fields) {
/**
* Enables a search index.
*
* @param $id
* @param string|int $id
* The ID or machine name of the index to enable.
*
* @throws SearchApiException
* If the index' server isn't enabled.
*
* @return
* @return int|false
* 1 on success, 0 or FALSE on failure.
*
* @throws SearchApiException
* If the index's server doesn't exist.
*/
function search_api_index_enable($id) {
$index = search_api_index_load($id, TRUE);
@ -2661,11 +2690,14 @@ function search_api_index_enable($id) {
/**
* Disables a search index.
*
* @param $id
* @param string|int $id
* The ID or machine name of the index to disable.
*
* @return
* @return int|false
* 1 on success, 0 or FALSE on failure.
*
* @throws SearchApiException
* If the index's server doesn't exist.
*/
function search_api_index_disable($id) {
$index = search_api_index_load($id, TRUE);
@ -2813,6 +2845,9 @@ function _search_api_convert_custom_type($callback, $value, $original_type, $typ
* @return int
* The number of items found on the server for this index, if the latter is
* enabled. 0 otherwise.
*
* @throws SearchApiException
* If an error prevented the search from completing.
*/
function _search_api_get_items_on_server(SearchApiIndex $index) {
if (!$index->enabled) {

View File

@ -52,6 +52,9 @@ function _search_api_rules_access() {
* Rules action for indexing an item.
*/
function _search_api_rules_action_index(EntityDrupalWrapper $wrapper, SearchApiIndex $index = NULL, $index_immediately = TRUE) {
// If we do not have an index, we need to guess the item type to use.
// @todo Since this can only be used with entities anyways, we can just loop
// over the item type information and use all types with that entity type.
$type = $wrapper->type();
$item_ids = array($wrapper->getIdentifier());
@ -61,6 +64,7 @@ function _search_api_rules_action_index(EntityDrupalWrapper $wrapper, SearchApiI
}
if ($index) {
$type = $index->item_type;
$indexes = array($index);
}
else {

View File

@ -10,9 +10,9 @@ files[] = search_api_test.module
hidden = TRUE
; Information added by Drupal.org packaging script on 2013-12-25
version = "7.x-1.11"
; Information added by Drupal.org packaging script on 2014-12-26
version = "7.x-1.14"
core = "7.x"
project = "search_api"
datestamp = "1387965506"
datestamp = "1419580682"