Skip to content

Commit 3d621ba

Browse files
authored
Start an AB test with Lunr search results as treatment (#18993)
* Start search_lunr AB test * Towards prewarming * Towards prewarming * Warm only english/dotcom index * Update warm-server.js
1 parent 66f7874 commit 3d621ba

File tree

4 files changed

+51
-6
lines changed

4 files changed

+51
-6
lines changed

javascripts/experiment.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,13 @@ export default function () {
2727
// const x = document.querySelector(...)
2828
// x.addEventListener('click', () => { sendSuccess(testName) })
2929
// if (xbucket === TREATMENT) applyTreatment(x)
30+
31+
const testName = 'search_lunr'
32+
const xbucket = bucket(testName)
33+
document.addEventListener('click', evt => {
34+
if (!evt.target.closest('.search-result > a')) return
35+
console.log(testName, xbucket) // eslint-disable-line
36+
sendSuccess(testName)
37+
})
38+
// Treatment code in middleware/search.js
3039
}

lib/search/lunr-search.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
const path = require('path')
22
const lunr = require('lunr')
3+
require('lunr-languages/lunr.stemmer.support')(lunr)
4+
require('lunr-languages/tinyseg')(lunr)
5+
require('lunr-languages/lunr.ja')(lunr)
6+
require('lunr-languages/lunr.es')(lunr)
7+
require('lunr-languages/lunr.pt')(lunr)
8+
require('lunr-languages/lunr.de')(lunr)
39
const { get } = require('lodash')
410
const readFileAsync = require('../readfile-async')
511
const { namePrefix } = require('./config')
@@ -9,7 +15,7 @@ const LUNR_DIR = './indexes'
915
const lunrIndexes = new Map()
1016
const lunrRecords = new Map()
1117

12-
module.exports = async function loadLunrResults ({ version, language, query, limit }) {
18+
async function loadLunrResults ({ version, language, query, limit }) {
1319
const indexName = `${namePrefix}-${version}-${language}`
1420
if (!lunrIndexes.has(indexName) || !lunrRecords.has(indexName)) {
1521
lunrIndexes.set(indexName, await loadLunrIndex(indexName))
@@ -33,6 +39,15 @@ module.exports = async function loadLunrResults ({ version, language, query, lim
3339
return results
3440
}
3541

42+
loadLunrResults.warmLunr = async function warmLunr () {
43+
// Took about 60 seconds on local to warm them all...
44+
// so doing just the most common for prewarming
45+
const indexName = 'github-docs-dotcom-en'
46+
lunrRecords.set(indexName, await loadLunrRecords(indexName))
47+
lunrIndexes.set(indexName, await loadLunrIndex(indexName))
48+
return true
49+
}
50+
3651
async function loadLunrIndex (indexName) {
3752
const filePath = path.posix.join(__dirname, LUNR_DIR, `${indexName}.json.br`)
3853
// Do not set to 'utf8' on file reads
@@ -82,3 +97,5 @@ function field (result, record, name) {
8297
function mark (text) {
8398
return `<mark>${text}</mark>`
8499
}
100+
101+
module.exports = loadLunrResults

lib/warm-server.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const { loadPages, loadPageMap } = require('./pages')
33
const loadRedirects = require('./redirects/precompile')
44
const loadSiteData = require('./site-data')
55
const loadSiteTree = require('./site-tree')
6+
const { warmLunr } = require('./search/lunr-search')
67

78
// Instrument these functions so that
89
// it's wrapped in a timer that reports to Datadog
@@ -11,15 +12,16 @@ const dog = {
1112
loadPageMap: statsd.asyncTimer(loadPageMap, 'load_page_map'),
1213
loadRedirects: statsd.asyncTimer(loadRedirects, 'load_redirects'),
1314
loadSiteData: statsd.timer(loadSiteData, 'load_site_data'),
14-
loadSiteTree: statsd.asyncTimer(loadSiteTree, 'load_site_tree')
15+
loadSiteTree: statsd.asyncTimer(loadSiteTree, 'load_site_tree'),
16+
warmLunr: statsd.asyncTimer(warmLunr, 'warm_lunr')
1517
}
1618

1719
// For local caching
18-
let pageList, pageMap, site, redirects, siteTree
20+
let pageList, pageMap, site, redirects, siteTree, isLunrWarmed
1921

2022
function isFullyWarmed () {
2123
// NOTE: Yes, `pageList` is specifically excluded here as it is transient data
22-
const fullyWarmed = !!(pageMap && site && redirects && siteTree)
24+
const fullyWarmed = !!(pageMap && site && redirects && siteTree && isLunrWarmed)
2325
return fullyWarmed
2426
}
2527

@@ -28,7 +30,8 @@ function getWarmedCache () {
2830
pages: pageMap,
2931
site,
3032
redirects,
31-
siteTree
33+
siteTree,
34+
isLunrWarmed
3235
}
3336
}
3437

@@ -59,6 +62,10 @@ async function warmServer () {
5962
siteTree = await dog.loadSiteTree(pageMap, site, redirects)
6063
}
6164

65+
if (!isLunrWarmed) {
66+
isLunrWarmed = /* await */ dog.warmLunr()
67+
}
68+
6269
if (process.env.NODE_ENV !== 'test') {
6370
console.log(`Context primed in ${Date.now() - startTime} ms`)
6471
}

middleware/search.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ const versions = new Set(Object.values(require('../lib/search/versions')))
44
const loadLunrResults = require('../lib/search/lunr-search')
55
const loadAlgoliaResults = require('../lib/search/algolia-search')
66

7+
// temp
8+
const murmur = require('imurmurhash')
9+
// end temp
10+
711
const router = express.Router()
812

913
router.get('/', async function postSearch (req, res, next) {
@@ -21,8 +25,16 @@ router.get('/', async function postSearch (req, res, next) {
2125
return res.status(200).json([])
2226
}
2327

28+
// temp
29+
const userEventsId = req.cookies['_docs-events']
30+
const experimentHash = userEventsId
31+
? murmur('search_lunr').hash(userEventsId).result()
32+
: 0
33+
const inTreatment = experimentHash % 2
34+
// end temp
35+
2436
try {
25-
const results = process.env.AIRGAP || req.cookies.AIRGAP
37+
const results = process.env.AIRGAP || req.cookies.AIRGAP || inTreatment
2638
? await loadLunrResults({ version, language, query: `${query} ${filters || ''}`, limit })
2739
: await loadAlgoliaResults({ version, language, query, filters, limit })
2840
return res.status(200).json(results)

0 commit comments

Comments
 (0)