Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix relevancy in media search results #415

Open
1 task
roborourke opened this issue Jun 20, 2022 · 0 comments
Open
1 task

Fix relevancy in media search results #415

roborourke opened this issue Jun 20, 2022 · 0 comments
Labels
could have Could be done, or nice to have, low priority for now to refine Issues needing refinement.

Comments

@roborourke
Copy link
Contributor

roborourke commented Jun 20, 2022

Currently search results in the media modal are force sorted by date by the backbone JS model. Elasticsearch gives us a relevancy score that is not being used.

Acceptance criteria:

  • Media search results are sorted by relevancy score rather than date

There is an writeup with the solution for implementing this here: https://dev.hmn.md/2022/06/16/increase-media-search-relevancy-with-elasticsearch/comment-page-1/#comment-6523

Copying in the details here:


The Solution

1. Add _score to ep_post_formatted_args for media search

/**
 * Modifies Sort Order From Media Search.
 *
 * @hook ep_post_formatted_args
 *
 * @param array $query The ElasticSearch query.
 * @param array $args  The WP_Query args for the current query.
 *
 * @return array New query
 */
function modifies_sort_order_for_media_search( array $query, array $args ) : array {
	// Check we have a search query and only apply to attachment.
	if ( empty( $args['s'] ) || $args['post_type'] !== 'attachment' ) {
		return $query;
	}

	unset( $query['sort'] );
	$query['sort'] = [
		[
			'_score' => [
				'order' => 'DESC',
			],
		],
	];

	return $query;
}

add_filter( 'ep_post_formatted_args', __NAMESPACE__ . '\\modifies_sort_order_for_media_search', 11, 2 );

2. Add _score as return args in ElasticPress

/**
 * Add _score field to post return fields.
 *
 * @hook ep_search_post_return_args
 * @param  array $return_fields Post Fields
 * @return  array Post Fields
 */
function add_score_to_post_return( array $return_fields ) : array {
	$return_fields[] = '_score';
	return $return_fields;
}

add_filter( 'ep_search_post_return_args', __NAMESPACE__ . '\\add_score_to_post_return', 10 );

3. Add _score as part of post object in ElasticPress

/**
 * Add _score to posts result from ES query.
 *
 * @hook ep_retrieve_the_{index_type}
 *
 * @param  array $document Document retrieved from Elasticsearch
 * @param  array $hit Raw Elasticsearch hit
 */
function add_score_to_post( array $document, array $hit ) : array {
	$document['_score'] = $hit['_score'] ?? '';
	return $document;
}

add_filter( 'ep_retrieve_the_post', __NAMESPACE__ . '\\add_score_to_post', 10, 2 );

4. Pass down the _score value to attachment data JavaScript object

/**
 * Filters the attachment data prepared for JavaScript.
 *
 * @hook  wp_prepare_attachment_for_js
 *
 * @param array   $response   Array of prepared attachment data.
 *                            @see wp_prepare_attachment_for_js().
 * @param WP_Post $attachment Attachment object.
 *
 * @return array
 */
function add_score_to_media_search_results( array $response, WP_Post $attachment ) : array {
	// The _score attribute come from the mapping added in add_score_to_post function.
	$response['_score'] = property_exists( $attachment, '_score' ) ? $attachment->_score : '';
	return $response;
}

add_filter( 'wp_prepare_attachment_for_js', __NAMESPACE__ . '\\add_score_to_media_search_results', 10, 2 );

5. Modify Media Library Backbone Model & View to use _score as the sort order.

Media Query

/**
 * Override media query.
 *
 * This is to switch orderby between date or _score during page load.
 * When search query string exists, use _score.
 *
 * @param {object} [props] Query props.
 * @returns {wp.media.model.Attachments} Attachment collection query.
 */
wp.media.query = function ( props ) {
	return new wp.media.model.Attachments(
		null, {
			props: _.extend(
				_.defaults(
					props || {},
					{ orderby: _.isUndefined( props.search ) ? 'date' : '_score' }
				), { query: true }
			),
		} );

};

Media Model

/**
 * Override wp.media.model.Query
 *
 * This is to add _score to allowed orderby and the props mapping.
 */
_.extend(
	wp.media.model.Query,
	{
		orderby: {
			allowed: [ ...wp.media.model.Query.orderby.allowed, '_score' ],
			valuemap: { ...wp.media.model.Query.orderby.valuemap },
		},
		propmap: {
			...wp.media.model.Query.propmap,
			'_score': '_score',
		},
	}
);

Media View

/**
 * Override wp.media.view.Search.prototype.search
 *
 * This is to enable sort by _score when doing search, and
 * sort by date when not.
 */
_.extend(
	wp.media.view.Search.prototype,
	{
		search: _.debounce( function ( event ) {
			let searchTerm = event.target.value.trim();
			const attributes = { ...this.model.attributes };

			// Trigger the search only after 2 ASCII characters.
			if ( searchTerm && searchTerm.length > 1 ) {
				attributes['search'] = searchTerm;
				attributes['orderby'] = '_score';
				this.model.set( attributes );
			} else {
				// Directly change the orderby without using `set` in purpose.
				// If we use `set` here, it will trigger double rendering.
				this.model.attributes['orderby'] = 'date';

				// Only need this to trigger the render.
				this.model.unset( 'search' );
			}
		}, 500 ),
	}
);
@roborourke roborourke added should have Should be done, medium priority for now to-refine-dev must have Must be done, high priority and removed should have Should be done, medium priority for now labels Jun 20, 2022
@missjwo missjwo added to refine Issues needing refinement. and removed to-refine-dev labels Nov 28, 2022
@kovshenin kovshenin added could have Could be done, or nice to have, low priority for now and removed must have Must be done, high priority labels Feb 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
could have Could be done, or nice to have, low priority for now to refine Issues needing refinement.
Projects
None yet
Development

No branches or pull requests

3 participants