PluginProbe
ElasticPress / 4.3.1
ElasticPress v4.3.1
5.3.5 5.3.4 3.6.5 3.6.6 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.7.0 4.7.1 4.7.2 5.0.0 5.0.1 5.0.2 All 108 releases
elasticpress / includes / classes / Indexable / Term / QueryIntegration.php

QueryIntegration.php in ElasticPress 4.3.1, at includes/classes/Indexable/Term/QueryIntegration.php

358 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Integrate with WP_Term_Query
4 *
5 * @since 3.1
6 * @package elasticpress
7 */
8
9 namespace ElasticPress\Indexable\Term;
10
11 use ElasticPress\Indexables as Indexables;
12 use \WP_Term_Query as WP_Term_Query;
13 use ElasticPress\Utils as Utils;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit; // Exit if accessed directly.
17 }
18
19 /**
20 * Query integration class
21 */
22 class QueryIntegration {
23
24 /**
25 * Checks to see if we should be integrating and if so, sets up the appropriate actions and filters.
26 *
27 * @param string $indexable_slug Indexable slug. Optional.
28 *
29 * @since 3.1
30 * @since 3.6.0 Added $indexable_slug
31 */
32 public function __construct( $indexable_slug = 'term' ) {
33 // Ensure that we are currently allowing ElasticPress to override the normal WP_Query
34 // Indexable->is_full_reindexing() is not available at this point yet, so using the IndexHelper version of it.
35 if ( \ElasticPress\IndexHelper::factory()->is_full_reindexing( $indexable_slug, get_current_blog_id() ) ) {
36 return;
37 }
38
39 // Add header
40 add_action( 'pre_get_terms', array( $this, 'action_pre_get_terms' ), 5 );
41
42 // Filter term query
43 add_filter( 'terms_pre_query', [ $this, 'maybe_filter_query' ], 10, 2 );
44 }
45
46 /**
47 * Add EP header
48 *
49 * @param WP_Term_Query $query Query object
50 * @since 3.1
51 * @return void
52 */
53 public function action_pre_get_terms( WP_Term_Query $query ) {
54 if ( ! Indexables::factory()->get( 'term' )->elasticpress_enabled( $query ) || apply_filters( 'ep_skip_term_query_integration', false, $query ) ) {
55 return;
56 }
57
58 if ( ! headers_sent() ) {
59 /**
60 * Manually setting a header as $wp_query isn't yet initialized
61 * when we call: add_filter('wp_headers', 'filter_wp_headers');
62 */
63 header( 'X-ElasticPress-Term-Search: true' );
64 }
65 }
66
67 /**
68 * If WP_Term_Query meets certain conditions, query results from ES
69 *
70 * @param array $results Term results.
71 * @param WP_Term_Query $query Current query.
72 * @since 3.1
73 * @return array
74 */
75 public function maybe_filter_query( $results, WP_Term_Query $query ) {
76 $indexable = Indexables::factory()->get( 'term' );
77
78 if ( ! $indexable->elasticpress_enabled( $query ) || apply_filters( 'ep_skip_term_query_integration', false, $query ) ) {
79 return $results;
80 }
81
82 if ( ! $this->is_searchable( $query ) ) {
83 return $results;
84 }
85
86 $new_terms = apply_filters( 'ep_wp_query_cached_terms', null, $query );
87
88 if ( null === $new_terms ) {
89 $formatted_args = $indexable->format_args( $query->query_vars );
90
91 $scope = 'current';
92 if ( ! empty( $query->query_vars['sites'] ) ) {
93 $scope = $query->query_vars['sites'];
94 }
95
96 /**
97 * Filter search scope
98 *
99 * @since 3.1
100 *
101 * @param mixed $scope The search scope. Accepts `all` (string), a single
102 * site id (int or string), or an array of site ids (array).
103 */
104 $scope = apply_filters( 'ep_term_search_scope', $scope );
105
106 if ( ! defined( 'EP_IS_NETWORK' ) || ! EP_IS_NETWORK ) {
107 $scope = 'current';
108 }
109
110 $index = null;
111
112 if ( 'all' === $scope ) {
113 $index = $indexable->get_network_alias();
114 } elseif ( is_numeric( $scope ) ) {
115 $index = $indexable->get_index_name( (int) $scope );
116 } elseif ( is_array( $scope ) ) {
117 $index = [];
118
119 foreach ( $scope as $site_id ) {
120 $index[] = $indexable->get_index_name( $site_id );
121 }
122
123 $index = implode( ',', $index );
124 }
125
126 $ep_query = $indexable->query_es( $formatted_args, $query->query_vars, $index );
127
128 if ( false === $ep_query ) {
129 $query->elasticsearch_success = false;
130 return $results;
131 }
132
133 /**
134 * Elasticsearch 5 will return found_documents as a number,
135 * ES 7+ will return it as an object with `value`. If any of that is found,
136 * fallback to a simple count of returned documents.
137 *
138 * @since 3.6.3
139 */
140 if ( is_integer( $ep_query['found_documents'] ) ) {
141 $query->found_terms = $ep_query['found_documents'];
142 } elseif ( is_array( $ep_query['found_documents'] ) && isset( $ep_query['found_documents']['value'] ) ) {
143 $query->found_terms = $ep_query['found_documents']['value'];
144 } else {
145 $query->found_terms = count( $ep_query['documents'] );
146 }
147
148 $query->elasticsearch_success = true;
149
150 // Determine how we should format the results from ES based on the fields parameter.
151 $fields = $query->query_vars['fields'];
152
153 $new_terms = [];
154
155 switch ( $fields ) {
156 case 'all_with_object_id':
157 $new_terms = $this->format_hits_as_terms( $ep_query['documents'], $new_terms, $query->query_vars );
158 break;
159
160 case 'count':
161 $new_terms = count( $ep_query['documents'] );
162 break;
163
164 case 'ids':
165 $new_terms = $this->format_hits_as_ids( $ep_query['documents'], $new_terms );
166 break;
167
168 case 'id=>name':
169 $new_terms = $this->format_hits_as_id_name( $ep_query['documents'], $new_terms );
170 break;
171
172 case 'id=>parent':
173 $new_terms = $this->format_hits_as_id_parent( $ep_query['documents'], $new_terms );
174 break;
175
176 case 'id=>slug':
177 $new_terms = $this->format_hits_as_id_slug( $ep_query['documents'], $new_terms );
178 break;
179
180 case 'names':
181 $new_terms = $this->format_hits_as_names( $ep_query['documents'], $new_terms );
182 break;
183
184 case 'tt_ids':
185 $new_terms = $this->format_hits_as_ids( $ep_query['documents'], $new_terms, 'term_taxonomy_id' );
186 break;
187
188 default:
189 $new_terms = $this->format_hits_as_terms( $ep_query['documents'], $new_terms, $query->query_vars );
190 break;
191 }
192
193 if ( ! empty( $query->query_vars['count'] ) ) {
194 $new_terms = count( $ep_query['documents'] );
195 }
196 }
197
198 return $new_terms;
199 }
200
201 /**
202 * Format the ES hits/results as term objects.
203 *
204 * @param array $terms The terms that should be formatted.
205 * @param array $new_terms Array of terms from cache.
206 * @param array $query_vars Query variables.
207 * @since 3.1
208 * @return array
209 */
210 protected function format_hits_as_terms( $terms, $new_terms, $query_vars ) {
211 foreach ( $terms as $term_array ) {
212 $term = new \stdClass();
213
214 $term->ID = $term_array['term_id'];
215 $term->site_id = get_current_blog_id();
216
217 if ( ! empty( $term_array['site_id'] ) ) {
218 $term->site_id = $term_array['site_id'];
219 }
220
221 $term_return_args = apply_filters(
222 'ep_search_term_return_args',
223 array(
224 'term_id',
225 'name',
226 'slug',
227 'term_group',
228 'term_taxonomy_id',
229 'taxonomy',
230 'description',
231 'parent',
232 'count',
233 'meta',
234 )
235 );
236
237 foreach ( $term_return_args as $key ) {
238 if ( isset( $term_array[ $key ] ) ) {
239 if ( 'count' === $key && ! empty( $query_vars['pad_counts'] ) ) {
240 $term_array[ $key ] += $term_array['hierarchy']['children']['count'];
241 }
242
243 $term->$key = $term_array[ $key ];
244 }
245 }
246
247 $term->elasticsearch = true; // Super useful for debugging.
248
249 $term = new \WP_Term( $term ); // Necessary for WordPress actions that expect WP_Term as the object type.
250
251 if ( $term ) {
252 $new_terms[] = $term;
253 }
254 }
255
256 return $new_terms;
257 }
258
259 /**
260 * Format the ES hits/results as an array of ids.
261 *
262 * @param array $terms The terms that should be formatted.
263 * @param array $new_terms Array of terms from cache.
264 * @param string $type Type of ID to return. Default 'term_id'.
265 * @since 3.1
266 * @return array
267 */
268 protected function format_hits_as_ids( $terms, $new_terms, $type = 'term_id' ) {
269 foreach ( $terms as $term_array ) {
270 $new_terms[] = 'term_id' === $type ? $term_array['term_id'] : $term_array['term_taxonomy_id'];
271 }
272
273 return $new_terms;
274 }
275
276 /**
277 * Format the ES hits/results as an array of term ID and term name.
278 *
279 * @param array $terms The terms that should be formatted.
280 * @param array $new_terms Array of terms from cache.
281 * @since 3.1
282 * @return array Returns an associative array with ids as keys, term names as values
283 */
284 protected function format_hits_as_id_name( $terms, $new_terms ) {
285 foreach ( $terms as $term_array ) {
286 $new_terms[ $term_array['term_id'] ] = $term_array['name'];
287 }
288
289 return $new_terms;
290 }
291
292 /**
293 * Format the ES hits/results as an array of term ID and parent ID.
294 *
295 * @param array $terms The terms that should be formatted.
296 * @param array $new_terms Array of terms from cache.
297 * @since 3.1
298 * @return array Returns an associative array with ids as keys, parent term IDs as values
299 */
300 protected function format_hits_as_id_parent( $terms, $new_terms ) {
301 foreach ( $terms as $term_array ) {
302 $new_terms[ $term_array['term_id'] ] = $term_array['parent'];
303 }
304
305 return $new_terms;
306 }
307
308 /**
309 * Format the ES hits/results as an array of term ID and term slug.
310 *
311 * @param array $terms The terms that should be formatted.
312 * @param array $new_terms Array of terms from cache.
313 * @since 3.1
314 * @return array Returns an associative array with ids as keys, term slugs as values
315 */
316 protected function format_hits_as_id_slug( $terms, $new_terms ) {
317 foreach ( $terms as $term_array ) {
318 $new_terms[ $term_array['term_id'] ] = $term_array['slug'];
319 }
320
321 return $new_terms;
322 }
323
324 /**
325 * Format the ES hits/results as an array of term names.
326 *
327 * @param array $terms The terms that should be formatted.
328 * @param array $new_terms Array of terms from cache.
329 * @since 3.1
330 * @return array Returns an array of term names.
331 */
332 protected function format_hits_as_names( $terms, $new_terms ) {
333 foreach ( $terms as $term_array ) {
334 $new_terms[] = $term_array['name'];
335 }
336
337 return $new_terms;
338 }
339
340 /**
341 * Determine whether ES should be used for the query if all taxonomies are indexable.
342 *
343 * @param \WP_Term_Query $query The WP_Term_Query object.
344 * @return boolean
345 */
346 protected function is_searchable( $query ) {
347
348 $taxonomies = $query->query_vars['taxonomy'];
349 if ( ! $taxonomies ) {
350 return true;
351 }
352
353 $indexable_taxonomies = Indexables::factory()->get( 'term' )->get_indexable_taxonomies();
354 return empty( array_diff( $taxonomies, $indexable_taxonomies ) );
355 }
356
357 }
358