@azteam/mongo-model
Version:
N/A
471 lines (413 loc) • 12 kB
JavaScript
import _ from 'lodash';
import {AVAILABLE_STATUS} from '@azteam/constant';
import {ErrorException, NOT_INIT_METHOD} from '@azteam/error';
class DataRepository {
constructor({activeModel, trashModel, redis}, dummyData = []) {
this._model = activeModel;
this._trashModel = trashModel;
this.redis = redis;
this._dummyData(dummyData);
}
async queryWithCache(cacheConfig, queryBuilder) {
if (cacheConfig.key) {
const redis = cacheConfig.redis ? cacheConfig.redis : this.redis;
if (redis) {
const cacheName = `${cacheConfig.trash ? 'trash_' : ''}${cacheConfig.key}`;
let data = await redis.get(cacheName);
if (!data) {
data = await queryBuilder;
redis.set(cacheName, data, cacheConfig.ttl ? cacheName.ttl : 300); // default cache 5 minutes
}
return data;
}
}
return queryBuilder;
}
getModel() {
if (this._model) {
return this._model;
}
throw new ErrorException(NOT_INIT_METHOD);
}
getTrashModel() {
if (this._trashModel) {
return this._trashModel;
}
throw new ErrorException(NOT_INIT_METHOD);
}
async getUniqueFields() {
const indexes = await this.getModel().collection.getIndexes({full: true});
return indexes
.filter((index) => index.unique)
.map((index) => Object.keys(index.key))
.flat();
}
_dummyData(data) {
data.map(async (item) => {
const obj = await this.findOne(item.query);
if (!obj) {
const Model = this.getModel();
const model = new Model();
_.map(item.data, (value, key) => {
model[key] = value;
});
await model.save();
}
});
}
count(query, options = {}) {
const queryBuilder = this.getModel().countDocuments(query, options);
return this.queryWithCache(
{
...options.cache,
trash: false,
},
queryBuilder
);
}
countTrash(query = {}, options = {}) {
const queryBuilder = this.getTrashModel().countDocuments(query, options);
return this.queryWithCache(
{
...options.cache,
trash: true,
},
queryBuilder
);
}
find(query = {}, options = {}) {
const {force, trash, cache, ...newOpts} = options;
if (newOpts.lean !== false) {
newOpts.lean = true;
newOpts.leanWithId = true;
}
if (!newOpts.sort) {
if (trash) {
newOpts.sort = {
deleted_at: 'desc',
};
} else {
newOpts.sort = {
_id: 'desc',
};
}
}
const limit = newOpts.limit && newOpts.limit < 10000 ? newOpts.limit : 10000;
if (newOpts.page) {
if (newOpts.page * limit >= 60000) {
newOpts.allowDiskUse = true;
}
} else {
newOpts.page = 1;
}
let queryBuilder = null;
if (trash) {
queryBuilder = this.getTrashModel().paginate(query, newOpts);
} else {
queryBuilder = this.getModel().paginate(query, newOpts);
}
return this.queryWithCache(
{
...cache,
trash,
},
queryBuilder
);
}
findTrash(query = {}, options = {}) {
return this.find(query, {
...options,
trash: true,
});
}
findAvailable(query = {}, options = {}) {
return this.find(
{
...query,
status: AVAILABLE_STATUS.AVAILABLE,
},
options
);
}
findUnavailable(query = {}, options = {}) {
return this.find(
{
...query,
status: AVAILABLE_STATUS.UNAVAILABLE,
},
options
);
}
findWaiting(query = {}, options = {}) {
return this.find(
{
...query,
status: AVAILABLE_STATUS.WAITING,
},
options
);
}
// findNear(geo = {}, query = {}, options = {}) {
// geo = {
// name: 'geo',
// coords: [],
// ...geo,
// };
//
// if (geo.maxDistance) {
// query[geo.name] = {
// $nearSphere: {
// $geometry: {
// type: 'Point',
// coordinates: geo.coords,
// },
// $maxDistance: geo.maxDistance,
// },
// };
// } else {
// query[geo.name] = {
// $nearSphere: {
// $geometry: {
// type: 'Point',
// coordinates: geo.coords,
// },
// },
// };
// }
//
// return this.find(query, {
// ...options,
// forceCountFn: true,
// });
// }
explain(query = {}, options = {}) {
const {force, trash, ...newOpts} = options;
let queryBuilder = null;
if (trash) {
queryBuilder = this.getTrashModel().findOne(query, null, {
...newOpts,
explain: true,
});
} else {
queryBuilder = this.getModel().findOne(query, null, {
...newOpts,
explain: true,
});
}
return queryBuilder;
}
findOne(query = {}, options = {}) {
const {force, trash, cache, ...newOpts} = options;
let queryBuilder = null;
if (trash) {
queryBuilder = this.getTrashModel().findOne(query, null, newOpts);
} else {
queryBuilder = this.getModel().findOne(query, null, newOpts);
}
return this.queryWithCache(
{
...cache,
trash,
},
queryBuilder
);
}
findAll(query = {}, options = {}) {
const {force, trash, cache, ...newOpts} = options;
let queryBuilder = null;
if (trash) {
queryBuilder = this.getTrashModel().find(query);
} else {
queryBuilder = this.getModel().find(query);
}
if (newOpts.sort) {
queryBuilder = queryBuilder.sort(newOpts.sort);
}
if (newOpts.limit) {
queryBuilder = queryBuilder.limit(newOpts.limit);
}
if (newOpts.select) {
queryBuilder = queryBuilder.select(newOpts.select);
}
if (newOpts.select || newOpts.lean) {
queryBuilder = queryBuilder.lean({virtuals: true});
}
return this.queryWithCache(
{
...cache,
trash,
},
queryBuilder
);
}
findOneAvailable(query = {}, options = {}) {
return this.findOne(
{
...query,
status: AVAILABLE_STATUS.AVAILABLE,
},
options
);
}
findOneUnavailable(query = {}, options = {}) {
return this.findOne(
{
...query,
status: AVAILABLE_STATUS.UNAVAILABLE,
},
options
);
}
findOneWaiting(query = {}, options = {}) {
return this.findOne(
{
...query,
status: AVAILABLE_STATUS.WAITING,
},
options
);
}
findOneTrash(query = {}, options = {}) {
return this.findOne(query, {
...options,
trash: true,
});
}
// findOneNear(geo = {}, query = {}, options = {}) {
// geo = {
// name: 'geo',
// coords: [],
// ...geo,
// };
//
// if (geo.maxDistance) {
// query[geo.name] = {
// $nearSphere: {
// $geometry: {
// type: 'Point',
// coordinates: geo.coords,
// },
// $maxDistance: geo.maxDistance,
// },
// };
// } else {
// query[geo.name] = {
// $nearSphere: {
// $geometry: {
// type: 'Point',
// coordinates: geo.coords,
// },
// },
// };
// }
//
// return this.findOne(query, options);
// }
findOneById(id, options = {}) {
return this.findOne(
{
_id: id,
},
options
);
}
findOneTrashById(id, options = {}) {
return this.findOneTrash(
{
_id: id,
},
options
);
}
async findOneBySlug(slug, options = {}) {
return this.findOne(
{
$or: [
{
slug,
},
{
related_slugs: slug,
},
],
},
options
);
}
async findOneOrCreate(query, data, options = {}) {
let model = await this.findOne(query, options);
if (!model) {
model = await this.create(data);
}
return model;
}
create(data, allow = [], createdId = null) {
const Model = this.getModel();
const model = new Model();
if (createdId) {
model.created_id = createdId;
model.modified_id = createdId;
}
return model.loadData(data, allow).save();
}
createByUser(createdId, data = {}, allow = []) {
return this.create(data, allow, createdId);
}
async update(query, data) {
const model = await this.findOne(query);
return model.modify(data);
}
async updateById(id, data) {
return this.update({_id: id}, data);
}
async fix(query, data) {
const model = await this.findOne(query);
return model.fix(data);
}
async fixById(id, data) {
return this.fix({_id: id}, data);
}
async updateAll(query, data) {
return this.getModel().updateMany(query, data);
}
async delete(obj, deletedId) {
await this.getTrashModel().create({
...obj.toJSON(),
deleted_id: deletedId,
deleted_at: Math.floor(Date.now() / 1000),
});
await this.getModel().deleteOne({
_id: obj.id,
});
}
async restore(obj, restoredId) {
await this.getModel().create({
...obj.toJSON(),
modified_id: restoredId,
});
await this.getTrashModel().deleteOne({
_id: obj.id,
});
}
async destroy(id) {
await this.getModel().deleteOne({
_id: id,
});
await this.getTrashModel().deleteOne({
_id: id,
});
}
async destroyAll(query) {
await this.getModel().deleteMany(query);
await this.getTrashModel().deleteMany(query);
}
async cleanTrash(durationDay = 30) {
const deletedAt = Math.floor(Date.now() / 1000) - 86400 * durationDay;
await this.getTrashModel().deleteMany({
deleted_at: {
$lte: deletedAt,
},
});
}
}
export default DataRepository;