From c5934fd64eea783fd7ba822940113d574de7e6b9 Mon Sep 17 00:00:00 2001 From: Hanna Date: Wed, 14 Oct 2020 15:59:22 +0200 Subject: [PATCH] eachFind: first draft --- src/JsonModel/JsonModel.js | 46 +++++++++++++++++++++++++++++++++++ src/lib/settleAllFindFirst.js | 24 ++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/lib/settleAllFindFirst.js diff --git a/src/JsonModel/JsonModel.js b/src/JsonModel/JsonModel.js index cac89ed..99a6217 100644 --- a/src/JsonModel/JsonModel.js +++ b/src/JsonModel/JsonModel.js @@ -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') @@ -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} 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 diff --git a/src/lib/settleAllFindFirst.js b/src/lib/settleAllFindFirst.js new file mode 100644 index 0000000..027ba5a --- /dev/null +++ b/src/lib/settleAllFindFirst.js @@ -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 +}