EBernhardson has uploaded a new change for review.
https://gerrit.wikimedia.org/r/311654
Change subject: Integrate did you mean collection into search satisfaction
......................................................................
Integrate did you mean collection into search satisfaction
The did you mean information is much more useful directly paired with
the search satisfaction schema. As such merge it together, and add some
test cases to validate data is collected as epected
Bug: T138087
Change-Id: I8983a9677ffd9d98c8a347ef0961c2f0e874393f
---
M WikimediaEventsHooks.php
M extension.json
D modules/ext.wikimediaEvents.didyoumean.js
M modules/ext.wikimediaEvents.searchSatisfaction.js
M tests/browser/SearchSatisfactionTests.php
5 files changed, 162 insertions(+), 151 deletions(-)
git pull
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/WikimediaEvents
refs/changes/54/311654/1
diff --git a/WikimediaEventsHooks.php b/WikimediaEventsHooks.php
index 50bfe0c..995b2cb 100644
--- a/WikimediaEventsHooks.php
+++ b/WikimediaEventsHooks.php
@@ -458,7 +458,6 @@
$wgOut->addJsConfigVars( array(
'wgIsSearchResultPage' => true,
) );
- $wgOut->addModules( 'ext.wikimediaEvents.didyoumean' );
return true;
}
diff --git a/extension.json b/extension.json
index 24558a9..be79cbe 100644
--- a/extension.json
+++ b/extension.json
@@ -101,11 +101,6 @@
"schema": "Edit",
"revision": 13457736
},
- "schema.DidYouMean": {
- "class": "ResourceLoaderSchemaModule",
- "schema": "DidYouMean",
- "revision": 13800499
- },
"schema.CompletionSuggestions": {
"class": "ResourceLoaderSchemaModule",
"schema": "CompletionSuggestions",
@@ -114,7 +109,7 @@
"schema.TestSearchSatisfaction2": {
"class": "ResourceLoaderSchemaModule",
"schema": "TestSearchSatisfaction2",
- "revision": 15700292
+ "revision": 15922352
},
"schema.GeoFeatures": {
"class": "ResourceLoaderSchemaModule",
@@ -154,15 +149,6 @@
"mobile"
],
"dependencies": []
- },
- "ext.wikimediaEvents.didyoumean": {
- "scripts": [
- "ext.wikimediaEvents.didyoumean.js"
- ],
- "targets": [
- "desktop"
- ],
- "dependencies": "mediawiki.Uri"
}
},
"ResourceFileModulePaths": {
diff --git a/modules/ext.wikimediaEvents.didyoumean.js
b/modules/ext.wikimediaEvents.didyoumean.js
deleted file mode 100644
index d2616fd..0000000
--- a/modules/ext.wikimediaEvents.didyoumean.js
+++ /dev/null
@@ -1,110 +0,0 @@
-/*!
- * Javacsript module for measuring usage of the 'did you mean' feature
- * of Special:Search.
- *
- * @license GNU GPL v2 or later
- * @author Erik Bernhardson <[email protected]>
- */
-( function ( mw, $ ) {
- var uri = new mw.Uri( location.href ),
- cirrusDYM = uri.query.wprov && uri.query.wprov.indexOf(
'cirrusDYM-' ) === 0;
-
- if ( cirrusDYM ) {
- cirrusDYM = uri.query.wprov;
- cirrusDYM = cirrusDYM.replace( /^cirrusDYM-/, '' ) + '-click';
- // cleanup the location bar in supported browsers
- if ( window.history.replaceState ) {
- delete uri.query.wprov;
- window.history.replaceState( {}, '', uri.toString() );
- }
- } else {
- cirrusDYM = 'no';
- }
-
- function oneIn( populationSize ) {
- return Math.floor( Math.random() * populationSize ) === 0;
- }
-
- function updateHref() {
- if ( this.id ) {
- var uri = new mw.Uri( this.href );
- uri.query.wprov = this.id.replace( /^mw-search-DYM/,
'cirrusDYM' );
- this.href = uri.toString();
- }
- }
-
- function participateInTest() {
- var didYouMean,
- $target = $( '.searchdidyoumean' ),
- suggestIsRewritten = $target.find( '.searchrewritten'
).length > 0,
- numResults = $( '.mw-search-result-heading' ).length,
- pageId = mw.user.generateRandomSessionId(),
- runSuggestion = +uri.query.runsuggestion,
- logEvent = function ( action ) {
- mw.eventLog.logEvent( 'DidYouMean', {
- // Used to correlate actions that
happen on the same
- // page. Otherwise a user opening
multiple search
- // results in tabs would have
overlapping events and we
- // wouldn't know how long between
visiting the page and
- // clicking something.
- pageId: pageId,
- // The number of normal search results
shown on the page
- numResults: numResults,
- // Either 'no', 'rewritten' or
'suggestion' indicating
- // the type of 'did you mean' we served
to the user
- didYouMean: didYouMean,
- // we noticed a number of events get
sent multiple
- // times from javascript, especially
when using sendBeacon.
- // This logId allows for later
deduplication.
- logId:
mw.user.generateRandomSessionId(),
- // Records if the user explicitly opted
out of auto-running
- // suggested queries
- runSuggestion: isNaN( runSuggestion ) ?
true : !!runSuggestion,
- // Records whether the user clicked on
a DYM link to get here
- // 'original-click', 'rewritten-click',
'suggestion-click', or 'no'
- didYouMeanSource: cirrusDYM,
- // Records the action taken on this page
- action: action
- } );
- },
- attachEvent = function ( selector, action ) {
- $target.find( selector ).on( 'click', function
() {
- logEvent( action );
- } );
- };
-
- if ( $target.length === 0 ) {
- didYouMean = 'no';
- } else if ( suggestIsRewritten ) {
- didYouMean = 'rewritten';
- } else {
- didYouMean = 'suggestion';
- }
-
- logEvent( 'visit-page' );
- if ( suggestIsRewritten ) {
- // showed the user the results of suggested query
- // instead of the one they provided.
- attachEvent( 'a.searchoriginal', 'clicked-original' );
- attachEvent( 'a.searchrewritten', 'clicked-rewritten' );
- } else {
- // suggesting a different query to the user
- attachEvent( 'a', 'clicked-did-you-mean' );
- }
- }
-
- $( document ).ready( function () {
- // tag "Did you mean" suggestion and original query
- $( '#mw-content-text .searchdidyoumean > a' ).each( updateHref
);
- // we fire most events from click handlers, so we need to
filter for
- // only browser that support sendBeacon and will reliably
deliver
- // these events.
- if ( navigator.sendBeacon && oneIn( 200 ) ) {
- mw.loader.using( [
- 'mediawiki.user',
- 'ext.eventLogging',
- 'schema.DidYouMean'
- ] ).then( participateInTest );
- }
- } );
-}( mediaWiki, jQuery ) );
diff --git a/modules/ext.wikimediaEvents.searchSatisfaction.js
b/modules/ext.wikimediaEvents.searchSatisfaction.js
index 12777e2..90f9a78 100644
--- a/modules/ext.wikimediaEvents.searchSatisfaction.js
+++ b/modules/ext.wikimediaEvents.searchSatisfaction.js
@@ -30,8 +30,15 @@
uri = new mw.Uri( location.href ),
checkinTimes = [ 10, 20, 30, 40, 50, 60, 90, 120, 150, 180,
210, 240, 300, 360, 420 ],
lastScrollTop = 0,
- articleId = mw.config.get( 'wgArticleId' )
- ;
+ articleId = mw.config.get( 'wgArticleId' ),
+ // map from dym wprov values to eventlogging inputLocation
values
+ didYouMeanMap = {
+ 'dym1': 'dym-suggest',
+ 'dymr1': 'dym-rewritten',
+ 'dymo1': 'dym-original',
+ },
+ // some browsers can't do Object.keys properly, so manually
maintain the list
+ didYouMeanList = [ 'dym1', 'dymr1', 'dymo1' ];
function extractResultPosition( uri, wprovPrefix ) {
return parseInt( uri.query.wprov &&
@@ -59,6 +66,11 @@
}
search = initFromWprov( 'srpw1_' );
+ search.didYouMean = uri.query.wprov &&
+ uri.query.wprov.substr( 0, search.wprovPrefix.length ) ===
search.wprovPrefix &&
+ didYouMeanList.indexOf( uri.query.wprov.substr(
search.wprovPrefix.length ) ) >= 0 &&
+ uri.query.wprov.substr( search.wprovPrefix.length );
+
autoComplete = initFromWprov( 'acrw1_' );
// with no position appended indicates the user submitted the
// autocomplete form.
@@ -74,7 +86,8 @@
// currently loaded state
var state = {},
storageNamespace = 'wmE-sS-',
- // persistent state keys that have a lifetime
+ // persistent state keys that have a lifetime. unlisted
+ // keys are not persisted between page loads.
ttl = {
sessionId: 10 * 60 * 1000,
subTest: 10 * 60 * 1000,
@@ -113,7 +126,7 @@
// take the first 52 bits of the rand
value to match js
// integer precision
parsed = parseInt( rand.slice(
0, 13 ), 16 );
- return parsed % populationSize === 0;
+ return true; //parsed % populationSize
=== 0;
},
/**
* Choose a single bucket from a list of
buckets with even
@@ -398,7 +411,7 @@
evt.searchToken = mw.config.get(
'wgCirrusSearchRequestSetToken' );
}
- // add any schema specific data
+ // add any action specific data
if ( extraData ) {
$.extend( evt, extraData );
}
@@ -408,6 +421,16 @@
eventLog = eventLog || extendMwEventLog();
eventLog.logEvent( 'TestSearchSatisfaction2',
evt );
} );
+ };
+ }
+
+ /**
+ */
+ function genAttachWprov( value ) {
+ return function () {
+ var uri = new mw.Uri( this.href );
+ uri.query.wprov = value;
+ this.href = uri.toString();
};
}
@@ -428,40 +451,86 @@
session.refresh( 'sessionId' );
session.refresh( 'subTest' );
+ // Standard did you mean suggestion when the user gets
results for
+ // their original query
+ $( '#mw-search-DYM-suggestion' ).each( genAttachWprov(
+ search.wprovPrefix + 'dym1'
+ ) );
+
+ // Link to the current (rewritten) search after we have
rewritten the original
+ // query into the did you mean query.
+ $( '#mw-search-DYM-rewritten' ).each( genAttachWprov(
+ search.wprovPrefix + 'dymr1'
+ ) );
+
+ // Link to the original search after we have rewritten
the original query
+ // into the did you mean query
+ $( '#mw-search-DYM-original' ).each( genAttachWprov(
+ search.wprovPrefix + 'dymo1'
+ ) );
+
$( '#mw-content-text' ).on(
'click',
- '.mw-search-result-heading a',
+ '.mw-search-result-heading a,
#mw-search-DYM-suggestion, #mw-search-DYM-original, #mw-search-DYM-rewritten',
function ( evt ) {
+ var wprov,
// Sometimes the click event is on a
span inside the anchor
- var $target = $( evt.target ).closest(
'a' ),
- uri = new mw.Uri( $target.attr(
'href' ) ),
- // Only the primary anchor has
the data-serp-pos attribute, but we
- // might be updating a sub-link
like a section.
- index = $target.closest(
'.mw-search-result-heading' )
- .find(
'[data-serp-pos]' )
- .data( 'serp-pos' );
+ $target = $( evt.target
).closest( 'a' ),
+ params = {
+ // Only the primary
anchor has the data-serp-pos attribute, but we
+ // might be updating a
sub-link like a section.
+ position:
$target.closest( '.mw-search-result-heading' )
+ .find(
'[data-serp-pos]' )
+ .data(
'serp-pos' )
+ };
- if ( index !== undefined ) {
- uri.query.wprov =
search.wprovPrefix + index;
- $target.attr( 'href',
uri.toString() );
+ if ( params.position !== undefined ) {
+ wprov = params.position;
+ } else if ( $target.is(
'#mw-search-DYM-suggestion' ) ) {
+ wprov = 'dym1';
+ } else if ( $target.is(
'#mw-search-DYM-original' ) ) {
+ wprov = 'dymo1';
+ } else if ( $target.is(
'#mw-search-DYM-rewritten' ) ) {
+ wprov = 'dymr1';
}
- logEvent( 'click', {
- position: index
- } );
+
+ if ( wprov !== undefined ) {
+ genAttachWprov(
search.wprovPrefix + wprov ).apply( $target.get( 0 ) );
+ }
+
+ // Only log click events for clicks on
search results, not did you mean
+ if ( params.position !== undefined ) {
+ logEvent( 'click', params );
+ }
}
);
params = {
query: mw.config.get( 'searchTerm' ),
- hitsReturned: $( '.mw-search-result-heading'
).length
+ hitsReturned: $( '.mw-search-result-heading'
).length,
};
+
+ // Track what did you mean suggestions were displayed
on the page
+ if ( $( '#mw-search-DYM-suggestion' ).length ) {
+ params.didYouMeanVisible = 'yes';
+ } else if ( $( '#mw-search-DYM-rewritten' ).length ) {
+ params.didYouMeanVisible = 'autorewrite';
+ } else {
+ params.didYouMeanVisible = 'no';
+ }
+
// This method is called from jQuery.ready which runs
on DOMContentLoaded. Use domInteractive since that
// is immediately before DOMContentLoaded per spec.
if ( window.performance && window.performance.timing ) {
params.msToDisplayResults =
window.performance.timing.domInteractive -
window.performance.timing.navigationStart;
}
+ if ( search.didYouMean ) {
+ params.inputLocation =
didYouMeanMap[search.didYouMean];
+ }
+
logEvent( 'searchResultPage', params );
- } else if ( search.cameFromSearch ) {
+ }
+ if ( search.cameFromSearch ) {
logEvent( 'visitPage', {
position: search.resultPosition
} );
@@ -533,6 +602,9 @@
};
if ( autoComplete.cameFromSearch ) {
+ // @todo should this still fire if autocomplete sent
the user
+ // to Special:Search? This is incredibly common, for
example,
+ // for the autocomplete on the main special search page.
logEvent( 'visitPage', {
position: autoComplete.resultPosition
} );
diff --git a/tests/browser/SearchSatisfactionTests.php
b/tests/browser/SearchSatisfactionTests.php
index fa35bfc..260ba71 100644
--- a/tests/browser/SearchSatisfactionTests.php
+++ b/tests/browser/SearchSatisfactionTests.php
@@ -428,10 +428,44 @@
),
// Note that this test requires some page to exist with
the text 'mani page', or the
// did you mean will be rewritten automatically and
return search results for 'main page'
- 'full text search click the "did you mean" result' =>
array(
+ 'full text search click the "did you mean" rewritten
result' => array(
// actions
array(
$this->visitPage(
"Special:Search?search=mani%20page" ),
+ // if the button is clicked too quickly
the event doesn't fire because
+ // js hasn't loaded.
+ $this->sleep( 2 ),
+ $this->clickDidYouMeanRewritten(),
+ $this->sleep( 2 ),
+ ),
+ // expected events
+ array(
+ array( 'action' => 'searchResultPage',
'source' => 'fulltext', 'position' => null, 'inputLocation' => null,
'didYouMeanVisible' => 'autorewrite' ),
+ array( 'action' => 'searchResultPage',
'source' => 'fulltext', 'position' => null, 'inputLocation' => 'dym-rewritten',
'didYouMeanVisible' => 'no' ),
+ ),
+ ),
+ 'full text search click the "did you mean" original
result' => array(
+ // actions
+ array(
+ $this->visitPage(
"Special:Search?search=mani%20page" ),
+ // if the button is clicked too quickly
the event doesn't fire because
+ // js hasn't loaded.
+ $this->sleep( 2 ),
+ $this->clickDidYouMeanOriginal(),
+ $this->sleep( 2 ),
+ ),
+ // expected events
+ array(
+ // @TODO the did you mean should be
integrated and trigger some click event
+ array( 'action' => 'searchResultPage',
'source' => 'fulltext', 'position' => null, 'inputLocation' => null,
'didYouMeanVisible' => 'autorewrite' ),
+ array( 'action' => 'searchResultPage',
'source' => 'fulltext', 'position' => null, 'inputLocation' => 'dym-original',
'didYouMeanVisible' => 'no' ),
+ ),
+ ),
+ 'full text search click the "did you mean" suggestion
result' => array(
+ // actions
+ array(
+ $this->ensurePage( "Misspelled", "main
paeg" ),
+ $this->visitPage(
"Special:Search?search=main%20paeg" ),
// if the button is clicked too quickly
the event doesn't fire because
// js hasn't loaded.
$this->sleep( 2 ),
@@ -440,8 +474,8 @@
// expected events
array(
// @TODO the did you mean should be
integrated and trigger some click event
- array( 'action' => 'searchResultPage',
'source' => 'fulltext', 'position' => null ),
- array( 'action' => 'searchResultPage',
'source' => 'fulltext', 'position' => null ),
+ array( 'action' => 'searchResultPage',
'source' => 'fulltext', 'position' => null, 'inputLocation' => null,
'didYouMeanVisible' => 'yes' ),
+ array( 'action' => 'searchResultPage',
'source' => 'fulltext', 'position' => null, 'inputLocation' => 'dym-suggest',
'didYouMeanVisible' => 'no' ),
),
),
'Special:Search bar type then enter' => array(
@@ -742,10 +776,40 @@
};
}
+ /**
+ * Shown when the original search query was run, but the
+ * search engine has a suggestion for a better query
+ */
protected function clickDidYouMeanSuggestion() {
return function ( $webDriver ) {
$webDriver->findElement( WebDriverBy::cssSelector(
- '.searchdidyoumean a'
+ '#mw-search-DYM-suggestion'
+ ) )->click();
+ };
+ }
+
+ /**
+ * Shown when the rewritten search query was run. Gives
+ * the user a direct link to this search, which might show
+ * a new did you mean.
+ */
+ protected function clickDidYouMeanRewritten() {
+ return function ( $webDriver ) {
+ $webDriver->findElement( WebDriverBy::cssSelector(
+ '#mw-search-DYM-rewritten'
+ ) )->click();
+ };
+ }
+
+ /**
+ * Shown when the rewritten search query was run. Gives
+ * the user a direct link to the original search without
+ * it being rewritten.
+ */
+ protected function clickDidYouMeanOriginal() {
+ return function ( $webDriver ) {
+ $webDriver->findElement( WebDriverBy::cssSelector(
+ '#mw-search-DYM-original'
) )->click();
};
}
--
To view, visit https://gerrit.wikimedia.org/r/311654
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I8983a9677ffd9d98c8a347ef0961c2f0e874393f
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/WikimediaEvents
Gerrit-Branch: master
Gerrit-Owner: EBernhardson <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits