PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-beta.2
Jetpack – WP Security, Backup, Speed, & Growth v16.1-beta.2
16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / jetpack-search / src / wpes / class-query-builder.php
jetpack / jetpack_vendor / automattic / jetpack-search / src / wpes Last commit date
class-query-builder.php 9 months ago class-query-parser.php 9 months ago
class-query-builder.php
438 lines
1 <?php
2 /**
3 * Provides an interface for easily building a complex search query that
4 * combines multiple ranking signals.
5 *
6 * $bldr = new Query_Builder();
7 * $bldr->add_filter( ... );
8 * $bldr->add_filter( ... );
9 * $bldr->add_query( ... );
10 * $es_query = $bldr->build_query();
11 *
12 *
13 * All ES queries take a standard form with main query (with some filters),
14 * wrapped in a function_score
15 *
16 * Most functions are chainable, e.g. $bldr->add_filter( ... )->add_query( ... )->build_query();
17 *
18 * Bucketed queries use an aggregation to diversify results. eg a bunch
19 * of separate filters where to get different sets of results.
20 *
21 * @package automattic/jetpack-search
22 */
23
24 // Disables comment checks.
25 // phpcs:disable Squiz.Commenting
26
27 namespace Automattic\Jetpack\Search\WPES;
28
29 /**
30 * Query builder class.
31 */
32 class Query_Builder {
33 /**
34 * ElasticSerach filters.
35 *
36 * @var array
37 */
38 protected $es_filters = array();
39
40 //
41 // Variables for handling custom boosting with function_score.
42 //
43 protected $functions = array();
44 protected $weighting_functions = array();
45 protected $decays = array();
46 protected $scripts = array();
47 protected $functions_max_boost = 2.0;
48 protected $functions_score_mode = 'multiply';
49 protected $functions_boost_mode = 'multiply';
50 protected $query_bool_boost = null;
51
52 //
53 // General aggregations for buckets and metrics.
54 //
55 protected $aggs_query = false;
56 protected $aggs = array();
57
58 //
59 // The set of top level text queries to combine.
60 //
61 protected $must_queries = array();
62 protected $should_queries = array();
63 protected $dis_max_queries = array();
64
65 protected $diverse_buckets_query = false;
66 protected $bucket_filters = array();
67 protected $bucket_sub_aggs = array();
68
69 /**
70 * Language codes.
71 *
72 * @var array
73 */
74 private $langs;
75
76 public function get_langs() {
77 if ( isset( $this->langs ) ) {
78 return $this->langs;
79 }
80 return false;
81 }
82
83 //
84 // Methods for building a query.
85 //
86 public function add_filter( $filter ) {
87 $this->es_filters[] = $filter;
88
89 return $this;
90 }
91
92 public function add_query( $query, $type = 'must' ) {
93 switch ( $type ) {
94 case 'dis_max':
95 $this->dis_max_queries[] = $query;
96 break;
97
98 case 'should':
99 $this->should_queries[] = $query;
100 break;
101
102 case 'must':
103 default:
104 $this->must_queries[] = $query;
105 break;
106 }
107
108 return $this;
109 }
110
111 /**
112 * Add any weighting function to the query
113 *
114 * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html
115 *
116 * @param $function array A function structure to apply to the query
117 *
118 * @return void
119 */
120 public function add_weighting_function( $function ) {
121 // check for danger.
122 if ( isset( $function['random_score'] ) ) {
123 return $this;
124 }
125 if ( isset( $function['script_score'] ) ) {
126 return $this;
127 }
128
129 $this->weighting_functions[] = $function;
130
131 return $this;
132 }
133
134 /**
135 * Add a scoring function to the query
136 *
137 * NOTE: For decays (linear, exp, or gauss), use Query_Builder::add_decay() instead
138 *
139 * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html
140 *
141 * @param $function string name of the function
142 * @param $params array functions parameters
143 *
144 * @return void
145 */
146 public function add_function( $function, $params ) {
147 $this->functions[ $function ][] = $params;
148
149 return $this;
150 }
151
152 /**
153 * Add a decay function to score results
154 *
155 * This method should be used instead of Query_Builder::add_function() for decays, as the internal ES structure
156 * is slightly different for them.
157 *
158 * @see https://www.elastic.co/guide/en/elasticsearch/guide/current/decay-functions.html
159 *
160 * @param $function string name of the decay function - linear, exp, or gauss
161 * @param $params array The decay functions parameters, passed to ES directly
162 *
163 * @return void
164 */
165 public function add_decay( $function, $params ) {
166 $this->decays[ $function ][] = $params;
167
168 return $this;
169 }
170
171 /**
172 * Add a scoring mode to the query
173 *
174 * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html
175 *
176 * @param $mode string name of how to score
177 *
178 * @return void
179 */
180 public function add_score_mode_to_functions( $mode = 'multiply' ) {
181 $this->functions_score_mode = $mode;
182
183 return $this;
184 }
185
186 public function add_boost_mode_to_functions( $mode = 'multiply' ) {
187 $this->functions_boost_mode = $mode;
188
189 return $this;
190 }
191
192 public function add_max_boost_to_functions( $boost ) {
193 $this->functions_max_boost = $boost;
194
195 return $this;
196 }
197
198 public function add_boost_to_query_bool( $boost ) {
199 $this->query_bool_boost = $boost;
200
201 return $this;
202 }
203
204 public function add_aggs( $aggs_name, $aggs ) {
205 $this->aggs_query = true;
206 $this->aggs[ $aggs_name ] = $aggs;
207
208 return $this;
209 }
210
211 public function set_all_aggs( $aggs ) {
212 $this->aggs_query = true;
213 $this->aggs = $aggs;
214
215 return $this;
216 }
217
218 public function add_aggs_sub_aggs( $aggs_name, $sub_aggs ) {
219 if ( ! array_key_exists( 'aggs', $this->aggs[ $aggs_name ] ) ) {
220 $this->aggs[ $aggs_name ]['aggs'] = array();
221 }
222 $this->aggs[ $aggs_name ]['aggs'] = $sub_aggs;
223
224 return $this;
225 }
226
227 public function add_bucketed_query( $name, $query ) {
228 $this->add_bucket_filter( $name, $query );
229
230 $this->add_query( $query, 'dis_max' );
231
232 return $this;
233 }
234
235 public function add_bucketed_terms( $name, $field, $terms, $boost = 1 ) {
236 if ( ! is_array( $terms ) ) {
237 $terms = array( $terms );
238 }
239
240 $this->add_bucket_filter(
241 $name,
242 array(
243 'terms' => array(
244 $field => $terms,
245 ),
246 )
247 );
248
249 $this->add_query(
250 array(
251 'constant_score' => array(
252 'filter' => array(
253 'terms' => array(
254 $field => $terms,
255 ),
256 ),
257 'boost' => $boost,
258 ),
259 ),
260 'dis_max'
261 );
262
263 return $this;
264 }
265
266 public function add_bucket_sub_aggs( $agg ) {
267 $this->bucket_sub_aggs = array_merge( $this->bucket_sub_aggs, $agg );
268
269 return $this;
270 }
271
272 protected function add_bucket_filter( $name, $filter ) {
273 $this->diverse_buckets_query = true;
274 $this->bucket_filters[ $name ] = $filter;
275 }
276
277 ////////////////////////////////////
278 // Building Final Query
279
280 /**
281 * Combine all the queries, functions, decays, scripts, and max_boost into an ES query
282 *
283 * @return array Array representation of the built ES query
284 */
285 public function build_query() {
286 $query = array();
287
288 //dis_max queries just become a single must query
289 if ( ! empty( $this->dis_max_queries ) ) {
290 $this->must_queries[] = array(
291 'dis_max' => array(
292 'queries' => $this->dis_max_queries,
293 ),
294 );
295 }
296
297 if ( empty( $this->must_queries ) ) {
298 $this->must_queries = array(
299 array(
300 'match_all' => array(),
301 ),
302 );
303 }
304
305 if ( empty( $this->should_queries ) ) {
306 $query = array(
307 'bool' => array(
308 'must' => $this->must_queries,
309 ),
310 );
311 } else {
312 $query = array(
313 'bool' => array(
314 'must' => $this->must_queries,
315 'should' => $this->should_queries,
316 ),
317 );
318 }
319
320 if ( $this->query_bool_boost !== null ) {
321 $query['bool']['boost'] = $this->query_bool_boost;
322 }
323
324 // If there are any function score adjustments, then combine those
325 if ( $this->functions || $this->decays || $this->scripts || $this->weighting_functions ) {
326 $weighting_functions = $this->weighting_functions;
327
328 if ( $this->functions ) {
329 foreach ( $this->functions as $function_type => $configs ) {
330 foreach ( $configs as $config ) {
331 foreach ( $config as $field => $params ) {
332 $func_arr = $params;
333
334 $func_arr['field'] = $field;
335
336 $weighting_functions[] = array(
337 $function_type => $func_arr,
338 );
339 }
340 }
341 }
342 }
343
344 if ( $this->decays ) {
345 foreach ( $this->decays as $decay_type => $configs ) {
346 foreach ( $configs as $config ) {
347 foreach ( $config as $field => $params ) {
348 $weighting_functions[] = array(
349 $decay_type => array(
350 $field => $params,
351 ),
352 );
353 }
354 }
355 }
356 }
357
358 if ( $this->scripts ) {
359 foreach ( $this->scripts as $script ) {
360 $weighting_functions[] = array(
361 'script_score' => array(
362 'script' => $script,
363 ),
364 );
365 }
366 }
367
368 $query = array(
369 'function_score' => array(
370 'query' => $query,
371 'functions' => $weighting_functions,
372 'max_boost' => $this->functions_max_boost,
373 'score_mode' => $this->functions_score_mode,
374 'boost_mode' => $this->functions_boost_mode,
375 ),
376 );
377 }
378
379 return $query;
380 }
381
382 /**
383 * Assemble the 'filter' portion of an ES query, from all registered filters
384 *
385 * @return array|null Combined ES filters, or null if none have been defined
386 */
387 public function build_filter() {
388 if ( empty( $this->es_filters ) ) {
389 $filter = null;
390 } elseif ( 1 === count( $this->es_filters ) ) {
391 $filter = $this->es_filters[0];
392 } else {
393 $filter = array(
394 'and' => $this->es_filters,
395 );
396 }
397
398 return $filter;
399 }
400
401 /**
402 * Assemble the 'aggregation' portion of an ES query, from all general aggregations.
403 *
404 * @return array An aggregation query as an array of topics, filters, and bucket names
405 */
406 public function build_aggregation() {
407 if ( empty( $this->bucket_sub_aggs ) && empty( $this->aggs_query ) ) {
408 return array();
409 }
410
411 if ( ! $this->diverse_buckets_query && empty( $this->aggs_query ) ) {
412 return $this->bucket_sub_aggs;
413 }
414
415 $aggregations = array(
416 'topics' => array(
417 'filters' => array(
418 'filters' => array(),
419 ),
420 ),
421 );
422
423 if ( ! empty( $this->bucket_sub_aggs ) ) {
424 $aggregations['topics']['aggs'] = $this->bucket_sub_aggs;
425 }
426
427 foreach ( $this->bucket_filters as $bucket_name => $filter ) {
428 $aggregations['topics']['filters']['filters'][ $bucket_name ] = $filter;
429 }
430
431 if ( ! empty( $this->aggs_query ) ) {
432 $aggregations = $this->aggs;
433 }
434
435 return $aggregations;
436 }
437 }
438