Automattic /
jetpack
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | |||
| 4 | /** |
||
| 5 | * Provides an interface for easily building a complex search query that |
||
| 6 | * combines multiple ranking signals. |
||
| 7 | * |
||
| 8 | * |
||
| 9 | * $bldr = new Jetpack_WPES_Query_Builder(); |
||
| 10 | * $bldr->add_filter( ... ); |
||
| 11 | * $bldr->add_filter( ... ); |
||
| 12 | * $bldr->add_query( ... ); |
||
| 13 | * $es_query = $bldr->build_query(); |
||
| 14 | * |
||
| 15 | * |
||
| 16 | * All ES queries take a standard form with main query (with some filters), |
||
| 17 | * wrapped in a function_score |
||
| 18 | * |
||
| 19 | * Bucketed queries use an aggregation to diversify results. eg a bunch |
||
| 20 | * of separate filters where to get different sets of results. |
||
| 21 | * |
||
| 22 | */ |
||
| 23 | |||
| 24 | class Jetpack_WPES_Query_Builder { |
||
| 25 | |||
| 26 | public $es_filters = array(); |
||
| 27 | |||
| 28 | // Custom boosting with function_score |
||
| 29 | public $functions = array(); |
||
| 30 | public $decays = array(); |
||
| 31 | public $scripts = array(); |
||
| 32 | public $functions_max_boost = 2.0; |
||
| 33 | public $functions_score_mode = 'multiply'; |
||
| 34 | public $query_bool_boost = null; |
||
| 35 | |||
| 36 | // General aggregations for buckets and metrics |
||
| 37 | public $aggs_query = false; |
||
| 38 | public $aggs = array(); |
||
| 39 | |||
| 40 | // The set of top level text queries to combine |
||
| 41 | public $must_queries = array(); |
||
| 42 | public $should_queries = array(); |
||
| 43 | public $dis_max_queries = array(); |
||
| 44 | |||
| 45 | public $diverse_buckets_query = false; |
||
| 46 | public $bucket_filters = array(); |
||
| 47 | public $bucket_sub_aggs = array(); |
||
| 48 | |||
| 49 | //////////////////////////////////// |
||
| 50 | // Methods for building a query |
||
| 51 | |||
| 52 | public function add_filter( $filter ) { |
||
| 53 | $this->es_filters[] = $filter; |
||
| 54 | } |
||
| 55 | |||
| 56 | public function add_query( $query, $type = 'must' ) { |
||
| 57 | switch ( $type ) { |
||
| 58 | case 'dis_max': |
||
| 59 | $this->dis_max_queries[] = $query; |
||
| 60 | break; |
||
| 61 | |||
| 62 | case 'should': |
||
| 63 | $this->should_queries[] = $query; |
||
| 64 | break; |
||
| 65 | |||
| 66 | case 'must': |
||
| 67 | default: |
||
| 68 | $this->must_queries[] = $query; |
||
| 69 | break; |
||
| 70 | } |
||
| 71 | } |
||
| 72 | |||
| 73 | /** |
||
| 74 | * Add a scoring function to the query |
||
| 75 | * |
||
| 76 | * NOTE: For decays (linear, exp, or gauss), use Jetpack_WPES_Query_Builder::add_decay() instead |
||
| 77 | * |
||
| 78 | * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html |
||
| 79 | * |
||
| 80 | * @param $function string name of the function |
||
| 81 | * @param $params array functions parameters |
||
| 82 | * |
||
| 83 | * @return void |
||
| 84 | */ |
||
| 85 | public function add_function( $function, $params ) { |
||
| 86 | $this->functions[ $function ][] = $params; |
||
| 87 | } |
||
| 88 | |||
| 89 | /** |
||
| 90 | * Add a decay function to score results |
||
| 91 | * |
||
| 92 | * This method should be used instead of Jetpack_WPES_Query_Builder::add_function() for decays, as the internal ES structure |
||
| 93 | * is slightly different for them. |
||
| 94 | * |
||
| 95 | * @see https://www.elastic.co/guide/en/elasticsearch/guide/current/decay-functions.html |
||
| 96 | * |
||
| 97 | * @param $function string name of the decay function - linear, exp, or gauss |
||
| 98 | * @param $params array The decay functions parameters, passed to ES directly |
||
| 99 | * |
||
| 100 | * @return void |
||
| 101 | */ |
||
| 102 | public function add_decay( $function, $params ) { |
||
| 103 | $this->decays[ $function ][] = $params; |
||
| 104 | } |
||
| 105 | |||
| 106 | /** |
||
| 107 | * Add a scoring mode to the query |
||
| 108 | * |
||
| 109 | * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html |
||
| 110 | * |
||
| 111 | * @param $mode string name of how to score |
||
| 112 | * |
||
| 113 | * @return void |
||
| 114 | */ |
||
| 115 | public function add_score_mode_to_functions( $mode='multiply' ) { |
||
| 116 | $this->functions_score_mode = $mode; |
||
| 117 | } |
||
| 118 | |||
| 119 | public function add_max_boost_to_functions( $boost ) { |
||
| 120 | $this->functions_max_boost = $boost; |
||
| 121 | } |
||
| 122 | |||
| 123 | public function add_boost_to_query_bool( $boost ) { |
||
| 124 | $this->query_bool_boost = $boost; |
||
| 125 | } |
||
| 126 | |||
| 127 | public function add_aggs( $aggs_name, $aggs ) { |
||
| 128 | $this->aggs_query = true; |
||
| 129 | $this->aggs[$aggs_name] = $aggs; |
||
| 130 | } |
||
| 131 | |||
| 132 | public function add_aggs_sub_aggs( $aggs_name, $sub_aggs ) { |
||
| 133 | if ( ! array_key_exists( 'aggs', $this->aggs[$aggs_name] ) ) { |
||
| 134 | $this->aggs[$aggs_name]['aggs'] = array(); |
||
| 135 | } |
||
| 136 | $this->aggs[$aggs_name]['aggs'] = $sub_aggs; |
||
| 137 | } |
||
| 138 | |||
| 139 | public function add_bucketed_query( $name, $query ) { |
||
| 140 | $this->_add_bucket_filter( $name, $query ); |
||
| 141 | |||
| 142 | $this->add_query( $query, 'dis_max' ); |
||
| 143 | } |
||
| 144 | |||
| 145 | public function add_bucketed_terms( $name, $field, $terms, $boost = 1 ) { |
||
| 146 | if ( ! is_array( $terms ) ) { |
||
| 147 | $terms = array( $terms ); |
||
| 148 | } |
||
| 149 | |||
| 150 | $this->_add_bucket_filter( $name, array( |
||
| 151 | 'terms' => array( |
||
| 152 | $field => $terms, |
||
| 153 | ), |
||
| 154 | )); |
||
| 155 | |||
| 156 | $this->add_query( array( |
||
| 157 | 'constant_score' => array( |
||
| 158 | 'filter' => array( |
||
| 159 | 'terms' => array( |
||
| 160 | $field => $terms, |
||
| 161 | ), |
||
| 162 | ), |
||
| 163 | 'boost' => $boost, |
||
| 164 | ), |
||
| 165 | ), 'dis_max' ); |
||
| 166 | } |
||
| 167 | |||
| 168 | public function add_bucket_sub_aggs( $agg ) { |
||
| 169 | $this->bucket_sub_aggs = array_merge( $this->bucket_sub_aggs, $agg ); |
||
| 170 | } |
||
| 171 | |||
| 172 | public function _add_bucket_filter( $name, $filter ) { |
||
| 173 | $this->diverse_buckets_query = true; |
||
| 174 | $this->bucket_filters[ $name ] = $filter; |
||
| 175 | } |
||
| 176 | |||
| 177 | //////////////////////////////////// |
||
| 178 | // Building Final Query |
||
| 179 | |||
| 180 | /** |
||
| 181 | * Combine all the queries, functions, decays, scripts, and max_boost into an ES query |
||
| 182 | * |
||
| 183 | * @return array Array representation of the built ES query |
||
| 184 | */ |
||
| 185 | public function build_query() { |
||
| 186 | $query = array(); |
||
|
0 ignored issues
–
show
|
|||
| 187 | |||
| 188 | //dis_max queries just become a single must query |
||
| 189 | if ( ! empty( $this->dis_max_queries ) ) { |
||
| 190 | $this->must_queries[] = array( |
||
| 191 | 'dis_max' => array( |
||
| 192 | 'queries' => $this->dis_max_queries, |
||
| 193 | ), |
||
| 194 | ); |
||
| 195 | } |
||
| 196 | |||
| 197 | if ( empty( $this->must_queries ) ) { |
||
| 198 | $this->must_queries = array( |
||
| 199 | array( |
||
| 200 | 'match_all' => array(), |
||
| 201 | ), |
||
| 202 | ); |
||
| 203 | } |
||
| 204 | |||
| 205 | if ( empty( $this->should_queries ) ) { |
||
| 206 | if ( 1 == count( $this->must_queries ) ) { |
||
| 207 | $query = $this->must_queries[0]; |
||
| 208 | } else { |
||
| 209 | $query = array( |
||
| 210 | 'bool' => array( |
||
| 211 | 'must' => $this->must_queries, |
||
| 212 | ), |
||
| 213 | ); |
||
| 214 | } |
||
| 215 | } else { |
||
| 216 | $query = array( |
||
| 217 | 'bool' => array( |
||
| 218 | 'must' => $this->must_queries, |
||
| 219 | 'should' => $this->should_queries, |
||
| 220 | ), |
||
| 221 | ); |
||
| 222 | } |
||
| 223 | |||
| 224 | if ( ! is_null( $this->query_bool_boost ) && isset( $query['bool'] ) ) { |
||
| 225 | $query['bool']['boost'] = $this->query_bool_boost; |
||
| 226 | } |
||
| 227 | |||
| 228 | // If there are any function score adjustments, then combine those |
||
| 229 | if ( $this->functions || $this->decays || $this->scripts ) { |
||
|
0 ignored issues
–
show
The expression
$this->functions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
The expression
$this->decays of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
The expression
$this->scripts of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
|
|||
| 230 | $weighting_functions = array(); |
||
| 231 | |||
| 232 | if ( $this->functions ) { |
||
|
0 ignored issues
–
show
The expression
$this->functions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
|
|||
| 233 | foreach ( $this->functions as $function_type => $configs ) { |
||
| 234 | foreach ( $configs as $config ) { |
||
| 235 | foreach ( $config as $field => $params ) { |
||
| 236 | $func_arr = $params; |
||
| 237 | |||
| 238 | $func_arr['field'] = $field; |
||
| 239 | |||
| 240 | $weighting_functions[] = array( |
||
| 241 | $function_type => $func_arr, |
||
| 242 | ); |
||
| 243 | } |
||
| 244 | } |
||
| 245 | } |
||
| 246 | } |
||
| 247 | |||
| 248 | if ( $this->decays ) { |
||
|
0 ignored issues
–
show
The expression
$this->decays of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
|
|||
| 249 | foreach ( $this->decays as $decay_type => $configs ) { |
||
| 250 | foreach ( $configs as $config ) { |
||
| 251 | foreach ( $config as $field => $params ) { |
||
| 252 | $weighting_functions[] = array( |
||
| 253 | $decay_type => array( |
||
| 254 | $field => $params, |
||
| 255 | ), |
||
| 256 | ); |
||
| 257 | } |
||
| 258 | } |
||
| 259 | } |
||
| 260 | } |
||
| 261 | |||
| 262 | if ( $this->scripts ) { |
||
|
0 ignored issues
–
show
The expression
$this->scripts of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
|
|||
| 263 | foreach ( $this->scripts as $script ) { |
||
| 264 | $weighting_functions[] = array( |
||
| 265 | 'script_score' => array( |
||
| 266 | 'script' => $script, |
||
| 267 | ), |
||
| 268 | ); |
||
| 269 | } |
||
| 270 | } |
||
| 271 | |||
| 272 | $query = array( |
||
| 273 | 'function_score' => array( |
||
| 274 | 'query' => $query, |
||
| 275 | 'functions' => $weighting_functions, |
||
| 276 | 'max_boost' => $this->functions_max_boost, |
||
| 277 | 'score_mode' => $this->functions_score_mode, |
||
| 278 | ), |
||
| 279 | ); |
||
| 280 | } // End if(). |
||
| 281 | |||
| 282 | return $query; |
||
| 283 | } |
||
| 284 | |||
| 285 | /** |
||
| 286 | * Assemble the 'filter' portion of an ES query, from all registered filters |
||
| 287 | * |
||
| 288 | * @return array|null Combiled ES filters, or null if none have been defined |
||
| 289 | */ |
||
| 290 | public function build_filter() { |
||
| 291 | if ( empty( $this->es_filters ) ) { |
||
| 292 | $filter = null; |
||
| 293 | } elseif ( 1 == count( $this->es_filters ) ) { |
||
| 294 | $filter = $this->es_filters[0]; |
||
| 295 | } else { |
||
| 296 | $filter = array( |
||
| 297 | 'and' => $this->es_filters, |
||
| 298 | ); |
||
| 299 | } |
||
| 300 | |||
| 301 | return $filter; |
||
| 302 | } |
||
| 303 | |||
| 304 | /** |
||
| 305 | * Assemble the 'aggregation' portion of an ES query, from all general aggregations. |
||
| 306 | * |
||
| 307 | * @return an aggregation query as an array of topics, filters, and bucket names |
||
| 308 | */ |
||
| 309 | function build_aggregation() { |
||
| 310 | if ( empty( $this->bucket_sub_aggs ) && empty( $this->aggs_query ) ) { |
||
| 311 | return array(); |
||
| 312 | } |
||
| 313 | |||
| 314 | if ( ! $this->diverse_buckets_query && empty( $this->aggs_query ) ) { |
||
| 315 | return $this->bucket_sub_aggs; |
||
| 316 | } |
||
| 317 | |||
| 318 | $aggregations = array( |
||
| 319 | 'topics' => array( |
||
| 320 | 'filters' => array( |
||
| 321 | 'filters' => array(), |
||
| 322 | ), |
||
| 323 | ), |
||
| 324 | ); |
||
| 325 | |||
| 326 | if ( ! empty( $this->bucket_sub_aggs ) ) { |
||
| 327 | $aggregations['topics']['aggs'] = $this->bucket_sub_aggs; |
||
| 328 | } |
||
| 329 | |||
| 330 | foreach ( $this->bucket_filters as $bucket_name => $filter ) { |
||
| 331 | $aggregations['topics']['filters']['filters'][ $bucket_name ] = $filter; |
||
| 332 | } |
||
| 333 | |||
| 334 | if ( ! empty( $this->aggs_query ) ) { |
||
| 335 | $aggregations = $this->aggs; |
||
| 336 | } |
||
| 337 | |||
| 338 | return $aggregations; |
||
| 339 | } |
||
| 340 | |||
| 341 | } |
||
| 342 |
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.
Both the
$myVarassignment in line 1 and the$higherassignment in line 2 are dead. The first because$myVaris never used and the second because$higheris always overwritten for every possible time line.