PluginProbe
ElasticPress / 4.5.2
ElasticPress v4.5.2
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.5.2, at includes/classes/Indexable/Term/QueryIntegration.php

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