ottoman
Version:
Ottoman Couchbase ODM
481 lines (480 loc) • 19.4 kB
TypeScript
import { Document, IDocument } from './document';
import { FindByIdOptions, FindOptions, ManyQueryResponse } from '../handler';
import { IConditionExpr, LogicalWhereExpr, Query } from '../query';
import { UpdateManyOptions } from './interfaces/update-many.interface';
import { FindOneAndUpdateOption } from './interfaces/find.interface';
import { CastOptions, MutationFunctionOptions } from '../utils/cast-strategy';
import { CountOptions as removeOptions, CountOptions, ModelTypes, saveOptions } from './model.types';
export declare class Model<T = any, R = any> extends Document {
}
/**
* Constructor to build a model instance based on schema and options.
* Provides methods to handle documents of the current collection in the database.
*
* @example
* ```javascript
* import { connect, model } from "ottoman";
* connect("couchbase://localhost/travel-sample@admin:password");
*
* // Create an `User` model
* const User = model('User', { name: String });
* ```
*/
export interface IModel<T = any, R = any> {
/**
* Creates a document from this model.
* Implements schema validations, defaults, methods, static and hooks
*/
new (data: T, options?: CastOptions): IDocument<T>;
/**
* Finds documents.
*
* @example
* ```javascript
* User.find({ name: "Jane" })
* // will return a list of all users with the name "Jane"
*
* User.find({ name: "Jane" }, { limit: 10 })
* // will return a list of all users with the name "Jane" and limited to 10 items
*
* User.find({ name: "Jane" }, { ignoreCase: true })
* // will return a list of all users with the name "Jane" ignoring case
*
* ```
*
* ```javascript
* const filter = {
* $or: [{ price: { $gt: 'amount_val', $isNotNull: true } }, { auto: { $gt: 10 } }, { amount: 10 }],
* $and: [
* { price2: { $gt: 1.99, $isNotNull: true } },
* { $or: [{ price3: { $gt: 1.99, $isNotNull: true } }, { id: '20' }] },
* {name: { $eq: 'John', $ignoreCase: true }}
* ],
* };
* User.find(filter)
* // Returns a list of the elements that match the applied filters.
* ```
*/
find<Doc = T>(filter?: LogicalWhereExpr<Doc>, options?: FindOptions): Promise<any>;
/**
* Returns the number of documents that match the query.
*
* @example
* ```javascript
* User.count({ name: { $like: "%Jane%" } })
* ```
*/
count(filter?: LogicalWhereExpr<T>, options?: CountOptions): Promise<any>;
/**
* Retrieves a document by id.
*
* @example
* ```javascript
* User.findById('userId')
* // will return the user document by id.
*
* User.findById('userId', {select: 'name, cards', populate: 'cards'})
* // will return the user document by id with only the fields name, and cards populated.
* ```
*/
findById<Result = R>(id: string, options?: FindByIdOptions): Promise<Result>;
findById<Result = R>(id: string, options?: FindByIdOptions): Promise<IDocument<Result>>;
/**
* Finds a document.
*
* @example
* ```javascript
* User.findOne({ name: "Jane" })
* // will return a document with a User name of "Jane", or if it doesn't exist, throw a DocumentNotFoundError exception.
*
* User.findOne({ name: "Jane" }, { ignoreCase: true })
* // will return a document with a User name of "Jane", ignoring the case.
* ```
*/
findOne<Doc = T, Result = R>(filter?: LogicalWhereExpr<Doc>, options?: FindOptions): Promise<Result>;
findOne<Doc = T, Result = R>(filter?: LogicalWhereExpr<Doc>, options?: FindOptions): Promise<IDocument<Result>>;
/**
* Creates a new document.
*
* @example
* ```javascript
* const user = await User.create({ name: "John Doe" });
* ```
*/
create<Doc = T>(doc: Doc, options?: saveOptions): Promise<IDocument<T>>;
/**
* Creates many documents at once.
*
* The response status will be **SUCCESS** as long as no error occurs, otherwise it will be **FAILURE**.
*
* @example
* ```javascript
* const user = await User.createMany([{ name: "John Doe" }, { name: "Jane Doe" }]);
* ```
*/
createMany<Doc = T, Result = R>(docs: Doc[] | Doc, options?: saveOptions): Promise<ManyQueryResponse<IDocument<Result>>>;
/**
* Updates a document.
*
* @example
* ```typescript
* const user = await User.updateById('userId', {name: "John Doe"});
* ```
*
* @example Using updateById on immutable properties
* ```typescript
* const cardData = {
* cardNumber: '5678 5678 5678 5678',
* zipCode: '56789',
* };
*
* const cardDataUpdate = {
* cardNumber: '4321 4321 4321 4321',
* zipCode: '43210',
* };
*
* const CardSchema = new Schema({
* cardNumber: { type: String, immutable: true },
* zipCode: String,
* });
* const Card = model('Card', CardSchema);
* const {id} = await Card.create(cardData);
*
* // with strategy:CAST_STRATEGY.THROW
* await Card.updateById(id, cardDataUpdate, { strict: CAST_STRATEGY.THROW });
* // ImmutableError: Field 'cardNumber' is immutable and current cast strategy is set to 'throw'
*
* // with strategy:true (default)
* await Card.updateById(id, cardDataUpdate, { strict: true });
* const result = await Card.findById(id);
* console.log(result); // {cardNumber:'5678 5678 5678 5678', zipCode:'43210'} only zipCode was changed
*
* // with strategy:false
* await Card.updateById(id, cardDataUpdate, { strict: false });
* const result = await Card.findById(id);
* console.log(result); // {cardNumber:'4321 4321 4321 4321', zipCode:'43210'} all properties were changed
* ```
*
* @throws **ImmutableError** if updateById is strict:CAST_STRATEGY.THROW and try to modify a immutable property.
*/
updateById<Doc = T, Result = R>(id: string, data: Doc | Partial<Doc> | IDocument<Doc>, options?: MutationFunctionOptions): Promise<Result>;
updateById<Doc = T, Result = R>(id: string, data: Doc | Partial<Doc> | IDocument<Doc>, options?: MutationFunctionOptions): Promise<IDocument<Result>>;
/**
* Same as **updateById**, except it replaces the existing document with the given document.
*
* @example
* ```javascript
* const user = await User.replaceById('userId', {name: "John Doe"});
* ```
*
* @example Using replaceById on immutable properties
* ```typescript
* const cardData = {
* cardNumber: '5678 5678 5678 5678',
* zipCode: '56789',
* };
*
* const cardDataUpdate = {
* cardNumber: '4321 4321 4321 4321',
* zipCode: '43210',
* };
*
* const CardSchema = new Schema({
* cardNumber: { type: String, immutable: true },
* zipCode: String,
* });
* const Card = model('Card', CardSchema);
* const {id} = await Card.create(cardData);
*
* // with strategy:CAST_STRATEGY.THROW
* await Card.replaceById(id, cardDataUpdate, { strict: CAST_STRATEGY.THROW });
* // ImmutableError: Field 'cardNumber' is immutable and current cast strategy is set to 'throw'
*
* // with strategy:true (default)
* await Card.replaceById(id, cardDataUpdate, { strict: true });
* const result = await Card.findById(id);
* console.log(result); // {cardNumber:'5678 5678 5678 5678', zipCode:'43210'} only zipCode was changed
*
* // with strategy:false
* await Card.replaceById(id, cardDataUpdate, { strict: false });
* const result = await Card.findById(id);
* console.log(result); // {cardNumber:'4321 4321 4321 4321', zipCode:'43210'} all properties were changed
* ```
*
* @throws **DocumentNotFoundError** if the document not exist.
* @throws **ImmutableError** if replaceById is strict:CAST_STRATEGY.THROW and try to modify a immutable property.
*/
replaceById<Doc = T, Result = R>(id: string, data: Doc | IDocument<Doc>, options?: MutationFunctionOptions): Promise<Result>;
replaceById<Doc = T, Result = R>(id: string, data: Doc | IDocument<Doc>, options?: MutationFunctionOptions): Promise<IDocument<Result>>;
/**
* Removes a document by id.
*
* @example
* ```javascript
* const result = await User.removeById('userId');
* ```
*/
removeById(id: string, options?: removeOptions): Promise<{
cas: any;
}>;
/**
* Creates a [document](/docs/api/classes/document) from the given data.
* Result will be the same that -> new Model(data)
*
* @example
* ```javascript
* const user = User.fromData({name: "John Doe"});
* // we create a `user` document, but it isn't saved yet.
*
* await user.save();
* // Now user was persisted to DB
* ```
*/
fromData(data: T | Partial<T>): ModelTypes<T>;
/**
* Deletes all of the documents that match conditions from the collection.
*
* The response status will be **SUCCESS** as long as no error occurs, otherwise it will be **FAILURE**.
*
* @example
* ```javascript
* const result = await User.removeMany({ name: { $like: '%John Doe%' } })
* // Could also use:
* const result = await User.removeMany({ name:'John Doe' }, { ignoreCase: true })
* ```
*
* @param filter Filter Condition [Where Expression](/docs/api/classes/query.html)
* @param options [Find Options](/docs/api/classes/findoptions.html#class-findoptions)
*
*
*/
removeMany<Doc = T, Result = string>(filter?: LogicalWhereExpr<Doc>, options?: FindOptions): Promise<ManyQueryResponse<Result>>;
/**
* Updates all of the documents that match conditions from the collection.
*
* The response status will be **SUCCESS** as long as no error occurs, otherwise it will be **FAILURE**.
*
* @example
* ```javascript
* const result = await User.updateMany({ name: { $like: '%John Doe%' } })
* ```
*
* @example Using immutable properties
* ```typescript
*
* // Function helper for example
* async function updateManyHelper(result: any[], strict: ApplyStrategy = true) {
* // First define the schema
* const CardSchema = new Schema({
* cardNumber: String,
* zipCode: { type: String, immutable: true },
* });
*
* //The model is created
* const Card = model('CardMany', CardSchema);
*
* // Created elements are stored here
* let card1;
* let card2;
*
* const batchCreate = async () => {
* card1 = await Card.create({ cardNumber: '5678 5678 1111 1111', zipCode: '11111' });
* card2 = await Card.create({ cardNumber: '5678 5678 2222 2222', zipCode: '22222' });
* };
*
* // Create elements
* await batchCreate();
*
* // Update by some criteria
* const response: IManyQueryResponse = await Card.updateMany(
* { cardNumber: { $like: '%5678 5678%' } },
* { zipCode: '12345', cardNumber: '0000 0000 0000 0000' },
* { strict },
* );
* const result1 = await Card.findById(card1.id);
* const result2 = await Card.findById(card2.id);
*
* // Reset data before end test round
* const cleanUp = async () => await Card.removeMany({ _type: 'CardMany' });
* await cleanUp();
*
* // Return results and response
* result.push(result1, result2, response);
* }
*
* // Store data to check response on each round
* let result: any[] = [];
*
* async function testRounds(){
*
* // Round 1: updateMany with strategy:false
* await updateManyHelper(result, false);
* const [result1, result2, response] = result;
* console.log(result1); // {"cardNumber":"0000 0000 0000 0000","zipCode":"12345"} both properties have been changed
* console.log(result2); // {"cardNumber":"0000 0000 0000 0000","zipCode":"12345"} both properties have been changed
* console.log(response); // {"status":"SUCCESS","message":{"success":2,"match_number":2,"errors":[]}}
* result = [];
*
* // Round 2: updateMany with strategy:true
* await updateManyHelper(result, true);
* const [result1, result2, response] = result;
* console.log(result1); // {"cardNumber":"0000 0000 0000 0000","zipCode":"11111"} only cardNumber has changed
* console.log(result2); // {"cardNumber":"0000 0000 0000 0000","zipCode":"22222"} only cardNumber has changed
* console.log(response); // {"status":"SUCCESS","message":{"success":2,"match_number":2,"errors":[]}}
* result = [];
*
* // Round 3: updateMany with strategy:CAST_STRATEGY.THROW
* await updateManyHelper(result, CAST_STRATEGY.THROW);
* const [result1, result2, response] = result;
* console.log(result1); // {"cardNumber":"5678 5678 1111 1111","zipCode":"11111"} only cardNumber has changed
* console.log(result2); // {"cardNumber":"5678 5678 2222 2222","zipCode":"22222"} only cardNumber has changed
* console.log(response); // {"status":"SUCCESS","message":{"success":2,"match_number":2,"errors":[]}}
* result = [];
*
* // RESPONSE FAILURE
* // {
* // "status": "FAILURE",
* // "message": {
* // "success": 0,
* // "match_number": 2,
* // "errors": [
* // {
* // "status": "FAILURE",
* // "exception": "ImmutableError",
* // "message": "Field 'zipCode' is immutable and current cast strategy is set to 'throw'"
* // },
* // {
* // "status": "FAILURE",
* // "exception": "ImmutableError",
* // "message": "Field 'zipCode' is immutable and current cast strategy is set to 'throw'"
* // }
* // ]
* // }
* // }
* }
* // Test rounds are run
* testRounds();
* ```
*
* @param filter Filter Condition [Where Expression](/docs/api/classes/query.html)
* @param doc Values for the fields to update
* @param options [Update Many Options](/docs/api/interfaces/updatemanyoptions.html)
*
*/
updateMany<Query = T, Result = T>(filter?: LogicalWhereExpr<Query>, doc?: T | Partial<T>, options?: UpdateManyOptions): Promise<ManyQueryResponse<IDocument<Result>>>;
/**
* Finds a document that matches the conditions of the collection and updates it.
*
* @example
* ```javascript
* const result = await User.findOneAndUpdate({ name: { $like: '%John Doe%' } }, { name: "John" })
* ```
*
* @param filter Filter Condition [Where Expression](/docs/api/classes/query.html#where)
* @param doc Values for the fields to update
* @param options [FindOneAndUpdateOptions](/docs/api/interfaces/findoneandupdateoption.html)
*
*
* Return a [Model](/docs/api/classes/model.html) if at least one item matching the condition, otherwise an [exception](https://docs.couchbase.com/sdk-api/couchbase-node-client/classes/DocumentNotFoundError.html) will be thrown.
* If options.new is **true** return the document after update otherwise by default return the document before update.
* If options.upsert is **true** insert a document if the document does not exist.
* options.strict used for strategy to apply on immutables properties
*
* @example Using findOneAndUpdate on immutable properties
* ```typescript
* const cardData = {
* cardNumber: '5678 5678 5678 5678',
* zipCode: '56789',
* };
*
* const cardDataUpdate = {
* cardNumber: '4321 4321 4321 4321',
* zipCode: '43210',
* };
*
* const CardSchema = new Schema({
* cardNumber: { type: String, immutable: true },
* zipCode: String,
* });
* const Card = model('Card', CardSchema);
* const {id} = await Card.create(cardData);
*
* // with strategy:CAST_STRATEGY.THROW
* await Card.findOneAndUpdate({ cardNumber: { $like: '%5678 5678 5678 5678%' } }, cardDataUpdate, {
* new: true,
* strict: CAST_STRATEGY.THROW,
* });
* // ImmutableError: Field 'cardNumber' is immutable and current cast strategy is set to 'throw'
*
* // with strategy:true (default)
* await Card.findOneAndUpdate({ cardNumber: { $like: '%5678 5678 5678 5678%' } }, cardDataUpdate, {
* new: true,
* strict: true,
* });
* const result = await Card.findById(id);
* console.log(result); // {cardNumber:'5678 5678 5678 5678', zipCode:'43210'} only zipCode was changed
*
* // with strategy:false
* await Card.findOneAndUpdate({ cardNumber: { $like: '%5678 5678 5678 5678%' } }, cardDataUpdate, {
* new: true,
* strict: false,
* });
* const result = await Card.findById(id);
* console.log(result); // {cardNumber:'4321 4321 4321 4321', zipCode:'43210'} all properties were changed
* ```
*
* @throws **ImmutableError** if findOneAndUpdate is strict:CAST_STRATEGY.THROW and try to modify a immutable property.
*/
findOneAndUpdate<Query = T, Result = R>(filter?: LogicalWhereExpr<Query>, doc?: T | Partial<T>, options?: FindOneAndUpdateOption): Promise<Result>;
findOneAndUpdate<Query = T, Result = R>(filter?: LogicalWhereExpr<Query>, doc?: T | Partial<T>, options?: FindOneAndUpdateOption): Promise<IDocument<Result>>;
/**
* Finds a document that matches the conditions of the collection and remove it.
*
* @example
* ```javascript
* const result = await User.findOneAndRemove({ name: { $like: '%John Doe%' } })
* ```
*
* @param filter Filter Condition [Where Expression](/docs/api/classes/query.html#where)
*
*/
findOneAndRemove<Query = T, Result = R>(filter?: LogicalWhereExpr<Query>): Promise<Result>;
findOneAndRemove<Query = T, Result = R>(filter?: LogicalWhereExpr<Query>): Promise<IDocument<Result>>;
/**
* Return a [Query](/docs/api/classes/query) instance in the model context
* @example
* ```typescript
* const query = User.query({name: 'John Smith'})
* console.log(query.build())
* // select * from `travel-sample`.`inventory`.`user` where name = 'John Smith'
* ```
*/
query(params: IConditionExpr): Query;
/**
* Return string value with the model context
* @example
* ```javascript
* console.log(User.namespace)
* // `travel-sample`.`inventory`.`user`
* ```
*/
namespace: string;
/**
* Return string value with the model collection name
* @example
* ```javascript
* console.log(User.collectionName)
* // "User"
* ```
*/
collectionName: string;
/**
* dropCollection drops a collection from a scope in a bucket.
* @param collectionName
* @param scopeName
* @param options
*/
dropCollection(collectionName?: string, scopeName?: string, options?: {
timeout?: number;
}): Promise<boolean | undefined>;
}