@koishijs/plugin-database-mongo 3.1.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -255,7 +255,7 @@ var MongoDatabase = class extends import_koishi2.Database {
255
255
  }));
256
256
  }
257
257
  async _syncTable(name) {
258
- await this._tableTasks[name];
258
+ await Promise.resolve(this._tableTasks[name]).catch(import_koishi2.noop);
259
259
  await this.db.createCollection(name).catch(import_koishi2.noop);
260
260
  await Promise.all([
261
261
  this._createIndexes(name),
@@ -278,17 +278,18 @@ var MongoDatabase = class extends import_koishi2.Database {
278
278
  return Object.fromEntries(entries);
279
279
  }
280
280
  async stats() {
281
- const [{ totalSize }, tables] = await Promise.all([
281
+ const [stats, tables] = await Promise.all([
282
282
  this.db.stats(),
283
283
  this._collStats()
284
284
  ]);
285
+ const totalSize = stats.indexSize + stats.storageSize;
285
286
  return { size: totalSize, tables };
286
287
  }
287
288
  async get(name, query, modifier) {
288
289
  const filter = this._createFilter(name, query);
289
290
  if (!filter)
290
291
  return [];
291
- await this._tableTasks[name];
292
+ await Promise.resolve(this._tableTasks[name]).catch(import_koishi2.noop);
292
293
  let cursor = this.db.collection(name).find(filter);
293
294
  const { fields, limit, offset = 0, sort } = this.resolveModifier(name, modifier);
294
295
  if (offset)
@@ -303,7 +304,7 @@ var MongoDatabase = class extends import_koishi2.Database {
303
304
  const filter = this._createFilter(name, query);
304
305
  if (!filter)
305
306
  return;
306
- await this._tableTasks[name];
307
+ await Promise.resolve(this._tableTasks[name]).catch(import_koishi2.noop);
307
308
  const { primary } = this.model.config[name];
308
309
  const indexFields = (0, import_koishi2.makeArray)(primary);
309
310
  const coll = this.db.collection(name);
@@ -360,7 +361,7 @@ var MongoDatabase = class extends import_koishi2.Database {
360
361
  if (!keys)
361
362
  keys = this.model.config[name].primary;
362
363
  const indexFields = (0, import_koishi2.makeArray)(keys);
363
- await this._tableTasks[name];
364
+ await Promise.resolve(this._tableTasks[name]).catch(import_koishi2.noop);
364
365
  const coll = this.db.collection(name);
365
366
  const original = await coll.find({ $or: data.map((item) => (0, import_koishi2.pick)(item, indexFields)) }).toArray();
366
367
  const bulk = coll.initializeUnorderedBulkOp();
package/lib/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts", "../src/utils.ts"],
4
- "sourcesContent": ["import { Db, IndexDescription, MongoClient, MongoError } from 'mongodb'\nimport { Context, Database, Dict, DriverError, isNullable, makeArray, Model, noop, omit, pick, Schema, Tables } from 'koishi'\nimport { executeEval, executeUpdate, Modifier, Query } from '@koishijs/orm'\nimport { URLSearchParams } from 'url'\nimport { transformEval, transformQuery } from './utils'\n\ndeclare module 'koishi' {\n interface Database {\n mongo: MongoDatabase\n }\n}\n\ninterface EvalTask {\n expr: any\n table: keyof Tables\n query: Query\n resolve: (value: any) => void\n reject: (error: Error) => void\n}\n\nclass MongoDatabase extends Database {\n public client: MongoClient\n public db: Db\n public mongo = this\n private _tableTasks: Dict<Promise<any>> = {}\n private _evalTasks: EvalTask[] = []\n\n constructor(public ctx: Context, private config: MongoDatabase.Config) {\n super(ctx)\n }\n\n private connectionStringFromConfig() {\n const { authDatabase, connectOptions, host, database: name, password, port, protocol, username } = this.config\n let mongourl = `${protocol}://`\n if (username) mongourl += `${encodeURIComponent(username)}${password ? `:${encodeURIComponent(password)}` : ''}@`\n mongourl += `${host}${port ? `:${port}` : ''}/${authDatabase || name}`\n if (connectOptions) {\n const params = new URLSearchParams(connectOptions)\n mongourl += `?${params}`\n }\n return mongourl\n }\n\n async start() {\n const mongourl = this.config.uri || this.connectionStringFromConfig()\n this.client = await MongoClient.connect(mongourl)\n this.db = this.client.db(this.config.database)\n\n for (const name in this.model.config) {\n this._tableTasks[name] = this._syncTable(name)\n }\n\n this.ctx.on('model', (name) => {\n this._tableTasks[name] = this._syncTable(name)\n })\n }\n\n stop() {\n return this.client.close()\n }\n\n private async _createIndexes(name: string) {\n const { primary, unique } = this.model.config[name]\n const coll = this.db.collection(name)\n const newSpecs: IndexDescription[] = []\n const oldSpecs = await coll.indexes()\n ;[primary, ...unique].forEach((keys, index) => {\n keys = makeArray(keys)\n const name = (index ? 'unique:' : 'primary:') + keys.join('+')\n if (oldSpecs.find(spec => spec.name === name)) return\n newSpecs.push({\n name,\n key: Object.fromEntries(keys.map(key => [key, 1])),\n unique: true,\n // https://docs.mongodb.com/manual/core/index-partial/#std-label-partial-index-with-unique-constraints\n partialFilterExpression: Object.fromEntries(keys.map(key => [key, { $exists: true }])),\n })\n })\n if (!newSpecs.length) return\n await coll.createIndexes(newSpecs)\n }\n\n private async _createFields(name: string) {\n const { fields } = this.model.config[name]\n const coll = this.db.collection(name)\n await Promise.all(Object.keys(fields).map((key) => {\n if (isNullable(fields[key].initial)) return\n return coll.updateMany({ [key]: { $exists: false } }, { $set: { [key]: fields[key].initial } })\n }))\n }\n\n /** synchronize table schema */\n private async _syncTable(name: string) {\n await this._tableTasks[name]\n await this.db.createCollection(name).catch(noop)\n await Promise.all([\n this._createIndexes(name),\n this._createFields(name),\n ])\n }\n\n private _createFilter(name: keyof Tables, query: Query) {\n return transformQuery(this.resolveQuery(name, query))\n }\n\n async drop() {\n await Promise.all(Object.keys(this.model.config).map(name => this.db.dropCollection(name)))\n }\n\n private async _collStats() {\n const tables = Object.keys(this.model.config)\n const entries = await Promise.all(tables.map(async (name) => {\n const coll = this.db.collection(name)\n const { count, size } = await coll.stats()\n return [coll.collectionName, { count, size }] as const\n }))\n return Object.fromEntries(entries)\n }\n\n async stats() {\n // https://docs.mongodb.com/manual/reference/command/dbStats/#std-label-dbstats-output\n const [{ totalSize }, tables] = await Promise.all([\n this.db.stats(),\n this._collStats(),\n ])\n return { size: totalSize, tables }\n }\n\n async get(name: keyof Tables, query: Query, modifier: Modifier) {\n const filter = this._createFilter(name, query)\n if (!filter) return []\n await this._tableTasks[name]\n let cursor = this.db.collection(name).find(filter)\n const { fields, limit, offset = 0, sort } = this.resolveModifier(name, modifier)\n if (offset) cursor = cursor.skip(offset)\n if (limit) cursor = cursor.limit(offset + limit)\n if (sort) cursor = cursor.sort(sort)\n return (await cursor.toArray()).map(row => this.resolveData(name, row, fields)) as any\n }\n\n async set(name: keyof Tables, query: Query, update: {}) {\n const filter = this._createFilter(name, query)\n if (!filter) return\n await this._tableTasks[name]\n const { primary } = this.model.config[name]\n const indexFields = makeArray(primary)\n const coll = this.db.collection(name)\n const original = await coll.find(filter).toArray()\n if (!original.length) return\n update = this.model.format(name, update)\n const updateFields = new Set(Object.keys(update).map(key => key.split('.', 1)[0]))\n const bulk = coll.initializeUnorderedBulkOp()\n for (const item of original) {\n bulk.find(pick(item, indexFields)).updateOne({\n $set: pick(executeUpdate(item, update), updateFields),\n })\n }\n await bulk.execute()\n }\n\n async remove(name: keyof Tables, query: Query) {\n const filter = this._createFilter(name, query)\n if (!filter) return\n await this.db.collection(name).deleteMany(filter)\n }\n\n private queue(name: keyof Tables, callback: () => Promise<any>) {\n return this._tableTasks[name] = Promise.resolve(this._tableTasks[name]).catch(noop).then(callback)\n }\n\n async create<T extends keyof Tables>(name: T, data: any) {\n const coll = this.db.collection(name)\n return this.queue(name, async () => {\n const { primary, fields, autoInc } = this.model.config[name]\n if (autoInc && !Array.isArray(primary) && !(primary in data)) {\n const [latest] = await coll.find().sort(primary, -1).limit(1).toArray()\n data[primary] = latest ? +latest[primary] + 1 : 1\n if (Model.Field.string.includes(fields[primary].type)) {\n data[primary] += ''\n data[primary] = data[primary].padStart(8, '0')\n }\n }\n const copy = this.model.create(name, data)\n try {\n await coll.insertOne(copy)\n delete copy['_id']\n return copy\n } catch (err) {\n if (err instanceof MongoError && err.code === 11000) {\n throw new DriverError('duplicate-entry', err.message)\n }\n throw err\n }\n })\n }\n\n async upsert(name: keyof Tables, data: any[], keys: string | string[]) {\n if (!data.length) return\n if (!keys) keys = this.model.config[name].primary\n const indexFields = makeArray(keys)\n await this._tableTasks[name]\n const coll = this.db.collection(name)\n const original = await coll.find({ $or: data.map(item => pick(item, indexFields)) }).toArray()\n const bulk = coll.initializeUnorderedBulkOp()\n for (const _item of data) {\n const update = this.model.format(name, _item)\n const item = original.find(item => indexFields.every(key => item[key].valueOf() === update[key].valueOf()))\n if (item) {\n const updateFields = new Set(Object.keys(update).map(key => key.split('.', 1)[0]))\n const override = omit(pick(executeUpdate(item, update), updateFields), indexFields)\n bulk.find(pick(item, indexFields)).updateOne({ $set: override })\n } else {\n bulk.insert(executeUpdate(this.model.create(name), update))\n }\n }\n await bulk.execute()\n }\n\n eval(table: keyof Tables, expr: any, query: Query) {\n return new Promise<any>((resolve, reject) => {\n this._evalTasks.push({ expr, table, query, resolve, reject })\n process.nextTick(() => this._flushEvalTasks())\n })\n }\n\n private async _flushEvalTasks() {\n const tasks = this._evalTasks\n if (!tasks.length) return\n this._evalTasks = []\n\n const stages: any[] = [{ $match: { _id: null } }]\n for (const task of tasks) {\n const { expr, table, query } = task\n task.expr = transformEval(expr, (pipeline) => {\n const filter = this._createFilter(table, query) || { _id: null }\n pipeline.unshift({ $match: filter })\n stages.push({ $unionWith: { coll: table, pipeline } })\n })\n }\n\n let data: any\n try {\n const results = await this.db.collection('user').aggregate(stages).toArray()\n data = Object.assign({}, ...results)\n } catch (error) {\n tasks.forEach(task => task.reject(error))\n return\n }\n\n for (const { expr, resolve, reject } of tasks) {\n try {\n resolve(executeEval(data, expr))\n } catch (error) {\n reject(error)\n }\n }\n }\n}\n\nnamespace MongoDatabase {\n export const name = 'database-mongo'\n\n export interface Config {\n username?: string\n password?: string\n protocol?: string\n host?: string\n port?: number\n /** database name */\n database?: string\n /** default auth database */\n authDatabase?: string\n connectOptions?: ConstructorParameters<typeof URLSearchParams>[0]\n /** connection string (will overwrite all configs except 'name') */\n uri?: string\n }\n\n export const Config = Schema.object({\n protocol: Schema.string().description('要使用的协议名。').default('mongodb'),\n host: Schema.string().description('要连接到的主机名。').default('localhost'),\n port: Schema.natural().max(65535).description('要连接到的端口号。'),\n username: Schema.string().description('要使用的用户名。'),\n password: Schema.string().description('要使用的密码。').role('secret'),\n database: Schema.string().description('要访问的数据库名。').default('koishi'),\n })\n}\n\nexport default MongoDatabase\n", "import { Random, valueMap } from 'koishi'\nimport { Query } from '@koishijs/orm'\nimport { Filter, FilterOperators } from 'mongodb'\n\nfunction createFieldFilter(query: Query.FieldQuery, key: string) {\n const filters: Filter<any>[] = []\n const result: Filter<any> = {}\n const child = transformFieldQuery(query, key, filters)\n if (child === false) return false\n if (child !== true) result[key] = child\n if (filters.length) result.$and = filters\n if (Object.keys(result).length) return result\n return true\n}\n\nfunction transformFieldQuery(query: Query.FieldQuery, key: string, filters: Filter<any>[]) {\n // shorthand syntax\n if (typeof query === 'string' || typeof query === 'number' || query instanceof Date) {\n return { $eq: query }\n } else if (Array.isArray(query)) {\n if (!query.length) return false\n return { $in: query }\n } else if (query instanceof RegExp) {\n return { $regex: query }\n }\n\n // query operators\n const result: FilterOperators<any> = {}\n for (const prop in query) {\n if (prop === '$and') {\n for (const item of query[prop]) {\n const child = createFieldFilter(item, key)\n if (child === false) return false\n if (child !== true) filters.push(child)\n }\n } else if (prop === '$or') {\n const $or: Filter<any>[] = []\n if (!query[prop].length) return false\n const always = query[prop].some((item) => {\n const child = createFieldFilter(item, key)\n if (typeof child === 'boolean') return child\n $or.push(child)\n })\n if (!always) filters.push({ $or })\n } else if (prop === '$not') {\n const child = createFieldFilter(query[prop], key)\n if (child === true) return false\n if (child !== false) filters.push({ $nor: [child] })\n } else if (prop === '$el') {\n const child = transformFieldQuery(query[prop], key, filters)\n if (child === false) return false\n if (child !== true) result.$elemMatch = child\n } else if (prop === '$regexFor') {\n filters.push({\n $expr: {\n $function: {\n body: function (data: string, value: string) {\n return new RegExp(data, 'i').test(value)\n }.toString(),\n args: ['$' + key, query.$regexFor],\n lang: 'js',\n },\n },\n })\n } else {\n result[prop] = query[prop]\n }\n }\n if (!Object.keys(result).length) return true\n return result\n}\n\nexport function transformQuery(query: Query.Expr) {\n const filter: Filter<any> = {}\n const additional: Filter<any>[] = []\n for (const key in query) {\n const value = query[key]\n if (key === '$and' || key === '$or') {\n // MongoError: $and/$or/$nor must be a nonempty array\n // { $and: [] } matches everything\n // { $or: [] } matches nothing\n if (value.length) {\n filter[key] = value.map(transformQuery)\n } else if (key === '$or') {\n return\n }\n } else if (key === '$not') {\n // MongoError: unknown top level operator: $not\n // https://stackoverflow.com/questions/25270396/mongodb-how-to-invert-query-with-not\n // this may solve this problem but lead to performance degradation\n const query = transformQuery(value)\n if (query) filter.$nor = [query]\n } else if (key === '$expr') {\n additional.push({ $expr: transformEval(value) })\n } else {\n const query = transformFieldQuery(value, key, additional)\n if (query === false) return\n if (query !== true) filter[key] = query\n }\n }\n if (additional.length) {\n (filter.$and ||= []).push(...additional)\n }\n return filter\n}\n\nfunction transformEvalExpr(expr: any, onAggr?: (pipeline: any[]) => void) {\n return valueMap(expr as any, (value) => {\n if (Array.isArray(value)) {\n return value.map(val => transformEval(val, onAggr))\n } else {\n return transformEval(value, onAggr)\n }\n })\n}\n\nfunction transformAggr(expr: any) {\n if (typeof expr === 'string') {\n return '$' + expr\n }\n return transformEvalExpr(expr)\n}\n\nconst aggrKeys = ['$sum', '$avg', '$min', '$max', '$count']\n\nexport function transformEval(expr: any, onAggr?: (pipeline: any[]) => void) {\n if (typeof expr === 'number' || typeof expr === 'string' || typeof expr === 'boolean') {\n return expr\n } else if (expr.$) {\n return '$' + expr.$\n }\n\n for (const key of aggrKeys) {\n if (!expr[key]) continue\n const value = transformAggr(expr[key])\n const $ = Random.id()\n if (key === '$count') {\n onAggr([\n { $group: { _id: value } },\n { $group: { _id: null, [$]: { $count: {} } } },\n ])\n } else {\n onAggr([{ $group: { _id: null, [$]: { [key]: value } } }])\n }\n return { $ }\n }\n\n return transformEvalExpr(expr, onAggr)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA,qBAA8D;AAC9D,qBAAqH;AACrH,iBAA4D;AAC5D,iBAAgC;;;ACHhC,oBAAiC;AAIjC,2BAA2B,OAAyB,KAAa;AAC/D,QAAM,UAAyB;AAC/B,QAAM,SAAsB;AAC5B,QAAM,QAAQ,oBAAoB,OAAO,KAAK;AAC9C,MAAI,UAAU;AAAO,WAAO;AAC5B,MAAI,UAAU;AAAM,WAAO,OAAO;AAClC,MAAI,QAAQ;AAAQ,WAAO,OAAO;AAClC,MAAI,OAAO,KAAK,QAAQ;AAAQ,WAAO;AACvC,SAAO;AAAA;AARA;AAWT,6BAA6B,OAAyB,KAAa,SAAwB;AAEzF,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,iBAAiB,MAAM;AACnF,WAAO,EAAE,KAAK;AAAA,aACL,MAAM,QAAQ,QAAQ;AAC/B,QAAI,CAAC,MAAM;AAAQ,aAAO;AAC1B,WAAO,EAAE,KAAK;AAAA,aACL,iBAAiB,QAAQ;AAClC,WAAO,EAAE,QAAQ;AAAA;AAInB,QAAM,SAA+B;AACrC,aAAW,QAAQ,OAAO;AACxB,QAAI,SAAS,QAAQ;AACnB,iBAAW,QAAQ,MAAM,OAAO;AAC9B,cAAM,QAAQ,kBAAkB,MAAM;AACtC,YAAI,UAAU;AAAO,iBAAO;AAC5B,YAAI,UAAU;AAAM,kBAAQ,KAAK;AAAA;AAAA,eAE1B,SAAS,OAAO;AACzB,YAAM,MAAqB;AAC3B,UAAI,CAAC,MAAM,MAAM;AAAQ,eAAO;AAChC,YAAM,SAAS,MAAM,MAAM,KAAK,CAAC,SAAS;AACxC,cAAM,QAAQ,kBAAkB,MAAM;AACtC,YAAI,OAAO,UAAU;AAAW,iBAAO;AACvC,YAAI,KAAK;AAAA;AAEX,UAAI,CAAC;AAAQ,gBAAQ,KAAK,EAAE;AAAA,eACnB,SAAS,QAAQ;AAC1B,YAAM,QAAQ,kBAAkB,MAAM,OAAO;AAC7C,UAAI,UAAU;AAAM,eAAO;AAC3B,UAAI,UAAU;AAAO,gBAAQ,KAAK,EAAE,MAAM,CAAC;AAAA,eAClC,SAAS,OAAO;AACzB,YAAM,QAAQ,oBAAoB,MAAM,OAAO,KAAK;AACpD,UAAI,UAAU;AAAO,eAAO;AAC5B,UAAI,UAAU;AAAM,eAAO,aAAa;AAAA,eAC/B,SAAS,aAAa;AAC/B,cAAQ,KAAK;AAAA,QACX,OAAO;AAAA,UACL,WAAW;AAAA,YACT,MAAM,SAAU,MAAc,OAAe;AAC3C,qBAAO,IAAI,OAAO,MAAM,KAAK,KAAK;AAAA,cAClC;AAAA,YACF,MAAM,CAAC,MAAM,KAAK,MAAM;AAAA,YACxB,MAAM;AAAA;AAAA;AAAA;AAAA,WAIP;AACL,aAAO,QAAQ,MAAM;AAAA;AAAA;AAGzB,MAAI,CAAC,OAAO,KAAK,QAAQ;AAAQ,WAAO;AACxC,SAAO;AAAA;AAtDA;AAyDF,wBAAwB,OAAmB;AAChD,QAAM,SAAsB;AAC5B,QAAM,aAA4B;AAClC,aAAW,OAAO,OAAO;AACvB,UAAM,QAAQ,MAAM;AACpB,QAAI,QAAQ,UAAU,QAAQ,OAAO;AAInC,UAAI,MAAM,QAAQ;AAChB,eAAO,OAAO,MAAM,IAAI;AAAA,iBACf,QAAQ,OAAO;AACxB;AAAA;AAAA,eAEO,QAAQ,QAAQ;AAIzB,YAAM,SAAQ,eAAe;AAC7B,UAAI;AAAO,eAAO,OAAO,CAAC;AAAA,eACjB,QAAQ,SAAS;AAC1B,iBAAW,KAAK,EAAE,OAAO,cAAc;AAAA,WAClC;AACL,YAAM,SAAQ,oBAAoB,OAAO,KAAK;AAC9C,UAAI,WAAU;AAAO;AACrB,UAAI,WAAU;AAAM,eAAO,OAAO;AAAA;AAAA;AAGtC,MAAI,WAAW,QAAQ;AACrB,IAAC,QAAO,QAAP,QAAO,OAAS,KAAI,KAAK,GAAG;AAAA;AAE/B,SAAO;AAAA;AA/BO;AAkChB,2BAA2B,MAAW,QAAoC;AACxE,SAAO,4BAAS,MAAa,CAAC,UAAU;AACtC,QAAI,MAAM,QAAQ,QAAQ;AACxB,aAAO,MAAM,IAAI,SAAO,cAAc,KAAK;AAAA,WACtC;AACL,aAAO,cAAc,OAAO;AAAA;AAAA;AAAA;AALzB;AAUT,uBAAuB,MAAW;AAChC,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO,MAAM;AAAA;AAEf,SAAO,kBAAkB;AAAA;AAJlB;AAOT,IAAM,WAAW,CAAC,QAAQ,QAAQ,QAAQ,QAAQ;AAE3C,uBAAuB,MAAW,QAAoC;AAC3E,MAAI,OAAO,SAAS,YAAY,OAAO,SAAS,YAAY,OAAO,SAAS,WAAW;AACrF,WAAO;AAAA,aACE,KAAK,GAAG;AACjB,WAAO,MAAM,KAAK;AAAA;AAGpB,aAAW,OAAO,UAAU;AAC1B,QAAI,CAAC,KAAK;AAAM;AAChB,UAAM,QAAQ,cAAc,KAAK;AACjC,UAAM,IAAI,qBAAO;AACjB,QAAI,QAAQ,UAAU;AACpB,aAAO;AAAA,QACL,EAAE,QAAQ,EAAE,KAAK;AAAA,QACjB,EAAE,QAAQ,EAAE,KAAK,OAAO,IAAI,EAAE,QAAQ;AAAA;AAAA,WAEnC;AACL,aAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,OAAO,IAAI,GAAG,MAAM;AAAA;AAE/C,WAAO,EAAE;AAAA;AAGX,SAAO,kBAAkB,MAAM;AAAA;AAtBjB;;;ADzGhB,kCAA4B,wBAAS;AAAA,EAOnC,YAAmB,KAAsB,QAA8B;AACrE,UAAM;AADW;AAAsB;AAJlC,iBAAQ;AACP,uBAAkC;AAClC,sBAAyB;AAAA;AAAA,EAMzB,6BAA6B;AACnC,UAAM,EAAE,cAAc,gBAAgB,MAAM,UAAU,MAAM,UAAU,MAAM,UAAU,aAAa,KAAK;AACxG,QAAI,WAAW,GAAG;AAClB,QAAI;AAAU,kBAAY,GAAG,mBAAmB,YAAY,WAAW,IAAI,mBAAmB,cAAc;AAC5G,gBAAY,GAAG,OAAO,OAAO,IAAI,SAAS,MAAM,gBAAgB;AAChE,QAAI,gBAAgB;AAClB,YAAM,SAAS,IAAI,2BAAgB;AACnC,kBAAY,IAAI;AAAA;AAElB,WAAO;AAAA;AAAA,QAGH,QAAQ;AACZ,UAAM,WAAW,KAAK,OAAO,OAAO,KAAK;AACzC,SAAK,SAAS,MAAM,2BAAY,QAAQ;AACxC,SAAK,KAAK,KAAK,OAAO,GAAG,KAAK,OAAO;AAErC,eAAW,QAAQ,KAAK,MAAM,QAAQ;AACpC,WAAK,YAAY,QAAQ,KAAK,WAAW;AAAA;AAG3C,SAAK,IAAI,GAAG,SAAS,CAAC,SAAS;AAC7B,WAAK,YAAY,QAAQ,KAAK,WAAW;AAAA;AAAA;AAAA,EAI7C,OAAO;AACL,WAAO,KAAK,OAAO;AAAA;AAAA,QAGP,eAAe,MAAc;AACzC,UAAM,EAAE,SAAS,WAAW,KAAK,MAAM,OAAO;AAC9C,UAAM,OAAO,KAAK,GAAG,WAAW;AAChC,UAAM,WAA+B;AACrC,UAAM,WAAW,MAAM,KAAK;AAC3B,KAAC,SAAS,GAAG,QAAQ,QAAQ,CAAC,MAAM,UAAU;AAC7C,aAAO,8BAAU;AACjB,YAAM,QAAQ,SAAQ,YAAY,cAAc,KAAK,KAAK;AAC1D,UAAI,SAAS,KAAK,UAAQ,KAAK,SAAS;AAAO;AAC/C,eAAS,KAAK;AAAA,QACZ;AAAA,QACA,KAAK,OAAO,YAAY,KAAK,IAAI,SAAO,CAAC,KAAK;AAAA,QAC9C,QAAQ;AAAA,QAER,yBAAyB,OAAO,YAAY,KAAK,IAAI,SAAO,CAAC,KAAK,EAAE,SAAS;AAAA;AAAA;AAGjF,QAAI,CAAC,SAAS;AAAQ;AACtB,UAAM,KAAK,cAAc;AAAA;AAAA,QAGb,cAAc,MAAc;AACxC,UAAM,EAAE,WAAW,KAAK,MAAM,OAAO;AACrC,UAAM,OAAO,KAAK,GAAG,WAAW;AAChC,UAAM,QAAQ,IAAI,OAAO,KAAK,QAAQ,IAAI,CAAC,QAAQ;AACjD,UAAI,+BAAW,OAAO,KAAK;AAAU;AACrC,aAAO,KAAK,WAAW,GAAG,MAAM,EAAE,SAAS,WAAW,EAAE,MAAM,GAAG,MAAM,OAAO,KAAK;AAAA;AAAA;AAAA,QAKzE,WAAW,MAAc;AACrC,UAAM,KAAK,YAAY;AACvB,UAAM,KAAK,GAAG,iBAAiB,MAAM,MAAM;AAC3C,UAAM,QAAQ,IAAI;AAAA,MAChB,KAAK,eAAe;AAAA,MACpB,KAAK,cAAc;AAAA;AAAA;AAAA,EAIf,cAAc,MAAoB,OAAc;AACtD,WAAO,eAAe,KAAK,aAAa,MAAM;AAAA;AAAA,QAG1C,OAAO;AACX,UAAM,QAAQ,IAAI,OAAO,KAAK,KAAK,MAAM,QAAQ,IAAI,UAAQ,KAAK,GAAG,eAAe;AAAA;AAAA,QAGxE,aAAa;AACzB,UAAM,SAAS,OAAO,KAAK,KAAK,MAAM;AACtC,UAAM,UAAU,MAAM,QAAQ,IAAI,OAAO,IAAI,OAAO,SAAS;AAC3D,YAAM,OAAO,KAAK,GAAG,WAAW;AAChC,YAAM,EAAE,OAAO,SAAS,MAAM,KAAK;AACnC,aAAO,CAAC,KAAK,gBAAgB,EAAE,OAAO;AAAA;AAExC,WAAO,OAAO,YAAY;AAAA;AAAA,QAGtB,QAAQ;AAEZ,UAAM,CAAC,EAAE,aAAa,UAAU,MAAM,QAAQ,IAAI;AAAA,MAChD,KAAK,GAAG;AAAA,MACR,KAAK;AAAA;AAEP,WAAO,EAAE,MAAM,WAAW;AAAA;AAAA,QAGtB,IAAI,MAAoB,OAAc,UAAoB;AAC9D,UAAM,SAAS,KAAK,cAAc,MAAM;AACxC,QAAI,CAAC;AAAQ,aAAO;AACpB,UAAM,KAAK,YAAY;AACvB,QAAI,SAAS,KAAK,GAAG,WAAW,MAAM,KAAK;AAC3C,UAAM,EAAE,QAAQ,OAAO,SAAS,GAAG,SAAS,KAAK,gBAAgB,MAAM;AACvE,QAAI;AAAQ,eAAS,OAAO,KAAK;AACjC,QAAI;AAAO,eAAS,OAAO,MAAM,SAAS;AAC1C,QAAI;AAAM,eAAS,OAAO,KAAK;AAC/B,WAAQ,OAAM,OAAO,WAAW,IAAI,SAAO,KAAK,YAAY,MAAM,KAAK;AAAA;AAAA,QAGnE,IAAI,MAAoB,OAAc,QAAY;AACtD,UAAM,SAAS,KAAK,cAAc,MAAM;AACxC,QAAI,CAAC;AAAQ;AACb,UAAM,KAAK,YAAY;AACvB,UAAM,EAAE,YAAY,KAAK,MAAM,OAAO;AACtC,UAAM,cAAc,8BAAU;AAC9B,UAAM,OAAO,KAAK,GAAG,WAAW;AAChC,UAAM,WAAW,MAAM,KAAK,KAAK,QAAQ;AACzC,QAAI,CAAC,SAAS;AAAQ;AACtB,aAAS,KAAK,MAAM,OAAO,MAAM;AACjC,UAAM,eAAe,IAAI,IAAI,OAAO,KAAK,QAAQ,IAAI,SAAO,IAAI,MAAM,KAAK,GAAG;AAC9E,UAAM,OAAO,KAAK;AAClB,eAAW,QAAQ,UAAU;AAC3B,WAAK,KAAK,yBAAK,MAAM,cAAc,UAAU;AAAA,QAC3C,MAAM,yBAAK,8BAAc,MAAM,SAAS;AAAA;AAAA;AAG5C,UAAM,KAAK;AAAA;AAAA,QAGP,OAAO,MAAoB,OAAc;AAC7C,UAAM,SAAS,KAAK,cAAc,MAAM;AACxC,QAAI,CAAC;AAAQ;AACb,UAAM,KAAK,GAAG,WAAW,MAAM,WAAW;AAAA;AAAA,EAGpC,MAAM,MAAoB,UAA8B;AAC9D,WAAO,KAAK,YAAY,QAAQ,QAAQ,QAAQ,KAAK,YAAY,OAAO,MAAM,qBAAM,KAAK;AAAA;AAAA,QAGrF,OAA+B,MAAS,MAAW;AACvD,UAAM,OAAO,KAAK,GAAG,WAAW;AAChC,WAAO,KAAK,MAAM,MAAM,YAAY;AAClC,YAAM,EAAE,SAAS,QAAQ,YAAY,KAAK,MAAM,OAAO;AACvD,UAAI,WAAW,CAAC,MAAM,QAAQ,YAAY,CAAE,YAAW,OAAO;AAC5D,cAAM,CAAC,UAAU,MAAM,KAAK,OAAO,KAAK,SAAS,IAAI,MAAM,GAAG;AAC9D,aAAK,WAAW,SAAS,CAAC,OAAO,WAAW,IAAI;AAChD,YAAI,qBAAM,MAAM,OAAO,SAAS,OAAO,SAAS,OAAO;AACrD,eAAK,YAAY;AACjB,eAAK,WAAW,KAAK,SAAS,SAAS,GAAG;AAAA;AAAA;AAG9C,YAAM,OAAO,KAAK,MAAM,OAAO,MAAM;AACrC,UAAI;AACF,cAAM,KAAK,UAAU;AACrB,eAAO,KAAK;AACZ,eAAO;AAAA,eACA,KAAP;AACA,YAAI,eAAe,6BAAc,IAAI,SAAS,MAAO;AACnD,gBAAM,IAAI,2BAAY,mBAAmB,IAAI;AAAA;AAE/C,cAAM;AAAA;AAAA;AAAA;AAAA,QAKN,OAAO,MAAoB,MAAa,MAAyB;AACrE,QAAI,CAAC,KAAK;AAAQ;AAClB,QAAI,CAAC;AAAM,aAAO,KAAK,MAAM,OAAO,MAAM;AAC1C,UAAM,cAAc,8BAAU;AAC9B,UAAM,KAAK,YAAY;AACvB,UAAM,OAAO,KAAK,GAAG,WAAW;AAChC,UAAM,WAAW,MAAM,KAAK,KAAK,EAAE,KAAK,KAAK,IAAI,UAAQ,yBAAK,MAAM,iBAAiB;AACrF,UAAM,OAAO,KAAK;AAClB,eAAW,SAAS,MAAM;AACxB,YAAM,SAAS,KAAK,MAAM,OAAO,MAAM;AACvC,YAAM,OAAO,SAAS,KAAK,WAAQ,YAAY,MAAM,SAAO,MAAK,KAAK,cAAc,OAAO,KAAK;AAChG,UAAI,MAAM;AACR,cAAM,eAAe,IAAI,IAAI,OAAO,KAAK,QAAQ,IAAI,SAAO,IAAI,MAAM,KAAK,GAAG;AAC9E,cAAM,WAAW,yBAAK,yBAAK,8BAAc,MAAM,SAAS,eAAe;AACvE,aAAK,KAAK,yBAAK,MAAM,cAAc,UAAU,EAAE,MAAM;AAAA,aAChD;AACL,aAAK,OAAO,8BAAc,KAAK,MAAM,OAAO,OAAO;AAAA;AAAA;AAGvD,UAAM,KAAK;AAAA;AAAA,EAGb,KAAK,OAAqB,MAAW,OAAc;AACjD,WAAO,IAAI,QAAa,CAAC,SAAS,WAAW;AAC3C,WAAK,WAAW,KAAK,EAAE,MAAM,OAAO,OAAO,SAAS;AACpD,cAAQ,SAAS,MAAM,KAAK;AAAA;AAAA;AAAA,QAIlB,kBAAkB;AAC9B,UAAM,QAAQ,KAAK;AACnB,QAAI,CAAC,MAAM;AAAQ;AACnB,SAAK,aAAa;AAElB,UAAM,SAAgB,CAAC,EAAE,QAAQ,EAAE,KAAK;AACxC,eAAW,QAAQ,OAAO;AACxB,YAAM,EAAE,MAAM,OAAO,UAAU;AAC/B,WAAK,OAAO,cAAc,MAAM,CAAC,aAAa;AAC5C,cAAM,SAAS,KAAK,cAAc,OAAO,UAAU,EAAE,KAAK;AAC1D,iBAAS,QAAQ,EAAE,QAAQ;AAC3B,eAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO;AAAA;AAAA;AAI7C,QAAI;AACJ,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,GAAG,WAAW,QAAQ,UAAU,QAAQ;AACnE,aAAO,OAAO,OAAO,IAAI,GAAG;AAAA,aACrB,OAAP;AACA,YAAM,QAAQ,UAAQ,KAAK,OAAO;AAClC;AAAA;AAGF,eAAW,EAAE,MAAM,SAAS,YAAY,OAAO;AAC7C,UAAI;AACF,gBAAQ,4BAAY,MAAM;AAAA,eACnB,OAAP;AACA,eAAO;AAAA;AAAA;AAAA;AAAA;AAzOf;AA+OA,UAAU,gBAAV;AACS,EAAM,sBAAO;AAiBb,EAAM,wBAAS,sBAAO,OAAO;AAAA,IAClC,UAAU,sBAAO,SAAS,YAAY,YAAY,QAAQ;AAAA,IAC1D,MAAM,sBAAO,SAAS,YAAY,aAAa,QAAQ;AAAA,IACvD,MAAM,sBAAO,UAAU,IAAI,OAAO,YAAY;AAAA,IAC9C,UAAU,sBAAO,SAAS,YAAY;AAAA,IACtC,UAAU,sBAAO,SAAS,YAAY,WAAW,KAAK;AAAA,IACtD,UAAU,sBAAO,SAAS,YAAY,aAAa,QAAQ;AAAA;AAAA,GAxBrD;AA4BV,IAAO,cAAQ;",
4
+ "sourcesContent": ["import { Db, IndexDescription, MongoClient, MongoError } from 'mongodb'\nimport { Context, Database, Dict, DriverError, isNullable, makeArray, Model, noop, omit, pick, Schema, Tables } from 'koishi'\nimport { executeEval, executeUpdate, Modifier, Query } from '@koishijs/orm'\nimport { URLSearchParams } from 'url'\nimport { transformEval, transformQuery } from './utils'\n\ndeclare module 'koishi' {\n interface Database {\n mongo: MongoDatabase\n }\n}\n\ninterface EvalTask {\n expr: any\n table: keyof Tables\n query: Query\n resolve: (value: any) => void\n reject: (error: Error) => void\n}\n\nclass MongoDatabase extends Database {\n public client: MongoClient\n public db: Db\n public mongo = this\n private _tableTasks: Dict<Promise<any>> = {}\n private _evalTasks: EvalTask[] = []\n\n constructor(public ctx: Context, private config: MongoDatabase.Config) {\n super(ctx)\n }\n\n private connectionStringFromConfig() {\n const { authDatabase, connectOptions, host, database: name, password, port, protocol, username } = this.config\n let mongourl = `${protocol}://`\n if (username) mongourl += `${encodeURIComponent(username)}${password ? `:${encodeURIComponent(password)}` : ''}@`\n mongourl += `${host}${port ? `:${port}` : ''}/${authDatabase || name}`\n if (connectOptions) {\n const params = new URLSearchParams(connectOptions)\n mongourl += `?${params}`\n }\n return mongourl\n }\n\n async start() {\n const mongourl = this.config.uri || this.connectionStringFromConfig()\n this.client = await MongoClient.connect(mongourl)\n this.db = this.client.db(this.config.database)\n\n for (const name in this.model.config) {\n this._tableTasks[name] = this._syncTable(name)\n }\n\n this.ctx.on('model', (name) => {\n this._tableTasks[name] = this._syncTable(name)\n })\n }\n\n stop() {\n return this.client.close()\n }\n\n private async _createIndexes(name: string) {\n const { primary, unique } = this.model.config[name]\n const coll = this.db.collection(name)\n const newSpecs: IndexDescription[] = []\n const oldSpecs = await coll.indexes()\n ;[primary, ...unique].forEach((keys, index) => {\n keys = makeArray(keys)\n const name = (index ? 'unique:' : 'primary:') + keys.join('+')\n if (oldSpecs.find(spec => spec.name === name)) return\n newSpecs.push({\n name,\n key: Object.fromEntries(keys.map(key => [key, 1])),\n unique: true,\n // https://docs.mongodb.com/manual/core/index-partial/#std-label-partial-index-with-unique-constraints\n partialFilterExpression: Object.fromEntries(keys.map(key => [key, { $exists: true }])),\n })\n })\n if (!newSpecs.length) return\n await coll.createIndexes(newSpecs)\n }\n\n private async _createFields(name: string) {\n const { fields } = this.model.config[name]\n const coll = this.db.collection(name)\n await Promise.all(Object.keys(fields).map((key) => {\n if (isNullable(fields[key].initial)) return\n return coll.updateMany({ [key]: { $exists: false } }, { $set: { [key]: fields[key].initial } })\n }))\n }\n\n /** synchronize table schema */\n private async _syncTable(name: string) {\n await Promise.resolve(this._tableTasks[name]).catch(noop)\n await this.db.createCollection(name).catch(noop)\n await Promise.all([\n this._createIndexes(name),\n this._createFields(name),\n ])\n }\n\n private _createFilter(name: keyof Tables, query: Query) {\n return transformQuery(this.resolveQuery(name, query))\n }\n\n async drop() {\n await Promise.all(Object.keys(this.model.config).map(name => this.db.dropCollection(name)))\n }\n\n private async _collStats() {\n const tables = Object.keys(this.model.config)\n const entries = await Promise.all(tables.map(async (name) => {\n const coll = this.db.collection(name)\n const { count, size } = await coll.stats()\n return [coll.collectionName, { count, size }] as const\n }))\n return Object.fromEntries(entries)\n }\n\n async stats() {\n // https://docs.mongodb.com/manual/reference/command/dbStats/#std-label-dbstats-output\n const [stats, tables] = await Promise.all([\n this.db.stats(),\n this._collStats(),\n ])\n // While MongoDB's document above says that the `stats.totalSize` is the sum of\n // `stats.dataSize` and `stats.storageSize`, it's actually `undefined` in some cases.\n // So we have to calculate it manually.\n const totalSize = stats.indexSize + stats.storageSize\n return { size: totalSize, tables }\n }\n\n async get(name: keyof Tables, query: Query, modifier: Modifier) {\n const filter = this._createFilter(name, query)\n if (!filter) return []\n await Promise.resolve(this._tableTasks[name]).catch(noop)\n let cursor = this.db.collection(name).find(filter)\n const { fields, limit, offset = 0, sort } = this.resolveModifier(name, modifier)\n if (offset) cursor = cursor.skip(offset)\n if (limit) cursor = cursor.limit(offset + limit)\n if (sort) cursor = cursor.sort(sort)\n return (await cursor.toArray()).map(row => this.resolveData(name, row, fields)) as any\n }\n\n async set(name: keyof Tables, query: Query, update: {}) {\n const filter = this._createFilter(name, query)\n if (!filter) return\n await Promise.resolve(this._tableTasks[name]).catch(noop)\n const { primary } = this.model.config[name]\n const indexFields = makeArray(primary)\n const coll = this.db.collection(name)\n const original = await coll.find(filter).toArray()\n if (!original.length) return\n update = this.model.format(name, update)\n const updateFields = new Set(Object.keys(update).map(key => key.split('.', 1)[0]))\n const bulk = coll.initializeUnorderedBulkOp()\n for (const item of original) {\n bulk.find(pick(item, indexFields)).updateOne({\n $set: pick(executeUpdate(item, update), updateFields),\n })\n }\n await bulk.execute()\n }\n\n async remove(name: keyof Tables, query: Query) {\n const filter = this._createFilter(name, query)\n if (!filter) return\n await this.db.collection(name).deleteMany(filter)\n }\n\n private queue(name: keyof Tables, callback: () => Promise<any>) {\n return this._tableTasks[name] = Promise.resolve(this._tableTasks[name]).catch(noop).then(callback)\n }\n\n async create<T extends keyof Tables>(name: T, data: any) {\n const coll = this.db.collection(name)\n return this.queue(name, async () => {\n const { primary, fields, autoInc } = this.model.config[name]\n if (autoInc && !Array.isArray(primary) && !(primary in data)) {\n const [latest] = await coll.find().sort(primary, -1).limit(1).toArray()\n data[primary] = latest ? +latest[primary] + 1 : 1\n if (Model.Field.string.includes(fields[primary].type)) {\n data[primary] += ''\n data[primary] = data[primary].padStart(8, '0')\n }\n }\n const copy = this.model.create(name, data)\n try {\n await coll.insertOne(copy)\n delete copy['_id']\n return copy\n } catch (err) {\n if (err instanceof MongoError && err.code === 11000) {\n throw new DriverError('duplicate-entry', err.message)\n }\n throw err\n }\n })\n }\n\n async upsert(name: keyof Tables, data: any[], keys: string | string[]) {\n if (!data.length) return\n if (!keys) keys = this.model.config[name].primary\n const indexFields = makeArray(keys)\n await Promise.resolve(this._tableTasks[name]).catch(noop)\n const coll = this.db.collection(name)\n const original = await coll.find({ $or: data.map(item => pick(item, indexFields)) }).toArray()\n const bulk = coll.initializeUnorderedBulkOp()\n for (const _item of data) {\n const update = this.model.format(name, _item)\n const item = original.find(item => indexFields.every(key => item[key].valueOf() === update[key].valueOf()))\n if (item) {\n const updateFields = new Set(Object.keys(update).map(key => key.split('.', 1)[0]))\n const override = omit(pick(executeUpdate(item, update), updateFields), indexFields)\n bulk.find(pick(item, indexFields)).updateOne({ $set: override })\n } else {\n bulk.insert(executeUpdate(this.model.create(name), update))\n }\n }\n await bulk.execute()\n }\n\n eval(table: keyof Tables, expr: any, query: Query) {\n return new Promise<any>((resolve, reject) => {\n this._evalTasks.push({ expr, table, query, resolve, reject })\n process.nextTick(() => this._flushEvalTasks())\n })\n }\n\n private async _flushEvalTasks() {\n const tasks = this._evalTasks\n if (!tasks.length) return\n this._evalTasks = []\n\n const stages: any[] = [{ $match: { _id: null } }]\n for (const task of tasks) {\n const { expr, table, query } = task\n task.expr = transformEval(expr, (pipeline) => {\n const filter = this._createFilter(table, query) || { _id: null }\n pipeline.unshift({ $match: filter })\n stages.push({ $unionWith: { coll: table, pipeline } })\n })\n }\n\n let data: any\n try {\n const results = await this.db.collection('user').aggregate(stages).toArray()\n data = Object.assign({}, ...results)\n } catch (error) {\n tasks.forEach(task => task.reject(error))\n return\n }\n\n for (const { expr, resolve, reject } of tasks) {\n try {\n resolve(executeEval(data, expr))\n } catch (error) {\n reject(error)\n }\n }\n }\n}\n\nnamespace MongoDatabase {\n export const name = 'database-mongo'\n\n export interface Config {\n username?: string\n password?: string\n protocol?: string\n host?: string\n port?: number\n /** database name */\n database?: string\n /** default auth database */\n authDatabase?: string\n connectOptions?: ConstructorParameters<typeof URLSearchParams>[0]\n /** connection string (will overwrite all configs except 'name') */\n uri?: string\n }\n\n export const Config = Schema.object({\n protocol: Schema.string().description('要使用的协议名。').default('mongodb'),\n host: Schema.string().description('要连接到的主机名。').default('localhost'),\n port: Schema.natural().max(65535).description('要连接到的端口号。'),\n username: Schema.string().description('要使用的用户名。'),\n password: Schema.string().description('要使用的密码。').role('secret'),\n database: Schema.string().description('要访问的数据库名。').default('koishi'),\n })\n}\n\nexport default MongoDatabase\n", "import { Random, valueMap } from 'koishi'\nimport { Query } from '@koishijs/orm'\nimport { Filter, FilterOperators } from 'mongodb'\n\nfunction createFieldFilter(query: Query.FieldQuery, key: string) {\n const filters: Filter<any>[] = []\n const result: Filter<any> = {}\n const child = transformFieldQuery(query, key, filters)\n if (child === false) return false\n if (child !== true) result[key] = child\n if (filters.length) result.$and = filters\n if (Object.keys(result).length) return result\n return true\n}\n\nfunction transformFieldQuery(query: Query.FieldQuery, key: string, filters: Filter<any>[]) {\n // shorthand syntax\n if (typeof query === 'string' || typeof query === 'number' || query instanceof Date) {\n return { $eq: query }\n } else if (Array.isArray(query)) {\n if (!query.length) return false\n return { $in: query }\n } else if (query instanceof RegExp) {\n return { $regex: query }\n }\n\n // query operators\n const result: FilterOperators<any> = {}\n for (const prop in query) {\n if (prop === '$and') {\n for (const item of query[prop]) {\n const child = createFieldFilter(item, key)\n if (child === false) return false\n if (child !== true) filters.push(child)\n }\n } else if (prop === '$or') {\n const $or: Filter<any>[] = []\n if (!query[prop].length) return false\n const always = query[prop].some((item) => {\n const child = createFieldFilter(item, key)\n if (typeof child === 'boolean') return child\n $or.push(child)\n })\n if (!always) filters.push({ $or })\n } else if (prop === '$not') {\n const child = createFieldFilter(query[prop], key)\n if (child === true) return false\n if (child !== false) filters.push({ $nor: [child] })\n } else if (prop === '$el') {\n const child = transformFieldQuery(query[prop], key, filters)\n if (child === false) return false\n if (child !== true) result.$elemMatch = child\n } else if (prop === '$regexFor') {\n filters.push({\n $expr: {\n $function: {\n body: function (data: string, value: string) {\n return new RegExp(data, 'i').test(value)\n }.toString(),\n args: ['$' + key, query.$regexFor],\n lang: 'js',\n },\n },\n })\n } else {\n result[prop] = query[prop]\n }\n }\n if (!Object.keys(result).length) return true\n return result\n}\n\nexport function transformQuery(query: Query.Expr) {\n const filter: Filter<any> = {}\n const additional: Filter<any>[] = []\n for (const key in query) {\n const value = query[key]\n if (key === '$and' || key === '$or') {\n // MongoError: $and/$or/$nor must be a nonempty array\n // { $and: [] } matches everything\n // { $or: [] } matches nothing\n if (value.length) {\n filter[key] = value.map(transformQuery)\n } else if (key === '$or') {\n return\n }\n } else if (key === '$not') {\n // MongoError: unknown top level operator: $not\n // https://stackoverflow.com/questions/25270396/mongodb-how-to-invert-query-with-not\n // this may solve this problem but lead to performance degradation\n const query = transformQuery(value)\n if (query) filter.$nor = [query]\n } else if (key === '$expr') {\n additional.push({ $expr: transformEval(value) })\n } else {\n const query = transformFieldQuery(value, key, additional)\n if (query === false) return\n if (query !== true) filter[key] = query\n }\n }\n if (additional.length) {\n (filter.$and ||= []).push(...additional)\n }\n return filter\n}\n\nfunction transformEvalExpr(expr: any, onAggr?: (pipeline: any[]) => void) {\n return valueMap(expr as any, (value) => {\n if (Array.isArray(value)) {\n return value.map(val => transformEval(val, onAggr))\n } else {\n return transformEval(value, onAggr)\n }\n })\n}\n\nfunction transformAggr(expr: any) {\n if (typeof expr === 'string') {\n return '$' + expr\n }\n return transformEvalExpr(expr)\n}\n\nconst aggrKeys = ['$sum', '$avg', '$min', '$max', '$count']\n\nexport function transformEval(expr: any, onAggr?: (pipeline: any[]) => void) {\n if (typeof expr === 'number' || typeof expr === 'string' || typeof expr === 'boolean') {\n return expr\n } else if (expr.$) {\n return '$' + expr.$\n }\n\n for (const key of aggrKeys) {\n if (!expr[key]) continue\n const value = transformAggr(expr[key])\n const $ = Random.id()\n if (key === '$count') {\n onAggr([\n { $group: { _id: value } },\n { $group: { _id: null, [$]: { $count: {} } } },\n ])\n } else {\n onAggr([{ $group: { _id: null, [$]: { [key]: value } } }])\n }\n return { $ }\n }\n\n return transformEvalExpr(expr, onAggr)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@koishijs/plugin-database-mongo",
3
3
  "description": "MongoDB support for Koishi",
4
- "version": "3.1.0",
4
+ "version": "3.1.1",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -34,7 +34,7 @@
34
34
  "@koishijs/plugin-mock": "^1.0.3"
35
35
  },
36
36
  "peerDependencies": {
37
- "koishi": "^4.5.0"
37
+ "koishi": "^4.5.1"
38
38
  },
39
39
  "dependencies": {
40
40
  "@koishijs/orm": "^1.0.0",