From f86e09363149c6af045dfaaaed765cbc09b3c132 Mon Sep 17 00:00:00 2001 From: Hieuzest Date: Fri, 19 Apr 2024 13:30:57 +0800 Subject: [PATCH 1/3] feat(minato): support subquery in set --- packages/core/src/database.ts | 4 +-- packages/core/src/eval.ts | 28 +++++++++++----- packages/core/src/type.ts | 1 + packages/mongo/src/builder.ts | 17 +++++++--- packages/mongo/src/index.ts | 59 +++++++++++++++++++++------------ packages/mysql/src/builder.ts | 3 +- packages/mysql/src/index.ts | 3 +- packages/sqlite/src/index.ts | 35 +++++++++++++++---- packages/tests/src/selection.ts | 11 ++++++ packages/tests/src/update.ts | 24 ++++++++++++++ 10 files changed, 140 insertions(+), 45 deletions(-) diff --git a/packages/core/src/database.ts b/packages/core/src/database.ts index bea2c851..bf60b0ec 100644 --- a/packages/core/src/database.ts +++ b/packages/core/src/database.ts @@ -1,4 +1,4 @@ -import { Dict, makeArray, mapValues, MaybeArray, omit, valueMap } from 'cosmokit' +import { defineProperty, Dict, makeArray, mapValues, MaybeArray, omit, valueMap } from 'cosmokit' import { Context, Service, Spread } from 'cordis' import { FlatKeys, FlatPick, Indexable, Keys, randomId, Row, unravel } from './utils.ts' import { Selection } from './selection.ts' @@ -117,7 +117,7 @@ export class Database extends Servi }) model.extend(fields, config) if (makeArray(model.primary).every(key => key in fields)) { - model.ctx = this[Context.origin] + defineProperty(model, 'ctx', this[Context.origin]) } this.prepareTasks[name] = this.prepare(name) ;(this.ctx as Context).emit('model', name) diff --git a/packages/core/src/eval.ts b/packages/core/src/eval.ts index 50381fbf..848f9f16 100644 --- a/packages/core/src/eval.ts +++ b/packages/core/src/eval.ts @@ -7,6 +7,18 @@ export function isEvalExpr(value: any): value is Eval.Expr { return value && Object.keys(value).some(key => key.startsWith('$')) } +export function hasSubquery(value: any): boolean { + if (!isEvalExpr(value)) return false + return Object.entries(value).filter(([k]) => k.startsWith('$')).some(([k, v]) => { + if (isNullable(v) || isComparable(v)) return false + if (k === '$exec') return true + if (isEvalExpr(v)) return hasSubquery(v) + if (Array.isArray(v)) return v.some(x => hasSubquery(x)) + if (typeof v === 'object') return Object.values(v).some(x => hasSubquery(x)) + return false + }) +} + export type Uneval = | U extends number ? Eval.Term : U extends string ? Eval.Term @@ -41,9 +53,9 @@ export namespace Eval { export type Binary = (x: Term, y: Term) => Expr export type Multi = (...args: Term[]) => Expr - export interface Aggr { - (value: Term): Expr - (value: Array): Expr + export interface Aggr { + (value: Term): Expr + (value: Array): Expr } export interface Branch { @@ -105,14 +117,14 @@ export namespace Eval { not: Unary // typecast - literal(value: T, type?: Field.Type | Field.NewType | string): Expr + literal(value: T, type?: Type | Field.Type | Field.NewType | string): Expr number: Unary // aggregation / json - sum: Aggr - avg: Aggr - max: Aggr & Aggr - min: Aggr & Aggr + sum: Aggr + avg: Aggr + max: Aggr + min: Aggr count(value: Any): Expr length(value: Any): Expr size(value: (Any | Expr)[] | Expr): Expr diff --git a/packages/core/src/type.ts b/packages/core/src/type.ts index 439e474a..ef94947c 100644 --- a/packages/core/src/type.ts +++ b/packages/core/src/type.ts @@ -43,6 +43,7 @@ export namespace Type { else if (typeof value === 'number') return Number as any else if (typeof value === 'string') return String as any else if (typeof value === 'boolean') return Boolean as any + else if (typeof value === 'bigint') return fromField('bigint' as any) else if (value instanceof Date) return fromField('timestamp' as any) else if (Binary.is(value)) return fromField('binary' as any) else if (globalThis.Array.isArray(value)) return Array(value.length ? fromPrimitive(value[0]) : undefined) as any diff --git a/packages/mongo/src/builder.ts b/packages/mongo/src/builder.ts index b73d5e02..a6e96ad8 100644 --- a/packages/mongo/src/builder.ts +++ b/packages/mongo/src/builder.ts @@ -128,8 +128,7 @@ export class Builder { $random: (arg, group) => ({ $rand: {} }), $literal: (arg, group) => { - const converter = this.driver.types[arg[1] as any] - return converter ? converter.dump(arg[0]) : arg[0] + return { $literal: this.dump(arg[0], arg[1] ? Type.fromField(arg[1]) : undefined) } }, $number: (arg, group) => { const value = this.eval(arg, group) @@ -381,8 +380,8 @@ export class Builder { return predecessor.select(sel) } - public select(sel: Selection.Immutable) { - const { table, query } = sel + public select(sel: Selection.Immutable, update?: any) { + const { model, table, query } = sel if (typeof table === 'string') { this.table = table this.refVirtualKeys[sel.ref] = this.virtualKey = (sel.driver as MongoDriver).getVirtualKey(table)! @@ -438,6 +437,16 @@ export class Builder { this.pipeline.push({ $group }, ...this.flushLookups(), { $project }) } this.evalKey = $ + } else if (sel.type === 'set') { + const $set = valueMap(update, (expr, key) => this.eval(isEvalExpr(expr) ? expr : Eval.literal(expr, model.getType(key)))) + this.pipeline.push(...this.flushLookups(), { $set }, { + $merge: { + into: table, + on: '_id', + whenMatched: 'replace', + whenNotMatched: 'discard', + }, + }) } return this } diff --git a/packages/mongo/src/index.ts b/packages/mongo/src/index.ts index 7701d402..b8acb552 100644 --- a/packages/mongo/src/index.ts +++ b/packages/mongo/src/index.ts @@ -1,6 +1,6 @@ import { BSONType, ClientSession, Collection, Db, IndexDescription, Long, MongoClient, MongoClientOptions, MongoError } from 'mongodb' import { Binary, Dict, isNullable, makeArray, mapValues, noop, omit, pick } from 'cosmokit' -import { Driver, Eval, executeUpdate, Query, RuntimeError, Selection, z } from 'minato' +import { Driver, Eval, executeUpdate, hasSubquery, Query, RuntimeError, Selection, z } from 'minato' import { URLSearchParams } from 'url' import { Builder } from './builder' @@ -302,17 +302,20 @@ export class MongoDriver extends Driver { async get(sel: Selection.Immutable) { const transformer = new Builder(this, Object.keys(sel.tables)).select(sel) if (!transformer) return [] - this.logger.debug('%s %s', transformer.table, JSON.stringify(transformer.pipeline)) + this.logPipeline(transformer.table, transformer.pipeline) return this.db .collection(transformer.table) .aggregate(transformer.pipeline, { allowDiskUse: true, session: this.session }) - .toArray().then(rows => rows.map(row => this.builder.load(row, sel.model))) + .toArray().then(rows => { + // console.dir(rows, { depth: 8 }) + return rows.map(row => this.builder.load(row, sel.model)) + }) } async eval(sel: Selection.Immutable, expr: Eval.Expr) { const transformer = new Builder(this, Object.keys(sel.tables)).select(sel) if (!transformer) return - this.logger.debug('%s %s', transformer.table, JSON.stringify(transformer.pipeline)) + this.logPipeline(transformer.table, transformer.pipeline) const res = await this.db .collection(transformer.table) .aggregate(transformer.pipeline, { allowDiskUse: true, session: this.session }) @@ -322,25 +325,33 @@ export class MongoDriver extends Driver { async set(sel: Selection.Mutable, update: {}) { const { query, table, model } = sel - const filter = this.transformQuery(sel, query, table) - if (!filter) return {} - const coll = this.db.collection(table) + if (hasSubquery(sel.query) || Object.values(update).some(x => hasSubquery(x))) { + const transformer = new Builder(this, Object.keys(sel.tables)).select(sel, update)! + await this.db.collection(transformer.table) + .aggregate(transformer.pipeline, { allowDiskUse: true, session: this.session }) + .toArray() + return {} // result not available + } else { + const filter = this.transformQuery(sel, query, table) + if (!filter) return {} + const coll = this.db.collection(table) - const transformer = new Builder(this, Object.keys(sel.tables), this.getVirtualKey(table), '$' + tempKey + '.') - const $set = this.builder.formatUpdateAggr(model.getType(), mapValues(this.builder.dump(update, model), - (value: any) => typeof value === 'string' && value.startsWith('$') ? { $literal: value } : transformer.eval(value))) - const $unset = Object.entries($set) - .filter(([_, value]) => typeof value === 'object') - .map(([key, _]) => key) - const preset = Object.fromEntries(transformer.walkedKeys.map(key => [tempKey + '.' + key, '$' + key])) - - const result = await coll.updateMany(filter, [ - ...transformer.walkedKeys.length ? [{ $set: preset }] : [], - ...$unset.length ? [{ $unset }] : [], - { $set }, - ...transformer.walkedKeys.length ? [{ $unset: [tempKey] }] : [], - ], { session: this.session }) - return { matched: result.matchedCount, modified: result.modifiedCount } + const transformer = new Builder(this, Object.keys(sel.tables), this.getVirtualKey(table), '$' + tempKey + '.') + const $set = this.builder.formatUpdateAggr(model.getType(), mapValues(this.builder.dump(update, model), + (value: any) => typeof value === 'string' && value.startsWith('$') ? { $literal: value } : transformer.eval(value))) + const $unset = Object.entries($set) + .filter(([_, value]) => typeof value === 'object') + .map(([key, _]) => key) + const preset = Object.fromEntries(transformer.walkedKeys.map(key => [tempKey + '.' + key, '$' + key])) + + const result = await coll.updateMany(filter, [ + ...transformer.walkedKeys.length ? [{ $set: preset }] : [], + ...$unset.length ? [{ $unset }] : [], + { $set }, + ...transformer.walkedKeys.length ? [{ $unset: [tempKey] }] : [], + ], { session: this.session }) + return { matched: result.matchedCount, modified: result.modifiedCount } + } } async remove(sel: Selection.Mutable) { @@ -476,6 +487,10 @@ See https://www.mongodb.com/docs/manual/tutorial/convert-standalone-to-replica-s await callback(undefined) } } + + logPipeline(table: string, pipeline: any) { + this.logger.debug('%s %s', table, JSON.stringify(pipeline, (_, value) => typeof value === 'bigint' ? `${value}n` : value)) + } } export namespace MongoDriver { diff --git a/packages/mysql/src/builder.ts b/packages/mysql/src/builder.ts index ffcf27f2..a76b41b9 100644 --- a/packages/mysql/src/builder.ts +++ b/packages/mysql/src/builder.ts @@ -118,8 +118,7 @@ export class MySQLBuilder extends Builder { protected encode(value: string, encoded: boolean, pure: boolean = false, type?: Type) { return this.asEncoded(encoded === this.isEncoded() && !pure ? value : encoded - ? (this.compat.maria ? `json_extract(json_object('v', ${this.transform(value, type, 'encode')}), '$.v')` - : `cast(${this.transform(value, type, 'encode')} as json)`) + ? `json_extract(json_object('v', ${this.transform(value, type, 'encode')}), '$.v')` : this.transform(`json_unquote(${value})`, type, 'decode'), pure ? undefined : encoded) } diff --git a/packages/mysql/src/index.ts b/packages/mysql/src/index.ts index c07a1be7..81366729 100644 --- a/packages/mysql/src/index.ts +++ b/packages/mysql/src/index.ts @@ -405,7 +405,8 @@ INSERT INTO mtt VALUES(json_extract(j, concat('$[', i, ']'))); SET i=i+1; END WH const escaped = escapeId(field) return `${escaped} = ${builder.toUpdateExpr(data, field, fields[field], false)}` }).join(', ') - const result = await this.query(`UPDATE ${escapeId(table)} ${ref} SET ${update} WHERE ${filter}`) + const sql = [...builder.prequeries, `UPDATE ${escapeId(table)} ${ref} SET ${update} WHERE ${filter}`].join('; ') + const result = await this.query(sql) return { matched: result.affectedRows, modified: result.changedRows } } diff --git a/packages/sqlite/src/index.ts b/packages/sqlite/src/index.ts index 7e10ed15..62224fa6 100644 --- a/packages/sqlite/src/index.ts +++ b/packages/sqlite/src/index.ts @@ -1,5 +1,5 @@ -import { Binary, deepEqual, Dict, difference, isNullable, makeArray } from 'cosmokit' -import { Driver, Eval, executeUpdate, Field, Selection, z } from 'minato' +import { Binary, deepEqual, Dict, difference, isNullable, makeArray, mapValues } from 'cosmokit' +import { Driver, Eval, executeUpdate, Field, hasSubquery, isEvalExpr, Selection, z } from 'minato' import { escapeId } from '@minatojs/sql-utils' import { resolve } from 'node:path' import { readFile, writeFile } from 'node:fs/promises' @@ -10,6 +10,15 @@ import zhCN from './locales/zh-CN.yml' import { SQLiteBuilder } from './builder' import { pathToFileURL } from 'node:url' +function getValue(obj: any, path: string) { + if (path.includes('.')) { + const index = path.indexOf('.') + return getValue(obj[path.slice(0, index)] ?? {}, path.slice(index + 1)) + } else { + return obj[path] + } +} + function getTypeDef({ deftype: type }: Field) { switch (type) { case 'primary': @@ -333,11 +342,25 @@ export class SQLiteDriver extends Driver { return Object.keys(fields).find(field => field === key || key.startsWith(field + '.'))! }))] const primaryFields = makeArray(primary) - const data = await this.database.get(table as never, query) - for (const row of data) { - this._update(sel, primaryFields, updateFields, update, row) + if ((Object.keys(query).length === 1 && query.$expr) || hasSubquery(sel.query) || Object.values(update).some(x => hasSubquery(x))) { + const sel2 = this.database.select(table as never, query) + sel2.tables[sel.ref] = sel2.table[sel2.ref] + delete sel2.table[sel2.ref] + sel2.ref = sel.ref + const project = mapValues(update as any, (value, key) => () => (isEvalExpr(value) ? value : Eval.literal(value, model.getType(key)))) + const rawUpsert = await sel2.project({ ...project, ...Object.fromEntries(primaryFields.map(x => [x, x])) } as any).execute() + const upsert = rawUpsert.map(row => ({ + ...mapValues(update, (_, key) => getValue(row, key)), + ...Object.fromEntries(primaryFields.map(x => [x, getValue(row, x)])), + })) + return this.database.upsert(table, upsert) + } else { + const data = await this.database.get(table as never, query) + for (const row of data) { + this._update(sel, primaryFields, updateFields, update, row) + } + return { matched: data.length } } - return { matched: data.length } } _create(table: string, data: {}) { diff --git a/packages/tests/src/selection.ts b/packages/tests/src/selection.ts index 79ad64da..4d156a8a 100644 --- a/packages/tests/src/selection.ts +++ b/packages/tests/src/selection.ts @@ -535,6 +535,17 @@ namespace SelectionTests { { x: [{ id: 3 }, { id: 4 }] }, ]) }) + + it('return aggregate', async () => { + await expect(database.select('foo') + .project({ x: row => database.select('bar', r => $.eq(r.pid, row.id)).evaluate(r => $.max(r.value)) }) + .execute() + ).to.eventually.have.shape([ + { x: 1 }, + { x: 0 }, + { x: 1 }, + ]) + }) } } diff --git a/packages/tests/src/update.ts b/packages/tests/src/update.ts index 5523dd82..f70a69fc 100644 --- a/packages/tests/src/update.ts +++ b/packages/tests/src/update.ts @@ -209,6 +209,16 @@ namespace OrmOperations { await expect(database.get('temp2', {})).to.eventually.have.shape(table) }) + it('using expressions in query', async () => { + const table = await setup(database, 'temp2', barTable) + table[1].num = table[1].id * 2 + table[2].num = table[2].id * 2 + await database.set('temp2', row => $.in(row.id, [table[1].id, table[2].id, 99]), row => ({ + num: $.multiply(2, row.id), + })) + await expect(database.get('temp2', {})).to.eventually.have.shape(table) + }) + it('enormous field', async () => { const row = await database.create('temp2', {}) row.bigtext = Array(1000000).fill('a').join('') @@ -420,6 +430,20 @@ namespace OrmOperations { await expect(database.drop('unknown')).to.be.rejected }) } + + export function subquery(database: Database) { + it('set query', async () => { + await setup(database, 'temp2', barTable) + await database.set('temp2', row => $.eq(row.text, database.select('temp2', r => $.eq(r.id, 2)).evaluate(r => $.max(r.text))), { text: 'ok' }) + await expect(database.get('temp2', 2)).to.eventually.have.shape([{ text: 'ok' }]) + }) + + it('set update', async () => { + const table = await setup(database, 'temp2', barTable) + await database.set('temp2', 1, row => ({ text: database.select('temp2', r => $.eq(r.id, $.add(1, row.id))).evaluate(r => $.max(r.text)) })) + await expect(database.get('temp2', 1)).to.eventually.have.shape([{ text: table[1].text }]) + }) + } } export default OrmOperations From bfaf19f6a4e9f4dd93ebcf88595f5e8e8881915a Mon Sep 17 00:00:00 2001 From: Hieuzest Date: Fri, 19 Apr 2024 13:56:48 +0800 Subject: [PATCH 2/3] chore: add test --- packages/tests/src/object.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/tests/src/object.ts b/packages/tests/src/object.ts index e7b79403..417627f6 100644 --- a/packages/tests/src/object.ts +++ b/packages/tests/src/object.ts @@ -136,6 +136,19 @@ namespace ObjectOperations { await expect(database.get('object', {})).to.eventually.deep.equal(table) }) + it('using subquery', async () => { + const table = await setup(database) + table[0].meta = { a: '0', embed: { b: 114 } } + table[1].meta = { a: '1', embed: { b: 514, c: 'world' } } + await expect(database.set('object', row => $.eq(row.id, database.select('object', '0').evaluate(r => $.max(r.id))), { + meta: { a: { $: 'id' }, embed: { b: 114 } }, + })).eventually.fulfilled + await expect(database.set('object', row => $.eq(row.id, database.select('object', '1').evaluate(r => $.max(r.id))), { + meta: { a: { $: 'id' }, 'embed.b': 514 }, + })).eventually.fulfilled + await expect(database.get('object', {})).to.eventually.deep.equal(table) + }) + it('nested property', async () => { const table = await setup(database) table[0].meta = { a: '0', embed: { b: 114, c: 'hello' } } From 21c264bf873ac93c500abc1b49e2ae81484560d8 Mon Sep 17 00:00:00 2001 From: Hieuzest Date: Fri, 19 Apr 2024 14:32:13 +0800 Subject: [PATCH 3/3] chore: replace all valueMap to mapValues --- packages/core/src/database.ts | 4 ++-- packages/core/src/driver.ts | 4 ++-- packages/core/src/eval.ts | 8 ++++---- packages/core/src/model.ts | 4 ++-- packages/core/src/selection.ts | 4 ++-- packages/memory/src/index.ts | 4 ++-- packages/mongo/src/builder.ts | 10 +++++----- packages/tests/src/model.ts | 6 +++--- 8 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/core/src/database.ts b/packages/core/src/database.ts index bf60b0ec..efffd882 100644 --- a/packages/core/src/database.ts +++ b/packages/core/src/database.ts @@ -1,4 +1,4 @@ -import { defineProperty, Dict, makeArray, mapValues, MaybeArray, omit, valueMap } from 'cosmokit' +import { defineProperty, Dict, makeArray, mapValues, MaybeArray, omit } from 'cosmokit' import { Context, Service, Spread } from 'cordis' import { FlatKeys, FlatPick, Indexable, Keys, randomId, Row, unravel } from './utils.ts' import { Selection } from './selection.ts' @@ -252,7 +252,7 @@ export class Database extends Servi if (Array.isArray(oldTables)) { tables = Object.fromEntries(oldTables.map((name) => [name, this.select(name)])) } - const sels = valueMap(tables, (t: TableLike) => { + const sels = mapValues(tables, (t: TableLike) => { return typeof t === 'string' ? this.select(t) : t }) if (Object.keys(sels).length === 0) throw new Error('no tables to join') diff --git a/packages/core/src/driver.ts b/packages/core/src/driver.ts index b7e734a1..63a828ef 100644 --- a/packages/core/src/driver.ts +++ b/packages/core/src/driver.ts @@ -1,4 +1,4 @@ -import { Awaitable, Dict, remove, valueMap } from 'cosmokit' +import { Awaitable, Dict, mapValues, remove } from 'cosmokit' import { Context, Logger } from 'cordis' import { Eval, Update } from './eval.ts' import { Direction, Modifier, Selection } from './selection.ts' @@ -95,7 +95,7 @@ export abstract class Driver { if (table instanceof Selection) { if (!table.args[0].fields) return table.model const model = new Model('temp') - model.fields = valueMap(table.args[0].fields, (expr, key) => ({ + model.fields = mapValues(table.args[0].fields, (expr, key) => ({ type: Type.fromTerm(expr), })) return model diff --git a/packages/core/src/eval.ts b/packages/core/src/eval.ts index 848f9f16..b3d9ac34 100644 --- a/packages/core/src/eval.ts +++ b/packages/core/src/eval.ts @@ -1,4 +1,4 @@ -import { defineProperty, isNullable, valueMap } from 'cosmokit' +import { defineProperty, isNullable, mapValues } from 'cosmokit' import { Comparable, Flatten, isComparable, makeRegExp, Row } from './utils.ts' import { Type } from './type.ts' import { Field } from './model.ts' @@ -258,7 +258,7 @@ defineProperty(Eval, 'length', unary('length', (expr, table) => Array.isArray(ta ? table.map(data => executeAggr(expr, data)).length : Array.from(executeEval(table, expr)).length, Type.Number)) -operators.$object = (field, table) => valueMap(field, value => executeAggr(value, table)) +operators.$object = (field, table) => mapValues(field, value => executeAggr(value, table)) Eval.object = (fields: any) => { if (fields.$model) { const modelFields: [string, Field][] = Object.entries(fields.$model.fields) @@ -267,9 +267,9 @@ Eval.object = (fields: any) => { .filter(([, field]) => !field.deprecated) .filter(([path]) => path.startsWith(prefix)) .map(([k]) => [k.slice(prefix.length), fields[k.slice(prefix.length)]])) - return Eval('object', fields, Type.Object(valueMap(fields, (value) => Type.fromTerm(value)))) + return Eval('object', fields, Type.Object(mapValues(fields, (value) => Type.fromTerm(value)))) } - return Eval('object', fields, Type.Object(valueMap(fields, (value) => Type.fromTerm(value)))) as any + return Eval('object', fields, Type.Object(mapValues(fields, (value) => Type.fromTerm(value)))) as any } Eval.array = unary('array', (expr, table) => Array.isArray(table) diff --git a/packages/core/src/model.ts b/packages/core/src/model.ts index 46a25d60..e5bd1bc6 100644 --- a/packages/core/src/model.ts +++ b/packages/core/src/model.ts @@ -1,4 +1,4 @@ -import { Binary, clone, isNullable, makeArray, MaybeArray, valueMap } from 'cosmokit' +import { Binary, clone, isNullable, makeArray, mapValues, MaybeArray } from 'cosmokit' import { Context } from 'cordis' import { Eval, isEvalExpr } from './eval.ts' import { Flatten, Keys, unravel } from './utils.ts' @@ -312,7 +312,7 @@ export class Model { getType(): Type getType(key: string): Type | undefined getType(key?: string): Type | undefined { - this.type ??= Type.Object(valueMap(this.fields!, field => Type.fromField(field!))) as any + this.type ??= Type.Object(mapValues(this.fields!, field => Type.fromField(field!))) as any return key ? Type.getInner(this.type, key) : this.type } } diff --git a/packages/core/src/selection.ts b/packages/core/src/selection.ts index aab3421f..4e5b78db 100644 --- a/packages/core/src/selection.ts +++ b/packages/core/src/selection.ts @@ -1,4 +1,4 @@ -import { defineProperty, Dict, filterKeys, valueMap } from 'cosmokit' +import { defineProperty, Dict, filterKeys, mapValues } from 'cosmokit' import { Driver } from './driver.ts' import { Eval, executeEval } from './eval.ts' import { Model } from './model.ts' @@ -119,7 +119,7 @@ class Executable { }) return Object.fromEntries(entries) } else { - return valueMap(fields, field => this.resolveField(field)) + return mapValues(fields, field => this.resolveField(field)) } } diff --git a/packages/memory/src/index.ts b/packages/memory/src/index.ts index e3327e9d..07b30d0b 100644 --- a/packages/memory/src/index.ts +++ b/packages/memory/src/index.ts @@ -1,4 +1,4 @@ -import { clone, Dict, makeArray, noop, omit, pick, valueMap } from 'cosmokit' +import { clone, Dict, makeArray, mapValues, noop, omit, pick } from 'cosmokit' import { Driver, Eval, executeEval, executeQuery, executeSort, executeUpdate, RuntimeError, Selection, z } from 'minato' export class MemoryDriver extends Driver { @@ -61,7 +61,7 @@ export class MemoryDriver extends Driver { } let index = row if (fields) { - index = valueMap(groupFields!, (expr) => executeEval({ ...env, [ref]: row }, expr)) + index = mapValues(groupFields!, (expr) => executeEval({ ...env, [ref]: row }, expr)) } let branch = branches.find((branch) => { if (!group || !groupFields) return false diff --git a/packages/mongo/src/builder.ts b/packages/mongo/src/builder.ts index a6e96ad8..17ba7419 100644 --- a/packages/mongo/src/builder.ts +++ b/packages/mongo/src/builder.ts @@ -1,4 +1,4 @@ -import { Dict, isNullable, mapValues, valueMap } from 'cosmokit' +import { Dict, isNullable, mapValues } from 'cosmokit' import { Driver, Eval, isComparable, isEvalExpr, Model, Query, Selection, Type, unravel } from 'minato' import { Filter, FilterOperators, ObjectId } from 'mongodb' import MongoDriver from '.' @@ -113,7 +113,7 @@ export class Builder { }, $if: (arg, group) => ({ $cond: arg.map(val => this.eval(val, group)) }), - $object: (arg, group) => valueMap(arg as any, x => this.transformEvalExpr(x)), + $object: (arg, group) => mapValues(arg as any, x => this.transformEvalExpr(x)), $regex: (arg, group) => ({ $regexMatch: { input: this.eval(arg[0], group), regex: this.eval(arg[1], group) } }), @@ -208,7 +208,7 @@ export class Builder { if (this.evalOperators[key]) { return this.evalOperators[key](expr[key], group) } else if (key?.startsWith('$') && Eval[key.slice(1)]) { - return valueMap(expr, (value) => { + return mapValues(expr, (value) => { if (Array.isArray(value)) { return value.map(val => this.eval(val, group)) } else { @@ -361,7 +361,7 @@ export class Builder { stages.push(...this.flushLookups(), ...groupStages, { $project }) $group['_id'] = unravel($group['_id']) } else if (fields) { - const $project = valueMap(fields, (expr) => this.eval(expr)) + const $project = mapValues(fields, (expr) => this.eval(expr)) $project._id = 0 stages.push(...this.flushLookups(), { $project }) } else { @@ -438,7 +438,7 @@ export class Builder { } this.evalKey = $ } else if (sel.type === 'set') { - const $set = valueMap(update, (expr, key) => this.eval(isEvalExpr(expr) ? expr : Eval.literal(expr, model.getType(key)))) + const $set = mapValues(update, (expr, key) => this.eval(isEvalExpr(expr) ? expr : Eval.literal(expr, model.getType(key)))) this.pipeline.push(...this.flushLookups(), { $set }, { $merge: { into: table, diff --git a/packages/tests/src/model.ts b/packages/tests/src/model.ts index 39bb8957..fe47fb81 100644 --- a/packages/tests/src/model.ts +++ b/packages/tests/src/model.ts @@ -1,4 +1,4 @@ -import { valueMap, isNullable, deduplicate, omit } from 'cosmokit' +import { mapValues, isNullable, deduplicate, omit } from 'cosmokit' import { $, Database, Field, Type, unravel } from 'minato' import { expect } from 'chai' @@ -434,7 +434,7 @@ namespace ModelOperations { .project({ obj: row => $.object(row) }) - .project(valueMap(database.tables['dtypes'].fields as any, (field, key) => row => row.obj[key])) + .project(mapValues(database.tables['dtypes'].fields as any, (field, key) => row => row.obj[key])) .execute() ).to.eventually.have.deep.members(table) }) @@ -543,7 +543,7 @@ namespace ModelOperations { .project({ obj: row => $.object(row) }) - .project(valueMap(database.tables['dobjects'].fields as any, (field, key) => row => row.obj[key])) + .project(mapValues(database.tables['dobjects'].fields as any, (field, key) => row => row.obj[key])) .execute() ).to.eventually.have.deep.members(table) })