diff --git a/elasticpress.php b/elasticpress.php index 95f796c233..07c5d11563 100644 --- a/elasticpress.php +++ b/elasticpress.php @@ -157,6 +157,13 @@ function register_indexable_posts() { new Feature\Terms\Terms() ); } + + /** + * Register search algorithms + */ + SearchAlgorithms::factory()->register( new SearchAlgorithm\DefaultAlgorithm() ); + SearchAlgorithms::factory()->register( new SearchAlgorithm\Version_350() ); + SearchAlgorithms::factory()->register( new SearchAlgorithm\Version_400() ); } add_action( 'plugins_loaded', __NAMESPACE__ . '\register_indexable_posts' ); diff --git a/includes/classes/Feature/Autosuggest/Autosuggest.php b/includes/classes/Feature/Autosuggest/Autosuggest.php index f209c7ae35..0c0bf4ae7c 100644 --- a/includes/classes/Feature/Autosuggest/Autosuggest.php +++ b/includes/classes/Feature/Autosuggest/Autosuggest.php @@ -79,7 +79,7 @@ public function setup() { add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); add_filter( 'ep_post_mapping', [ $this, 'mapping' ] ); add_filter( 'ep_post_sync_args', [ $this, 'filter_term_suggest' ], 10 ); - add_filter( 'ep_fuzziness_arg', [ $this, 'set_fuzziness' ], 10, 3 ); + add_filter( 'ep_post_fuzziness_arg', [ $this, 'set_fuzziness' ], 10, 3 ); add_filter( 'ep_weighted_query_for_post_type', [ $this, 'adjust_fuzzy_fields' ], 10, 3 ); add_filter( 'ep_saved_weighting_configuration', [ $this, 'epio_send_autosuggest_public_request' ] ); add_filter( 'wp', [ $this, 'epio_send_autosuggest_allowed' ] ); diff --git a/includes/classes/Indexable.php b/includes/classes/Indexable.php index 00a620654f..05f27090f2 100644 --- a/includes/classes/Indexable.php +++ b/includes/classes/Indexable.php @@ -1172,4 +1172,30 @@ public function generate_mapping() { return []; } + + /** + * Get the search algorithm that should be used. + * + * @since 4.3.0 + * @param string $search_text Search term(s) + * @param array $search_fields Search fields + * @param array $query_vars Query vars + * @return SearchAlgorithm Instance of search algorithm to be used + */ + public function get_search_algorithm( string $search_text, array $search_fields, array $query_vars ) : \ElasticPress\SearchAlgorithm { + /** + * Filter the search algorithm to be used + * + * @hook ep_{$indexable_slug}_search_algorithm + * @since 4.3.0 + * @param {string} $search_algorithm Slug of the search algorithm used as fallback + * @param {string} $search_term Search term + * @param {array} $search_fields Fields to be searched + * @param {array} $query_vars Query variables + * @return {string} New search algorithm slug + */ + $search_algorithm = apply_filters( "ep_{$this->slug}_search_algorithm", 'basic', $search_text, $search_fields, $query_vars ); + + return \ElasticPress\SearchAlgorithms::factory()->get( $search_algorithm ); + } } diff --git a/includes/classes/Indexable/Comment/Comment.php b/includes/classes/Indexable/Comment/Comment.php index ea48fa1714..c145924687 100644 --- a/includes/classes/Indexable/Comment/Comment.php +++ b/includes/classes/Indexable/Comment/Comment.php @@ -529,84 +529,8 @@ public function format_args( $query_vars ) { */ $prepared_search_fields = apply_filters( 'ep_comment_search_fields', $prepared_search_fields, $query_vars ); - $query = [ - 'bool' => [ - 'should' => [ - [ - 'multi_match' => [ - 'query' => $search, - 'type' => 'phrase', - 'fields' => $prepared_search_fields, - /** - * Filter boost for comment match phrase query - * - * @hook ep_comment_match_phrase_boost - * @since 3.6.0 - * @param {int} $boost Phrase boost - * @param {array} $prepared_search_fields Search fields - * @param {array} $query_vars Query variables - * @return {int} New phrase boost - */ - 'boost' => apply_filters( 'ep_comment_match_phrase_boost', 4, $prepared_search_fields, $query_vars ), - ], - ], - [ - 'multi_match' => [ - 'query' => $search, - 'fields' => $prepared_search_fields, - /** - * Filter boost for comment match query - * - * @hook ep_comment_match_boost - * @param {int} $boost Boost - * @param {array} $prepared_search_fields Search fields - * @param {array} $query_vars Query variables - * @return {int} New boost - */ - 'boost' => apply_filters( 'ep_comment_match_boost', 2, $prepared_search_fields, $query_vars ), - 'fuzziness' => 0, - 'operator' => 'and', - ], - ], - [ - 'multi_match' => [ - 'fields' => $prepared_search_fields, - 'query' => $search, - /** - * Filter fuzziness for post query - * - * @hook ep_comment_fuzziness_arg - * @since 3.6.0 - * @param {int} $fuzziness Fuzziness - * @param {array} $prepared_search_fields Search fields - * @param {array} $query_vars Query variables - * @return {int} New fuzziness - */ - 'fuzziness' => apply_filters( 'ep_comment_fuzziness_arg', 1, $prepared_search_fields, $query_vars ), - ], - ], - ], - ], - ]; - - /** - * Filter formatted Elasticsearch post query (only contains query part) - * - * @hook ep_comment_formatted_args_query - * @since 3.6.0 - * @param {array} $query Current query - * @param {array} $query_vars Query variables - * @param {string} $search_text Search text - * @param {array} $search_fields Search fields - * @return {array} New query - */ - $formatted_args['query'] = apply_filters( - 'ep_comment_formatted_args_query', - $query, - $query_vars, - $search, - $prepared_search_fields - ); + $search_algorithm = $this->get_search_algorithm( $search, $prepared_search_fields, $query_vars ); + $formatted_args['query'] = $search_algorithm->get_query( 'comment', $search, $prepared_search_fields, $query_vars ); } else { $formatted_args['query']['match_all'] = [ 'boost' => 1, diff --git a/includes/classes/Indexable/Post/Post.php b/includes/classes/Indexable/Post/Post.php index d2b09cee93..cfa790127e 100644 --- a/includes/classes/Indexable/Post/Post.php +++ b/includes/classes/Indexable/Post/Post.php @@ -1427,190 +1427,8 @@ public function format_args( $args, $wp_query ) { */ $search_fields = apply_filters( 'ep_search_fields', $search_fields, $args ); - $default_algorithm_version = '4.0'; - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) { - $search_algorithm_version_option = get_site_option( 'ep_search_algorithm_version', $default_algorithm_version ); - } else { - $search_algorithm_version_option = get_option( 'ep_search_algorithm_version', $default_algorithm_version ); - } - - /** - * Filter the algorithm version to be used. - * - * @since 3.5 - * @hook ep_search_algorithm_version - * @param {string} $search_algorithm_version Algorithm version. - * @return {string} New algorithm version - */ - $search_algorithm_version = apply_filters( 'ep_search_algorithm_version', $search_algorithm_version_option ); - $search_text = ( ! empty( $args['s'] ) ) ? $args['s'] : ''; - if ( '4.0' === $search_algorithm_version ) { - $query = array( - 'bool' => array( - 'should' => array( - array( - 'multi_match' => array( - 'query' => $search_text, - 'type' => 'phrase', - 'fields' => $search_fields, - /** - * Filter boost for post match phrase query - * - * @hook ep_match_phrase_boost - * @param {int} $boost Phrase boost - * @param {array} $prepared_search_fields Search fields - * @param {array} $query_vars Query variables - * @return {int} New phrase boost - */ - 'boost' => apply_filters( 'ep_match_phrase_boost', 3, $search_fields, $args ), - ), - ), - array( - 'multi_match' => array( - 'query' => $search_text, - 'fields' => $search_fields, - /** - * Filter boost for post match query - * - * @hook ep_match_boost - * @param {int} $boost Boost - * @param {array} $prepared_search_fields Search fields - * @param {array} $query_vars Query variables - * @return {int} New boost - */ - 'boost' => apply_filters( 'ep_match_boost', 1, $search_fields, $args ), - /** - * Filter fuzziness for post match query - * - * @hook ep_match_fuzziness - * @since 4.0.0 - * @param {string|int} $fuzziness Fuzziness - * @param {array} $prepared_search_fields Search fields - * @param {array} $query_vars Query variables - * @return {string} New boost - */ - 'fuzziness' => apply_filters( 'ep_match_fuzziness', 'auto', $search_fields, $args ), - 'operator' => 'and', - ), - ), - array( - 'multi_match' => [ - 'query' => $search_text, - 'type' => 'cross_fields', - 'fields' => $search_fields, - /** - * Filter boost for post match query - * - * @hook ep_match_cross_fields_boost - * @since 4.0.0 - * @param {int} $boost Boost - * @param {array} $prepared_search_fields Search fields - * @param {array} $query_vars Query variables - * @return {int} New boost - */ - 'boost' => apply_filters( 'ep_match_cross_fields_boost', 1, $search_fields, $args ), - 'analyzer' => 'standard', - 'tie_breaker' => 0.5, - 'operator' => 'and', - ], - ), - ), - ), - ); - } elseif ( '3.5' === $search_algorithm_version ) { - $query = array( - 'bool' => array( - 'should' => array( - array( - 'multi_match' => array( - 'query' => $search_text, - 'type' => 'phrase', - 'fields' => $search_fields, - /** - * Filter boost for post match phrase query - * - * @hook ep_match_phrase_boost - * @param {int} $boost Phrase boost - * @param {array} $prepared_search_fields Search fields - * @param {array} $query_vars Query variables - * @return {int} New phrase boost - */ - 'boost' => apply_filters( 'ep_match_phrase_boost', 3, $search_fields, $args ), - ), - ), - array( - 'multi_match' => array( - 'query' => $search_text, - 'fields' => $search_fields, - 'type' => 'phrase', - 'slop' => 5, - ), - ), - ), - ), - ); - } else { - $query = array( - 'bool' => array( - 'should' => array( - array( - 'multi_match' => array( - 'query' => $search_text, - 'type' => 'phrase', - 'fields' => $search_fields, - /** - * Filter boost for post match phrase query - * - * @hook ep_match_phrase_boost - * @param {int} $boost Phrase boost - * @param {array} $prepared_search_fields Search fields - * @param {array} $query_vars Query variables - * @return {int} New phrase boost - */ - 'boost' => apply_filters( 'ep_match_phrase_boost', 4, $search_fields, $args ), - ), - ), - array( - 'multi_match' => array( - 'query' => $search_text, - 'fields' => $search_fields, - /** - * Filter boost for post match query - * - * @hook ep_match_boost - * @param {int} $boost Boost - * @param {array} $prepared_search_fields Search fields - * @param {array} $query_vars Query variables - * @return {int} New boost - */ - 'boost' => apply_filters( 'ep_match_boost', 2, $search_fields, $args ), - 'fuzziness' => 0, - 'operator' => 'and', - ), - ), - array( - 'multi_match' => array( - 'query' => $search_text, - 'fields' => $search_fields, - /** - * Filter fuzziness for post query - * - * @hook ep_fuzziness_arg - * @param {int} $fuzziness Fuzziness - * @param {array} $prepared_search_fields Search fields - * @param {array} $query_vars Query variables - * @return {int} New fuzziness - */ - 'fuzziness' => apply_filters( 'ep_fuzziness_arg', 1, $search_fields, $args ), - ), - ), - ), - ), - ); - } - /** * We are using ep_integrate instead of ep_match_all. ep_match_all will be * supported for legacy code but may be deprecated and removed eventually. @@ -1618,28 +1436,11 @@ public function format_args( $args, $wp_query ) { * @since 1.3 */ - if ( ! empty( $args['s'] ) ) { - add_filter( 'ep_formatted_args_query', [ $this, 'adjust_query_fuzziness' ], 100, 4 ); + if ( ! empty( $search_text ) ) { + add_filter( 'ep_post_formatted_args_query', [ $this, 'adjust_query_fuzziness' ], 100, 4 ); - /** - * Filter formatted Elasticsearch post query (only contains query part) - * - * @hook ep_formatted_args_query - * @param {array} $query Current query - * @param {array} $query_vars Query variables - * @param {string} $search_text Search text - * @param {array} $search_fields Search fields - * @return {array} New query - * - * @since 3.5.5 $search_text and $search_fields parameters added. - */ - $formatted_args['query'] = apply_filters( - 'ep_formatted_args_query', - $query, - $args, - $search_text, - $search_fields - ); + $search_algorithm = $this->get_search_algorithm( $search_text, $search_fields, $args ); + $formatted_args['query'] = $search_algorithm->get_query( 'post', $search_text, $search_fields, $args ); } elseif ( ! empty( $args['ep_match_all'] ) || ! empty( $args['ep_integrate'] ) ) { $formatted_args['query']['match_all'] = array( 'boost' => 1, @@ -2296,4 +2097,42 @@ protected function apply_aggregations( $formatted_args, $agg, $use_filters, $fil return $formatted_args; } + + /** + * Get the search algorithm that should be used. + * + * @since 4.3.0 + * @param string $search_text Search term(s) + * @param array $search_fields Search fields + * @param array $query_vars Query vars + * @return SearchAlgorithm Instance of search algorithm to be used + */ + public function get_search_algorithm( string $search_text, array $search_fields, array $query_vars ) : \ElasticPress\SearchAlgorithm { + $search_algorithm_version_option = \ElasticPress\Utils\get_option( 'ep_search_algorithm_version', '4.0' ); + + /** + * Filter the algorithm version to be used. + * + * @since 3.5 + * @hook ep_search_algorithm_version + * @param {string} $search_algorithm_version Algorithm version. + * @return {string} New algorithm version + */ + $search_algorithm = apply_filters( 'ep_search_algorithm_version', $search_algorithm_version_option ); + + /** + * Filter the search algorithm to be used + * + * @hook ep_{$indexable_slug}_search_algorithm + * @since 4.3.0 + * @param {string} $search_algorithm Slug of the search algorithm used as fallback + * @param {string} $search_term Search term + * @param {array} $search_fields Fields to be searched + * @param {array} $query_vars Query variables + * @return {string} New search algorithm slug + */ + $search_algorithm = apply_filters( "ep_{$this->slug}_search_algorithm", $search_algorithm, $search_text, $search_fields, $query_vars ); + + return \ElasticPress\SearchAlgorithms::factory()->get( $search_algorithm ); + } } diff --git a/includes/classes/Indexable/Term/Term.php b/includes/classes/Indexable/Term/Term.php index 66e6f1a1c2..197fbd346a 100644 --- a/includes/classes/Indexable/Term/Term.php +++ b/includes/classes/Indexable/Term/Term.php @@ -354,75 +354,8 @@ public function format_args( $query_vars ) { */ $prepared_search_fields = apply_filters( 'ep_term_search_fields', $prepared_search_fields, $query_vars ); - $query = [ - 'bool' => [ - 'should' => [ - [ - 'multi_match' => [ - 'query' => $search, - 'type' => 'phrase', - 'fields' => $prepared_search_fields, - /** - * Filter term match phrase boost amount - * - * @hook ep_term_match_phrase_boost - * @param {int} $boos Boost amount for match phrase - * @param {array} $query_vars Query variables - * @since 3.4 - * @return {int} New boost amount - */ - 'boost' => apply_filters( 'ep_term_match_phrase_boost', 4, $prepared_search_fields, $query_vars ), - ], - ], - [ - 'multi_match' => [ - 'query' => $search, - 'fields' => $prepared_search_fields, - /** - * Filter term match boost amount - * - * @hook ep_term_match_boost - * @param {int} $boost Boost amount for match - * @param {array} $query_vars Query variables - * @since 3.4 - * @return {int} New boost amount - */ - 'boost' => apply_filters( 'ep_term_match_boost', 2, $prepared_search_fields, $query_vars ), - 'fuzziness' => 0, - 'operator' => 'and', - ], - ], - [ - 'multi_match' => [ - 'fields' => $prepared_search_fields, - 'query' => $search, - /** - * Filter term fuzziness amount - * - * @hook ep_term_fuzziness_arg - * @param {int} $fuzziness Amount of fuziness to factor into search - * @param {array} $query_vars Query variables - * @since 3.4 - * @return {int} New boost amount - */ - 'fuzziness' => apply_filters( 'ep_term_fuzziness_arg', 1, $prepared_search_fields, $query_vars ), - ], - ], - ], - ], - ]; - - /** - * Filter Elasticsearch query used for Terms indexable - * - * @hook ep_term_formatted_args_query - * @param {array} $query Elasticsearch query - * @param {array} $query_vars Query variables - * @since 3.4 - * @return {array} New query - */ - $formatted_args['query'] = apply_filters( 'ep_term_formatted_args_query', $query, $query_vars ); - + $search_algorithm = $this->get_search_algorithm( $search, $prepared_search_fields, $query_vars ); + $formatted_args['query'] = $search_algorithm->get_query( 'term', $search, $prepared_search_fields, $query_vars ); } else { $formatted_args['query']['match_all'] = [ 'boost' => 1, diff --git a/includes/classes/Indexable/User/User.php b/includes/classes/Indexable/User/User.php index 7f9167c415..54d5052ba0 100644 --- a/includes/classes/Indexable/User/User.php +++ b/includes/classes/Indexable/User/User.php @@ -477,78 +477,8 @@ public function format_args( $query_vars, $query ) { */ $prepared_search_fields = apply_filters( 'ep_user_search_fields', $prepared_search_fields, $query_vars ); - $query = array( - 'bool' => array( - 'should' => array( - array( - 'multi_match' => array( - 'query' => $query_vars['search'], - 'type' => 'phrase', - 'fields' => $prepared_search_fields, - /** - * Filter boost for user match phrase query - * - * @hook ep_user_match_phrase_boost - * @param {int} $boost Phrase boost - * @param {array} $prepared_search_fields Search fields - * @param {array} $query_vars Query variables - * @since 3.0 - * @return {int} New phrase boost - */ - 'boost' => apply_filters( 'ep_user_match_phrase_boost', 4, $prepared_search_fields, $query_vars ), - ), - ), - array( - 'multi_match' => array( - 'query' => $query_vars['search'], - 'fields' => $prepared_search_fields, - /** - * Filter boost for user match query - * - * @hook ep_user_match_boost - * @param {int} $boost Boost - * @param {array} $prepared_search_fields Search fields - * @param {array} $query_vars Query variables - * @since 3.0 - * @return {int} New boost - */ - 'boost' => apply_filters( 'ep_user_match_boost', 2, $prepared_search_fields, $query_vars ), - 'fuzziness' => 0, - 'operator' => 'and', - ), - ), - array( - 'multi_match' => array( - 'fields' => $prepared_search_fields, - 'query' => $query_vars['search'], - /** - * Filter fuzziness for user query - * - * @hook ep_user_fuzziness_arg - * @param {int} $fuzziness Fuzziness - * @param {array} $prepared_search_fields Search fields - * @param {array} $query_vars Query variables - * @since 3.0 - * @return {int} New fuzziness - */ - 'fuzziness' => apply_filters( 'ep_user_fuzziness_arg', 1, $prepared_search_fields, $query_vars ), - ), - ), - ), - ), - ); - - /** - * Filter formatted Elasticsearch user query (only contains query part) - * - * @hook ep_user_formatted_args_query - * @param {array} $query Current query - * @param {array} $query_vars Query variables - * @since 3.0 - * @return {array} New query - */ - $formatted_args['query'] = apply_filters( 'ep_user_formatted_args_query', $query, $query_vars ); - + $search_algorithm = $this->get_search_algorithm( $query_vars['search'], $prepared_search_fields, $query_vars ); + $formatted_args['query'] = $search_algorithm->get_query( 'user', $query_vars['search'], $prepared_search_fields, $query_vars ); } else { $formatted_args['query']['match_all'] = [ 'boost' => 1, diff --git a/includes/classes/SearchAlgorithm.php b/includes/classes/SearchAlgorithm.php new file mode 100644 index 0000000000..faa84dc59c --- /dev/null +++ b/includes/classes/SearchAlgorithm.php @@ -0,0 +1,111 @@ +get_raw_query( $indexable_slug, $search_term, $search_fields, $query_vars ); + + /** + * Filter formatted Elasticsearch query (only contains query part) + * + * @hook ep_{$indexable_slug}_formatted_args_query + * @param {array} $query Current query + * @param {array} $query_vars Query variables + * @param {string} $search_text Search text + * @param {array} $search_fields Search fields + * @return {array} New query + * + * @since 4.3.0 + */ + $query = apply_filters( + "ep_{$indexable_slug}_formatted_args_query", + $query, + $query_vars, + $search_term, + $search_fields + ); + + // Backwards-compatibility. + if ( 'post' === $indexable_slug ) { + /** + * Filter formatted Elasticsearch query for posts. + * + * This filter exists to keep backwards-compatibility. Newer implementations should use `ep_post_formatted_args_query`. + * + * @hook ep_formatted_args_query + * @param {array} $query Current query + * @param {array} $query_vars Query variables + * @param {string} $search_text Search text + * @param {array} $search_fields Search fields + * @return {array} New query + * + * @since 3.5.5 $search_text and $search_fields parameters added. + */ + $query = apply_filters_deprecated( + 'ep_formatted_args_query', + [ $query, $query_vars, $search_term, $search_fields ], + '4.3.0', + 'ep_post_formatted_args_query' + ); + } + + return $query; + } +} diff --git a/includes/classes/SearchAlgorithm/DefaultAlgorithm.php b/includes/classes/SearchAlgorithm/DefaultAlgorithm.php new file mode 100644 index 0000000000..b6e1580de7 --- /dev/null +++ b/includes/classes/SearchAlgorithm/DefaultAlgorithm.php @@ -0,0 +1,194 @@ + [ + 'should' => [ + [ + 'multi_match' => [ + 'query' => $search_term, + 'type' => 'phrase', + 'fields' => $search_fields, + /** + * Filter match phrase boost amount + * + * @since 4.3.0 + * @hook ep_{$indexable_slug}_match_phrase_boost + * @param {int} $boost Phrase boost + * @param {array} $search_fields Search fields + * @param {array} $query_vars Query variables + * @return {int} New boost amount + */ + 'boost' => apply_filters( "ep_{$indexable_slug}_match_phrase_boost", 4, $search_fields, $query_vars ), + ], + ], + [ + 'multi_match' => [ + 'query' => $search_term, + 'fields' => $search_fields, + /** + * Filter match boost amount + * + * @since 4.3.0 + * @hook ep_{$indexable_slug}_match_boost + * @param {int} $boost Boost + * @param {array} $search_fields Search fields + * @param {array} $query_vars Query variables + * @return {int} New boost + */ + 'boost' => apply_filters( "ep_{$indexable_slug}_match_boost", 2, $search_fields, $query_vars ), + 'fuzziness' => 0, + 'operator' => 'and', + ], + ], + [ + 'multi_match' => [ + 'fields' => $search_fields, + 'query' => $search_term, + /** + * Filter fuzziness amount + * + * @since 4.3.0 + * @hook ep_{$indexable_slug}_fuzziness_arg + * @param {int} $fuzziness Fuzziness + * @param {array} $search_fields Search fields + * @param {array} $query_vars Query variables + * @return {int} New fuzziness + */ + 'fuzziness' => apply_filters( "ep_{$indexable_slug}_fuzziness_arg", 1, $search_fields, $query_vars ), + ], + ], + ], + ], + ]; + + $query = $this->apply_legacy_filters( $query, $indexable_slug, $search_fields, $query_vars ); + + return $query; + } + + /** + * Apply legacy filters. + * + * @param array $query ES `query` + * @param string $indexable_slug Indexable slug + * @param array $search_fields Search term(s) + * @param array $query_vars Query vars + * @return array ES `query` + */ + protected function apply_legacy_filters( array $query, string $indexable_slug, array $search_fields, array $query_vars ) : array { + if ( 'post' !== $indexable_slug ) { + return $query; + } + + /** + * Filter boost for post match phrase query. + * + * This filter exists to keep backwards-compatibility. Newer implementations should use `ep_post_match_phrase_boost`. + * + * @hook ep_match_phrase_boost + * @param {int} $boost Phrase boost + * @param {array} $search_fields Search fields + * @param {array} $query_vars Query variables + * @return {int} New boost amount + */ + $query['bool']['should'][0]['multi_match']['boost'] = apply_filters_deprecated( + 'ep_match_phrase_boost', + [ 4, $search_fields, $query_vars ], + '4.3.0', + 'ep_post_match_phrase_boost' + ); + + /** + * Filter boost for post match query + * + * This filter exists to keep backwards-compatibility. Newer implementations should use `ep_post_match_boost`. + * + * @hook ep_match_boost + * @param {int} $boost Boost + * @param {array} $search_fields Search fields + * @param {array} $query_vars Query variables + * @return {int} New boost + */ + $query['bool']['should'][1]['multi_match']['boost'] = apply_filters_deprecated( + 'ep_match_boost', + [ 2, $search_fields, $query_vars ], + '4.3.0', + 'ep_post_match_boost' + ); + + /** + * Filter fuzziness for post query + * + * This filter exists to keep backwards-compatibility. Newer implementations should use `ep_post_fuzziness_arg`. + * + * @hook ep_fuzziness_arg + * @param {int} $fuzziness Fuzziness + * @param {array} $search_fields Search fields + * @param {array} $query_vars Query variables + * @return {int} New fuzziness + */ + $query['bool']['should'][2]['multi_match']['fuzziness'] = apply_filters_deprecated( + 'ep_fuzziness_arg', + [ 1, $search_fields, $query_vars ], + '4.3.0', + 'ep_post_fuzziness_arg' + ); + + return $query; + } +} diff --git a/includes/classes/SearchAlgorithm/Version_350.php b/includes/classes/SearchAlgorithm/Version_350.php new file mode 100644 index 0000000000..5c52b646cc --- /dev/null +++ b/includes/classes/SearchAlgorithm/Version_350.php @@ -0,0 +1,111 @@ + [ + 'should' => [ + [ + 'multi_match' => [ + 'query' => $search_term, + 'type' => 'phrase', + 'fields' => $search_fields, + /** This filter is documented in /includes/classes/SearchAlgorithm/Basic.php */ + 'boost' => apply_filters( "ep_{$indexable_slug}_match_phrase_boost", 3, $search_fields, $query_vars ), + ], + ], + [ + 'multi_match' => [ + 'query' => $search_term, + 'fields' => $search_fields, + 'type' => 'phrase', + 'slop' => 5, + ], + ], + ], + ], + ]; + + $query = $this->apply_legacy_filters( $query, $indexable_slug, $search_fields, $query_vars ); + + return $query; + } + + /** + * Apply legacy filters. + * + * @param array $query ES `query` + * @param string $indexable_slug Indexable slug + * @param array $search_fields Search term(s) + * @param array $query_vars Query vars + * @return array ES `query` + */ + protected function apply_legacy_filters( array $query, string $indexable_slug, array $search_fields, array $query_vars ) : array { + if ( 'post' !== $indexable_slug ) { + return $query; + } + + /** This filter is documented in /includes/classes/SearchAlgorithm/Basic.php */ + $query['bool']['should'][0]['multi_match']['boost'] = apply_filters_deprecated( + 'ep_match_phrase_boost', + [ 3, $search_fields, $query_vars ], + '4.3.0', + 'ep_post_match_phrase_boost' + ); + + return $query; + } +} diff --git a/includes/classes/SearchAlgorithm/Version_400.php b/includes/classes/SearchAlgorithm/Version_400.php new file mode 100644 index 0000000000..083dceb828 --- /dev/null +++ b/includes/classes/SearchAlgorithm/Version_400.php @@ -0,0 +1,190 @@ + [ + 'should' => [ + [ + 'multi_match' => [ + 'query' => $search_term, + 'type' => 'phrase', + 'fields' => $search_fields, + /** This filter is documented in /includes/classes/SearchAlgorithm/Basic.php */ + 'boost' => apply_filters( "ep_{$indexable_slug}_match_phrase_boost", 3, $search_fields, $query_vars ), + ], + ], + [ + 'multi_match' => [ + 'query' => $search_term, + 'fields' => $search_fields, + 'operator' => 'and', + /** This filter is documented in /includes/classes/SearchAlgorithm/Basic.php */ + 'boost' => apply_filters( "ep_{$indexable_slug}_match_boost", 1, $search_fields, $query_vars ), + /** + * Filter fuzziness for match query + * + * @hook ep_{$indexable_slug}_match_fuzziness + * @since 4.3.0 + * @param {string|int} $fuzziness Fuzziness + * @param {array} $search_fields Search fields + * @param {array} $query_vars Query variables + * @return {string} New fuzziness + */ + 'fuzziness' => apply_filters( "ep_{$indexable_slug}_match_fuzziness", 'auto', $search_fields, $query_vars ), + ], + ], + [ + 'multi_match' => [ + 'query' => $search_term, + 'type' => 'cross_fields', + 'fields' => $search_fields, + /** + * Filter boost for match cross_fields query + * + * @hook ep_{$indexable_slug}_match_cross_fields_boost + * @since 4.3.0 + * @param {int} $boost Boost + * @param {array} $search_fields Search fields + * @param {array} $query_vars Query variables + * @return {int} New boost + */ + 'boost' => apply_filters( "ep_{$indexable_slug}_match_cross_fields_boost", 1, $search_fields, $query_vars ), + 'analyzer' => 'standard', + 'tie_breaker' => 0.5, + 'operator' => 'and', + ], + ], + ], + ], + ]; + + $query = $this->apply_legacy_filters( $query, $indexable_slug, $search_fields, $query_vars ); + + return $query; + } + + /** + * Apply legacy filters. + * + * @param array $query ES `query` + * @param string $indexable_slug Indexable slug + * @param array $search_fields Search term(s) + * @param array $query_vars Query vars + * @return array ES `query` + */ + protected function apply_legacy_filters( array $query, string $indexable_slug, array $search_fields, array $query_vars ) : array { + if ( 'post' !== $indexable_slug ) { + return $query; + } + + /** This filter is documented in /includes/classes/SearchAlgorithm/Basic.php */ + $query['bool']['should'][0]['multi_match']['boost'] = apply_filters_deprecated( + 'ep_match_phrase_boost', + [ 3, $search_fields, $query_vars ], + '4.3.0', + 'ep_post_match_phrase_boost' + ); + + /** This filter is documented in /includes/classes/SearchAlgorithm/Basic.php */ + $query['bool']['should'][1]['multi_match']['boost'] = apply_filters_deprecated( + 'ep_match_boost', + [ 1, $search_fields, $query_vars ], + '4.3.0', + 'ep_post_match_boost' + ); + + /** + * Filter fuzziness for post match query + * + * This filter exists to keep backwards-compatibility. Newer implementations should use `ep_post_match_fuzziness`. + * + * @hook ep_match_fuzziness + * @since 4.0.0 + * @param {string|int} $fuzziness Fuzziness + * @param {array} $search_fields Search fields + * @param {array} $query_vars Query variables + * @return {string} New fuzziness + */ + $query['bool']['should'][1]['multi_match']['fuzziness'] = apply_filters_deprecated( + 'ep_match_fuzziness', + [ 'auto', $search_fields, $query_vars ], + '4.3.0', + 'ep_post_match_fuzziness' + ); + + /** + * Filter boost for post match cross_fields query + * + * This filter exists to keep backwards-compatibility. Newer implementations should use `ep_post_match_cross_fields_boost`. + * + * @hook ep_{$indexable_slug}_match_cross_fields_boost + * @since 4.0.0 + * @param {int} $boost Boost + * @param {array} $search_fields Search fields + * @param {array} $query_vars Query variables + * @return {int} New boost + */ + $query['bool']['should'][2]['multi_match']['boost'] = apply_filters_deprecated( + 'ep_match_cross_fields_boost', + [ 1, $search_fields, $query_vars ], + '4.3.0', + 'ep_post_match_cross_fields_boost' + ); + + return $query; + } +} diff --git a/includes/classes/SearchAlgorithms.php b/includes/classes/SearchAlgorithms.php new file mode 100644 index 0000000000..5f2a15dda1 --- /dev/null +++ b/includes/classes/SearchAlgorithms.php @@ -0,0 +1,92 @@ +registered_search_algorithms[ $search_algorithm->get_slug() ] = $search_algorithm; + } + + /** + * Get a search algorithm instance given a slug + * + * @param string $slug Search Algorithm slug + * @return SearchAlgorithm + */ + public function get( string $slug ) { + return ( ! empty( $this->registered_search_algorithms[ $slug ] ) ) ? + $this->registered_search_algorithms[ $slug ] : + $this->registered_search_algorithms['default']; + } + + /** + * Unregister a search algorithm. + * + * A search algorithm can only be unregistered if it is not the only one left. + * + * @param string $slug Search Algorithm slug + * @return bool Whether the search algorithm was unregistered or not. + */ + public function unregister( string $slug ) { + if ( isset( $this->registered_search_algorithms[ $slug ] ) && count( $this->registered_search_algorithms ) >= 2 ) { + unset( $this->registered_search_algorithms[ $slug ] ); + return true; + } + return false; + } + + /** + * Get all search algorithm instances + * + * @param boolean $slug_only True returns an array of only string slugs. + * @return array + */ + public function get_all( $slug_only = false ) { + if ( $slug_only ) { + return array_keys( $this->registered_search_algorithms ); + } + + return $this->registered_search_algorithms; + } + + /** + * Return singleton instance of class + * + * @return object + */ + public static function factory() { + static $instance = false; + + if ( ! $instance ) { + $instance = new self(); + } + + return $instance; + } +} diff --git a/tests/php/TestSearchAlgorithm.php b/tests/php/TestSearchAlgorithm.php new file mode 100644 index 0000000000..224ad46436 --- /dev/null +++ b/tests/php/TestSearchAlgorithm.php @@ -0,0 +1,78 @@ +stub = $this->getMockForAbstractClass( SearchAlgorithm::class ); + $this->stub->expects( $this->any() ) + ->method( 'get_raw_query' ) + ->will( $this->returnValue( [] ) ); + + parent::setUp(); + } + + /** + * Test filters + * + * @group searchAlgorithms + */ + public function testFilters() { + $test_filter = function() { + return [ 'changed' ]; + }; + + /** + * Test the `ep_{$indexable_slug}_formatted_args_query` filter. + */ + add_filter( 'ep_indexable_formatted_args_query', $test_filter ); + + $query = $this->stub->get_query( 'indexable', '', [], [] ); + $this->assertEquals( [ 'changed' ], $query ); + + remove_filter( 'ep_indexable_formatted_args_query', $test_filter ); + } + + /** + * Test deprecated/legacy filters + * + * @expectedDeprecated ep_formatted_args_query + * @group searchAlgorithms + */ + public function testLegacyFilters() { + $test_filter = function() { + return [ 'changed' ]; + }; + + /** + * Test the `ep_formatted_args_query` filter. + */ + add_filter( 'ep_formatted_args_query', $test_filter ); + + $query = $this->stub->get_query( 'post', '', [], [] ); + $this->assertEquals( [ 'changed' ], $query ); + + remove_filter( 'ep_formatted_args_query', $test_filter ); + } +} diff --git a/tests/php/TestSearchAlgorithms.php b/tests/php/TestSearchAlgorithms.php new file mode 100644 index 0000000000..96782c5955 --- /dev/null +++ b/tests/php/TestSearchAlgorithms.php @@ -0,0 +1,85 @@ +getMockForAbstractClass( SearchAlgorithm::class ); + $stub->expects( $this->any() ) + ->method( 'get_slug' ) + ->will( $this->returnValue( 'stub' ) ); + + SearchAlgorithms::factory()->register( $stub ); + + $this->assertSame( $stub, SearchAlgorithms::factory()->get( 'stub' ) ); + + /** + * Test getting a non-existent search algorithm. `Basic` should be used. + */ + $this->assertSame( 'default', SearchAlgorithms::factory()->get( 'foobar' )->get_slug() ); + } + + /** + * Test unregistering search algorithms + * + * @depends testRegisterAndGetSearchAlgorithms + * @group searchAlgorithms + */ + public function testUnregisterSearchAlgorithms() { + $this->assertTrue( SearchAlgorithms::factory()->unregister( 'stub' ) ); + $this->assertFalse( SearchAlgorithms::factory()->unregister( 'foobar' ) ); + + /** + * Store these search algorithms to register them back later. + */ + $default = SearchAlgorithms::factory()->get( 'default' ); + $version_35 = SearchAlgorithms::factory()->get( '3.5' ); + + $this->assertTrue( SearchAlgorithms::factory()->unregister( 'default' ) ); + $this->assertTrue( SearchAlgorithms::factory()->unregister( '3.5' ) ); + + /** + * This is the last one remaining and will not be unregistered + */ + $this->assertFalse( SearchAlgorithms::factory()->unregister( '4.0' ) ); + + SearchAlgorithms::factory()->register( $default ); + SearchAlgorithms::factory()->register( $version_35 ); + } + + /** + * Test getting all search algorithms + * + * @depends testUnregisterSearchAlgorithms + * @group searchAlgorithms + */ + public function testGetAll() { + $this->assertEqualsCanonicalizing( [ 'default', '3.5', '4.0' ], SearchAlgorithms::factory()->get_all( true ) ); + + $search_algorithms = SearchAlgorithms::factory()->get_all(); + $this->assertCount( 3, $search_algorithms ); + foreach ( $search_algorithms as $search_algorithm ) { + $this->assertInstanceOf( SearchAlgorithm::class, $search_algorithm ); + } + } +} diff --git a/tests/php/indexables/TestPost.php b/tests/php/indexables/TestPost.php index 2f77be218d..d04256542e 100644 --- a/tests/php/indexables/TestPost.php +++ b/tests/php/indexables/TestPost.php @@ -6865,7 +6865,64 @@ public function testMetaWithoutValue() { $this->assertTrue( $query->elasticsearch_success ); $this->assertEquals( $expected_result, $query->post_count ); + } + + /** + * Test the get_search_algorithm implementation + */ + public function testGetSearchAlgorithm() { + /** + * Test default search algorithm + */ + $version_40 = \ElasticPress\SearchAlgorithms::factory()->get( '4.0' ); + + $post_indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $search_algorithm = $post_indexable->get_search_algorithm( '', [], [] ); + + $this->assertSame( $version_40, $search_algorithm ); + + /** + * Test setting a diffent algorithm through the `ep_search_algorithm_version` filter + */ + $version_35 = \ElasticPress\SearchAlgorithms::factory()->get( '3.5' ); + + $set_version_35 = function() { + return '3.5'; + }; + + add_filter( 'ep_search_algorithm_version', $set_version_35 ); + + $search_algorithm = $post_indexable->get_search_algorithm( '', [], [] ); + $this->assertSame( $version_35, $search_algorithm ); + + remove_filter( 'ep_search_algorithm_version', $set_version_35 ); + + /** + * Test setting a non-existent algorithm through the `ep_search_algorithm_version` filter + * It should use `basic` + */ + $basic = \ElasticPress\SearchAlgorithms::factory()->get( 'basic' ); + + $set_non_existent_version = function() { + return 'foobar'; + }; + + add_filter( 'ep_search_algorithm_version', $set_non_existent_version ); + + $search_algorithm = $post_indexable->get_search_algorithm( '', [], [] ); + $this->assertSame( $basic, $search_algorithm ); + + remove_filter( 'ep_search_algorithm_version', $set_non_existent_version ); + + /** + * Test the `ep_{$indexable_slug}_search_algorithm` filter + */ + add_filter( 'ep_post_search_algorithm', $set_version_35 ); + + $search_algorithm = $post_indexable->get_search_algorithm( '', [], [] ); + $this->assertSame( $version_35, $search_algorithm ); + remove_filter( 'ep_post_search_algorithm', $set_version_35 ); } /** diff --git a/tests/php/searchAlgorithms/TestDefaultSearchAlgorithm.php b/tests/php/searchAlgorithms/TestDefaultSearchAlgorithm.php new file mode 100644 index 0000000000..f2b2df1c16 --- /dev/null +++ b/tests/php/searchAlgorithms/TestDefaultSearchAlgorithm.php @@ -0,0 +1,180 @@ +assertSame( 'default', $default->get_slug() ); + } + + /** + * Test default query + * + * @group searchAlgorithms + */ + public function testGetQuery() { + $default = new DefaultAlgorithm(); + + $search_term = 'search_term'; + $search_fields = [ 'post_title', 'post_content' ]; + + $query = $default->get_query( 'indexable', $search_term, $search_fields, [] ); + + $model = $this->getModel( $search_term, $search_fields); + + $this->assertEquals( $model, $query ); + } + + /** + * Test filters + * + * @group searchAlgorithms + */ + public function testFilters() { + $default = new DefaultAlgorithm(); + + $search_term = 'search_term'; + $search_fields = [ 'post_title', 'post_content' ]; + + $test_filter = function() { + return 1234; + }; + + /** + * Test the `ep_{$indexable_slug}_match_phrase_boost` filter. + */ + add_filter( 'ep_indexable_match_phrase_boost', $test_filter ); + + $query = $default->get_query( 'indexable', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][0]['multi_match']['boost'] ); + + remove_filter( 'ep_indexable_match_phrase_boost', $test_filter ); + + /** + * Test the `ep_{$indexable_slug}_match_boost` filter. + */ + add_filter( 'ep_indexable_match_boost', $test_filter ); + + $query = $default->get_query( 'indexable', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][1]['multi_match']['boost'] ); + + remove_filter( 'ep_indexable_match_boost', $test_filter ); + + /** + * Test the `ep_{$indexable_slug}_fuzziness_arg` filter. + */ + add_filter( 'ep_indexable_fuzziness_arg', $test_filter ); + + $query = $default->get_query( 'indexable', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][2]['multi_match']['fuzziness'] ); + + remove_filter( 'ep_indexable_fuzziness_arg', $test_filter ); + } + + /** + * Test deprecated/legacy filters + * + * @expectedDeprecated ep_match_phrase_boost + * @expectedDeprecated ep_match_boost + * @expectedDeprecated ep_fuzziness_arg + * @group searchAlgorithms + */ + public function testLegacyFilters() { + $default = new DefaultAlgorithm(); + + $search_term = 'search_term'; + $search_fields = [ 'post_title', 'post_content' ]; + + $test_filter = function() { + return 1234; + }; + + /** + * Test the `ep_match_phrase_boost` filter. + */ + add_filter( 'ep_match_phrase_boost', $test_filter ); + + $query = $default->get_query( 'post', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][0]['multi_match']['boost'] ); + + remove_filter( 'ep_match_phrase_boost', $test_filter ); + + /** + * Test the `ep_match_boost` filter. + */ + add_filter( 'ep_match_boost', $test_filter ); + + $query = $default->get_query( 'post', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][1]['multi_match']['boost'] ); + + remove_filter( 'ep_match_boost', $test_filter ); + + /** + * Test the `ep_fuzziness_arg` filter. + */ + add_filter( 'ep_fuzziness_arg', $test_filter ); + + $query = $default->get_query( 'post', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][2]['multi_match']['fuzziness'] ); + + remove_filter( 'ep_fuzziness_arg', $test_filter ); + } + + /** + * ES Query model + * + * @param string $search_term Search term + * @param array $search_fields Search fields + * @return array + */ + protected function getModel( string $search_term, array $search_fields ) : array { + return [ + 'bool' => [ + 'should' => [ + [ + 'multi_match' => [ + 'query' => $search_term, + 'type' => 'phrase', + 'fields' => $search_fields, + 'boost' => 4, + ], + ], + [ + 'multi_match' => [ + 'query' => $search_term, + 'fields' => $search_fields, + 'boost' => 2, + 'fuzziness' => 0, + 'operator' => 'and', + ], + ], + [ + 'multi_match' => [ + 'fields' => $search_fields, + 'query' => $search_term, + 'fuzziness' => 1, + ], + ], + ], + ], + ]; + } +} diff --git a/tests/php/searchAlgorithms/TestVersion_350SearchAlgorithm.php b/tests/php/searchAlgorithms/TestVersion_350SearchAlgorithm.php new file mode 100644 index 0000000000..d1362cafd2 --- /dev/null +++ b/tests/php/searchAlgorithms/TestVersion_350SearchAlgorithm.php @@ -0,0 +1,130 @@ +assertSame( '3.5', $basic->get_slug() ); + } + + /** + * Test default query + * + * @group searchAlgorithms + */ + public function testGetQuery() { + $basic = new Version_350(); + + $search_term = 'search_term'; + $search_fields = [ 'post_title', 'post_content' ]; + + $query = $basic->get_query( 'indexable', $search_term, $search_fields, [] ); + + $model = $this->getModel( $search_term, $search_fields); + + $this->assertEquals( $model, $query ); + } + + /** + * Test filters + * + * @group searchAlgorithms + */ + public function testFilters() { + $basic = new Version_350(); + + $search_term = 'search_term'; + $search_fields = [ 'post_title', 'post_content' ]; + + $test_filter = function() { + return 1234; + }; + + /** + * Test the `ep_{$indexable_slug}_match_phrase_boost` filter. + */ + add_filter( 'ep_indexable_match_phrase_boost', $test_filter ); + + $query = $basic->get_query( 'indexable', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][0]['multi_match']['boost'] ); + + remove_filter( 'ep_indexable_match_phrase_boost', $test_filter ); + } + + /** + * Test deprecated/legacy filters + * + * @expectedDeprecated ep_match_phrase_boost + * @group searchAlgorithms + */ + public function testLegacyFilters() { + $basic = new Version_350(); + + $search_term = 'search_term'; + $search_fields = [ 'post_title', 'post_content' ]; + + $test_filter = function() { + return 1234; + }; + + /** + * Test the `ep_match_phrase_boost` filter. + */ + add_filter( 'ep_match_phrase_boost', $test_filter ); + + $query = $basic->get_query( 'post', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][0]['multi_match']['boost'] ); + + remove_filter( 'ep_match_phrase_boost', $test_filter ); + } + + /** + * ES Query model + * + * @param string $search_term Search term + * @param array $search_fields Search fields + * @return array + */ + protected function getModel( string $search_term, array $search_fields ) : array { + return [ + 'bool' => [ + 'should' => [ + [ + 'multi_match' => [ + 'query' => $search_term, + 'type' => 'phrase', + 'fields' => $search_fields, + 'boost' => 3, + ], + ], + [ + 'multi_match' => [ + 'query' => $search_term, + 'fields' => $search_fields, + 'type' => 'phrase', + 'slop' => 5, + ], + ], + ], + ], + ]; + } +} diff --git a/tests/php/searchAlgorithms/TestVersion_400SearchAlgorithm.php b/tests/php/searchAlgorithms/TestVersion_400SearchAlgorithm.php new file mode 100644 index 0000000000..61261c96de --- /dev/null +++ b/tests/php/searchAlgorithms/TestVersion_400SearchAlgorithm.php @@ -0,0 +1,205 @@ +assertSame( '4.0', $basic->get_slug() ); + } + + /** + * Test default query + * + * @group searchAlgorithms + */ + public function testGetQuery() { + $basic = new Version_400(); + + $search_term = 'search_term'; + $search_fields = [ 'post_title', 'post_content' ]; + + $query = $basic->get_query( 'indexable', $search_term, $search_fields, [] ); + + $model = $this->getModel( $search_term, $search_fields); + + $this->assertEquals( $model, $query ); + } + + /** + * Test filters + * + * @group searchAlgorithms + */ + public function testFilters() { + $basic = new Version_400(); + + $search_term = 'search_term'; + $search_fields = [ 'post_title', 'post_content' ]; + + $test_filter = function() { + return 1234; + }; + + /** + * Test the `ep_{$indexable_slug}_match_phrase_boost` filter. + */ + add_filter( 'ep_indexable_match_phrase_boost', $test_filter ); + + $query = $basic->get_query( 'indexable', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][0]['multi_match']['boost'] ); + + remove_filter( 'ep_indexable_match_phrase_boost', $test_filter ); + + /** + * Test the `ep_{$indexable_slug}_match_boost` filter. + */ + add_filter( 'ep_indexable_match_boost', $test_filter ); + + $query = $basic->get_query( 'indexable', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][1]['multi_match']['boost'] ); + + remove_filter( 'ep_indexable_match_boost', $test_filter ); + + /** + * Test the `ep_{$indexable_slug}_match_fuzziness` filter. + */ + add_filter( 'ep_indexable_match_fuzziness', $test_filter ); + + $query = $basic->get_query( 'indexable', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][1]['multi_match']['fuzziness'] ); + + remove_filter( 'ep_indexable_match_fuzziness', $test_filter ); + + /** + * Test the `ep_{$indexable_slug}_match_cross_fields_boost` filter. + */ + add_filter( 'ep_indexable_match_cross_fields_boost', $test_filter ); + + $query = $basic->get_query( 'indexable', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][2]['multi_match']['boost'] ); + + remove_filter( 'ep_indexable_match_cross_fields_boost', $test_filter ); + } + + /** + * Test deprecated/legacy filters + * + * @expectedDeprecated ep_match_phrase_boost + * @expectedDeprecated ep_match_boost + * @expectedDeprecated ep_match_fuzziness + * @expectedDeprecated ep_match_cross_fields_boost + * @group searchAlgorithms + */ + public function testLegacyFilters() { + $basic = new Version_400(); + + $search_term = 'search_term'; + $search_fields = [ 'post_title', 'post_content' ]; + + $test_filter = function() { + return 1234; + }; + + /** + * Test the `ep_match_phrase_boost` filter. + */ + add_filter( 'ep_match_phrase_boost', $test_filter ); + + $query = $basic->get_query( 'post', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][0]['multi_match']['boost'] ); + + remove_filter( 'ep_match_phrase_boost', $test_filter ); + + /** + * Test the `ep_match_boost` filter. + */ + add_filter( 'ep_match_boost', $test_filter ); + + $query = $basic->get_query( 'post', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][1]['multi_match']['boost'] ); + + remove_filter( 'ep_match_boost', $test_filter ); + + /** + * Test the `ep_match_fuzziness` filter. + */ + add_filter( 'ep_match_fuzziness', $test_filter ); + + $query = $basic->get_query( 'post', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][1]['multi_match']['fuzziness'] ); + + remove_filter( 'ep_match_fuzziness', $test_filter ); + + /** + * Test the `ep_match_cross_fields_boost` filter. + */ + add_filter( 'ep_match_cross_fields_boost', $test_filter ); + + $query = $basic->get_query( 'post', $search_term, $search_fields, [] ); + $this->assertEquals( 1234, $query['bool']['should'][2]['multi_match']['boost'] ); + + remove_filter( 'ep_match_cross_fields_boost', $test_filter ); + } + + /** + * ES Query model + * + * @param string $search_term Search term + * @param array $search_fields Search fields + * @return array + */ + protected function getModel( string $search_term, array $search_fields ) : array { + return [ + 'bool' => [ + 'should' => [ + [ + 'multi_match' => [ + 'query' => $search_term, + 'type' => 'phrase', + 'fields' => $search_fields, + 'boost' => 3, + ], + ], + [ + 'multi_match' => [ + 'query' => $search_term, + 'fields' => $search_fields, + 'operator' => 'and', + 'boost' => 1, + 'fuzziness' => 'auto', + ], + ], + [ + 'multi_match' => [ + 'query' => $search_term, + 'type' => 'cross_fields', + 'fields' => $search_fields, + 'boost' => 1, + 'analyzer' => 'standard', + 'tie_breaker' => 0.5, + 'operator' => 'and', + ], + ], + ], + ], + ]; + } +}