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:
parent
d3dcf44a1e
commit
ca9413af4d
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
50
sites/all/modules/contrib/search/search_api/0004-Icons.patch
Normal file
50
sites/all/modules/contrib/search/search_api/0004-Icons.patch
Normal 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
|
||||||
|
|
@ -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):
|
Search API 1.11 (12/25/2013):
|
||||||
-----------------------------
|
-----------------------------
|
||||||
- #1879196 by drunken monkey: Fixed invalid old indexes causing errors.
|
- #1879196 by drunken monkey: Fixed invalid old indexes causing errors.
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -57,14 +57,94 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
|
|||||||
if ($active = $this->adapter->getActiveItems($this->facet)) {
|
if ($active = $this->adapter->getActiveItems($this->facet)) {
|
||||||
$item = end($active);
|
$item = end($active);
|
||||||
$field = $this->facet['field'];
|
$field = $this->facet['field'];
|
||||||
$regex = str_replace(array('^', '$'), '', FACETAPI_REGEX_DATE);
|
$filter = $this->createRangeFilter($item['value']);
|
||||||
$filter = preg_replace_callback($regex, array($this, 'replaceDateString'), $item['value']);
|
|
||||||
$this->addFacetFilter($query, $field, $filter);
|
$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.
|
* Replacement callback for replacing ISO dates with timestamps.
|
||||||
|
*
|
||||||
|
* Not used anymore, but kept for backwards compatibility with potential
|
||||||
|
* subclasses.
|
||||||
*/
|
*/
|
||||||
public function replaceDateString($matches) {
|
public function replaceDateString($matches) {
|
||||||
return strtotime($matches[0]);
|
return strtotime($matches[0]);
|
||||||
@ -86,15 +166,9 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
|
|||||||
$build = array();
|
$build = array();
|
||||||
$search = search_api_current_search($search_id);
|
$search = search_api_current_search($search_id);
|
||||||
$results = $search[1];
|
$results = $search[1];
|
||||||
if (!$results['result count']) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
// Gets total number of documents matched in search.
|
// Gets total number of documents matched in search.
|
||||||
$total = $results['result count'];
|
$total = $results['result count'];
|
||||||
|
|
||||||
// Most of the code below is copied from search_facetapi's implementation of
|
|
||||||
// this method.
|
|
||||||
|
|
||||||
// Executes query, iterates over results.
|
// Executes query, iterates over results.
|
||||||
if (isset($results['search_api_facets']) && isset($results['search_api_facets'][$this->facet['name']])) {
|
if (isset($results['search_api_facets']) && isset($results['search_api_facets'][$this->facet['name']])) {
|
||||||
$values = $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 {
|
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'];
|
$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.
|
// Get the finest level of detail we're allowed to drill down to.
|
||||||
$settings = $facet->getSettings()->settings;
|
$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.
|
// Gets active facets, starts building hierarchy.
|
||||||
$parent = $gap = NULL;
|
$parent = $granularity = NULL;
|
||||||
$active_items = $this->adapter->getActiveItems($this->facet);
|
$active_items = $this->adapter->getActiveItems($this->facet);
|
||||||
foreach ($active_items as $value => $item) {
|
foreach ($active_items as $value => $item) {
|
||||||
// If the item is active, the count is the result set count.
|
// If the item is active, the count is the result set count.
|
||||||
$build[$value] = array('#count' => $total);
|
$build[$value] = array('#count' => $total);
|
||||||
|
|
||||||
// Gets next "gap" increment.
|
// Gets next "gap" increment. Ignore any filters passed directly from the
|
||||||
if ($value[0] != '[' || $value[strlen($value) - 1] != ']' || !($pos = strpos($value, ' TO '))) {
|
// server (range or missing). We always create filters starting with a
|
||||||
|
// year.
|
||||||
|
$value = "$value";
|
||||||
|
if (!$value || !ctype_digit($value[0])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$start = substr($value, 1, $pos);
|
|
||||||
$end = substr($value, $pos + 4, -1);
|
$granularity = search_api_facetapi_date_get_granularity($value);
|
||||||
$date_gap = facetapi_get_date_gap($start, $end);
|
if (!$granularity) {
|
||||||
$gap = facetapi_get_next_date_gap($date_gap, $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
|
// If there is a previous item, there is a parent, uses a reference so the
|
||||||
// arrays are populated when they are updated.
|
// arrays are populated when they are updated.
|
||||||
@ -156,6 +228,7 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
|
|||||||
// Stores the last value iterated over.
|
// Stores the last value iterated over.
|
||||||
$parent = $value;
|
$parent = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($raw_values)) {
|
if (empty($raw_values)) {
|
||||||
return $build;
|
return $build;
|
||||||
}
|
}
|
||||||
@ -165,7 +238,7 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
|
|||||||
$timestamps = array_keys($raw_values);
|
$timestamps = array_keys($raw_values);
|
||||||
if (NULL === $parent) {
|
if (NULL === $parent) {
|
||||||
if (count($raw_values) > 1) {
|
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
|
// Array of numbers used to determine whether the next gap is smaller than
|
||||||
// the minimum gap allowed in the drilldown.
|
// the minimum gap allowed in the drilldown.
|
||||||
$gap_numbers = array(
|
$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
|
// Gets gap numbers for both the gap and minimum gap, checks if the gap
|
||||||
// is within the limit set by the $granularity parameter.
|
// is within the limit set by the $granularity parameter.
|
||||||
if ($gap_numbers[$gap] < $gap_numbers[$granularity]) {
|
if ($gap_numbers[$granularity] < $gap_numbers[$max_granularity]) {
|
||||||
$gap = $granularity;
|
$granularity = $max_granularity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$gap = $granularity;
|
$granularity = $max_granularity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts all timestamps to dates in ISO 8601 format.
|
// Groups dates by the range they belong to, builds the $build array with
|
||||||
$dates = array();
|
// the facet counts and formatted range values.
|
||||||
foreach ($timestamps as $timestamp) {
|
$format = search_api_facetapi_date_get_granularity_format($granularity);
|
||||||
$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.
|
|
||||||
foreach ($raw_values as $value => $count) {
|
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])) {
|
if (!isset($build[$new_value])) {
|
||||||
$build[$new_value] = array('#count' => $count);
|
$build[$new_value] = array('#count' => $count);
|
||||||
}
|
}
|
||||||
@ -226,4 +283,5 @@ class SearchApiFacetapiDate extends SearchApiFacetapiTerm implements FacetapiQue
|
|||||||
|
|
||||||
return $build;
|
return $build;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy
|
|||||||
// Return terms for this facet.
|
// Return terms for this facet.
|
||||||
$this->adapter->addFacet($this->facet, $query);
|
$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.
|
// First check if the facet is enabled for this search.
|
||||||
$default_true = isset($settings['default_true']) ? $settings['default_true'] : TRUE;
|
$default_true = isset($settings['default_true']) ? $settings['default_true'] : TRUE;
|
||||||
@ -56,7 +56,12 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy
|
|||||||
$conjunction = 'OR';
|
$conjunction = 'OR';
|
||||||
}
|
}
|
||||||
else {
|
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']);
|
$tags = array('facet:' . $this->facet['field']);
|
||||||
$facet_filter = $query->createFilter($conjunction, $tags);
|
$facet_filter = $query->createFilter($conjunction, $tags);
|
||||||
@ -77,7 +82,7 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy
|
|||||||
// Test if this filter should be negated.
|
// Test if this filter should be negated.
|
||||||
$settings = $this->adapter->getFacet($this->facet)->getSettings();
|
$settings = $this->adapter->getFacet($this->facet)->getSettings();
|
||||||
$exclude = !empty($settings->settings['exclude']);
|
$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.
|
// comparison expressions.
|
||||||
$filter = (string) $filter;
|
$filter = (string) $filter;
|
||||||
if ($filter == '!') {
|
if ($filter == '!') {
|
||||||
@ -143,9 +148,15 @@ class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTy
|
|||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
$search_id = $search_ids[$facet['name']];
|
$search_id = $search_ids[$facet['name']];
|
||||||
$search = search_api_current_search($search_id);
|
list(, $results) = search_api_current_search($search_id);
|
||||||
$build = array();
|
$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']])) {
|
if (isset($results['search_api_facets']) && isset($results['search_api_facets'][$this->facet['name']])) {
|
||||||
$values = $results['search_api_facets'][$this->facet['name']];
|
$values = $results['search_api_facets'][$this->facet['name']];
|
||||||
foreach ($values as $value) {
|
foreach ($values as $value) {
|
||||||
|
@ -9,9 +9,9 @@ files[] = plugins/facetapi/adapter.inc
|
|||||||
files[] = plugins/facetapi/query_type_term.inc
|
files[] = plugins/facetapi/query_type_term.inc
|
||||||
files[] = plugins/facetapi/query_type_date.inc
|
files[] = plugins/facetapi/query_type_date.inc
|
||||||
|
|
||||||
; Information added by Drupal.org packaging script on 2013-12-25
|
; Information added by Drupal.org packaging script on 2014-12-26
|
||||||
version = "7.x-1.11"
|
version = "7.x-1.14"
|
||||||
core = "7.x"
|
core = "7.x"
|
||||||
project = "search_api"
|
project = "search_api"
|
||||||
datestamp = "1387965506"
|
datestamp = "1419580682"
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ function search_api_facetapi_facetapi_searcher_info() {
|
|||||||
$info = array();
|
$info = array();
|
||||||
$indexes = search_api_index_load_multiple(FALSE);
|
$indexes = search_api_index_load_multiple(FALSE);
|
||||||
foreach ($indexes as $index) {
|
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;
|
$searcher_name = 'search_api@' . $index->machine_name;
|
||||||
$info[$searcher_name] = array(
|
$info[$searcher_name] = array(
|
||||||
'label' => t('Search service: @name', array('@name' => $index->name)),
|
'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(
|
'date' => array(
|
||||||
'query type' => 'date',
|
'query type' => 'date',
|
||||||
'map options' => array(
|
'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'])),
|
'description' => t('Filter by @type.', array('@type' => $field['name'])),
|
||||||
'allowed operators' => array(
|
'allowed operators' => array(
|
||||||
FACETAPI_OPERATOR_AND => TRUE,
|
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'),
|
'dependency plugins' => array('role'),
|
||||||
'facet missing allowed' => TRUE,
|
'facet missing allowed' => TRUE,
|
||||||
@ -218,7 +218,7 @@ function search_api_facetapi_settings($realm_name, SearchApiIndex $index) {
|
|||||||
if (!$index->enabled) {
|
if (!$index->enabled) {
|
||||||
return array('#markup' => t('Since this index is at the moment disabled, no facets can be activated.'));
|
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.'));
|
return array('#markup' => t('This index uses a server that does not support facet functionality.'));
|
||||||
}
|
}
|
||||||
$searcher_name = 'search_api@' . $index->machine_name;
|
$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);
|
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.
|
* 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);
|
$entities = entity_load($type, $values);
|
||||||
foreach ($entities as $id => $entity) {
|
foreach ($entities as $id => $entity) {
|
||||||
$label = entity_label($type, $entity);
|
$label = entity_label($type, $entity);
|
||||||
if ($label) {
|
if ($label !== FALSE) {
|
||||||
$map[$id] = $label;
|
$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 . ':';
|
$cid = 'facetapi:facet_info:search_api@' . $form_state['index']->machine_name . ':';
|
||||||
cache_clear_all($cid, 'cache', TRUE);
|
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]);
|
||||||
|
}
|
||||||
|
@ -66,8 +66,14 @@ class SearchApiViewsHandlerArgumentFulltext extends SearchApiViewsHandlerArgumen
|
|||||||
*/
|
*/
|
||||||
public function query($group_by = FALSE) {
|
public function query($group_by = FALSE) {
|
||||||
if ($this->options['fields']) {
|
if ($this->options['fields']) {
|
||||||
|
try {
|
||||||
$this->query->fields($this->options['fields']);
|
$this->query->fields($this->options['fields']);
|
||||||
}
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
$this->query->abort($e->getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if ($this->options['conjunction'] != 'AND') {
|
if ($this->options['conjunction'] != 'AND') {
|
||||||
$this->query->setOption('conjunction', $this->options['conjunction']);
|
$this->query->setOption('conjunction', $this->options['conjunction']);
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,7 @@ class SearchApiViewsHandlerArgumentMoreLikeThis extends SearchApiViewsHandlerArg
|
|||||||
* The argument sent may be found at $this->argument.
|
* The argument sent may be found at $this->argument.
|
||||||
*/
|
*/
|
||||||
public function query($group_by = FALSE) {
|
public function query($group_by = FALSE) {
|
||||||
|
try {
|
||||||
$server = $this->query->getIndex()->server();
|
$server = $this->query->getIndex()->server();
|
||||||
if (!$server->supportsFeature('search_api_mlt')) {
|
if (!$server->supportsFeature('search_api_mlt')) {
|
||||||
$class = search_api_get_service_info($server->class);
|
$class = search_api_get_service_info($server->class);
|
||||||
@ -82,4 +83,9 @@ class SearchApiViewsHandlerArgumentMoreLikeThis extends SearchApiViewsHandlerArg
|
|||||||
);
|
);
|
||||||
$this->query->getSearchApiQuery()->setOption('search_api_mlt', $mlt);
|
$this->query->getSearchApiQuery()->setOption('search_api_mlt', $mlt);
|
||||||
}
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
$this->query->abort($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,8 @@ abstract class SearchApiViewsHandlerFilterEntity extends SearchApiViewsHandlerFi
|
|||||||
|
|
||||||
// Set the correct default value in case the admin-set value is used (and a
|
// 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,
|
// 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']))) {
|
if ($this->value && (empty($form_state['input']) || !empty($form_state['input']['live_preview']))) {
|
||||||
$form['value']['#default_value'] = $this->ids_to_strings($this->value);
|
$form['value']['#default_value'] = $this->ids_to_strings($this->value);
|
||||||
}
|
}
|
||||||
@ -102,6 +103,7 @@ abstract class SearchApiViewsHandlerFilterEntity extends SearchApiViewsHandlerFi
|
|||||||
public function value_validate($form, &$form_state) {
|
public function value_validate($form, &$form_state) {
|
||||||
if (!empty($form['value'])) {
|
if (!empty($form['value'])) {
|
||||||
$value = &$form_state['values']['options']['value'];
|
$value = &$form_state['values']['options']['value'];
|
||||||
|
if (strlen($value)) {
|
||||||
$values = $this->isMultiValued($form_state['values']['options']) ? drupal_explode_tags($value) : array($value);
|
$values = $this->isMultiValued($form_state['values']['options']) ? drupal_explode_tags($value) : array($value);
|
||||||
$ids = $this->validate_entity_strings($form['value'], $values);
|
$ids = $this->validate_entity_strings($form['value'], $values);
|
||||||
|
|
||||||
@ -110,6 +112,7 @@ abstract class SearchApiViewsHandlerFilterEntity extends SearchApiViewsHandlerFi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
@ -135,6 +138,7 @@ abstract class SearchApiViewsHandlerFilterEntity extends SearchApiViewsHandlerFi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->validated_exposed_input = FALSE;
|
||||||
$identifier = $this->options['expose']['identifier'];
|
$identifier = $this->options['expose']['identifier'];
|
||||||
$input = $form_state['values'][$identifier];
|
$input = $form_state['values'][$identifier];
|
||||||
|
|
||||||
@ -143,14 +147,14 @@ abstract class SearchApiViewsHandlerFilterEntity extends SearchApiViewsHandlerFi
|
|||||||
$input = $this->options['group_info']['group_items'][$input]['value'];
|
$input = $this->options['group_info']['group_items'][$input]['value'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strlen($input)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
$values = $this->isMultiValued() ? drupal_explode_tags($input) : array($input);
|
$values = $this->isMultiValued() ? drupal_explode_tags($input) : array($input);
|
||||||
|
|
||||||
if (!$this->options['is_grouped'] || ($this->options['is_grouped'] && ($input != 'All'))) {
|
if (!$this->options['is_grouped'] || ($this->options['is_grouped'] && ($input != 'All'))) {
|
||||||
$this->validated_exposed_input = $this->validate_entity_strings($form[$identifier], $values);
|
$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}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function admin_summary() {
|
public function admin_summary() {
|
||||||
|
if (!is_array($this->value)) {
|
||||||
|
$this->value = $this->value ? array($this->value) : array();
|
||||||
|
}
|
||||||
$value = $this->value;
|
$value = $this->value;
|
||||||
$this->value = empty($value) ? '' : $this->ids_to_strings($value);
|
$this->value = empty($value) ? '' : $this->ids_to_strings($value);
|
||||||
$ret = parent::admin_summary();
|
$ret = parent::admin_summary();
|
||||||
|
@ -156,8 +156,9 @@ class SearchApiViewsHandlerFilterFulltext extends SearchApiViewsHandlerFilterTex
|
|||||||
|
|
||||||
if ($filter) {
|
if ($filter) {
|
||||||
$filter = $this->query->createFilter('OR');
|
$filter = $this->query->createFilter('OR');
|
||||||
|
$op = $this->operator === 'NOT' ? '<>' : '=';
|
||||||
foreach ($fields as $field) {
|
foreach ($fields as $field) {
|
||||||
$filter->condition($field, $this->value, $this->operator);
|
$filter->condition($field, $this->value, $op);
|
||||||
}
|
}
|
||||||
$this->query->filter($filter);
|
$this->query->filter($filter);
|
||||||
return;
|
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
|
// 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".)
|
// also set for NOT since otherwise it would be "not all of these words".)
|
||||||
if ($this->operator != 'AND') {
|
if ($this->operator != 'AND') {
|
||||||
$this->query->setOption('conjunction', $this->operator);
|
$this->query->setOption('conjunction', 'OR');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
$this->query->fields($fields);
|
$this->query->fields($fields);
|
||||||
$old = $this->query->getOriginalKeys();
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
$this->query->abort($e->getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$old = $this->query->getKeys();
|
||||||
|
$old_original = $this->query->getOriginalKeys();
|
||||||
$this->query->keys($this->value);
|
$this->query->keys($this->value);
|
||||||
if ($this->operator == 'NOT') {
|
if ($this->operator == 'NOT') {
|
||||||
$keys = &$this->query->getKeys();
|
$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.
|
// 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) {
|
if ($old) {
|
||||||
$keys = &$this->query->getKeys();
|
$keys = &$this->query->getKeys();
|
||||||
|
// Array-valued keys are combined.
|
||||||
if (is_array($keys)) {
|
if (is_array($keys)) {
|
||||||
$keys[] = $old;
|
// If the old keys weren't parsed into an array, we instead have to
|
||||||
}
|
// combine the original keys.
|
||||||
elseif (is_array($old)) {
|
if (is_scalar($old)) {
|
||||||
// We don't support such nonsense.
|
$keys = "($old) ({$this->value})";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$keys = "($old) ($keys)";
|
// 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,26 +14,14 @@
|
|||||||
class SearchApiViewsHandlerFilterLanguage extends SearchApiViewsHandlerFilterOptions {
|
class SearchApiViewsHandlerFilterLanguage extends SearchApiViewsHandlerFilterOptions {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide a form for setting options.
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function value_form(&$form, &$form_state) {
|
protected function get_value_options() {
|
||||||
parent::value_form($form, $form_state);
|
parent::get_value_options();
|
||||||
$form['value']['#options'] = array(
|
$this->value_options = array(
|
||||||
'current' => t("Current user's language"),
|
'current' => t("Current user's language"),
|
||||||
'default' => t('Default site language'),
|
'default' => t('Default site language'),
|
||||||
) + $form['value']['#options'];
|
) + $this->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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,8 +28,23 @@ class SearchApiViewsHandlerSort extends views_handler_sort {
|
|||||||
unset($this->query->orderby);
|
unset($this->query->orderby);
|
||||||
$sort = &$this->query->getSort();
|
$sort = &$this->query->getSort();
|
||||||
$sort = array();
|
$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']);
|
$this->query->sort($this->real_field, $this->options['order']);
|
||||||
}
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
$this->query->abort($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
global $user;
|
||||||
|
|
||||||
if (!isset($this->_results_key)) {
|
if (!isset($this->_results_key)) {
|
||||||
$query = $this->getSearchApiQuery();
|
$query = $this->getSearchApiQuery();
|
||||||
$query->preExecute();
|
$query->preExecute();
|
||||||
$key_data = array(
|
$key_data += array(
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
'roles' => array_keys($user->roles),
|
'roles' => array_keys($user->roles),
|
||||||
'super-user' => $user->uid == 1, // special caching for super user.
|
'super-user' => $user->uid == 1, // special caching for super user.
|
||||||
'language' => $GLOBALS['language']->language,
|
'language' => $GLOBALS['language']->language,
|
||||||
'base_url' => $GLOBALS['base_url'],
|
'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
|
// 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
|
// 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'])) {
|
if (isset($_GET['exposed_info'])) {
|
||||||
$key_data['exposed_info'] = $_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;
|
return $this->_results_key;
|
||||||
|
@ -169,7 +169,7 @@ class SearchApiViewsQuery extends views_plugin_query {
|
|||||||
'#default_value' => $this->options['search_api_bypass_access'],
|
'#default_value' => $this->options['search_api_bypass_access'],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (entity_get_info($this->index->item_type)) {
|
if ($this->index->getEntityType()) {
|
||||||
$form['entity_access'] = array(
|
$form['entity_access'] = array(
|
||||||
'#type' => 'checkbox',
|
'#type' => 'checkbox',
|
||||||
'#title' => t('Additional access checks on result entities'),
|
'#title' => t('Additional access checks on result entities'),
|
||||||
@ -342,7 +342,7 @@ class SearchApiViewsQuery extends views_plugin_query {
|
|||||||
catch (Exception $e) {
|
catch (Exception $e) {
|
||||||
$this->errors[] = $e->getMessage();
|
$this->errors[] = $e->getMessage();
|
||||||
// Recursion to get the same error behaviour as above.
|
// 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
|
// First off, we try to gather as much field values as possible without
|
||||||
// loading any items.
|
// loading any items.
|
||||||
foreach ($results as $id => $result) {
|
foreach ($results as $id => $result) {
|
||||||
if (!empty($this->options['entity_access'])) {
|
if (!empty($this->options['entity_access']) && ($entity_type = $this->index->getEntityType())) {
|
||||||
$entity = entity_load($this->index->item_type, array($id));
|
$entity = entity_load($entity_type, array($id));
|
||||||
if (!entity_access('view', $this->index->item_type, $entity[$id])) {
|
if (!entity_access('view', $entity_type, $entity[$id])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -660,16 +660,18 @@ class SearchApiViewsQuery extends views_plugin_query {
|
|||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getOption($name) {
|
public function getOption($name, $default = NULL) {
|
||||||
if (!$this->errors) {
|
if (!$this->errors) {
|
||||||
return $this->query->getOption($name);
|
return $this->query->getOption($name, $default);
|
||||||
}
|
}
|
||||||
|
return $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setOption($name, $value) {
|
public function setOption($name, $value) {
|
||||||
if (!$this->errors) {
|
if (!$this->errors) {
|
||||||
return $this->query->setOption($name, $value);
|
return $this->query->setOption($name, $value);
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function &getOptions() {
|
public function &getOptions() {
|
||||||
|
@ -27,9 +27,9 @@ files[] = includes/handler_sort.inc
|
|||||||
files[] = includes/plugin_cache.inc
|
files[] = includes/plugin_cache.inc
|
||||||
files[] = includes/query.inc
|
files[] = includes/query.inc
|
||||||
|
|
||||||
; Information added by Drupal.org packaging script on 2013-12-25
|
; Information added by Drupal.org packaging script on 2014-12-26
|
||||||
version = "7.x-1.11"
|
version = "7.x-1.14"
|
||||||
core = "7.x"
|
core = "7.x"
|
||||||
project = "search_api"
|
project = "search_api"
|
||||||
datestamp = "1387965506"
|
datestamp = "1419580682"
|
||||||
|
|
||||||
|
@ -134,8 +134,8 @@ function search_api_views_views_data() {
|
|||||||
if (isset($field['entity_type']) && $field['entity_type'] === 'taxonomy_term') {
|
if (isset($field['entity_type']) && $field['entity_type'] === 'taxonomy_term') {
|
||||||
$field_id = ($pos = strrpos($key, ':')) ? substr($key, $pos + 1) : $key;
|
$field_id = ($pos = strrpos($key, ':')) ? substr($key, $pos + 1) : $key;
|
||||||
$field_info = field_info_field($field_id);
|
$field_info = field_info_field($field_id);
|
||||||
if (isset($field_info['settings']['allowed_values'][0]['vocabulary'])) {
|
if ($vocabulary = _search_api_views_get_field_vocabulary($field_info)) {
|
||||||
$vocabulary_fields[$field_info['settings']['allowed_values'][0]['vocabulary']][] = $key;
|
$vocabulary_fields[$vocabulary][] = $key;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$vocabulary_fields[''][] = $key;
|
$vocabulary_fields[''][] = $key;
|
||||||
@ -184,7 +184,7 @@ function _search_api_views_add_handlers($id, array $field, EntityMetadataWrapper
|
|||||||
if ($inner_type == 'text') {
|
if ($inner_type == 'text') {
|
||||||
$table[$id] += array(
|
$table[$id] += array(
|
||||||
'argument' => array(
|
'argument' => array(
|
||||||
'handler' => 'SearchApiViewsHandlerArgument',
|
'handler' => 'SearchApiViewsHandlerArgumentString',
|
||||||
),
|
),
|
||||||
'filter' => array(
|
'filter' => array(
|
||||||
'handler' => 'SearchApiViewsHandlerFilterText',
|
'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') {
|
elseif (isset($field['entity_type']) && $field['entity_type'] === 'taxonomy_term') {
|
||||||
$table[$id]['filter']['handler'] = 'SearchApiViewsHandlerFilterTaxonomyTerm';
|
$table[$id]['filter']['handler'] = 'SearchApiViewsHandlerFilterTaxonomyTerm';
|
||||||
$info = $wrapper->info();
|
|
||||||
$field_info = field_info_field($info['name']);
|
$field_info = field_info_field($info['name']);
|
||||||
// For the "Parent terms" and "All parent terms" properties, we can
|
// For the "Parent terms" and "All parent terms" properties, we can
|
||||||
// extrapolate the vocabulary from the parent in the selector. (E.g.,
|
// 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]);
|
$field_info = field_info_field($parts[count($parts) - 2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isset($field_info['settings']['allowed_values'][0]['vocabulary'])) {
|
if ($vocabulary = _search_api_views_get_field_vocabulary($field_info)) {
|
||||||
$table[$id]['filter']['vocabulary'] = $field_info['settings']['allowed_values'][0]['vocabulary'];
|
$table[$id]['filter']['vocabulary'] = $vocabulary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -293,3 +292,31 @@ function search_api_views_views_plugins() {
|
|||||||
|
|
||||||
return $ret;
|
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;
|
||||||
|
}
|
||||||
|
@ -193,6 +193,12 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback {
|
|||||||
return isset($a) ? min($a, $b) : $b;
|
return isset($a) ? min($a, $b) : $b;
|
||||||
case 'first':
|
case 'first':
|
||||||
return isset($a) ? $a : $b;
|
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'),
|
'max' => t('Maximum'),
|
||||||
'min' => t('Minimum'),
|
'min' => t('Minimum'),
|
||||||
'first' => t('First'),
|
'first' => t('First'),
|
||||||
|
'list' => t('List'),
|
||||||
);
|
);
|
||||||
case 'type':
|
case 'type':
|
||||||
return array(
|
return array(
|
||||||
@ -270,6 +277,7 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback {
|
|||||||
'max' => 'integer',
|
'max' => 'integer',
|
||||||
'min' => 'integer',
|
'min' => 'integer',
|
||||||
'first' => 'string',
|
'first' => 'string',
|
||||||
|
'list' => 'list<string>',
|
||||||
);
|
);
|
||||||
case 'description':
|
case 'description':
|
||||||
return array(
|
return array(
|
||||||
@ -279,6 +287,7 @@ class SearchApiAlterAddAggregation extends SearchApiAbstractAlterCallback {
|
|||||||
'max' => t('The Maximum aggregation computes the numerically largest contained field value.'),
|
'max' => t('The Maximum aggregation computes the numerically largest contained field value.'),
|
||||||
'min' => t('The Minimum aggregation computes the numerically smallest 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.'),
|
'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) {
|
public function formButtonSubmit(array $form, array &$form_state) {
|
||||||
$button_name = $form_state['triggering_element']['#name'];
|
$button_name = $form_state['triggering_element']['#name'];
|
||||||
if ($button_name == 'op') {
|
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) {
|
for ($i = 1; isset($this->options['fields']['search_api_aggregation_' . $i]); ++$i) {
|
||||||
}
|
}
|
||||||
$this->options['fields']['search_api_aggregation_' . $i] = array(
|
$this->options['fields']['search_api_aggregation_' . $i] = array(
|
||||||
|
@ -172,20 +172,25 @@ class SearchApiIndex extends Entity {
|
|||||||
/**
|
/**
|
||||||
* Constructor as a helper to the parent constructor.
|
* Constructor as a helper to the parent constructor.
|
||||||
*/
|
*/
|
||||||
public function __construct(array $values = array()) {
|
public function __construct(array $values = array(), $entity_type = 'search_api_index') {
|
||||||
parent::__construct($values, 'search_api_index');
|
parent::__construct($values, $entity_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute necessary tasks for a newly created index.
|
* Execute necessary tasks for a newly created index.
|
||||||
*/
|
*/
|
||||||
public function postCreate() {
|
public function postCreate() {
|
||||||
if ($this->enabled) {
|
try {
|
||||||
$this->queueItems();
|
|
||||||
}
|
|
||||||
if ($server = $this->server()) {
|
if ($server = $this->server()) {
|
||||||
// Tell the server about the new index.
|
// Tell the server about the new index.
|
||||||
$server->addIndex($this);
|
$server->addIndex($this);
|
||||||
|
if ($this->enabled) {
|
||||||
|
$this->queueItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
watchdog_exception('search_api', $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,9 +198,14 @@ class SearchApiIndex extends Entity {
|
|||||||
* Execute necessary tasks when the index is removed from the database.
|
* Execute necessary tasks when the index is removed from the database.
|
||||||
*/
|
*/
|
||||||
public function postDelete() {
|
public function postDelete() {
|
||||||
|
try {
|
||||||
if ($server = $this->server()) {
|
if ($server = $this->server()) {
|
||||||
$server->removeIndex($this);
|
$server->removeIndex($this);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
watchdog_exception('search_api', $e);
|
||||||
|
}
|
||||||
|
|
||||||
// Stop tracking entities for indexing.
|
// Stop tracking entities for indexing.
|
||||||
$this->dequeueItems();
|
$this->dequeueItems();
|
||||||
@ -206,16 +216,26 @@ class SearchApiIndex extends Entity {
|
|||||||
*/
|
*/
|
||||||
public function queueItems() {
|
public function queueItems() {
|
||||||
if (!$this->read_only) {
|
if (!$this->read_only) {
|
||||||
|
try {
|
||||||
$this->datasource()->startTracking(array($this));
|
$this->datasource()->startTracking(array($this));
|
||||||
}
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
watchdog_exception('search_api', $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all records of entities to index.
|
* Remove all records of entities to index.
|
||||||
*/
|
*/
|
||||||
public function dequeueItems() {
|
public function dequeueItems() {
|
||||||
|
try {
|
||||||
$this->datasource()->stopTracking(array($this));
|
$this->datasource()->stopTracking(array($this));
|
||||||
}
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
watchdog_exception('search_api', $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves this index to the database.
|
* Saves this index to the database.
|
||||||
@ -231,16 +251,25 @@ class SearchApiIndex extends Entity {
|
|||||||
if (empty($this->description)) {
|
if (empty($this->description)) {
|
||||||
$this->description = NULL;
|
$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->server = NULL;
|
||||||
$this->enabled = FALSE;
|
$this->enabled = FALSE;
|
||||||
}
|
}
|
||||||
// This will also throw an exception if the server doesn't exist – which is good.
|
if (!empty($this->options['fields'])) {
|
||||||
elseif (!$this->server(TRUE)->enabled) {
|
ksort($this->options['fields']);
|
||||||
$this->enabled = FALSE;
|
|
||||||
$this->server = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->resetCaches();
|
||||||
|
|
||||||
return parent::save();
|
return parent::save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +334,12 @@ class SearchApiIndex extends Entity {
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
$this->server()->deleteItems('all', $this);
|
$this->server()->deleteItems('all', $this);
|
||||||
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
watchdog_exception('search_api', $e);
|
||||||
|
}
|
||||||
|
|
||||||
_search_api_index_reindex($this);
|
_search_api_index_reindex($this);
|
||||||
module_invoke_all('search_api_index_reindex', $this, TRUE);
|
module_invoke_all('search_api_index_reindex', $this, TRUE);
|
||||||
@ -350,8 +384,13 @@ class SearchApiIndex extends Entity {
|
|||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
public function getEntityType() {
|
public function getEntityType() {
|
||||||
|
try {
|
||||||
return $this->datasource()->getEntityType();
|
return $this->datasource()->getEntityType();
|
||||||
}
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the server this index lies on.
|
* Get the server this index lies on.
|
||||||
@ -385,7 +424,7 @@ class SearchApiIndex extends Entity {
|
|||||||
* SearchApiQueryInterface::__construct().
|
* SearchApiQueryInterface::__construct().
|
||||||
*
|
*
|
||||||
* @throws SearchApiException
|
* @throws SearchApiException
|
||||||
* If the index is currently disabled.
|
* If the index is currently disabled or its server doesn't exist.
|
||||||
*
|
*
|
||||||
* @return SearchApiQueryInterface
|
* @return SearchApiQueryInterface
|
||||||
* A query object for searching this index.
|
* 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
|
* Indexes items on this index.
|
||||||
* should be marked as indexed – i.e., items that were either rejected by a
|
*
|
||||||
* data-alter callback or were successfully indexed.
|
* 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
|
* @param array $items
|
||||||
* An array of items to index.
|
* An array of items to index, of this index's item type.
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
* An array of the IDs of all items that should be marked as indexed.
|
* 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) {
|
public function index(array $items) {
|
||||||
if ($this->read_only) {
|
if ($this->read_only) {
|
||||||
@ -925,13 +969,19 @@ class SearchApiIndex extends Entity {
|
|||||||
* @return EntityMetadataWrapper
|
* @return EntityMetadataWrapper
|
||||||
* A wrapper for the item type of this index, optionally loaded with the
|
* A wrapper for the item type of this index, optionally loaded with the
|
||||||
* given data and having additional fields according to the data alterations
|
* 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) {
|
public function entityWrapper($item = NULL, $alter = TRUE) {
|
||||||
|
try {
|
||||||
$info['property info alter'] = $alter ? array($this, 'propertyInfoAlter') : '_search_api_wrapper_add_all_properties';
|
$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';
|
$info['property defaults']['property info alter'] = '_search_api_wrapper_add_all_properties';
|
||||||
return $this->datasource()->getMetadataWrapper($item, $info);
|
return $this->datasource()->getMetadataWrapper($item, $info);
|
||||||
}
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
watchdog_exception('search_api', $e);
|
||||||
|
return entity_metadata_wrapper($this->item_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to load items from the type lying on this index.
|
* Helper method to load items from the type lying on this index.
|
||||||
@ -945,16 +995,24 @@ class SearchApiIndex extends Entity {
|
|||||||
* @see SearchApiDataSourceControllerInterface::loadItems()
|
* @see SearchApiDataSourceControllerInterface::loadItems()
|
||||||
*/
|
*/
|
||||||
public function loadItems(array $ids) {
|
public function loadItems(array $ids) {
|
||||||
|
try {
|
||||||
return $this->datasource()->loadItems($ids);
|
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
|
* Should be used when things like fields or data alterations change to avoid
|
||||||
* using stale data.
|
* using stale data.
|
||||||
*/
|
*/
|
||||||
public function resetCaches() {
|
public function resetCaches() {
|
||||||
|
cache_clear_all($this->getCacheId(''), 'cache', TRUE);
|
||||||
|
|
||||||
$this->datasource = NULL;
|
$this->datasource = NULL;
|
||||||
$this->server_object = NULL;
|
$this->server_object = NULL;
|
||||||
$this->callbacks = NULL;
|
$this->callbacks = NULL;
|
||||||
|
@ -22,8 +22,6 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
|
|||||||
/**
|
/**
|
||||||
* PREG regular expression for splitting words.
|
* PREG regular expression for splitting words.
|
||||||
*
|
*
|
||||||
* We highlight around non-indexable or CJK characters.
|
|
||||||
*
|
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected static $split;
|
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{F900}-\x{FAFF}\x{FF21}-\x{FF3A}\x{FF41}-\x{FF5A}\x{FF66}-\x{FFDC}' .
|
||||||
'\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}';
|
'\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}';
|
||||||
self::$boundary = '(?:(?<=[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . $cjk . '])|(?=[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . $cjk . ']))';
|
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' => TRUE,
|
||||||
'excerpt_length' => 256,
|
'excerpt_length' => 256,
|
||||||
'highlight' => 'always',
|
'highlight' => 'always',
|
||||||
|
'exclude_fields' => array(),
|
||||||
);
|
);
|
||||||
|
|
||||||
$form['prefix'] = 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(
|
$form['highlight'] = array(
|
||||||
'#type' => 'select',
|
'#type' => 'select',
|
||||||
'#title' => t('Highlight returned field data'),
|
'#title' => t('Highlight returned field data'),
|
||||||
@ -106,21 +121,29 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
|
|||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function configurationFormValidate(array $form, array &$values, array &$form_state) {
|
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}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function postprocessSearchResults(array &$response, SearchApiQuery $query) {
|
public function postprocessSearchResults(array &$response, SearchApiQuery $query) {
|
||||||
if (!$response['result count'] || !($keys = $this->getKeywords($query))) {
|
if (empty($response['results']) || !($keys = $this->getKeywords($query))) {
|
||||||
return;
|
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) {
|
foreach ($response['results'] as $id => &$result) {
|
||||||
if ($this->options['excerpt']) {
|
if ($this->options['excerpt']) {
|
||||||
$text = array();
|
$text = array();
|
||||||
$fields = $this->getFulltextFields($response['results'], $id);
|
$fields = $this->getFulltextFields($response['results'], $id, $fulltext_fields);
|
||||||
foreach ($fields as $data) {
|
foreach ($fields as $data) {
|
||||||
if (is_array($data)) {
|
if (is_array($data)) {
|
||||||
$text = array_merge($text, $data);
|
$text = array_merge($text, $data);
|
||||||
@ -129,10 +152,11 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
|
|||||||
$text[] = $data;
|
$text[] = $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$result['excerpt'] = $this->createExcerpt(implode("\n\n", $text), $keys);
|
|
||||||
|
$result['excerpt'] = $this->createExcerpt($this->flattenArrayValues($text), $keys);
|
||||||
}
|
}
|
||||||
if ($this->options['highlight'] != 'never') {
|
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) {
|
foreach ($fields as $field => $data) {
|
||||||
if (is_array($data)) {
|
if (is_array($data)) {
|
||||||
foreach ($data as $i => $text) {
|
foreach ($data as $i => $text) {
|
||||||
@ -155,6 +179,8 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
|
|||||||
* @param int|string $i
|
* @param int|string $i
|
||||||
* The index in the results array of the result whose data should be
|
* The index in the results array of the result whose data should be
|
||||||
* returned.
|
* returned.
|
||||||
|
* @param array $fulltext_fields
|
||||||
|
* The fulltext fields from which the excerpt should be created.
|
||||||
* @param bool $load
|
* @param bool $load
|
||||||
* TRUE if the item should be loaded if necessary, FALSE if only fields
|
* TRUE if the item should be loaded if necessary, FALSE if only fields
|
||||||
* already returned in the results should be used.
|
* 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
|
* An array containing fulltext field names mapped to the text data
|
||||||
* contained in them for the given result.
|
* 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;
|
global $language;
|
||||||
$data = array();
|
$data = array();
|
||||||
|
|
||||||
@ -171,7 +197,6 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
|
|||||||
// Act as if $load is TRUE if we have a loaded item.
|
// Act as if $load is TRUE if we have a loaded item.
|
||||||
$load |= !empty($result['entity']);
|
$load |= !empty($result['entity']);
|
||||||
$result += array('fields' => array());
|
$result += array('fields' => array());
|
||||||
$fulltext_fields = $this->index->getFulltextFields();
|
|
||||||
// We only need detailed fields data if $load is TRUE.
|
// We only need detailed fields data if $load is TRUE.
|
||||||
$fields = $load ? $this->index->getFields() : array();
|
$fields = $load ? $this->index->getFields() : array();
|
||||||
$needs_extraction = array();
|
$needs_extraction = array();
|
||||||
@ -309,7 +334,7 @@ class SearchApiHighlight extends SearchApiAbstractProcessor {
|
|||||||
// Locate a keyword (position $p, always >0 because $text starts with a
|
// Locate a keyword (position $p, always >0 because $text starts with a
|
||||||
// space).
|
// space).
|
||||||
$p = 0;
|
$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];
|
$p = $match[0][1];
|
||||||
}
|
}
|
||||||
// Now locate a space in front (position $q) and behind it (position $s),
|
// 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 = (isset($newranges[0]) ? '' : $dots[0]) . implode($dots[1], $out) . $dots[2];
|
||||||
$text = check_plain($text);
|
$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.
|
* The text of the field.
|
||||||
* @param array $keys
|
* @param array $keys
|
||||||
* Search keywords entered by the user.
|
* 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
|
* @return string
|
||||||
* The field's text with all occurrences of search keywords highlighted.
|
* 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'];
|
$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 . ' ');
|
$text = preg_replace('/' . self::$boundary . '(' . $keys . ')' . self::$boundary . '/iu', $replace, ' ' . $text . ' ');
|
||||||
return substr($text, 1, -1);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,8 @@ class SearchApiHtmlFilter extends SearchApiAbstractProcessor {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$value = strip_tags($text);
|
$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();
|
$ret = array();
|
||||||
while (($pos = strpos($text, '<')) !== FALSE) {
|
while (($pos = strpos($text, '<')) !== FALSE) {
|
||||||
if ($boost && $pos > 0) {
|
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(
|
$ret[] = array(
|
||||||
'value' => html_entity_decode(substr($text, 0, $pos), ENT_QUOTES, 'UTF-8'),
|
'value' => $token,
|
||||||
'score' => $boost,
|
'score' => $boost,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -130,8 +135,11 @@ class SearchApiHtmlFilter extends SearchApiAbstractProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($text) {
|
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(
|
$ret[] = array(
|
||||||
'value' => html_entity_decode($text, ENT_QUOTES, 'UTF-8'),
|
'value' => $token,
|
||||||
'score' => $boost,
|
'score' => $boost,
|
||||||
);
|
);
|
||||||
$text = '';
|
$text = '';
|
||||||
|
@ -226,6 +226,9 @@ interface SearchApiQueryInterface {
|
|||||||
*
|
*
|
||||||
* This method should always be called by execute() and contain all necessary
|
* This method should always be called by execute() and contain all necessary
|
||||||
* operations before the query is passed to the server's search() method.
|
* 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();
|
public function preExecute();
|
||||||
|
|
||||||
@ -366,7 +369,7 @@ class SearchApiQuery implements SearchApiQueryInterface {
|
|||||||
/**
|
/**
|
||||||
* The index's machine name.
|
* 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
|
* @var string
|
||||||
*/
|
*/
|
||||||
@ -811,6 +814,31 @@ class SearchApiQuery implements SearchApiQueryInterface {
|
|||||||
$this->filter = clone $this->filter;
|
$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 all conditions and nested filters contained in this filter.
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
* An array containing this filter's subfilters. Each of these is either an
|
* An array containing this filter's subfilters. Each of these is either a
|
||||||
* array (field, value, operator), or another SearchApiFilter object.
|
* 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();
|
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) : '';
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -74,8 +74,8 @@ class SearchApiServer extends Entity {
|
|||||||
/**
|
/**
|
||||||
* Constructor as a helper to the parent constructor.
|
* Constructor as a helper to the parent constructor.
|
||||||
*/
|
*/
|
||||||
public function __construct(array $values = array()) {
|
public function __construct(array $values = array(), $entity_type = 'search_api_server') {
|
||||||
parent::__construct($values, 'search_api_server');
|
parent::__construct($values, $entity_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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']['#prefix'] = '<div id="search-api-class-options">';
|
||||||
$form['options']['#suffix'] = '</div>';
|
$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(
|
$form['submit'] = array(
|
||||||
'#type' => 'submit',
|
'#type' => 'submit',
|
||||||
'#value' => t('Create server'),
|
'#value' => t('Create server'),
|
||||||
@ -834,6 +841,15 @@ function search_api_admin_index_view(SearchApiIndex $index, $action = NULL) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$status = search_api_index_status($index);
|
$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(
|
$ret['view'] = array(
|
||||||
'#theme' => 'search_api_index',
|
'#theme' => 'search_api_index',
|
||||||
'#id' => $index->id,
|
'#id' => $index->id,
|
||||||
@ -842,15 +858,21 @@ function search_api_admin_index_view(SearchApiIndex $index, $action = NULL) {
|
|||||||
'#description' => $index->description,
|
'#description' => $index->description,
|
||||||
'#item_type' => $index->item_type,
|
'#item_type' => $index->item_type,
|
||||||
'#enabled' => $index->enabled,
|
'#enabled' => $index->enabled,
|
||||||
'#server' => $index->server(),
|
'#server' => $server,
|
||||||
'#options' => $index->options,
|
'#options' => $index->options,
|
||||||
'#fields' => $index->getFields(),
|
'#fields' => $index->getFields(),
|
||||||
'#indexed_items' => $status['indexed'],
|
'#indexed_items' => $status['indexed'],
|
||||||
'#on_server' => _search_api_get_items_on_server($index),
|
'#on_server' => NULL,
|
||||||
'#total_items' => $status['total'],
|
'#total_items' => $status['total'],
|
||||||
'#status' => $index->status,
|
'#status' => $index->status,
|
||||||
'#read_only' => $index->read_only,
|
'#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) {
|
if ($index->enabled && !$index->read_only) {
|
||||||
$ret['form'] = drupal_get_form('search_api_admin_index_status_form', $index, $status);
|
$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.
|
* - fields: All indexed fields of the index.
|
||||||
* - indexed_items: The number of items already indexed in their latest
|
* - indexed_items: The number of items already indexed in their latest
|
||||||
* version on this index.
|
* 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
|
* - total_items: The total number of items that have to be indexed for this
|
||||||
* index.
|
* index.
|
||||||
* - status: The entity configuration status (in database, in code, etc.).
|
* - 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);
|
$rows[] = _search_api_deep_copy($row);
|
||||||
|
|
||||||
$theme = array(
|
$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)),
|
'message' => t('@indexed/@total indexed', array('@indexed' => $indexed_items, '@total' => $total_items)),
|
||||||
);
|
);
|
||||||
$output .= '<h3>' . t('Index status') . '</h3>';
|
$output .= '<h3>' . t('Index status') . '</h3>';
|
||||||
$output .= '<div class="search-api-index-status">' . theme('progress_bar', $theme) . '</div>';
|
$output .= '<div class="search-api-index-status">' . theme('progress_bar', $theme) . '</div>';
|
||||||
|
|
||||||
|
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');
|
$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);
|
$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 = '';
|
$class = '';
|
||||||
|
}
|
||||||
$label = t('Server index status');
|
$label = t('Server index status');
|
||||||
$rows[] = _search_api_deep_copy($row);
|
$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,
|
'#default_value' => $index->name,
|
||||||
'#required' => TRUE,
|
'#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(
|
$form['enabled'] = array(
|
||||||
'#type' => 'checkbox',
|
'#type' => 'checkbox',
|
||||||
'#title' => t('Enabled'),
|
'#title' => t('Enabled'),
|
||||||
'#default_value' => $index->enabled,
|
'#default_value' => $index->enabled,
|
||||||
// Can't enable an index lying on a disabled server, or no server at all.
|
// 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(
|
$form['description'] = array(
|
||||||
'#type' => 'textarea',
|
'#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) {
|
foreach ($form_state['callbacks'] as $name => $callback) {
|
||||||
// Check whether callback status has changed.
|
// Check whether callback status has changed.
|
||||||
if ($values['callbacks'][$name]['status'] == empty($options['data_alter_callbacks'][$name]['status'])) {
|
if ($values['callbacks'][$name]['status'] == empty($options['data_alter_callbacks'][$name]['status'])) {
|
||||||
|
$callbacks_changed = TRUE;
|
||||||
if ($values['callbacks'][$name]['status']) {
|
if ($values['callbacks'][$name]['status']) {
|
||||||
// Callback was just enabled, add its fields.
|
// Callback was just enabled, add its fields.
|
||||||
$properties = $callback->propertyInfo();
|
$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['data_alter_callbacks'], 'search_api_admin_element_compare');
|
||||||
uasort($index->options['processors'], '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
|
// Re-calculate the fields, since they might have changed in hard-to-predict
|
||||||
// new data alterations.
|
// ways.
|
||||||
$index->resetCaches();
|
search_api_index_recalculate_fields(array($index));
|
||||||
|
|
||||||
$index->save();
|
$index->save();
|
||||||
$index->reindex();
|
$index->reindex();
|
||||||
@ -1661,6 +1690,8 @@ function search_api_admin_index_fields(array $form, array &$form_state, SearchAp
|
|||||||
// $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'));
|
$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'));
|
$fulltext_types = array(0 => array('text'));
|
||||||
// Add all custom data types with fallback "text" to fulltext types as well.
|
// Add all custom data types with fallback "text" to fulltext types as well.
|
||||||
foreach (search_api_get_data_type_info() as $id => $type) {
|
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'];
|
$action = $values['action'];
|
||||||
$id = $values['id'];
|
$id = $values['id'];
|
||||||
|
|
||||||
|
$success = FALSE;
|
||||||
$function = "search_api_{$type}_{$action}";
|
$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']);
|
drupal_set_message($values['message']);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -335,7 +335,7 @@ function hook_search_api_items_indexed(SearchApiIndex $index, array $item_ids) {
|
|||||||
* Lets modules alter a search query before executing it.
|
* Lets modules alter a search query before executing it.
|
||||||
*
|
*
|
||||||
* @param SearchApiQueryInterface $query
|
* @param SearchApiQueryInterface $query
|
||||||
* The SearchApiQueryInterface object representing the search query.
|
* The search query being executed.
|
||||||
*/
|
*/
|
||||||
function hook_search_api_query_alter(SearchApiQueryInterface $query) {
|
function hook_search_api_query_alter(SearchApiQueryInterface $query) {
|
||||||
// Exclude entities with ID 0. (Assume the ID field is always indexed.)
|
// Exclude entities with ID 0. (Assume the ID field is always indexed.)
|
||||||
|
@ -108,6 +108,19 @@ function search_api_drush_command() {
|
|||||||
'aliases' => array('sapi-c'),
|
'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;
|
return $items;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,15 +150,21 @@ function drush_search_api_list() {
|
|||||||
foreach ($indexes as $index) {
|
foreach ($indexes as $index) {
|
||||||
$type = search_api_get_item_type_info($index->item_type);
|
$type = search_api_get_item_type_info($index->item_type);
|
||||||
$type = isset($type['name']) ? $type['name'] : $index->item_type;
|
$type = isset($type['name']) ? $type['name'] : $index->item_type;
|
||||||
|
try {
|
||||||
$server = $index->server();
|
$server = $index->server();
|
||||||
$server = $server ? $server->name : '(' . t('none') . ')';
|
$server = $server ? $server->name : '(' . dt('none') . ')';
|
||||||
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
watchdog_exception('search_api', $e);
|
||||||
|
$server = '(' . dt('unknown: !server', array('server' => $index->server)) . ')';
|
||||||
|
}
|
||||||
$row = array(
|
$row = array(
|
||||||
$index->id,
|
$index->id,
|
||||||
$index->name,
|
$index->name,
|
||||||
$index->machine_name,
|
$index->machine_name,
|
||||||
$server,
|
$server,
|
||||||
$type,
|
$type,
|
||||||
$index->enabled ? t('enabled') : t('disabled'),
|
$index->enabled ? dt('enabled') : dt('disabled'),
|
||||||
$index->options['cron_limit'],
|
$index->options['cron_limit'],
|
||||||
);
|
);
|
||||||
$rows[] = $row;
|
$rows[] = $row;
|
||||||
@ -168,17 +187,24 @@ function drush_search_api_enable($index_id = NULL) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
foreach ($indexes as $index) {
|
foreach ($indexes as $index) {
|
||||||
|
$vars = array('!index' => $index->name);
|
||||||
if (!$index->enabled) {
|
if (!$index->enabled) {
|
||||||
drush_log(dt("Enabling index !index and queueing items for indexing.", array('!index' => $index->name)), 'notice');
|
drush_log(dt("Enabling index !index and queueing items for indexing.", $vars), 'notice');
|
||||||
if (search_api_index_enable($index->id)) {
|
$success = FALSE;
|
||||||
drush_log(dt("The index !index was successfully enabled.", array('!index' => $index->name)), 'ok');
|
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 {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
foreach ($indexes as $index) {
|
foreach ($indexes as $index) {
|
||||||
|
$vars = array('!index' => $index->name);
|
||||||
if ($index->enabled) {
|
if ($index->enabled) {
|
||||||
if (search_api_index_disable($index->id)) {
|
$success = FALSE;
|
||||||
drush_log(dt("The index !index was successfully disabled.", array('!index' => $index->name)), 'ok');
|
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 {
|
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,14 +297,48 @@ function drush_search_api_index($index_id = NULL, $limit = NULL, $batch_size = N
|
|||||||
if (empty($indexes)) {
|
if (empty($indexes)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$process_batch = FALSE;
|
||||||
foreach ($indexes as $index) {
|
foreach ($indexes as $index) {
|
||||||
// Get the number of remaing items to index.
|
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();
|
$datasource = $index->datasource();
|
||||||
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
drush_log($e->getMessage(), 'error');
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
$index_status = $datasource->getIndexStatus($index);
|
$index_status = $datasource->getIndexStatus($index);
|
||||||
$remaining = $index_status['total'] - $index_status['indexed'];
|
$remaining = $index_status['total'] - $index_status['indexed'];
|
||||||
if ($remaining <= 0) {
|
if ($remaining <= 0) {
|
||||||
drush_log(dt("The index !index is up to date.", array('!index' => $index->name)), 'ok');
|
drush_log(dt("The index !index is up to date.", array('!index' => $index->name)), 'ok');
|
||||||
continue;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the number of items to index per batch run.
|
// Get the number of items to index per batch run.
|
||||||
@ -282,7 +349,7 @@ function drush_search_api_index($index_id = NULL, $limit = NULL, $batch_size = N
|
|||||||
$batch_size = $remaining;
|
$batch_size = $remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the number items to index.
|
// Get the total number of items to index.
|
||||||
if (!isset($limit) || !is_int($limit += 0) || $limit <= 0) {
|
if (!isset($limit) || !is_int($limit += 0) || $limit <= 0) {
|
||||||
$limit = $remaining;
|
$limit = $remaining;
|
||||||
}
|
}
|
||||||
@ -292,12 +359,10 @@ function drush_search_api_index($index_id = NULL, $limit = NULL, $batch_size = N
|
|||||||
// Create the batch.
|
// Create the batch.
|
||||||
if (!_search_api_batch_indexing_create($index, $batch_size, $limit, $remaining, TRUE)) {
|
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');
|
drush_log(dt("Couldn't create a batch, please check the batch size and limit parameters."), 'error');
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// Launch the batch process.
|
return TRUE;
|
||||||
drush_backend_batch_process();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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
|
* @param string|int|null $index_id
|
||||||
* (optional) The provided 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.
|
* An array of indexes.
|
||||||
*/
|
*/
|
||||||
function search_api_drush_get_index($index_id = NULL) {
|
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
|
* @see http://drupal.org/node/704848
|
||||||
*/
|
*/
|
||||||
@ -397,4 +531,5 @@ function search_api_drush_static($function) {
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
$index[$function] = TRUE;
|
$index[$function] = TRUE;
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,9 @@ files[] = includes/service.inc
|
|||||||
|
|
||||||
configure = admin/config/search/search_api
|
configure = admin/config/search/search_api
|
||||||
|
|
||||||
; Information added by Drupal.org packaging script on 2013-12-25
|
; Information added by Drupal.org packaging script on 2014-12-26
|
||||||
version = "7.x-1.11"
|
version = "7.x-1.14"
|
||||||
core = "7.x"
|
core = "7.x"
|
||||||
project = "search_api"
|
project = "search_api"
|
||||||
datestamp = "1387965506"
|
datestamp = "1419580682"
|
||||||
|
|
||||||
|
@ -349,9 +349,14 @@ function search_api_enable() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($types as $type => $indexes) {
|
foreach ($types as $type => $indexes) {
|
||||||
|
try {
|
||||||
$controller = search_api_get_datasource_controller($type);
|
$controller = search_api_get_datasource_controller($type);
|
||||||
$controller->startTracking($indexes);
|
$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')
|
$insert = db_insert('search_api_task')
|
||||||
->fields(array('server_id', 'type', 'index_id', 'data'));
|
->fields(array('server_id', 'type', 'index_id', 'data'));
|
||||||
foreach ($tasks as $task) {
|
foreach ($tasks as $task) {
|
||||||
|
$task += array(
|
||||||
|
'index_id' => NULL,
|
||||||
|
'data' => NULL,
|
||||||
|
);
|
||||||
$insert->values($task);
|
$insert->values($task);
|
||||||
}
|
}
|
||||||
$insert->execute();
|
$insert->execute();
|
||||||
|
@ -275,7 +275,7 @@ function search_api_theme() {
|
|||||||
'options' => array(),
|
'options' => array(),
|
||||||
'fields' => array(),
|
'fields' => array(),
|
||||||
'indexed_items' => 0,
|
'indexed_items' => 0,
|
||||||
'on_server' => 0,
|
'on_server' => NULL,
|
||||||
'total_items' => 0,
|
'total_items' => 0,
|
||||||
'status' => ENTITY_CUSTOM,
|
'status' => ENTITY_CUSTOM,
|
||||||
'read_only' => 0,
|
'read_only' => 0,
|
||||||
@ -662,38 +662,39 @@ function search_api_search_api_index_update(SearchApiIndex $index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($index->server) {
|
if ($index->server) {
|
||||||
|
try {
|
||||||
$new_server = $index->server(TRUE);
|
$new_server = $index->server(TRUE);
|
||||||
// If the server is enabled, we call addIndex(); otherwise, we save the task.
|
// If the server is enabled, we call addIndex(); otherwise, we save the task.
|
||||||
$new_server->addIndex($index);
|
$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.
|
// We also have to re-index all content.
|
||||||
_search_api_index_reindex($index);
|
_search_api_index_reindex($index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the fields were changed, call the appropriate service class hook method
|
// 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 = $index->original->options + array('fields' => array());
|
||||||
$old_fields = $old_fields['fields'];
|
$old_fields = $old_fields['fields'];
|
||||||
$new_fields = $index->options + array('fields' => array());
|
$new_fields = $index->options + array('fields' => array());
|
||||||
$new_fields = $new_fields['fields'];
|
$new_fields = $new_fields['fields'];
|
||||||
if ($old_fields != $new_fields) {
|
if ($old_fields != $new_fields) {
|
||||||
cache_clear_all($index->getCacheId(), 'cache', TRUE);
|
|
||||||
if ($index->server) {
|
if ($index->server) {
|
||||||
$index->server()->fieldsUpdated($index);
|
$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
|
// If the index's enabled or read-only status is being changed, queue or
|
||||||
// dequeue items for indexing.
|
// dequeue items for indexing.
|
||||||
if (!$index->read_only && $index->enabled != $index->original->enabled) {
|
if (!$index->read_only && $index->enabled != $index->original->enabled) {
|
||||||
@ -1098,7 +1099,14 @@ function search_api_track_item_insert($type, array $item_ids) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
search_api_get_datasource_controller($type)->trackItemInsert($item_ids, $indexes);
|
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) {
|
foreach ($indexes as $index) {
|
||||||
if (!empty($index->options['index_directly'])) {
|
if (!empty($index->options['index_directly'])) {
|
||||||
@ -1128,6 +1136,7 @@ function search_api_track_item_change($type, array $item_ids) {
|
|||||||
if (!$indexes) {
|
if (!$indexes) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
search_api_get_datasource_controller($type)->trackItemChange($item_ids, $indexes);
|
search_api_get_datasource_controller($type)->trackItemChange($item_ids, $indexes);
|
||||||
foreach ($indexes as $index) {
|
foreach ($indexes as $index) {
|
||||||
if (!empty($index->options['index_directly'])) {
|
if (!empty($index->options['index_directly'])) {
|
||||||
@ -1142,6 +1151,12 @@ function search_api_track_item_change($type, array $item_ids) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks items as queued for indexing for the specified index.
|
* Marks items as queued for indexing for the specified index.
|
||||||
@ -1158,8 +1173,13 @@ function search_api_track_item_change($type, array $item_ids) {
|
|||||||
* the Drupal 8 version of this module.
|
* the Drupal 8 version of this module.
|
||||||
*/
|
*/
|
||||||
function search_api_track_item_queued(SearchApiIndex $index, array $item_ids) {
|
function search_api_track_item_queued(SearchApiIndex $index, array $item_ids) {
|
||||||
|
try {
|
||||||
$index->datasource()->trackItemQueued($item_ids, $index);
|
$index->datasource()->trackItemQueued($item_ids, $index);
|
||||||
}
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
watchdog_exception('search_api', $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks items as successfully indexed for the specified index.
|
* Marks items as successfully indexed for the specified index.
|
||||||
@ -1191,18 +1211,29 @@ function search_api_track_item_delete($type, array $item_ids) {
|
|||||||
);
|
);
|
||||||
$indexes = search_api_index_load_multiple(FALSE, $conditions);
|
$indexes = search_api_index_load_multiple(FALSE, $conditions);
|
||||||
if ($indexes) {
|
if ($indexes) {
|
||||||
|
try {
|
||||||
search_api_get_datasource_controller($type)->trackItemDelete($item_ids, $indexes);
|
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
|
// Then, delete it from all servers. Servers of disabled indexes have to be
|
||||||
// considered, too!
|
// considered, too!
|
||||||
unset($conditions['enabled']);
|
unset($conditions['enabled']);
|
||||||
foreach (search_api_index_load_multiple(FALSE, $conditions) as $index) {
|
foreach (search_api_index_load_multiple(FALSE, $conditions) as $index) {
|
||||||
if ($index->server) {
|
try {
|
||||||
$server = $index->server();
|
if ($server = $index->server()) {
|
||||||
$server->deleteItems($item_ids, $index);
|
$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
|
// Use a more accurate method of determining if the fields settings are
|
||||||
// equal to avoid needlessly re-indexing the whole index.
|
// 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 = $index->options;
|
||||||
$options['fields'] = $fields;
|
$options['fields'] = $fields;
|
||||||
$index->update(array('options' => $options));
|
$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.
|
* 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
|
* @param mixed $setting1
|
||||||
* The first setting (array).
|
* The first setting (array).
|
||||||
* @param mixed $setting2
|
* @param mixed $setting2
|
||||||
@ -1410,6 +1438,8 @@ function search_api_index_recalculate_fields($indexes = FALSE) {
|
|||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
* TRUE if both settings are identical, FALSE otherwise.
|
* TRUE if both settings are identical, FALSE otherwise.
|
||||||
|
*
|
||||||
|
* @deprecated The simple "==" operator will achieve the same.
|
||||||
*/
|
*/
|
||||||
function _search_api_settings_equals($setting1, $setting2) {
|
function _search_api_settings_equals($setting1, $setting2) {
|
||||||
if (!is_array($setting1) || !is_array($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.
|
// some specific setups.
|
||||||
$type = search_api_get_item_type_info($index->item_type);
|
$type = search_api_get_item_type_info($index->item_type);
|
||||||
$type = $type ? $type['name'] : $index->item_type;
|
$type = $type ? $type['name'] : $index->item_type;
|
||||||
watchdog('search_api',
|
watchdog('search_api', "Error during indexing: invalid item loaded for @type with ID @id.", array('@id' => $id, '@type' => $type), WATCHDOG_WARNING);
|
||||||
"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();
|
$indexed = $items ? $index->index($cloned_items) : array();
|
||||||
@ -1578,25 +1605,14 @@ function search_api_get_items_to_index(SearchApiIndex $index, $limit = -1) {
|
|||||||
* @param $id
|
* @param $id
|
||||||
* The ID or machine name of the index to execute the search on.
|
* The ID or machine name of the index to execute the search on.
|
||||||
* @param $options
|
* @param $options
|
||||||
* An associative array of options. The following are recognized:
|
* An associative array of options to be passed to
|
||||||
* - filters: Either a SearchApiQueryFilterInterface object or an array of
|
* SearchApiQueryInterface::__construct().
|
||||||
* filters used to filter the search.
|
|
||||||
* - sort: An array of sort directives of the form $field => $order, where
|
|
||||||
* $order is either 'ASC' or 'DESC'.
|
|
||||||
* - offset: The position of the first returned search results relative to the
|
|
||||||
* whole result in the index.
|
|
||||||
* - limit: The maximum number of search results to return. -1 means no limit.
|
|
||||||
* - 'query class': The query class to use. Must be a subtype of
|
|
||||||
* SearchApiQueryInterface.
|
|
||||||
* - conjunction: The type of conjunction to use for this query - either
|
|
||||||
* 'AND' or 'OR'. 'AND' by default.
|
|
||||||
* - 'parse mode': The mode with which to parse the $keys variable, if it
|
|
||||||
* is set and not already an array. See SearchApiQuery::parseModes() for
|
|
||||||
* parse modes recognized by the SearchApiQuery class.
|
|
||||||
* Subclasses might define additional modes.
|
|
||||||
*
|
*
|
||||||
* @return SearchApiQueryInterface
|
* @return SearchApiQueryInterface
|
||||||
* An object for searching on the specified index.
|
* 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()) {
|
function search_api_query($id, array $options = array()) {
|
||||||
$index = search_api_index_load($id);
|
$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
|
* Stores or retrieves a search executed in this page request.
|
||||||
* used to store an executed search, or to retrieve a previously stored
|
*
|
||||||
* search.
|
* 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
|
* @param $search_id
|
||||||
* For pages displaying multiple searches, an optional ID identifying the
|
* For pages displaying multiple searches, an optional ID identifying the
|
||||||
@ -1935,7 +1952,14 @@ function search_api_search_api_query_alter(SearchApiQueryInterface $query) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
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);
|
$query->filter($filter);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// /!\ in previous patches i commented the next line, why ? maybe will have to do it again
|
|
||||||
$query->condition('status', $published);
|
$query->condition('status', $published);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2561,13 +2584,19 @@ function search_api_index_url(SearchApiIndex $index) {
|
|||||||
* @param SearchApiIndex $index
|
* @param SearchApiIndex $index
|
||||||
* The index whose server should be returned.
|
* 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
|
* The server this index currently resides on, or NULL if the index is
|
||||||
* currently unassigned.
|
* currently unassigned.
|
||||||
*/
|
*/
|
||||||
function search_api_index_get_server(SearchApiIndex $index) {
|
function search_api_index_get_server(SearchApiIndex $index) {
|
||||||
|
try {
|
||||||
return $index->server();
|
return $index->server();
|
||||||
}
|
}
|
||||||
|
catch (SearchApiException $e) {
|
||||||
|
watchdog_exception('search_api', $e);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an options list for the "status" property.
|
* Returns an options list for the "status" property.
|
||||||
@ -2643,14 +2672,14 @@ function search_api_index_edit_fields($id, array $fields) {
|
|||||||
/**
|
/**
|
||||||
* Enables a search index.
|
* Enables a search index.
|
||||||
*
|
*
|
||||||
* @param $id
|
* @param string|int $id
|
||||||
* The ID or machine name of the index to enable.
|
* The ID or machine name of the index to enable.
|
||||||
*
|
*
|
||||||
* @throws SearchApiException
|
* @return int|false
|
||||||
* If the index' server isn't enabled.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* 1 on success, 0 or FALSE on failure.
|
* 1 on success, 0 or FALSE on failure.
|
||||||
|
*
|
||||||
|
* @throws SearchApiException
|
||||||
|
* If the index's server doesn't exist.
|
||||||
*/
|
*/
|
||||||
function search_api_index_enable($id) {
|
function search_api_index_enable($id) {
|
||||||
$index = search_api_index_load($id, TRUE);
|
$index = search_api_index_load($id, TRUE);
|
||||||
@ -2661,11 +2690,14 @@ function search_api_index_enable($id) {
|
|||||||
/**
|
/**
|
||||||
* Disables a search index.
|
* Disables a search index.
|
||||||
*
|
*
|
||||||
* @param $id
|
* @param string|int $id
|
||||||
* The ID or machine name of the index to disable.
|
* The ID or machine name of the index to disable.
|
||||||
*
|
*
|
||||||
* @return
|
* @return int|false
|
||||||
* 1 on success, 0 or FALSE on failure.
|
* 1 on success, 0 or FALSE on failure.
|
||||||
|
*
|
||||||
|
* @throws SearchApiException
|
||||||
|
* If the index's server doesn't exist.
|
||||||
*/
|
*/
|
||||||
function search_api_index_disable($id) {
|
function search_api_index_disable($id) {
|
||||||
$index = search_api_index_load($id, TRUE);
|
$index = search_api_index_load($id, TRUE);
|
||||||
@ -2813,6 +2845,9 @@ function _search_api_convert_custom_type($callback, $value, $original_type, $typ
|
|||||||
* @return int
|
* @return int
|
||||||
* The number of items found on the server for this index, if the latter is
|
* The number of items found on the server for this index, if the latter is
|
||||||
* enabled. 0 otherwise.
|
* enabled. 0 otherwise.
|
||||||
|
*
|
||||||
|
* @throws SearchApiException
|
||||||
|
* If an error prevented the search from completing.
|
||||||
*/
|
*/
|
||||||
function _search_api_get_items_on_server(SearchApiIndex $index) {
|
function _search_api_get_items_on_server(SearchApiIndex $index) {
|
||||||
if (!$index->enabled) {
|
if (!$index->enabled) {
|
||||||
|
@ -52,6 +52,9 @@ function _search_api_rules_access() {
|
|||||||
* Rules action for indexing an item.
|
* Rules action for indexing an item.
|
||||||
*/
|
*/
|
||||||
function _search_api_rules_action_index(EntityDrupalWrapper $wrapper, SearchApiIndex $index = NULL, $index_immediately = TRUE) {
|
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();
|
$type = $wrapper->type();
|
||||||
$item_ids = array($wrapper->getIdentifier());
|
$item_ids = array($wrapper->getIdentifier());
|
||||||
|
|
||||||
@ -61,6 +64,7 @@ function _search_api_rules_action_index(EntityDrupalWrapper $wrapper, SearchApiI
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($index) {
|
if ($index) {
|
||||||
|
$type = $index->item_type;
|
||||||
$indexes = array($index);
|
$indexes = array($index);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -10,9 +10,9 @@ files[] = search_api_test.module
|
|||||||
|
|
||||||
hidden = TRUE
|
hidden = TRUE
|
||||||
|
|
||||||
; Information added by Drupal.org packaging script on 2013-12-25
|
; Information added by Drupal.org packaging script on 2014-12-26
|
||||||
version = "7.x-1.11"
|
version = "7.x-1.14"
|
||||||
core = "7.x"
|
core = "7.x"
|
||||||
project = "search_api"
|
project = "search_api"
|
||||||
datestamp = "1387965506"
|
datestamp = "1419580682"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user