2020-11-24 22:16:03 +00:00
|
|
|
/* global FetchEndEvent, SuggestionClickEvent, SubmitEvent */
|
|
|
|
/** @module Instrumentation */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The value of the `inputLocation` property of any and all SearchSatisfaction events sent by the
|
|
|
|
* corresponding instrumentation.
|
|
|
|
*
|
|
|
|
* @see https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/skins/Vector/+/refs/heads/master/includes/Constants.php
|
|
|
|
*/
|
|
|
|
var INPUT_LOCATION_MOVED = 'header-moved',
|
2020-12-01 22:41:44 +00:00
|
|
|
wgScript = mw.config.get( 'wgScript' ),
|
|
|
|
// T251544: Collect search performance metrics to compare Vue search with
|
|
|
|
// mediawiki.searchSuggest performance. Marks and Measures will only be
|
|
|
|
// recorded on the Vector skin and only if browser supported.
|
|
|
|
shouldTestSearchPerformance = !!( window.performance &&
|
|
|
|
window.requestAnimationFrame &&
|
|
|
|
performance.mark &&
|
|
|
|
performance.measure &&
|
|
|
|
performance.getEntriesByName &&
|
|
|
|
performance.clearMarks ),
|
|
|
|
loadStartMark = 'mwVectorVueSearchLoadStart',
|
|
|
|
queryMark = 'mwVectorVueSearchQuery',
|
|
|
|
renderMark = 'mwVectorVueSearchRender',
|
|
|
|
queryToRenderMeasure = 'mwVectorVueSearchQueryToRender',
|
|
|
|
loadStartToFirstRenderMeasure = 'mwVectorVueSearchLoadStartToFirstRender';
|
|
|
|
|
|
|
|
function onFetchStart() {
|
|
|
|
if ( !shouldTestSearchPerformance ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear past marks that are no longer relevant. This likely means that the
|
|
|
|
// search request failed or was cancelled. Whatever the reason, the mark
|
|
|
|
// is no longer needed since we are only interested in collecting the time
|
|
|
|
// from query to render.
|
|
|
|
if ( performance.getEntriesByName( queryMark ).length ) {
|
|
|
|
performance.clearMarks( queryMark );
|
|
|
|
}
|
|
|
|
|
|
|
|
performance.mark( queryMark );
|
|
|
|
}
|
2020-11-24 22:16:03 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {FetchEndEvent} event
|
|
|
|
*/
|
|
|
|
function onFetchEnd( event ) {
|
|
|
|
mw.track( 'mediawiki.searchSuggest', {
|
|
|
|
action: 'impression-results',
|
|
|
|
numberOfResults: event.numberOfResults,
|
|
|
|
// resultSetType: '',
|
|
|
|
// searchId: '',
|
|
|
|
query: event.query,
|
|
|
|
inputLocation: INPUT_LOCATION_MOVED
|
|
|
|
} );
|
2020-12-01 22:41:44 +00:00
|
|
|
|
|
|
|
if ( shouldTestSearchPerformance ) {
|
|
|
|
// Schedule the mark after the search results have rendered and are
|
|
|
|
// visible to the user. Two rAF's are needed for this since rAF will
|
|
|
|
// execute before the rendering steps happen (e.g. layout and paint). A
|
|
|
|
// nested rAF will execute after these rendering steps have completed
|
|
|
|
// and ensure the search results are visible to the user.
|
|
|
|
requestAnimationFrame( function () {
|
|
|
|
requestAnimationFrame( function () {
|
|
|
|
if ( !performance.getEntriesByName( queryMark ).length ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
performance.mark( renderMark );
|
|
|
|
performance.measure( queryToRenderMeasure, queryMark, renderMark );
|
|
|
|
|
|
|
|
// Measure from the start of the lazy load to the first render if we
|
|
|
|
// haven't already captured that info.
|
|
|
|
if ( performance.getEntriesByName( loadStartMark ).length &&
|
|
|
|
!performance.getEntriesByName( loadStartToFirstRenderMeasure ).length ) {
|
|
|
|
performance.measure( loadStartToFirstRenderMeasure, loadStartMark, renderMark );
|
|
|
|
}
|
|
|
|
|
|
|
|
// The measures are the most meaningful info so we remove the marks
|
|
|
|
// after we have the measure.
|
|
|
|
performance.clearMarks( queryMark );
|
|
|
|
performance.clearMarks( renderMark );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
}
|
2020-11-24 22:16:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {SuggestionClickEvent|SubmitEvent} event
|
|
|
|
*/
|
|
|
|
function onSuggestionClick( event ) {
|
|
|
|
mw.track( 'mediawiki.searchSuggest', {
|
|
|
|
action: 'click-result',
|
|
|
|
numberOfResults: event.numberOfResults,
|
|
|
|
index: event.index
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates the value of the `wprov` parameter to be used in the URL of a search result and the
|
|
|
|
* `wprov` hidden input.
|
|
|
|
*
|
|
|
|
* See https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/extensions/WikimediaEvents/+/refs/heads/master/modules/ext.wikimediaEvents/searchSatisfaction.js
|
|
|
|
* and also the top of that file for additional detail about the shape of the parameter.
|
|
|
|
*
|
|
|
|
* @param {number} index
|
|
|
|
* @return {string}
|
|
|
|
*/
|
|
|
|
function getWprovFromResultIndex( index ) {
|
|
|
|
|
|
|
|
// If the user hasn't highlighted an autocomplete result.
|
|
|
|
if ( index === -1 ) {
|
|
|
|
return 'acrw1';
|
|
|
|
}
|
|
|
|
|
|
|
|
return 'acrw1' + index;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {Object} SearchResultPartial
|
|
|
|
* @property {string} title
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {Object} GenerateUrlMeta
|
|
|
|
* @property {number} index
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Used by the `wvui-typeahead-search` component to generate URLs for the search results. Adds a
|
|
|
|
* `wprov` paramater to the URL to satisfy the SearchSatisfaction instrumentation.
|
|
|
|
*
|
|
|
|
* @see getWprovFromResultIndex
|
|
|
|
*
|
|
|
|
* @param {SearchResultPartial|string} suggestion
|
|
|
|
* @param {GenerateUrlMeta} meta
|
|
|
|
* @return {string}
|
|
|
|
*/
|
|
|
|
function generateUrl( suggestion, meta ) {
|
|
|
|
var result = new mw.Uri( wgScript );
|
|
|
|
|
|
|
|
if ( typeof suggestion !== 'string' ) {
|
|
|
|
suggestion = suggestion.title;
|
|
|
|
}
|
|
|
|
|
|
|
|
result.query.title = 'Special:Search';
|
|
|
|
result.query.suggestion = suggestion;
|
|
|
|
result.query.wprov = getWprovFromResultIndex( meta.index );
|
|
|
|
|
|
|
|
return result.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
listeners: {
|
2020-12-01 22:41:44 +00:00
|
|
|
onFetchStart: onFetchStart,
|
2020-11-24 22:16:03 +00:00
|
|
|
onFetchEnd: onFetchEnd,
|
|
|
|
onSuggestionClick: onSuggestionClick,
|
|
|
|
|
|
|
|
// As of writing (2020/12/08), both the "click-result" and "submit-form" kind of
|
|
|
|
// mediawiki.searchSuggestion events result in a "click" SearchSatisfaction event being
|
|
|
|
// logged [0]. However, when processing the "submit-form" kind of mediawiki.searchSuggestion
|
|
|
|
// event, the SearchSatisfaction instrument will modify the DOM, adding a hidden input
|
|
|
|
// element, in order to set the appropriate provenance parameter (see [1] for additional
|
|
|
|
// detail).
|
|
|
|
//
|
|
|
|
// In this implementation of the mediawiki.searchSuggestion protocol, we don't want to
|
|
|
|
// trigger the above behavior as we're using Vue.js, which doesn't expect the DOM to be
|
|
|
|
// modified underneath it.
|
|
|
|
//
|
|
|
|
// [0] https://gerrit.wikimedia.org/g/mediawiki/extensions/WikimediaEvents/+/df97aa9c9407507e8c48827666beeab492fd56a8/modules/ext.wikimediaEvents/searchSatisfaction.js#735
|
|
|
|
// [1] https://phabricator.wikimedia.org/T257698#6416826
|
|
|
|
onSubmit: onSuggestionClick
|
|
|
|
},
|
|
|
|
getWprovFromResultIndex: getWprovFromResultIndex,
|
|
|
|
generateUrl: generateUrl
|
|
|
|
};
|