Implement asyncSearch (WIP)
This commit is contained in:
parent
ac078e445b
commit
fd673a4ea0
|
@ -284,8 +284,6 @@ export function createNewValue (doc, selection, type) {
|
|||
: ''
|
||||
})
|
||||
|
||||
console.log('structure', jsonExample, structure)
|
||||
|
||||
return structure
|
||||
} else {
|
||||
// no example structure
|
||||
|
|
|
@ -130,6 +130,85 @@ function searchRecursive (key, doc, searchText) {
|
|||
return results
|
||||
}
|
||||
|
||||
async function tick () {
|
||||
return new Promise(setTimeout)
|
||||
}
|
||||
|
||||
// TODO: comment
|
||||
export function searchAsync (searchText, doc, { onPartlyResults, onDone }) {
|
||||
const yieldAfterItemCount = 10000 // TODO: what is a good value?
|
||||
const search = searchGenerator(searchText, doc, yieldAfterItemCount)
|
||||
|
||||
// TODO: implement pause after having found x results (like 999)
|
||||
|
||||
let cancelled = false
|
||||
const results = []
|
||||
|
||||
async function executeSearch () {
|
||||
let next
|
||||
do {
|
||||
next = search.next()
|
||||
if (next.value) {
|
||||
results.push(next.value) // TODO: make this immutable?
|
||||
onPartlyResults(results)
|
||||
}
|
||||
await tick() // TODO: be able to wait longer than just one tick? So the UI stays fully responsive?
|
||||
} while (!cancelled && !next.done)
|
||||
|
||||
if (next.done) {
|
||||
onDone(results)
|
||||
} // else: cancelled
|
||||
}
|
||||
|
||||
// start searching on the next tick
|
||||
setTimeout(executeSearch)
|
||||
|
||||
return {
|
||||
cancel: () => {
|
||||
cancelled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: comment
|
||||
export function * searchGenerator (searchText, doc, yieldAfterItemCount = undefined) {
|
||||
let count = 0
|
||||
|
||||
function * incrementCounter () {
|
||||
count++
|
||||
if (typeof yieldAfterItemCount === 'number' && count % yieldAfterItemCount === 0) {
|
||||
// pause every x items
|
||||
yield null
|
||||
}
|
||||
}
|
||||
|
||||
function * searchRecursiveAsync (searchText, doc, path) {
|
||||
const type = valueType(doc)
|
||||
|
||||
if (type === 'array') {
|
||||
for (let i = 0; i < doc.length; i++) {
|
||||
yield * searchRecursiveAsync(searchText, doc[i], path.concat([i]))
|
||||
}
|
||||
} else if (type === 'object') {
|
||||
for (const prop of Object.keys(doc)) {
|
||||
if (typeof prop === 'string' && containsCaseInsensitive(prop, searchText)) {
|
||||
yield path.concat([prop, STATE_SEARCH_PROPERTY])
|
||||
}
|
||||
yield * incrementCounter()
|
||||
|
||||
yield * searchRecursiveAsync(searchText, doc[prop], path.concat([prop]))
|
||||
}
|
||||
} else { // type is a value
|
||||
if (containsCaseInsensitive(doc, searchText)) {
|
||||
yield path.concat([STATE_SEARCH_VALUE])
|
||||
}
|
||||
yield * incrementCounter()
|
||||
}
|
||||
}
|
||||
|
||||
return yield * searchRecursiveAsync(searchText, doc, [])
|
||||
}
|
||||
|
||||
function flattenSearch (searchResult) {
|
||||
const resultArray = []
|
||||
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
import assert from 'assert'
|
||||
import { times } from 'lodash-es'
|
||||
import { searchAsync, searchGenerator } from './search.js'
|
||||
import { STATE_SEARCH_PROPERTY, STATE_SEARCH_VALUE } from '../constants.js'
|
||||
|
||||
describe('search', () => {
|
||||
it('should search with generator', () => {
|
||||
const doc = {
|
||||
b: { c: 'a' },
|
||||
a: [
|
||||
{ a: 'b', c: 'a' },
|
||||
'e',
|
||||
'a'
|
||||
]
|
||||
}
|
||||
|
||||
const search = searchGenerator('a', doc)
|
||||
|
||||
assert.deepStrictEqual(search.next(), { done: false, value: ['b', 'c', STATE_SEARCH_VALUE] })
|
||||
assert.deepStrictEqual(search.next(), { done: false, value: ['a', STATE_SEARCH_PROPERTY] })
|
||||
assert.deepStrictEqual(search.next(), { done: false, value: ['a', 0, 'a', STATE_SEARCH_PROPERTY] })
|
||||
assert.deepStrictEqual(search.next(), { done: false, value: ['a', 0, 'c', STATE_SEARCH_VALUE] })
|
||||
assert.deepStrictEqual(search.next(), { done: false, value: ['a', 2, STATE_SEARCH_VALUE] })
|
||||
assert.deepStrictEqual(search.next(), { done: true, value: undefined })
|
||||
})
|
||||
|
||||
it('should yield every x items during search', () => {
|
||||
const doc = times(30, index => String(index))
|
||||
|
||||
const search = searchGenerator('4', doc, 10)
|
||||
assert.deepStrictEqual(search.next(), { done: false, value: [4, STATE_SEARCH_VALUE] })
|
||||
assert.deepStrictEqual(search.next(), { done: false, value: null }) // at 10
|
||||
assert.deepStrictEqual(search.next(), { done: false, value: [14, STATE_SEARCH_VALUE] })
|
||||
assert.deepStrictEqual(search.next(), { done: false, value: null }) // at 20
|
||||
assert.deepStrictEqual(search.next(), { done: false, value: [24, STATE_SEARCH_VALUE] })
|
||||
assert.deepStrictEqual(search.next(), { done: false, value: null }) // at 30
|
||||
assert.deepStrictEqual(search.next(), { done: true, value: undefined })
|
||||
})
|
||||
|
||||
it('should search async', (done) => {
|
||||
const doc = times(30, index => String(index))
|
||||
|
||||
const callbacks = []
|
||||
|
||||
function onPartlyResults (results) {
|
||||
callbacks.push(results.slice(0))
|
||||
}
|
||||
|
||||
function onDone (results) {
|
||||
assert.deepStrictEqual(results, [
|
||||
[4, STATE_SEARCH_VALUE],
|
||||
[14, STATE_SEARCH_VALUE],
|
||||
[24, STATE_SEARCH_VALUE]
|
||||
])
|
||||
|
||||
assert.deepStrictEqual(callbacks, [
|
||||
results.slice(0, 1),
|
||||
results.slice(0, 2),
|
||||
results.slice(0, 3)
|
||||
])
|
||||
|
||||
done()
|
||||
}
|
||||
|
||||
searchAsync('4', doc, { onPartlyResults, onDone })
|
||||
|
||||
// should not have results right after creation, but only on the first next tick
|
||||
assert.deepStrictEqual(callbacks, [])
|
||||
})
|
||||
|
||||
it('should cancel async search', (done) => {
|
||||
const doc = times(30, index => String(index))
|
||||
|
||||
const callbacks = []
|
||||
|
||||
function onPartlyResults (results) {
|
||||
callbacks.push(results.slice(0))
|
||||
}
|
||||
|
||||
function onDone () {
|
||||
throw new Error('onDone should not be invoked')
|
||||
}
|
||||
|
||||
const { cancel } = searchAsync('4', doc, { onPartlyResults, onDone })
|
||||
|
||||
// should not have results right after creation, but only on the first next tick
|
||||
assert.deepStrictEqual(callbacks, [])
|
||||
|
||||
setTimeout(() => {
|
||||
cancel()
|
||||
|
||||
assert.deepStrictEqual(callbacks, [
|
||||
[
|
||||
[4, STATE_SEARCH_VALUE]
|
||||
]
|
||||
])
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue