nodedb-json
Version:
A lightweight JSON-based database for Node.js with TypeScript support, indexing, and complex query capabilities
885 lines • 32.5 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const fs = __importStar(require("fs"));
const _ = __importStar(require("lodash"));
/**
* A class to manage JSON-based database operations.
*/
class NodedbJson {
/**
* Creates an instance of NodedbJson.
* @param {string} filePath - The path to the JSON file.
* @param {DbOptions} [options] - Database options.
*/
constructor(filePath, options = {}) {
this._pendingChanges = 0;
this._indexes = {};
this._indexDefinitions = {};
this.filePath = filePath;
this.options = {
autoSave: true,
createIfNotExists: true,
defaultValue: {},
enableIndexing: true,
autoIndex: true,
...options
};
this.data = this.readJSONFile();
// 自动创建索引
if (this.options.enableIndexing && this.options.autoIndex) {
this._rebuildAllIndexes();
}
}
/**
* Reads the JSON file.
* @returns {object} - The parsed JSON data.
*/
readJSONFile() {
if (!fs.existsSync(this.filePath)) {
if (this.options.createIfNotExists) {
fs.writeFileSync(this.filePath, JSON.stringify(this.options.defaultValue || {}), "utf-8");
}
else {
throw new Error(`Database file does not exist: ${this.filePath}`);
}
}
const fileData = fs.readFileSync(this.filePath, "utf-8");
return JSON.parse(fileData);
}
/**
* Writes the JSON data to the file.
*/
writeJSONFile() {
fs.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2), "utf-8");
this._pendingChanges = 0;
}
/**
* Manually save changes to file.
* @returns {NodedbJson} - The instance of the database for chaining.
*/
save() {
if (this._pendingChanges > 0) {
this.writeJSONFile();
}
return this;
}
/**
* Sets a value in the JSON data.
* @param {string} key - The key to set.
* @param {any} value - The value to set.
* @returns {NodedbJson} - The instance of the database for chaining.
*/
set(key, value) {
// 检查是否需要更新索引
const oldValue = _.get(this.data, key);
const needsIndexUpdate = this.options.enableIndexing &&
(Array.isArray(oldValue) || Array.isArray(value)) &&
this._hasIndexDefinition(key);
_.set(this.data, key, value);
this._pendingChanges++;
// 如果修改了带索引的数组,重建索引
if (needsIndexUpdate) {
this._rebuildIndexesForKey(key);
}
if (this.options.autoSave) {
this.writeJSONFile();
}
return this;
}
/**
* Gets a value from the JSON data.
* @param {string} key - The key to get.
* @returns {any} - The value.
*/
get(key) {
return _.get(this.data, key);
}
/**
* Checks if a key exists in the JSON data.
* @param {string} key - The key to check.
* @returns {boolean} - True if the key exists, otherwise false.
*/
has(key) {
return _.has(this.data, key);
}
/**
* Updates a value in the JSON data.
* @param {string} key - The key to update.
* @param {function|object} predicateOrUpdater - The predicate function or updater object.
* @param {object} [updater] - The updater object if a predicate function is provided.
* @returns {NodedbJson} - The instance of the database for chaining.
*/
update(key, predicateOrUpdater, updater) {
const data = this.get(key);
if (Array.isArray(data)) {
// 如果使用索引进行更新
if (typeof predicateOrUpdater === 'object' &&
this.options.enableIndexing &&
this._hasIndexDefinition(key)) {
// 尝试使用索引查找
const foundIndex = this._findIndexedItemPosition(key, predicateOrUpdater);
if (foundIndex !== -1) {
_.merge(data[foundIndex], updater);
this._pendingChanges++;
// 更新索引
this._rebuildIndexesForKey(key);
}
else {
throw new Error(`No item found matching the predicate.`);
}
}
else {
// 常规查找
const item = _.find(data, predicateOrUpdater);
if (item && updater) {
_.merge(item, updater);
this._pendingChanges++;
// 如果有索引,需要更新
if (this.options.enableIndexing && this._hasIndexDefinition(key)) {
this._rebuildIndexesForKey(key);
}
}
else if (!item) {
throw new Error(`No item found matching the predicate.`);
}
}
}
else if (_.isObject(data)) {
_.update(this.data, key, predicateOrUpdater);
this._pendingChanges++;
}
else {
throw new Error(`Key "${key}" does not reference a collection or array.`);
}
if (this.options.autoSave) {
this.writeJSONFile();
}
return this;
}
/**
* Deletes a value from the JSON data.
* @param {string} key - The key to delete.
* @param {function|string[]} [predicateOrKeys] - The predicate function or array of keys to delete.
* @param {string} [field='id'] - The field to match for array deletion.
* @returns {NodedbJson} - The instance of the database for chaining.
*/
delete(key, predicateOrKeys, field = 'id') {
const data = this.get(key);
const hasIndex = this.options.enableIndexing && this._hasIndexDefinition(key);
if (Array.isArray(data)) {
if (typeof predicateOrKeys === 'function') {
_.remove(data, predicateOrKeys);
}
else if (Array.isArray(predicateOrKeys)) {
// 如果有索引,尝试使用索引删除
if (hasIndex && this._hasIndexOnField(key, field)) {
for (const fieldValue of predicateOrKeys) {
const index = this._getItemIndexByField(key, field, fieldValue);
if (index !== -1) {
data.splice(index, 1);
}
}
}
else {
_.remove(data, (item) => predicateOrKeys.includes(item[field]));
}
}
else {
throw new Error(`Predicate or keys array must be provided for array deletion.`);
}
}
else if (this.has(key)) {
if (Array.isArray(predicateOrKeys)) {
predicateOrKeys.forEach(itemKey => {
_.unset(this.data[key], itemKey);
});
}
else {
_.unset(this.data, key);
}
}
else {
throw new Error(`Key "${key}" does not exist.`);
}
// 如果有索引,更新索引
if (hasIndex) {
this._rebuildIndexesForKey(key);
}
this._pendingChanges++;
if (this.options.autoSave) {
this.writeJSONFile();
}
return this;
}
/**
* Finds a value in the JSON data.
* @param {string} key - The key to find.
* @param {function} predicate - The predicate function to match.
* @returns {any} - The found value.
*/
find(key, predicate) {
const data = this.get(key);
if (Array.isArray(data)) {
return _.find(data, predicate);
}
else if (_.isObject(data)) {
return _.find(Object.values(data), predicate);
}
else {
throw new Error(`Key "${key}" does not reference a collection or array.`);
}
}
/**
* Finds a value by field and value using index if available.
* @param {string} key - The key to find.
* @param {string} field - The field to match.
* @param {any} value - The value to match.
* @returns {any} - The found value.
*/
findByField(key, field, value) {
const data = this.get(key);
if (!Array.isArray(data)) {
throw new Error(`Key "${key}" does not reference an array.`);
}
// 使用索引进行查找
if (this.options.enableIndexing && this._hasIndexOnField(key, field)) {
const index = this._getItemIndexByField(key, field, value);
return index !== -1 ? data[index] : undefined;
}
// 常规查找
return _.find(data, item => item[field] === value);
}
/**
* Filters values in the JSON data.
* @param {string} key - The key to filter.
* @param {function} predicate - The predicate function to match.
* @returns {any[]} - The filtered values.
*/
filter(key, predicate) {
const data = this.get(key);
if (Array.isArray(data)) {
return _.filter(data, predicate);
}
else if (_.isObject(data)) {
return _.filter(Object.values(data), predicate);
}
else {
throw new Error(`Key "${key}" does not reference a collection or array.`);
}
}
/**
* Filters values by field and possible values using index if available.
* @param {string} key - The key to filter.
* @param {string} field - The field to match.
* @param {any[]} values - The values to match.
* @returns {any[]} - The filtered values.
*/
filterByField(key, field, values) {
const data = this.get(key);
if (!Array.isArray(data)) {
throw new Error(`Key "${key}" does not reference an array.`);
}
// 使用索引进行过滤
if (this.options.enableIndexing && this._hasIndexOnField(key, field)) {
const result = [];
for (const value of values) {
const indexes = this._getItemIndexesByField(key, field, value);
for (const index of indexes) {
result.push(data[index]);
}
}
return result;
}
// 常规过滤
return _.filter(data, item => values.includes(item[field]));
}
/**
* Pushes a value into an array in the JSON data.
* @param {string} key - The key to push to.
* @param {any|any[]} value - The value or values to push.
* @returns {NodedbJson} - The instance of the database for chaining.
*/
push(key, value) {
if (!this.has(key)) {
if (Array.isArray(value)) {
this.set(key, value);
}
else {
this.set(key, [value]);
}
}
else {
const array = this.get(key);
if (Array.isArray(array)) {
if (Array.isArray(value)) {
array.push(...value);
}
else {
array.push(value);
}
this._pendingChanges++;
// 如果有索引,更新索引
if (this.options.enableIndexing && this._hasIndexDefinition(key)) {
this._rebuildIndexesForKey(key);
}
if (this.options.autoSave) {
this.writeJSONFile();
}
}
else {
throw new Error(`Key "${key}" is not an array.`);
}
}
return this;
}
/**
* Executes multiple operations in batch.
* @param {Array<{method: string, args: any[]}>} operations - Array of operations to execute.
* @returns {NodedbJson} - The instance of the database for chaining.
*/
batch(operations) {
const originalAutoSave = this.options.autoSave;
this.options.autoSave = false;
try {
operations.forEach(op => {
const { method, args } = op;
if (typeof this[method] === 'function') {
this[method].apply(this, args);
}
});
this.writeJSONFile();
}
finally {
this.options.autoSave = originalAutoSave;
}
return this;
}
/**
* 创建一个索引
* @param {string} key - 要索引的集合路径
* @param {IndexDefinition} indexDefinition - 索引定义
* @returns {NodedbJson} - 实例,支持链式调用
*/
createIndex(key, indexDefinition) {
if (!this.options.enableIndexing) {
throw new Error('Indexing is not enabled. Set enableIndexing option to true.');
}
const data = this.get(key);
if (!Array.isArray(data)) {
throw new Error(`Cannot create index on non-array data at "${key}"`);
}
// 保存索引定义
if (!this._indexDefinitions[key]) {
this._indexDefinitions[key] = {};
}
this._indexDefinitions[key][indexDefinition.field] = indexDefinition;
// 创建索引
this._buildIndex(key, indexDefinition);
return this;
}
/**
* 删除索引
* @param {string} key - 集合路径
* @param {string} field - 字段名
* @returns {NodedbJson} - 实例,支持链式调用
*/
dropIndex(key, field) {
if (!this.options.enableIndexing) {
return this;
}
if (this._indexDefinitions[key] && this._indexDefinitions[key][field]) {
delete this._indexDefinitions[key][field];
// 如果没有索引了,删除整个键
if (Object.keys(this._indexDefinitions[key]).length === 0) {
delete this._indexDefinitions[key];
}
// 删除索引数据
const indexKey = `${key}:${field}`;
if (this._indexes[indexKey]) {
delete this._indexes[indexKey];
}
}
return this;
}
/**
* 获取所有索引信息
* @returns {Record<string, Record<string, IndexDefinition>>} - 索引定义
*/
getIndexes() {
return _.cloneDeep(this._indexDefinitions);
}
/**
* 检查指定路径是否有索引定义
* @param {string} key - 集合路径
* @returns {boolean} - 是否有索引
*/
_hasIndexDefinition(key) {
return !!this._indexDefinitions[key] && Object.keys(this._indexDefinitions[key]).length > 0;
}
/**
* 检查指定路径的指定字段是否有索引
* @param {string} key - 集合路径
* @param {string} field - 字段名
* @returns {boolean} - 是否有索引
*/
_hasIndexOnField(key, field) {
return !!this._indexDefinitions[key] && !!this._indexDefinitions[key][field];
}
/**
* 重建所有索引
*/
_rebuildAllIndexes() {
// 清空所有索引
this._indexes = {};
// 重建每个定义的索引
for (const key in this._indexDefinitions) {
for (const field in this._indexDefinitions[key]) {
this._buildIndex(key, this._indexDefinitions[key][field]);
}
}
}
/**
* 重建指定路径的所有索引
* @param {string} key - 集合路径
*/
_rebuildIndexesForKey(key) {
if (!this._indexDefinitions[key]) {
return;
}
// 重建该路径的所有索引
for (const field in this._indexDefinitions[key]) {
this._buildIndex(key, this._indexDefinitions[key][field]);
}
}
/**
* 构建索引
* @param {string} key - 集合路径
* @param {IndexDefinition} indexDef - 索引定义
*/
_buildIndex(key, indexDef) {
const data = this.get(key);
if (!Array.isArray(data)) {
return;
}
const indexKey = `${key}:${indexDef.field}`;
this._indexes[indexKey] = {};
// 遍历数组构建索引
data.forEach((item, index) => {
if (!item || typeof item !== 'object') {
return;
}
const fieldValue = item[indexDef.field];
if (fieldValue === undefined || fieldValue === null) {
return;
}
const strValue = String(fieldValue);
// 根据索引类型处理
if (indexDef.type === 'unique') {
// 唯一索引
this._indexes[indexKey][strValue] = index;
}
else {
// 多值索引
if (!this._indexes[indexKey][strValue]) {
this._indexes[indexKey][strValue] = [];
}
this._indexes[indexKey][strValue].push(index);
}
});
}
/**
* 根据对象查找索引位置
* @param {string} key - 集合路径
* @param {object} obj - 查询对象
* @returns {number} - 找到的索引位置,-1表示未找到
*/
_findIndexedItemPosition(key, obj) {
// 检查是否有匹配的索引
for (const field in obj) {
if (this._hasIndexOnField(key, field)) {
const index = this._getItemIndexByField(key, field, obj[field]);
if (index !== -1) {
return index;
}
}
}
return -1;
}
/**
* 根据字段和值获取数组中的项索引
* @param {string} key - 集合路径
* @param {string} field - 字段名
* @param {any} value - 字段值
* @returns {number} - 找到的索引位置,-1表示未找到
*/
_getItemIndexByField(key, field, value) {
const indexKey = `${key}:${field}`;
const strValue = String(value);
if (!this._indexes[indexKey] || this._indexes[indexKey][strValue] === undefined) {
return -1;
}
const indexData = this._indexes[indexKey][strValue];
return typeof indexData === 'number' ? indexData : indexData[0] || -1;
}
/**
* 根据字段和值获取数组中的多个项索引
* @param {string} key - 集合路径
* @param {string} field - 字段名
* @param {any} value - 字段值
* @returns {number[]} - 找到的索引位置数组
*/
_getItemIndexesByField(key, field, value) {
const indexKey = `${key}:${field}`;
const strValue = String(value);
if (!this._indexes[indexKey] || this._indexes[indexKey][strValue] === undefined) {
return [];
}
const indexData = this._indexes[indexKey][strValue];
return typeof indexData === 'number' ? [indexData] : indexData;
}
/**
* 复杂查询操作,支持排序、分页、聚合等
* @param {string} key - 集合路径
* @param {QueryOptions} options - 查询选项
* @returns {QueryResult} - 查询结果
*/
query(key, options = {}) {
const startTime = performance.now();
const data = this.get(key);
if (!Array.isArray(data)) {
throw new Error(`Key "${key}" does not reference an array.`);
}
const totalRecords = data.length;
let filteredData = [...data];
let usedIndex = false;
// 1. 应用过滤条件 (WHERE)
if (options.where) {
const filterResult = this._applyFilter(key, filteredData, options.where);
filteredData = filterResult.data;
usedIndex = filterResult.usedIndex;
}
const filteredRecords = filteredData.length;
// 2. 应用排序 (ORDER BY)
if (options.sort) {
filteredData = this._applySort(filteredData, options.sort);
}
// 3. 应用跳过和限制 (SKIP/LIMIT)
if (options.skip && options.skip > 0) {
filteredData = filteredData.slice(options.skip);
}
if (options.limit && options.limit > 0) {
filteredData = filteredData.slice(0, options.limit);
}
// 4. 应用分页 (PAGINATION)
let paginationInfo;
if (options.pagination) {
const paginationResult = this._applyPagination(filteredData, options.pagination);
filteredData = paginationResult.data;
paginationInfo = paginationResult.pagination;
}
// 5. 应用字段选择 (SELECT)
if (options.select && options.select.length > 0) {
filteredData = this._applySelect(filteredData, options.select);
}
// 6. 计算聚合 (AGGREGATION)
let aggregations;
if (options.aggregation && options.aggregation.length > 0) {
// 注意:聚合使用原始过滤后的数据(未分页)
const originalFiltered = this.get(key);
let aggregationData = Array.isArray(originalFiltered) ? [...originalFiltered] : [];
if (options.where) {
const filterResult = this._applyFilter(key, aggregationData, options.where);
aggregationData = filterResult.data;
}
aggregations = this._applyAggregation(aggregationData, options.aggregation);
}
const executionTime = performance.now() - startTime;
return {
data: filteredData,
pagination: paginationInfo,
aggregations,
stats: {
totalRecords,
filteredRecords,
executionTime,
usedIndex
}
};
}
/**
* 应用过滤条件
* @param {string} key - 集合路径
* @param {T[]} data - 数据数组
* @param {PredicateFunction<T> | Record<string, any>} where - 过滤条件
* @returns {{data: T[], usedIndex: boolean}} - 过滤结果
*/
_applyFilter(key, data, where) {
let usedIndex = false;
if (typeof where === 'function') {
// 使用谓词函数过滤
return { data: data.filter(where), usedIndex: false };
}
// 使用对象条件过滤,尝试使用索引优化
if (typeof where === 'object' && this.options.enableIndexing) {
// 检查是否可以使用索引
const indexableFields = Object.keys(where).filter(field => this._hasIndexOnField(key, field));
if (indexableFields.length > 0) {
// 使用第一个有索引的字段进行快速过滤
const field = indexableFields[0];
const value = where[field];
const indexes = this._getItemIndexesByField(key, field, value);
if (indexes.length > 0) {
usedIndex = true;
let filteredData = indexes.map(index => data[index]).filter(Boolean);
// 应用其他条件
const otherConditions = _.omit(where, field);
if (Object.keys(otherConditions).length > 0) {
filteredData = filteredData.filter(item => this._matchesConditions(item, otherConditions));
}
return { data: filteredData, usedIndex };
}
}
}
// 常规过滤
const filteredData = data.filter(item => this._matchesConditions(item, where));
return { data: filteredData, usedIndex };
}
/**
* 检查项是否匹配条件
* @param {any} item - 数据项
* @param {Record<string, any>} conditions - 条件对象
* @returns {boolean} - 是否匹配
*/
_matchesConditions(item, conditions) {
for (const [field, value] of Object.entries(conditions)) {
if (_.get(item, field) !== value) {
return false;
}
}
return true;
}
/**
* 应用排序
* @param {T[]} data - 数据数组
* @param {SortOption | SortOption[]} sort - 排序选项
* @returns {T[]} - 排序后的数据
*/
_applySort(data, sort) {
const sortOptions = Array.isArray(sort) ? sort : [sort];
return _.orderBy(data, sortOptions.map(option => option.field), sortOptions.map(option => option.direction));
}
/**
* 应用分页
* @param {T[]} data - 数据数组
* @param {PaginationOption} pagination - 分页选项
* @returns {PaginationResult<T>} - 分页结果
*/
_applyPagination(data, pagination) {
const { page, pageSize } = pagination;
const totalItems = data.length;
const totalPages = Math.ceil(totalItems / pageSize);
const currentPage = Math.max(1, Math.min(page, totalPages));
const startIndex = (currentPage - 1) * pageSize;
const endIndex = startIndex + pageSize;
const paginatedData = data.slice(startIndex, endIndex);
return {
data: paginatedData,
pagination: {
currentPage,
pageSize,
totalItems,
totalPages,
hasPrevious: currentPage > 1,
hasNext: currentPage < totalPages
}
};
}
/**
* 应用字段选择
* @param {T[]} data - 数据数组
* @param {string[]} select - 选择的字段
* @returns {T[]} - 选择后的数据
*/
_applySelect(data, select) {
return data.map(item => _.pick(item, select));
}
/**
* 应用聚合操作
* @param {any[]} data - 数据数组
* @param {AggregationOption[]} aggregations - 聚合选项
* @returns {AggregationResult[]} - 聚合结果
*/
_applyAggregation(data, aggregations) {
return aggregations.map(agg => {
var _a, _b;
switch (agg.type) {
case 'count':
return {
type: 'count',
value: data.length
};
case 'sum':
if (!agg.field) {
throw new Error('Sum aggregation requires a field');
}
return {
type: 'sum',
field: agg.field,
value: _.sumBy(data, agg.field)
};
case 'avg':
if (!agg.field) {
throw new Error('Average aggregation requires a field');
}
return {
type: 'avg',
field: agg.field,
value: _.meanBy(data, agg.field)
};
case 'min':
if (!agg.field) {
throw new Error('Min aggregation requires a field');
}
return {
type: 'min',
field: agg.field,
value: ((_a = _.minBy(data, agg.field)) === null || _a === void 0 ? void 0 : _a[agg.field]) || 0
};
case 'max':
if (!agg.field) {
throw new Error('Max aggregation requires a field');
}
return {
type: 'max',
field: agg.field,
value: ((_b = _.maxBy(data, agg.field)) === null || _b === void 0 ? void 0 : _b[agg.field]) || 0
};
case 'group':
if (!agg.groupBy) {
throw new Error('Group aggregation requires a groupBy field');
}
return {
type: 'group',
groupBy: agg.groupBy,
value: _.groupBy(data, agg.groupBy)
};
default:
throw new Error(`Unsupported aggregation type: ${agg.type}`);
}
});
}
/**
* 快速排序查询(优化版本)
* @param {string} key - 集合路径
* @param {SortOption | SortOption[]} sort - 排序选项
* @param {number} [limit] - 限制返回数量
* @returns {T[]} - 排序后的数据
*/
orderBy(key, sort, limit) {
const data = this.get(key);
if (!Array.isArray(data)) {
throw new Error(`Key "${key}" does not reference an array.`);
}
const sortOptions = Array.isArray(sort) ? sort : [sort];
let sortedData = _.orderBy(data, sortOptions.map(option => option.field), sortOptions.map(option => option.direction));
if (limit && limit > 0) {
sortedData = sortedData.slice(0, limit);
}
return sortedData;
}
/**
* 快速分页查询
* @param {string} key - 集合路径
* @param {number} page - 页码(从1开始)
* @param {number} pageSize - 每页数量
* @param {PredicateFunction<T> | Record<string, any>} [where] - 过滤条件
* @returns {PaginationResult<T>} - 分页结果
*/
paginate(key, page, pageSize, where) {
const result = this.query(key, {
where,
pagination: { page, pageSize }
});
return {
data: result.data,
pagination: result.pagination
};
}
/**
* 聚合查询
* @param {string} key - 集合路径
* @param {AggregationOption[]} aggregations - 聚合选项
* @param {PredicateFunction<T> | Record<string, any>} [where] - 过滤条件
* @returns {AggregationResult[]} - 聚合结果
*/
aggregate(key, aggregations, where) {
const result = this.query(key, {
where,
aggregation: aggregations
});
return result.aggregations || [];
}
/**
* 统计查询
* @param {string} key - 集合路径
* @param {PredicateFunction<T> | Record<string, any>} [where] - 过滤条件
* @returns {number} - 统计数量
*/
count(key, where) {
var _a;
const result = this.aggregate(key, [{ type: 'count' }], where);
return ((_a = result[0]) === null || _a === void 0 ? void 0 : _a.value) || 0;
}
/**
* 去重查询
* @param {string} key - 集合路径
* @param {string} field - 去重字段
* @returns {any[]} - 去重后的值数组
*/
distinct(key, field) {
const data = this.get(key);
if (!Array.isArray(data)) {
throw new Error(`Key "${key}" does not reference an array.`);
}
return _.uniqBy(data, field).map(item => _.get(item, field));
}
}
// 导出类
exports.default = NodedbJson;
// 为了兼容 CommonJS 导出
module.exports = NodedbJson;
module.exports.default = NodedbJson;
//# sourceMappingURL=index.js.map