ottoman
Version:
Ottoman Couchbase ODM
377 lines • 19.9 kB
JavaScript
;
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