UNPKG

ottoman

Version:
377 lines 19.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); exports._buildModel = exports.createModel = void 0; const couchbase_1 = require("couchbase"); const __1 = require(".."); const ottoman_errors_1 = require("../exceptions/ottoman-errors"); const handler_1 = require("../handler"); const schema_1 = require("../schema"); const cast_strategy_1 = require("../utils/cast-strategy"); const constants_1 = require("../utils/constants"); const noenumarable_1 = require("../utils/noenumarable"); const extract_select_1 = require("../utils/query/extract-select"); const index_field_names_1 = require("./index/helpers/index-field-names"); const build_index_query_1 = require("./index/n1ql/build-index-query"); const build_index_refdoc_1 = require("./index/refdoc/build-index-refdoc"); const build_view_index_query_1 = require("./index/view/build-view-index-query"); const model_utils_1 = require("./utils/model.utils"); const merge_1 = require("../utils/merge"); /** * @ignore */ const createModel = ({ name, schemaDraft, options, ottoman }) => { const schema = schemaDraft instanceof schema_1.Schema ? schemaDraft : new schema_1.Schema(schemaDraft); const { idKey: ID_KEY, modelKey, scopeName, collectionName, keyGenerator, keyGeneratorDelimiter } = options; const collection = () => ottoman.getCollection(collectionName, scopeName); const maxExpiry = options && options.maxExpiry ? options.maxExpiry : constants_1.DEFAULT_MAX_EXPIRY; const metadata = { modelName: name, collectionName, scopeName, collection, schema, ID_KEY, ottoman, modelKey, keyGenerator, maxExpiry, keyGeneratorDelimiter, }; const ModelFactory = (0, exports._buildModel)(metadata); (0, model_utils_1.setModelMetadata)(ModelFactory, metadata); // Adding dynamic static methods for (const key in schema === null || schema === void 0 ? void 0 : schema.statics) { ModelFactory[key] = schema === null || schema === void 0 ? void 0 : schema.statics[key].bind(ModelFactory); } // Adding indexes for (const key in schema === null || schema === void 0 ? void 0 : schema.index) { if (schema.index.hasOwnProperty(key)) { const { by, options, type } = schema.index[key]; const fields = Array.isArray(by) ? by : [by]; let indexName = `${ottoman.bucketName}_${scopeName}_${collectionName}_${metadata.modelName}$${(0, index_field_names_1.indexFieldsName)(fields)}`; indexName = indexName.replace(/-/g, '_'); switch (type) { case 'n1ql': case undefined: // Register access method e.g FindByName ModelFactory[key] = (0, build_index_query_1.buildIndexQuery)(ModelFactory, fields, key, options); // Register index to sync later with the server ottoman.registerIndex(indexName, fields, name); break; case 'view': indexName = key; const designDocName = `${scopeName}${collectionName}`; ModelFactory[key] = (0, build_view_index_query_1.buildViewIndexQuery)(ottoman, designDocName, indexName, fields, ModelFactory); ottoman.registerViewIndex(designDocName, indexName, fields, metadata); break; case 'refdoc': const prefix = `${scopeName}${collectionName}`; ModelFactory[key] = (0, build_index_refdoc_1.buildViewRefdoc)(metadata, ModelFactory, fields, prefix); ottoman.registerRefdocIndex(fields, prefix); break; default: throw new ottoman_errors_1.BuildIndexQueryError(`Unexpected index type '${type}' in index '${key}', was expected 'refdoc', 'n1ql' or 'view'`); } } } // @ts-ignore return ModelFactory; }; exports.createModel = createModel; const _buildModel = (metadata) => { var _a; const { schema, collection, ID_KEY, modelKey, scopeName, ottoman, modelName, keyGenerator, keyGeneratorDelimiter } = metadata; return _a = class _Model extends __1.Model { constructor(data, options = {}) { super(data); const strategy = options.strategy || cast_strategy_1.CAST_STRATEGY.DEFAULT_OR_DROP; const strict = options.strict !== undefined ? options.strict : schema.options.strict; const skip = options.skip || [modelKey.split('.')[0], ID_KEY]; const schemaData = (0, cast_strategy_1.cast)(data, schema, { strategy, strict, skip }); this._applyData(schemaData, strategy === cast_strategy_1.CAST_STRATEGY.THROW ? cast_strategy_1.CAST_STRATEGY.THROW : true); // Adding methods to the model instance if (schema === null || schema === void 0 ? void 0 : schema.methods) { for (const key in schema === null || schema === void 0 ? void 0 : schema.methods) { (0, noenumarable_1.nonenumerable)(this, key); this[key] = schema.methods[key]; } } // Adding queries index for (const key in schema === null || schema === void 0 ? void 0 : schema.queries) { const { by, of } = schema.queries[key]; if (by && of) { const QueryModel = ottoman.getModel(of); if (!QueryModel) { throw new ottoman_errors_1.BuildIndexQueryError(`Collection '${of}' does not exist.`); } const queryMetadata = (0, model_utils_1.getModelMetadata)(QueryModel); let indexName = `${ottoman.bucketName}_${scopeName}_${of}$${(0, index_field_names_1.indexFieldsName)([by])}`; indexName = indexName.replace(/-/g, '_'); (0, noenumarable_1.nonenumerable)(this, key); this[key] = () => (0, handler_1.find)(Object.assign(Object.assign({}, metadata), { modelName: of, collectionName: queryMetadata.collectionName }))({ [by]: this._getId() }); // Register index to sync later with the server ottoman.registerIndex(indexName, [by], of); } else { throw new ottoman_errors_1.BuildIndexQueryError('The "by" and "of" properties are required to build the queries.'); } } } static get namespace() { const { ottoman, collectionName, scopeName } = metadata; if (collectionName !== '_default') { return `\`${ottoman.bucketName}\`.\`${scopeName}\`.\`${collectionName}\``; } return ottoman.bucketName; } static get collectionName() { return metadata.collectionName; } static query(params) { return new __1.Query(params, this.namespace); } /** * dropCollection drops a collection from a scope in a bucket. * @param collectionName * @param scopeName * @param options */ static dropCollection(collectionName, scopeName, options = {}) { const _collectionName = collectionName || metadata.collectionName; const _scopeName = scopeName || metadata.scopeName; return ottoman.dropCollection(_scopeName, _collectionName, options); } static fromData(data) { return new _Model(data); } }, _a.find = (filter = {}, options = {}) => { return (0, handler_1.find)(metadata)(filter, Object.assign(Object.assign({}, options), { noCollection: false })); }, _a.collection = collection, _a.count = (filter = {}, options = {}) => __awaiter(void 0, void 0, void 0, function* () { const response = yield (0, handler_1.find)(metadata)(filter, { select: 'RAW COUNT(*) as count', noCollection: true, consistency: __1.SearchConsistency.LOCAL, transactionContext: options.transactionContext, }); if (response.hasOwnProperty('rows') && response.rows.length > 0) { return response.rows[0]; } throw new ottoman_errors_1.OttomanError('The query did not return any results.'); }), _a.findById = (id, options = {}) => __awaiter(void 0, void 0, void 0, function* () { const { populate, populateMaxDeep: deep, select, lean, transactionContext, enforceRefCheck = false } = options, findOptions = __rest(options, ["populate", "populateMaxDeep", "select", "lean", "transactionContext", "enforceRefCheck"]); const modelKeyClean = modelKey.split('.')[0]; let isModelKeyAddedToSelect = false; if (select) { const selectArray = typeof select === 'string' ? select.split(',').map((x) => x.trim()) : select; if (selectArray.findIndex((x) => x === modelKeyClean) === -1) { selectArray.push(modelKeyClean); isModelKeyAddedToSelect = true; } findOptions['project'] = (0, extract_select_1.extractSelect)(selectArray, { noCollection: true }, false, modelKey.split('.')[0]); } const key = constants_1._keyGenerator(keyGenerator, { metadata, id }, keyGeneratorDelimiter); let pojo; if (transactionContext) { const { content } = yield transactionContext.get(collection(), key); pojo = content; } else { const { value } = yield collection().get(key, findOptions); pojo = value; } if ((0, __1.getValueByPath)(pojo, metadata.modelKey) !== metadata.modelName) { throw new couchbase_1.DocumentNotFoundError(); } if (isModelKeyAddedToSelect) { delete pojo[modelKeyClean]; } if (populate) { return (0, model_utils_1.getPopulated)({ fieldsName: populate, deep, lean, pojo, schema, modelName, ottoman, enforceRefCheck, transactionContext, }); } if (lean) return pojo; const ModelFactory = ottoman.getModel(modelName); return new ModelFactory(pojo, { strict: false, strategy: cast_strategy_1.CAST_STRATEGY.KEEP }).$wasNew(); }), _a.findOne = (filter = {}, options = {}) => __awaiter(void 0, void 0, void 0, function* () { var _b; const response = yield (0, handler_1.find)(metadata)(filter, Object.assign(Object.assign({}, options), { limit: 1 })); if ((_b = response === null || response === void 0 ? void 0 : response.rows) === null || _b === void 0 ? void 0 : _b.length) { return response.rows[0]; } throw new couchbase_1.DocumentNotFoundError(); }), _a.create = (data, options = {}) => __awaiter(void 0, void 0, void 0, function* () { const instance = new _a(Object.assign({}, data)); yield instance.save(true, options); return instance; }), _a.createMany = (docs, options = {}) => __awaiter(void 0, void 0, void 0, function* () { const _docs = Array.isArray(docs) ? docs : [docs]; return yield (0, handler_1.createMany)(metadata)(_docs, options); }), _a.updateById = (id, data, options = { strict: true }) => __awaiter(void 0, void 0, void 0, function* () { if (data[ID_KEY] && id !== data[ID_KEY]) { throw new Error(`data contains id field with different value to the id provided! -> ${id} != ${data[ID_KEY]}`); } const key = id || data[ID_KEY]; const value = yield _a.findById(key, { withExpiry: !!options.maxExpiry, transactionContext: options.transactionContext, }); if (value[ID_KEY]) { const strategy = cast_strategy_1.CAST_STRATEGY.THROW; const obj = (0, merge_1.mergeDoc)(value, data); value._applyData(obj, options.strict); const instance = new _a(Object.assign({}, value), { strategy }); const _options = {}; if (options.maxExpiry) { _options.maxExpiry = options.maxExpiry; } if (options.transactionContext) { _options.transactionContext = options.transactionContext; } return instance.save(false, options); } }), _a.replaceById = (id, data, options = { strict: true }) => __awaiter(void 0, void 0, void 0, function* () { const key = id || data[ID_KEY]; const value = yield _a.findById(key, { withExpiry: !!options.maxExpiry, transactionContext: options.transactionContext, }); if (value[ID_KEY]) { const temp = {}; Object.keys(data).map((key) => { if (value.hasOwnProperty(key)) { temp[key] = value[key]; } }); if (options === null || options === void 0 ? void 0 : options.strict) { Object.keys(value).map((key) => { if (value.immutableHasOwnProperty(key)) { temp[key] = value[key]; } if (!data.hasOwnProperty(key) && options.strict === cast_strategy_1.CAST_STRATEGY.THROW) { data[key] = undefined; } }); } const replace = new _a(Object.assign({}, temp)).$wasNew(); replace._applyData(Object.assign(Object.assign({}, data), { [ID_KEY]: key }), options === null || options === void 0 ? void 0 : options.strict); const _options = { enforceRefCheck: options.enforceRefCheck }; if (options.maxExpiry) { _options.maxExpiry = options.maxExpiry; } if (options.transactionContext) { _options.transactionContext = options.transactionContext; } if (options.hasOwnProperty('enforceRefCheck')) { _options.enforceRefCheck = options.enforceRefCheck; } return replace.save(false, _options); } }), _a.removeById = (id, options = {}) => { const modelKeyObj = {}; (0, __1.setValueByPath)(modelKeyObj, modelKey, modelName); const instance = new _a(Object.assign({}, Object.assign({ [ID_KEY]: id }, modelKeyObj))); return instance.remove(options); }, _a.removeMany = (filter = {}, options = {}) => __awaiter(void 0, void 0, void 0, function* () { const response = yield (0, handler_1.find)(metadata)(filter, options); if (response.hasOwnProperty('rows') && response.rows.length > 0) { return (0, handler_1.removeMany)(metadata)(response.rows.map((v) => v[ID_KEY]), options.transactionContext ? { transactionContext: options.transactionContext } : {}); } return new handler_1.ManyQueryResponse('SUCCESS', { match_number: 0, success: 0, errors: [], data: [] }); }), _a.updateMany = (filter = {}, doc, options = { strict: true }) => __awaiter(void 0, void 0, void 0, function* () { var _c; const response = yield (0, handler_1.find)(metadata)(filter, options); if ((_c = response === null || response === void 0 ? void 0 : response.rows) === null || _c === void 0 ? void 0 : _c.length) { return (0, handler_1.updateMany)(metadata)(response.rows, doc, options); } if (options.upsert) { const ModelFactory = ottoman.getModel(modelName); yield ModelFactory.create(doc); } return new handler_1.ManyQueryResponse('SUCCESS', { success: options.upsert ? 1 : 0, match_number: 0, errors: [], data: [], }); }), _a.findOneAndUpdate = (filter = {}, doc, options = { strict: true }) => __awaiter(void 0, void 0, void 0, function* () { const saveOptions = {}; if (options.maxExpiry) { saveOptions.maxExpiry = options.maxExpiry; } if (options.transactionContext) { saveOptions.transactionContext = options.transactionContext; } if (options.hasOwnProperty('enforceRefCheck')) { saveOptions.enforceRefCheck = options.enforceRefCheck; } try { const before = yield _a.findOne(filter, Object.assign(Object.assign({}, options), { consistency: __1.SearchConsistency.LOCAL })); if (before) { const toSave = new _a(Object.assign({}, before)).$wasNew(); toSave._applyData(Object.assign({}, doc), options.strict); const after = yield toSave.save(false, saveOptions); return options.new ? after : before; } } catch (e) { if (options.upsert) { return yield _a.create(doc, saveOptions); } throw e; } }), _a.findOneAndRemove = (filter = {}, options = {}) => __awaiter(void 0, void 0, void 0, function* () { const doc = yield _a.findOne(filter, Object.assign(Object.assign({}, options), { consistency: 1 })); if (doc) { yield doc.remove(options); return doc; } throw new couchbase_1.DocumentNotFoundError(); }), _a; }; exports._buildModel = _buildModel; //# sourceMappingURL=create-model.js.map