query_type_term.inc 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. <?php
  2. /**
  3. * @file
  4. * Term query type plugin for the Apache Solr adapter.
  5. */
  6. /**
  7. * Plugin for "term" query types.
  8. */
  9. class SearchApiFacetapiTerm extends FacetapiQueryType implements FacetapiQueryTypeInterface {
  10. /**
  11. * Returns the query type associated with the plugin.
  12. *
  13. * @return string
  14. * The query type.
  15. */
  16. static public function getType() {
  17. return 'term';
  18. }
  19. /**
  20. * Adds the filter to the query object.
  21. *
  22. * @param SearchApiQueryInterface $query
  23. * An object containing the query in the backend's native API.
  24. */
  25. public function execute($query) {
  26. // Return terms for this facet.
  27. $this->adapter->addFacet($this->facet, $query);
  28. $settings = $this->getSettings()->settings;
  29. // First check if the facet is enabled for this search.
  30. $default_true = isset($settings['default_true']) ? $settings['default_true'] : TRUE;
  31. $facet_search_ids = isset($settings['facet_search_ids']) ? $settings['facet_search_ids'] : array();
  32. if ($default_true != empty($facet_search_ids[$query->getOption('search id')])) {
  33. // Facet is not enabled for this search ID.
  34. return;
  35. }
  36. // Retrieve the active facet filters.
  37. $active = $this->adapter->getActiveItems($this->facet);
  38. if (empty($active)) {
  39. return;
  40. }
  41. // Create the facet filter, and add a tag to it so that it can be easily
  42. // identified down the line by services when they need to exclude facets.
  43. $operator = $settings['operator'];
  44. if ($operator == FACETAPI_OPERATOR_AND) {
  45. $conjunction = 'AND';
  46. }
  47. elseif ($operator == FACETAPI_OPERATOR_OR) {
  48. $conjunction = 'OR';
  49. }
  50. else {
  51. $vars = array(
  52. '%operator' => $operator,
  53. '%facet' => !empty($this->facet['label']) ? $this->facet['label'] : $this->facet['name'],
  54. );
  55. watchdog('search_api_facetapi', 'Unknown facet operator %operator used for facet %facet.', $vars, WATCHDOG_WARNING);
  56. return;
  57. }
  58. $tags = array('facet:' . $this->facet['field']);
  59. $facet_filter = $query->createFilter($conjunction, $tags);
  60. foreach ($active as $filter => $filter_array) {
  61. $field = $this->facet['field'];
  62. $this->addFacetFilter($facet_filter, $field, $filter);
  63. }
  64. // Now add the filter to the query.
  65. $query->filter($facet_filter);
  66. }
  67. /**
  68. * Helper method for setting a facet filter on a query or query filter object.
  69. */
  70. protected function addFacetFilter($query_filter, $field, $filter) {
  71. // Test if this filter should be negated.
  72. $settings = $this->adapter->getFacet($this->facet)->getSettings();
  73. $exclude = !empty($settings->settings['exclude']);
  74. // Integer (or other non-string) filters might mess up some of the following
  75. // comparison expressions.
  76. $filter = (string) $filter;
  77. if ($filter == '!') {
  78. $query_filter->condition($field, NULL, $exclude ? '<>' : '=');
  79. }
  80. elseif ($filter[0] == '[' && $filter[strlen($filter) - 1] == ']' && ($pos = strpos($filter, ' TO '))) {
  81. $lower = trim(substr($filter, 1, $pos));
  82. $upper = trim(substr($filter, $pos + 4, -1));
  83. if ($lower == '*' && $upper == '*') {
  84. $query_filter->condition($field, NULL, $exclude ? '=' : '<>');
  85. }
  86. elseif (!$exclude) {
  87. if ($lower != '*') {
  88. // Iff we have a range with two finite boundaries, we set two
  89. // conditions (larger than the lower bound and less than the upper
  90. // bound) and therefore have to make sure that we have an AND
  91. // conjunction for those.
  92. if ($upper != '*' && !($query_filter instanceof SearchApiQueryInterface || $query_filter->getConjunction() === 'AND')) {
  93. $original_query_filter = $query_filter;
  94. $query_filter = new SearchApiQueryFilter('AND');
  95. }
  96. $query_filter->condition($field, $lower, '>=');
  97. }
  98. if ($upper != '*') {
  99. $query_filter->condition($field, $upper, '<=');
  100. }
  101. }
  102. else {
  103. // Same as above, but with inverted logic.
  104. if ($lower != '*') {
  105. if ($upper != '*' && ($query_filter instanceof SearchApiQueryInterface || $query_filter->getConjunction() === 'AND')) {
  106. $original_query_filter = $query_filter;
  107. $query_filter = new SearchApiQueryFilter('OR');
  108. }
  109. $query_filter->condition($field, $lower, '<');
  110. }
  111. if ($upper != '*') {
  112. $query_filter->condition($field, $upper, '>');
  113. }
  114. }
  115. }
  116. else {
  117. $query_filter->condition($field, $filter, $exclude ? '<>' : '=');
  118. }
  119. if (isset($original_query_filter)) {
  120. $original_query_filter->filter($query_filter);
  121. }
  122. }
  123. /**
  124. * Initializes the facet's build array.
  125. *
  126. * @return array
  127. * The initialized render array.
  128. */
  129. public function build() {
  130. $facet = $this->adapter->getFacet($this->facet);
  131. // The current search per facet is stored in a static variable (during
  132. // initActiveFilters) so that we can retrieve it here and get the correct
  133. // current search for this facet.
  134. $search_ids = drupal_static('search_api_facetapi_active_facets', array());
  135. if (empty($search_ids[$facet['name']]) || !search_api_current_search($search_ids[$facet['name']])) {
  136. return array();
  137. }
  138. $search_id = $search_ids[$facet['name']];
  139. list(, $results) = search_api_current_search($search_id);
  140. $build = array();
  141. // Always include the active facet items.
  142. foreach ($this->adapter->getActiveItems($this->facet) as $filter) {
  143. $build[$filter['value']]['#count'] = $results['result count'];
  144. }
  145. // Then, add the facets returned by the server.
  146. if (isset($results['search_api_facets']) && isset($results['search_api_facets'][$this->facet['name']])) {
  147. $values = $results['search_api_facets'][$this->facet['name']];
  148. foreach ($values as $value) {
  149. $filter = $value['filter'];
  150. // As Facet API isn't really suited for our native facet filter
  151. // representations, convert the format here. (The missing facet can
  152. // stay the same.)
  153. if ($filter[0] == '"') {
  154. $filter = substr($filter, 1, -1);
  155. }
  156. elseif ($filter != '!') {
  157. // This is a range filter.
  158. $filter = substr($filter, 1, -1);
  159. $pos = strpos($filter, ' ');
  160. if ($pos !== FALSE) {
  161. $filter = '[' . substr($filter, 0, $pos) . ' TO ' . substr($filter, $pos + 1) . ']';
  162. }
  163. }
  164. $build[$filter] = array(
  165. '#count' => $value['count'],
  166. );
  167. }
  168. }
  169. return $build;
  170. }
  171. }