Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions src/JsonModel/JsonModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {verifyOptions, verifyColumn} from './verifyOptions'
import {makeMigrations} from './makeMigrations'
import {makeIdValue} from './makeDefaultIdValue'
import {settleAll} from '../lib/settleAll'
import {settleAllFindFirst} from '../lib/settleAllFindFirst'
import {DEV, deprecated} from '../lib/warning'

const dbg = debug('strato-db/JSON')
Expand Down Expand Up @@ -829,6 +830,51 @@ class JsonModel {
} while (cursor)
}

/**
* Iterate through search results. Calls `fn` on every result.
* Finish on the first positive fn result (in limit portion).
* The iteration uses a cursored search, so changes to the model
* during the iteration can influence the iteration.
*
* @param {SearchAttrs|RowCallback} attrsOrFn
* @param {RowCallback|SearchOptions} [optionsOrFn]
* @param {RowCallback} [fn]
* @returns {Promise<void>} table iteration completed
*/
async eachFind(attrsOrFn, optionsOrFn, fn) {
if (!fn) {
if (optionsOrFn) {
if (typeof optionsOrFn === 'function') {
fn = optionsOrFn
optionsOrFn = undefined
} else {
fn = optionsOrFn.fn
delete optionsOrFn.fn
}
} else if (typeof attrsOrFn === 'function') {
fn = attrsOrFn
attrsOrFn = undefined
}
if (!fn) throw new Error('each requires function')
}
if (!optionsOrFn) optionsOrFn = {}
if (!optionsOrFn.limit) optionsOrFn.limit = 10
optionsOrFn.noCursor = false
optionsOrFn.noTotal = true
let cursor
let i = 0
let stop = false

do {
// eslint-disable-next-line no-await-in-loop
const result = await this.search(attrsOrFn, {...optionsOrFn, cursor})
cursor = result.cursor
// eslint-disable-next-line no-await-in-loop
const resFound = await settleAllFindFirst(result.items, v => fn(v, i++))
if (resFound) stop = true
} while (cursor && !stop)
}

// --- Mutator methods below ---

// Contract: All subclasses use set() to store values
Expand Down
24 changes: 24 additions & 0 deletions src/lib/settleAllFindFirst.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export /**
* Execute all fn (asynchronously), if fn returns values, it returns the first result
*
* @param {[objects]} items
* @param {function} fn
* @returns {any} first positive result
*/
const settleAllFindFirst = async (items, fn) => {
let err
const results = await Promise.all(
items.map(async i => {
try {
const res = await fn(i)
if (res) return res
} catch (error) {
// last one wins
err = error
}
})
)
if (err) throw err
if (results?.filter(Boolean).length) return results[0]
return false
}