Skip to content

Commit

Permalink
feat(jsdoc): Document ObservableCollection and ObservableCursor
Browse files Browse the repository at this point in the history
  • Loading branch information
dotansimha committed Oct 31, 2016
1 parent c63917d commit 4b7391a
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 4 deletions.
157 changes: 153 additions & 4 deletions src/ObservableCollection.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import {Observable, Subscriber} from 'rxjs';

import {ObservableCursor} from './ObservableCursor';

import {removeObserver} from './utils';

import Selector = Mongo.Selector;
Expand All @@ -27,42 +25,104 @@ export module MongoObservable {
transform ? : Function;
}

export function fromExisting<T>(collection: Mongo.Collection<T>) {
/**
* Creates a new MongoObservable.Collection from an existing of predefined Mongo.Collection.
* Use this feature to wrap existing collections such as Meteor.users.
* @param {Mongo.Collection} collection - The collection.
* @returns {MongoObservable.Collection} - Wrapped collection.
* @static
*/
export function fromExisting<T>(collection: Mongo.Collection<T>): MongoObservable.Collection<T> {
return new MongoObservable.Collection(collection);
}

/**
* A class represents a MongoDB collection in the client side, wrapped with RxJS
* Observables, so you can use it with your Angular 2 easier.
* The wrapper has the same API as Mongo.Collection, only the "find" method returns
* an ObservableCursor instead of regular Mongo.Cursor.
*
* T is a generic type - should be used with the type of the objects inside the collection.
*/
export class Collection<T> {
private _collection: Mongo.Collection<T>;

/**
* Creates a new Mongo.Collection instance wrapped with Observable features.
* @param {String | Mongo.Collection} nameOrExisting - The name of the collection. If null, creates an
* unmanaged (unsynchronized) local collection. If provided an instance of existing collection, will
* create a wrapper for the existing Mongo.Collection.
* @param {ConstructorOptions} options - Creation options.
* @constructor
*/
constructor(nameOrExisting: string | Mongo.Collection<T>,
options?: ConstructorOptions) {
if (nameOrExisting instanceof Mongo.Collection) {
this._collection = nameOrExisting;
} else {
this._collection = new Mongo.Collection<T>(nameOrExisting, options);
this._collection = new Mongo.Collection<T>(<string>nameOrExisting, options);
}
}

/**
* Returns the Mongo.Collection object that wrapped with the MongoObservable.Collection.
* @returns {Mongo.Collection<T>} The Collection instance
*/
get collection(): Mongo.Collection<T> {
return this._collection;
}

/**
* Allow users to write directly to this collection from client code, subject to limitations you define.
*
* @returns {Boolean}
*/
allow(options: AllowDenyOptionsObject<T>): boolean {
return this._collection.allow(options);
}

/**
* Override allow rules.
*
* @returns {Boolean}
*/
deny(options: AllowDenyOptionsObject<T>): boolean {
return this._collection.deny(options);
}

/**
* Returns the Collection object corresponding to this collection from the npm
* mongodb driver module which is wrapped by Mongo.Collection.
*
* @returns {Mongo.Collection} The Collection instance
*
* @see {@link https://docs.meteor.com/api/collections.html#Mongo-Collection-rawCollection|rawCollection on Meteor documentation}
*/
rawCollection(): any {
return this._collection.rawCollection();
}

/**
* Returns the Db object corresponding to this collection's database connection from the
* npm mongodb driver module which is wrapped by Mongo.Collection.
*
* @returns {Mongo.Db} The Db instance
*
* @see {@link https://docs.meteor.com/api/collections.html#Mongo-Collection-rawDatabase|rawDatabase on Meteor documentation}
*/
rawDatabase(): any {
return this._collection.rawDatabase();
}

/**
* Insert a document in the collection.
*
* @param {T} doc - The document to insert. May not yet have an _id
* attribute, in which case Meteor will generate one for you.
* @returns {Observable<string>} Observable which completes with the inserted ObjectId
*
* @see {@link https://docs.meteor.com/api/collections.html#Mongo-Collection-insert|insert on Meteor documentation}
*/
insert(doc: T): Observable<string> {
let observers: Subscriber<string>[] = [];
let obs = this._createObservable<string>(observers);
Expand All @@ -78,6 +138,14 @@ export module MongoObservable {
return obs;
}

/**
* Remove documents from the collection.
*
* @param {Collection~MongoQuerySelector} selector - Specifies which documents to modify
* @returns {Observable<Number>} Observable which completes with the number of affected rows
*
* @see {@link https://docs.meteor.com/api/collections.html#Mongo-Collection-remove|remove on Meteor documentation}
*/
remove(selector: Selector | ObjectID | string): Observable<number> {
let observers: Subscriber<number>[] = [];
let obs = this._createObservable<number>(observers);
Expand All @@ -94,6 +162,17 @@ export module MongoObservable {
return obs;
}

/**
* Modify one or more documents in the collection.
*
* @param {Collection~MongoQuerySelector} selector - Specifies which documents to modify
* @param {Modifier} modifier - Specifies how to modify the documents
* @param {MongoUpdateOptions} options - Update options
* first argument and, if no error, the number of affected documents as the second
* @returns {Observable<Number>} Observable which completes with the number of affected rows
*
* @see {@link https://docs.meteor.com/api/collections.html#Mongo-Collection-update|update on Meteor documentation}
*/
update(
selector: Selector | ObjectID | string,
modifier: Modifier,
Expand All @@ -113,6 +192,18 @@ export module MongoObservable {
return obs;
}

/**
* Finds the first document that matches the selector, as ordered by sort and skip options.
*
* @param {Collection~MongoQuerySelector} selector - Specifies which documents to modify
* @param {Modifier} modifier - Specifies how to modify the documents
* @param {MongoUpsertOptions} options - Upsert options
* first argument and, if no error, the number of affected documents as the second.
* @returns {Observable<{numberAffected, insertedId}>} Observable which completes with an
* Object that contain the keys numberAffected and insertedId.
*
* @see {@link https://docs.meteor.com/api/collections.html#Mongo-Collection-upsert|upsert on Meteor documentation}
*/
upsert(
selector: Selector | ObjectID | string,
modifier: Modifier,
Expand All @@ -132,6 +223,25 @@ export module MongoObservable {
return obs;
}

/**
* Method has the same notation as Mongo.Collection.find, only returns Observable.
*
* @param {Collection~MongoQuerySelector} selector - A query describing the documents to find
* @param {Collection~MongoQueryOptions} options - Query options, such as sort, limit, etc.
* @returns {ObservableCursor<T>} RxJS Observable wrapped with Meteor features.
* @example <caption>Using Angular2 Component</caption>
* const MyCollection = MongoObservable.Collection("myCollection");
*
* class MyComponent {
* private myData: ObservableCursor<any>;
*
* constructor() {
* this.myData = MyCollection.find({}, {limit: 10});
* }
* }
*
* @see {@link https://docs.meteor.com/api/collections.html#Mongo-Collection-find|find on Meteor documentation}
*/
find(selector?: Selector | ObjectID | string, options?: {
sort?: SortSpecifier;
skip?: number;
Expand All @@ -145,6 +255,15 @@ export module MongoObservable {
return ObservableCursor.create<T>(cursor);
}

/**
* Finds the first document that matches the selector, as ordered by sort and skip options.
*
* @param {Collection~MongoQuerySelector} selector - A query describing the documents to find
* @param {Collection~MongoQueryOptions} options - Query options, such as sort, limit, etc.
* @returns {any} The first object, or `undefined` in case of non-existing object.
*
* @see {@link https://docs.meteor.com/api/collections.html#Mongo-Collection-findOne|findOne on Meteor documentation}
*/
findOne(selector?: Selector | ObjectID | string, options?: {
sort?: SortSpecifier;
skip?: number;
Expand All @@ -166,3 +285,33 @@ export module MongoObservable {
}
}
}


/**
* An options object for MongoDB queries.
* @typedef {Object} Collection~MongoQueryOptions
* @property {Object} sort - Sort order (default: natural order)
* @property {Number} skip - Number of results to skip at the beginning
* @property {Object} fields - Dictionary of fields to return or exclude.
* @property {Boolean} reactive - (Client only) Default true; pass false to disable reactivity
* @property {Function} transform - Overrides transform on the Collection for this cursor. Pass null to disable transformation.
*/

/**
* A MongoDB query selector representation.
* @typedef {(Mongo.Selector|Mongo.ObjectID|string)} Collection~MongoQuerySelector
*/

/**
* A MongoDB query options for upsert action
* @typedef {Object} Collection~MongoUpsertOptions
* @property {Boolean} multi - True to modify all matching documents;
* false to only modify one of the matching documents (the default).
*/

/**
* A MongoDB query options for update action
* @typedef {Object} Collection~MongoUpdateOptions
* @property {Boolean} multi - True to modify all matching documents;
* @property {Boolean} upsert - True to use upsert logic.
*/
51 changes: 51 additions & 0 deletions src/ObservableCursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import {Observable, Subscriber, Subject} from 'rxjs';
import {gZone, forkZone, removeObserver} from './utils';

/**
* A class represents a Monog.Cursor wrapped with RxJS features.
* @extends Observable
*/
export class ObservableCursor<T> extends Observable<T[]> {
private _zone: Zone;
private _data: Array <T> = [];
Expand All @@ -11,10 +15,23 @@ export class ObservableCursor<T> extends Observable<T[]> {
private _observers: Subscriber<T[]>[] = [];
private _countObserver: Subject<number> = new Subject<number>();

/**
* Static method which creates an ObservableCursor from Mongo.Cursor.
* Use this to create an ObservableCursor object from an existing Mongo.Cursor.
* Prefer to create an Cursors from the ObservableCollection instance instead.
*
* @param {Mongo.Cursor<T>} cursor - The Mongo.Cursor to wrap.
* @returns {ObservableCursor<T>} Wrapped Cursor.
*/
static create<T>(cursor: Mongo.Cursor<T>): ObservableCursor<T> {
return new ObservableCursor<T>(cursor);
}

/**
* @constructor
* @extends Observable
* @param {Mongo.Cursor<T>} cursor - The Mongo.Cursor to wrap.
*/
constructor(cursor: Mongo.Cursor<T>) {
super((observer: Subscriber<T[]>) => {
this._observers.push(observer);
Expand All @@ -35,14 +52,28 @@ export class ObservableCursor<T> extends Observable<T[]> {
this._zone = forkZone();
}

/**
* Returns the actual Mongo.Cursor that wrapped by current ObservableCursor instance.
* @return {Mongo.Cursor<T>} The actual MongoDB Cursor.
*/
get cursor(): Mongo.Cursor<T> {
return this._cursor;
}

/**
* A wrapper for Mongo.Cursor.count() method - returns an Observable of number, which
* triggers each time there is a change in the collection, and exposes the number of
* objects in the collection.
* @returns {Observable} Observable which trigger the callback when the
* count of the object changes.
*/
collectionCount(): Observable<number> {
return this._countObserver.asObservable();
}

/**
* Stops the observation on the cursor.
*/
stop() {
this._zone.run(() => {
this._runComplete();
Expand All @@ -56,19 +87,39 @@ export class ObservableCursor<T> extends Observable<T[]> {
this._hCursor = null;
}

/**
* Clears the Observable definition.
* Use this method only when the Observable is still cold, and there are no active subscriptions yet.
*/
dispose() {
this._observers = null;
this._cursor = null;
}

/**
* Return all matching documents as an Array.
*
* @return {Array<T>} The array with the matching documents.
*/
fetch(): Array<T> {
return this._cursor.fetch();
}

/**
* Watch a query. Receive callbacks as the result set changes.
* @param {Mongo.ObserveCallbacks} callbacks - The callbacks object.
* @return {Meteor.LiveQueryHandle} The array with the matching documents.
*/
observe(callbacks: Mongo.ObserveCallbacks): Meteor.LiveQueryHandle {
return this._cursor.observe(callbacks);
}

/**
* Watch a query. Receive callbacks as the result set changes.
* Only the differences between the old and new documents are passed to the callbacks.
* @param {Mongo.ObserveChangesCallbacks} callbacks - The callbacks object.
* @return {Meteor.LiveQueryHandle} The array with the matching documents.
*/
observeChanges(callbacks: Mongo.ObserveChangesCallbacks): Meteor.LiveQueryHandle {
return this._cursor.observeChanges(callbacks);
}
Expand Down

0 comments on commit 4b7391a

Please sign in to comment.