hydrate-mongodb
Advanced tools
Comparing version 0.11.0 to 0.12.0
@@ -25,3 +25,3 @@ /*! | ||
export interface ResultCallback<T> { | ||
(err: Error, result?: T): void; | ||
(err?: Error, result?: T): void; | ||
} | ||
@@ -28,0 +28,0 @@ export interface IteratorCallback<T> { |
export { NamingStrategies, NamingStrategy } from "./config/namingStrategies"; | ||
export { PersistenceError } from "./persistenceError"; | ||
export { CascadeFlags, ChangeTrackingType, FlushPriority } from "./mapping/mappingModel"; | ||
export { CascadeFlags, ChangeTrackingType, FlushPriority, FetchType } from "./mapping/mappingModel"; | ||
export { Configuration } from "./config/configuration"; | ||
@@ -5,0 +5,0 @@ export { ObjectIdGenerator } from "./config/objectIdGenerator"; |
@@ -15,2 +15,3 @@ import { IdentityGenerator } from "../config/configuration"; | ||
import { WriteContext } from "./writeContext"; | ||
import { QueryDocument } from "../query/queryBuilder"; | ||
/** | ||
@@ -36,2 +37,3 @@ * @hidden | ||
identityProperty: Property; | ||
private _defaultFields; | ||
constructor(baseClass?: EntityMapping); | ||
@@ -51,3 +53,5 @@ setDocumentVersion(obj: any, version: number): void; | ||
fetchInverse(session: InternalSession, parentEntity: any, propertyName: string, path: string[], depth: number, callback: ResultCallback<any>): void; | ||
protected fetchPropertyValue(session: InternalSession, value: any, property: Property, callback: ResultCallback<any>): void; | ||
getDefaultFields(): QueryDocument; | ||
protected resolveCore(context: ResolveContext): void; | ||
} |
@@ -199,2 +199,19 @@ "use strict"; | ||
}; | ||
EntityMapping.prototype.fetchPropertyValue = function (session, value, property, callback) { | ||
session.getPersister(this).fetchPropertyValue(value, property, callback); | ||
}; | ||
EntityMapping.prototype.getDefaultFields = function () { | ||
var fields = {}; | ||
if (this._defaultFields === undefined) { | ||
// find any lazy fields and mark them with a 0 so they are not loaded | ||
for (var i = 0; i < this.properties.length; i++) { | ||
var property = this.properties[i]; | ||
if ((property.flags & 2048 /* FetchLazy */) != 0) { | ||
property.setFieldValue(fields, 0); | ||
} | ||
} | ||
this._defaultFields = fields; | ||
} | ||
return this._defaultFields; | ||
}; | ||
EntityMapping.prototype.resolveCore = function (context) { | ||
@@ -201,0 +218,0 @@ if (!context.isFirst) { |
@@ -7,2 +7,5 @@ "use strict"; | ||
function createErrorMessage(errors) { | ||
if (!errors) { | ||
return ""; | ||
} | ||
var message = []; | ||
@@ -9,0 +12,0 @@ for (var i = 0, l = errors.length; i < l; i++) { |
@@ -132,6 +132,14 @@ import { Index } from "./index"; | ||
/** | ||
* Indicates the field should be eagerly fetched. | ||
*/ | ||
FetchEager = 1024, | ||
/** | ||
* Indicates the field should be lazily fetched. | ||
*/ | ||
FetchLazy = 2048, | ||
/** | ||
* All non-walk flags. | ||
* @hidden | ||
*/ | ||
All = 1023, | ||
All = 4095, | ||
/** | ||
@@ -384,1 +392,11 @@ * Indicates that refererences to entities should be walked. | ||
} | ||
export declare const enum FetchType { | ||
/** | ||
* Indicates the field should be eagerly fetched. | ||
*/ | ||
Eager = 1024, | ||
/** | ||
* Indicates the field should be lazily fetched. | ||
*/ | ||
Lazy = 2048, | ||
} |
@@ -37,3 +37,4 @@ import { MappingBase } from "./mappingBase"; | ||
fetch(session: InternalSession, parentEntity: any, value: any, path: string[], depth: number, callback: ResultCallback<any>): void; | ||
protected fetchPropertyValue(session: InternalSession, value: any, property: Property, callback: ResultCallback<any>): void; | ||
protected resolveCore(context: ResolveContext): void; | ||
} |
@@ -109,2 +109,5 @@ "use strict"; | ||
context.path = base + property.name; | ||
if ((property.flags & 1024 /* FetchEager */) != 0) { | ||
context.addFetch(context.path); | ||
} | ||
propertyValue = property.mapping.read(context, fieldValue); | ||
@@ -234,3 +237,3 @@ context.path = savedPath; | ||
if ((property.flags & 1 /* Ignored */) == 0 | ||
&& ((property.flags & flags) != 0 || ((flags & 1023 /* All */) == 0))) { | ||
&& ((property.flags & flags) != 0 || ((flags & 4095 /* All */) == 0))) { | ||
property.mapping.walk(session, property.getPropertyValue(value), flags, entities, embedded, references); | ||
@@ -253,2 +256,5 @@ } | ||
} | ||
else if ((property.flags & 2048 /* FetchLazy */) != 0 && propertyValue === undefined) { | ||
this.fetchPropertyValue(session, value, property, handleCallback); | ||
} | ||
else { | ||
@@ -266,2 +272,6 @@ property.mapping.fetch(session, parentEntity, propertyValue, path, depth + 1, handleCallback); | ||
}; | ||
ObjectMapping.prototype.fetchPropertyValue = function (session, value, property, callback) { | ||
// property values should be fully fetched on all non-entity types. | ||
callback(null, property.getPropertyValue(value)); | ||
}; | ||
ObjectMapping.prototype.resolveCore = function (context) { | ||
@@ -268,0 +278,0 @@ var property = this.getProperty(context.currentProperty); |
@@ -1,2 +0,2 @@ | ||
import { PropertyConverter, FlushPriority } from "../mappingModel"; | ||
import { PropertyConverter, FlushPriority, FetchType } from "../mappingModel"; | ||
import { CollectionOptions } from "../collectionOptions"; | ||
@@ -235,2 +235,11 @@ import { IndexOptions } from "../indexOptions"; | ||
*/ | ||
export declare class FetchAnnotation extends Annotation implements PropertyAnnotation { | ||
type: FetchType; | ||
constructor(type: FetchType); | ||
toString(): string; | ||
processPropertyAnnotation(context: MappingBuilderContext, mapping: MappingModel.ObjectMapping, property: MappingModel.Property, symbol: Property, annotation: FetchAnnotation): void; | ||
} | ||
/** | ||
* @hidden | ||
*/ | ||
export declare class TypeAnnotation extends Annotation implements TargetClassAnnotation { | ||
@@ -237,0 +246,0 @@ target: Constructor<any> | string; |
@@ -435,2 +435,20 @@ "use strict"; | ||
*/ | ||
var FetchAnnotation = (function (_super) { | ||
__extends(FetchAnnotation, _super); | ||
function FetchAnnotation(type) { | ||
_super.call(this); | ||
this.type = type; | ||
} | ||
FetchAnnotation.prototype.toString = function () { | ||
return "@Fetch"; | ||
}; | ||
FetchAnnotation.prototype.processPropertyAnnotation = function (context, mapping, property, symbol, annotation) { | ||
property.setFlags(annotation.type & (1024 /* FetchEager */ | 2048 /* FetchLazy */)); | ||
}; | ||
return FetchAnnotation; | ||
}(Annotation)); | ||
exports.FetchAnnotation = FetchAnnotation; | ||
/** | ||
* @hidden | ||
*/ | ||
var TypeAnnotation = (function (_super) { | ||
@@ -437,0 +455,0 @@ __extends(TypeAnnotation, _super); |
import { Constructor, ParameterlessConstructor } from "../../index"; | ||
import { FieldDescription, CollectionDescription, ClassIndexDescription, PropertyIndexDescription } from "./annotations"; | ||
import { PropertyConverter } from "../mappingModel"; | ||
import { PropertyConverter, FetchType } from "../mappingModel"; | ||
import { ChangeTrackingType } from "../mappingModel"; | ||
@@ -426,2 +426,44 @@ import { CascadeFlags } from "../mappingModel"; | ||
/** | ||
* ## FetchType.Eager | ||
* | ||
* By default entity references are not loaded and must be fetched using Session#fetch or similar. If a FetchType of Eager is specified on | ||
* an entity reference then that reference is automatically fetched when the entity is loaded. | ||
* | ||
* ### Notes | ||
* * This works on entity reference in Embeddable objects as well. | ||
* * It is generally preferable to fetch references as needed. | ||
* * A FetchType of Eager on a property that is not an entity reference has no effect. | ||
* | ||
* ### Example | ||
* ```typescript | ||
* @Entity() | ||
* export class Task { | ||
* | ||
* @Fetch(FetchType.Eager) | ||
* owner: Person; | ||
* } | ||
* ``` | ||
* | ||
* ## FetchType.Lazy | ||
* | ||
* When an entity is loaded, all fields for that entity are retrieved from the database. Specifying a FetchType of Lazy for a field causes | ||
* that field to not be retrieved from the database when the entity is loaded. The field is only loaded by calling Session#fetch and | ||
* indicating which field to load. | ||
* | ||
* ### Notes | ||
* * Useful for properties that contain large amounts of data, such as images, that are not always needed. | ||
* * A FetchType of Lazy on a property in an Embeddable objects is ignored. All properties in an embeddable object are always loaded from the database. | ||
* * It is generally not advisable to use a FetchType of Lazy on a property that is an entity reference. | ||
* | ||
* ### Example | ||
* ```typescript | ||
* @Entity() | ||
* export class Person { | ||
* | ||
* @Fetch(FetchType.Lazy) | ||
* image: Buffer; | ||
* } | ||
*/ | ||
export declare function Fetch(type: FetchType): PropertyDecorator; | ||
/** | ||
* Specifies the type of a property. | ||
@@ -428,0 +470,0 @@ * |
@@ -17,2 +17,3 @@ "use strict"; | ||
exports.Cascade = reflect_helper_1.makeDecorator(annotations_1.CascadeAnnotation); | ||
exports.Fetch = reflect_helper_1.makeDecorator(annotations_1.FetchAnnotation); | ||
exports.InverseOf = reflect_helper_1.makeDecorator(annotations_1.InverseOfAnnotation); | ||
@@ -19,0 +20,0 @@ exports.Type = reflect_helper_1.makeDecorator(annotations_1.TypeAnnotation); |
@@ -25,2 +25,6 @@ import { MappingError } from "./mappingError"; | ||
observer: Observer; | ||
/** | ||
* Fetches found while walking the object. | ||
*/ | ||
fetches: string[]; | ||
constructor(session: InternalSession); | ||
@@ -33,2 +37,3 @@ /** | ||
addError(message: string, path?: string): void; | ||
addFetch(path: string): void; | ||
/** | ||
@@ -35,0 +40,0 @@ * Gets a string summarizing all errors in the context. |
@@ -13,6 +13,2 @@ "use strict"; | ||
this.path = ""; | ||
/** | ||
* A list of errors that occurred during deserialization. | ||
*/ | ||
this.errors = []; | ||
} | ||
@@ -25,2 +21,6 @@ /** | ||
ReadContext.prototype.addError = function (message, path) { | ||
if (!this.errors) { | ||
this.errors = []; | ||
this.hasErrors = true; | ||
} | ||
this.errors.push({ | ||
@@ -30,4 +30,9 @@ message: message, | ||
}); | ||
this.hasErrors = true; | ||
}; | ||
ReadContext.prototype.addFetch = function (path) { | ||
if (!this.fetches) { | ||
this.fetches = []; | ||
} | ||
this.fetches.push(path); | ||
}; | ||
/** | ||
@@ -34,0 +39,0 @@ * Gets a string summarizing all errors in the context. |
{ | ||
"name": "hydrate-mongodb", | ||
"description": "An Object Document Mapper (ODM) for MongoDB.", | ||
"version": "0.11.0", | ||
"version": "0.12.0", | ||
"author": { | ||
@@ -6,0 +6,0 @@ "name": "Artifact Health, LLC", |
@@ -12,2 +12,3 @@ import * as mongodb from "mongodb"; | ||
import { Observer } from "./observer"; | ||
import { Property } from "./mapping/property"; | ||
/** | ||
@@ -23,2 +24,3 @@ * @hidden | ||
fetch(entity: Object, path: string, callback: Callback): void; | ||
fetchPropertyValue(entity: any, property: Property, callback: ResultCallback<any>): void; | ||
refresh(entity: Object, callback: ResultCallback<Object>): void; | ||
@@ -45,2 +47,3 @@ watch(value: any, observer: Observer): void; | ||
private _traceEnabled; | ||
private _defaultFields; | ||
constructor(session: InternalSession, mapping: EntityMapping, collection: mongodb.Collection); | ||
@@ -62,2 +65,9 @@ dirtyCheck(batch: Batch, entity: Object, originalDocument: Object, callback: ResultCallback<Object>): void; | ||
fetch(entity: any, path: string, callback: Callback): void; | ||
/** | ||
* Fetches a property value from the collection. | ||
* @param entity The entity to load the property on. | ||
* @param property The property to fetch. | ||
* @param callback Called after the property has been loaded. | ||
*/ | ||
fetchPropertyValue(entity: any, property: Property, callback: ResultCallback<any>): void; | ||
findInverseOf(entity: Object, path: string, callback: ResultCallback<any[]>): void; | ||
@@ -64,0 +74,0 @@ findOneInverseOf(entity: Object, path: string, callback: ResultCallback<any>): void; |
@@ -24,2 +24,3 @@ "use strict"; | ||
this._traceEnabled = this._session.factory.logger != null; | ||
this._defaultFields = mapping.getDefaultFields(); | ||
} | ||
@@ -98,2 +99,50 @@ PersisterImpl.prototype.dirtyCheck = function (batch, entity, originalDocument, callback) { | ||
}; | ||
/** | ||
* Fetches a property value from the collection. | ||
* @param entity The entity to load the property on. | ||
* @param property The property to fetch. | ||
* @param callback Called after the property has been loaded. | ||
*/ | ||
PersisterImpl.prototype.fetchPropertyValue = function (entity, property, callback) { | ||
var _this = this; | ||
// indicate the field we want to pull back | ||
var fields = { | ||
_id: 0 | ||
}; | ||
property.setFieldValue(fields, 1); | ||
// if the entity is versioned then return the version number as well | ||
var version; | ||
if (this._versioned) { | ||
// get the current version | ||
version = this._session.getVersion(entity); | ||
// include the document version in the projection | ||
this._mapping.setDocumentVersion(fields, 1); | ||
} | ||
var criteria = { | ||
_id: entity["_id"] | ||
}; | ||
// setup trace logging. | ||
var handleCallback = callback; | ||
if (this._traceEnabled) { | ||
handleCallback = this._createTraceableCallback({ kind: queryKind_1.QueryKind[queryKind_1.QueryKind.FindOne], criteria: criteria, fields: fields }, callback); | ||
} | ||
this._collection.findOne(criteria, fields, function (err, document) { | ||
if (err) | ||
return callback(err); | ||
// check to make sure the version has not changed | ||
if (_this._versioned) { | ||
if (_this._mapping.getDocumentVersion(document) != version) { | ||
return callback(new Error("Cannot fetch property value because document version has changed.")); | ||
} | ||
} | ||
// read the property based on the property mapping | ||
var readContext = new readContext_1.ReadContext(_this._session); | ||
readContext.path = property.name; | ||
var propertyValue = property.mapping.read(readContext, property.getFieldValue(document)); | ||
if (readContext.hasErrors) { | ||
return callback(new persistenceError_1.PersistenceError("Error deserializing property value: " + readContext.getErrorMessage())); | ||
} | ||
handleCallback(null, propertyValue); | ||
}); | ||
}; | ||
PersisterImpl.prototype.findInverseOf = function (entity, path, callback) { | ||
@@ -145,12 +194,17 @@ var criteria = this._prepareInverseQuery(entity, path, callback); | ||
PersisterImpl.prototype.findOne = function (criteria, callback) { | ||
var query = { | ||
criteria: criteria, | ||
fields: this._defaultFields | ||
}; | ||
// setup trace logging. | ||
var handleCallback = callback; | ||
// setup trace logging. | ||
if (this._traceEnabled) { | ||
handleCallback = this._createTraceableCallback({ kind: queryKind_1.QueryKind[queryKind_1.QueryKind.FindOne], criteria: criteria }, callback); | ||
query.kind = queryKind_1.QueryKind[queryKind_1.QueryKind.FindOne]; | ||
handleCallback = this._createTraceableCallback(query, callback); | ||
} | ||
this._findOne({ criteria: criteria }, handleCallback); | ||
this._findOne(query, handleCallback); | ||
}; | ||
PersisterImpl.prototype._findOne = function (query, callback) { | ||
var _this = this; | ||
this._collection.findOne(query.criteria, function (err, document) { | ||
this._collection.findOne(query.criteria, query.fields, function (err, document) { | ||
if (err) | ||
@@ -162,8 +216,13 @@ return callback(err); | ||
PersisterImpl.prototype.findAll = function (criteria, callback) { | ||
var query = { | ||
criteria: criteria, | ||
fields: this._defaultFields | ||
}; | ||
// setup trace logging. | ||
var handleCallback = callback; | ||
// setup trace logging. | ||
if (this._traceEnabled) { | ||
handleCallback = this._createTraceableCallback({ kind: queryKind_1.QueryKind[queryKind_1.QueryKind.FindAll], criteria: criteria }, callback); | ||
query.kind = queryKind_1.QueryKind[queryKind_1.QueryKind.FindAll]; | ||
handleCallback = this._createTraceableCallback(query, callback); | ||
} | ||
this._findAll({ criteria: criteria }, handleCallback); | ||
this._findAll(query, handleCallback); | ||
}; | ||
@@ -212,2 +271,4 @@ PersisterImpl.prototype._findAll = function (query, callback) { | ||
} | ||
// todo: handle field projection | ||
query.fields = this._defaultFields; | ||
// map update document if it's defined | ||
@@ -414,3 +475,3 @@ if (query.updateDocument) { | ||
PersisterImpl.prototype._prepareFind = function (query) { | ||
var cursor = this._collection.find(query.criteria); | ||
var cursor = this._collection.find(query.criteria, query.fields); | ||
if (query.orderDocument !== undefined) { | ||
@@ -609,3 +670,9 @@ cursor.sort(query.orderDocument); | ||
this._session.registerManaged(this, entity, document); | ||
callback(null, entity); | ||
if (!context.fetches) { | ||
callback(null, entity); | ||
} | ||
else { | ||
// requested fetches were found when reading the entity | ||
this._session.fetchInternal(entity, context.fetches, callback); | ||
} | ||
return; | ||
@@ -612,0 +679,0 @@ } |
@@ -38,2 +38,3 @@ import { Callback } from "../core/callback"; | ||
execute(callback: ResultCallback<T>): void; | ||
asPromise(): Promise<T>; | ||
} | ||
@@ -40,0 +41,0 @@ export interface CountQuery extends Query<number> { |
@@ -239,2 +239,17 @@ "use strict"; | ||
}; | ||
QueryObject.prototype.asPromise = function () { | ||
var _this = this; | ||
// create and return the promise. | ||
return new Promise(function (resolve, reject) { | ||
// wrapping of the classic callback handler. | ||
_this.handleCallback(function (err, result) { | ||
if (err) { | ||
reject(err); | ||
} | ||
else { | ||
resolve(result); | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
@@ -247,2 +262,3 @@ * Creates an object for logging purposes. | ||
criteria: this.criteria, | ||
fields: this.fields, | ||
update: this.updateDocument, | ||
@@ -249,0 +265,0 @@ wantsUpdated: this.wantsUpdated, |
@@ -18,2 +18,3 @@ import {ResultCallback} from "../core/callback"; | ||
criteria: QueryDocument; | ||
fields: QueryDocument; | ||
updateDocument: QueryDocument; | ||
@@ -20,0 +21,0 @@ |
@@ -255,2 +255,22 @@ [![Build Status](https://travis-ci.org/artifacthealth/hydrate-mongodb.svg?branch=master)](https://travis-ci.org/artifacthealth/hydrate-mongodb) | ||
### Promises | ||
All queries can return a Promise by chaining a call to `asPromise`. | ||
Converting a find-by-id query to a Promise. | ||
```typescript | ||
session.find(Task, id).asPromise().then((task) => { | ||
... | ||
}); | ||
``` | ||
Converting a find-all query to a promise | ||
```typescript | ||
session.query(Task).findAll({ assigned: person }).asPromise().then((tasks) => { | ||
... | ||
}); | ||
``` | ||
## Modeling | ||
@@ -624,1 +644,40 @@ | ||
### Fetching | ||
#### Eager Fetching of Entity References | ||
By default entity references are not loaded and must be fetched using Session#fetch or similar. If a FetchType of Eager is specified on | ||
an entity reference then that reference is automatically fetched when the entity is loaded. | ||
* This works on entity reference in Embeddable objects as well. | ||
* It is generally preferable to fetch references as needed. | ||
* A FetchType of Eager on a property that is not an entity reference has no effect. | ||
```typescript | ||
@Entity() | ||
export class Task { | ||
@Fetch(FetchType.Eager) | ||
owner: Person; | ||
} | ||
``` | ||
#### Lazy Fetching of Properties | ||
When an entity is loaded, all fields for that entity are retrieved from the database. Specifying a FetchType of Lazy for a field causes | ||
that field to not be retrieved from the database when the entity is loaded. The field is only loaded by calling Session#fetch and | ||
indicating which field to load. | ||
* Useful for properties that contain large amounts of data, such as images, that are not always needed. | ||
* A FetchType of Lazy on a property in an Embeddable objects is ignored. All properties in an embeddable object are always loaded from the database. | ||
* It is generally not advisable to use a FetchType of Lazy on a property that is an entity reference. | ||
```typescript | ||
@Entity() | ||
export class Person { | ||
@Fetch(FetchType.Lazy) | ||
image: Buffer; | ||
} | ||
``` |
@@ -180,2 +180,6 @@ "use strict"; | ||
SessionImpl.prototype.getVersion = function (obj) { | ||
var links = this._getObjectLinks(obj); | ||
if (!links || links.state == 1 /* Detached */) { | ||
return null; | ||
} | ||
var mapping = this.factory.getMappingForObject(obj); | ||
@@ -185,3 +189,3 @@ if (!mapping) { | ||
} | ||
return mapping.getDocumentVersion(obj); | ||
return mapping.getDocumentVersion(links.originalDocument); | ||
}; | ||
@@ -188,0 +192,0 @@ SessionImpl.prototype.getReferenceInternal = function (mapping, id) { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
399516
9598
681