Skip to content

Instantly share code, notes, and snippets.

@olchyk98
Last active November 4, 2024 20:47
Show Gist options
  • Select an option

  • Save olchyk98/868d0ef4ae4e2fdcf751e554effe52b9 to your computer and use it in GitHub Desktop.

Select an option

Save olchyk98/868d0ef4ae4e2fdcf751e554effe52b9 to your computer and use it in GitHub Desktop.
import { MongoClient } from 'mongodb'
import Bluebird from 'bluebird'
import { add, identity, times } from 'ramda'
const client = new MongoClient('<redacted>')
const db = client.db('searchtest')
const limit = 1e6
const runs = 1
function setupIndex () {
return db
.collection('file')
.createIndex({ name: 'text' })
}
function choice<T> (i: T[]): T | never {
const length = i.length
const index = Math.floor(Math.random() * length)
if (!i[index]) throw new Error()
return i[index]!
}
async function fixture () {
const lib = [
[ 'Super', 'Extra', 'Extreme', 'Cool' ],
[ 'Sales', 'Fails', 'Pales', 'Fales', 'Males', 'Rails' ],
]
const payloads = times(() => {
const firstLib = lib[0]!
const secondLib = lib[1]!
const first = choice<string>(firstLib)
const second = choice<string>(secondLib)
const third = Math.floor(Math.random() * 1e6)
return {
_id: Math.random().toString(16),
name: `${first} ${second} ${third}`,
}
}, 1e6)
console.log(`Generated ${payloads.length} payloads`)
console.log('Inserting')
return db
.collection<File>('file')
.insertMany(payloads)
}
async function searchWithRegex (query: string) {
return db
.collection<File>('file')
.find({
name: { $regex: query, $options: 'i' },
})
.limit(limit)
.explain('executionStats')
.then((r) => r.executionStats.executionTimeMillis)
}
async function searchWithText (query: string) {
return db
.collection<File>('file')
.find({
$text: { $search: query, $caseSensitive: false },
})
.limit(limit)
.explain('executionStats')
.then((r) => r.executionStats.executionTimeMillis)
}
async function searchWithAtlasSearch (query: string) {
return db
.collection<File>('file')
.aggregate(
[
{
$search: {
index: 'name',
text: {
query,
path: {
wildcard: '*',
},
},
},
},
{
$limit: limit,
},
],
{ explain: true },
)
.explain('executionStats')
.then((r) => r.stages[0].executionTimeMillisEstimate)
}
function avg (i: number[]) {
const sum = i.reduce(add, 0)
return sum / i.length
}
async function time (fn: () => Promise<number>, pattern: string): Promise<string> {
const t: number[] = []
const tt: number[] = []
console.log(`Benching "${pattern}":`)
await Bluebird.mapSeries(
times(identity, runs),
async () => {
const s = Date.now()
const r = await fn()
t.push(r)
tt.push(Date.now() - s)
},
)
const statsObj = {
avg: avg(t) + 'ms',
min: Math.min(...t) + 'ms',
max: Math.max(...t) + 'ms',
avgRuntime: avg(tt) + 'ms',
}
const stats = `avgDb: ${statsObj.avg} | avgRuntime: ${statsObj.avgRuntime} | minDb: ${statsObj.min} | maxDb: ${statsObj.max} | runs: ${runs} | queryLimit: ${limit}`
return `${JSON.stringify(stats)}`
}
async function main () {
console.log('Connecting')
await client.connect()
console.log('Dropping DB')
await db.dropDatabase()
console.log('Indexing')
await setupIndex()
console.log('Applying fixture')
await fixture()
console.log('Searching')
const query = 'Extreme'
console.log(await time(() => searchWithAtlasSearch(query), '$search'))
console.log(await time(() => searchWithText(query), '$text'))
console.log(await time(() => searchWithRegex(query), '$regex'))
process.exit(0)
}
main()
interface File {
_id: string
name: string
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment