mirror of https://gitee.com/bigwinds/arangodb
809 lines
27 KiB
JavaScript
809 lines
27 KiB
JavaScript
'use strict';
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// Foxx Repository
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2013 triagens GmbH, Cologne, Germany
|
|
///
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
/// you may not use this file except in compliance with the License.
|
|
/// You may obtain a copy of the License at
|
|
///
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
///
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
/// See the License for the specific language governing permissions and
|
|
/// limitations under the License.
|
|
///
|
|
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
|
///
|
|
/// @author Lucas Dohmen
|
|
/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const Model = require('@arangodb/foxx/model').Model;
|
|
const _ = require('underscore');
|
|
const extend = require('@arangodb/extend').extend;
|
|
const EventEmitter = require('events').EventEmitter;
|
|
|
|
const EVENTS = [
|
|
'beforeSave', 'beforeCreate', 'beforeUpdate', 'beforeRemove',
|
|
'afterSave', 'afterCreate', 'afterUpdate', 'afterRemove'
|
|
];
|
|
|
|
class Repository extends EventEmitter {
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_initializer
|
|
/// `new FoxxRepository(collection, opts)`
|
|
///
|
|
/// Create a new instance of Repository.
|
|
///
|
|
/// A Foxx Repository is always initialized with a collection object. You can get
|
|
/// your collection object by asking your Foxx.Controller for it: the
|
|
/// *collection* method takes the name of the collection (and will prepend
|
|
/// the prefix of your application). It also takes two optional arguments:
|
|
///
|
|
/// 1. Model: The prototype of a model. If you do not provide it, it will default
|
|
/// to Foxx.Model
|
|
/// 2. Prefix: You can provide the prefix of the application if you need it in
|
|
/// your Repository (for some AQL queries probably)
|
|
///
|
|
/// If the Model has any static methods named after the lifecycle events, they
|
|
/// will automatically be registered as listeners to the events emitted by this
|
|
/// repository.
|
|
///
|
|
/// **Examples**
|
|
///
|
|
/// ```js
|
|
/// instance = new Repository(appContext.collection("my_collection"));
|
|
/// // or:
|
|
/// instance = new Repository(appContext.collection("my_collection"), {
|
|
/// model: MyModelPrototype
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// Example with listeners:
|
|
///
|
|
/// ```js
|
|
/// var ValidatedModel = Model.extend({
|
|
/// schema: {...}
|
|
/// }, {
|
|
/// beforeSave(modelInstance) {
|
|
/// if (!modelInstance.valid) {
|
|
/// throw new Error('Refusing to save: model is not valid!');
|
|
/// }
|
|
/// }
|
|
/// });
|
|
/// instance = new Repository(collection, {model: ValidatedModel});
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
constructor(collection, opts) {
|
|
super();
|
|
|
|
this.options = opts || {};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- Attributes
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_collection
|
|
/// The wrapped ArangoDB collection object.
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
this.collection = collection;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_model
|
|
/// The model of this repository. Formerly called "modelPrototype".
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
this.model = this.options.model || Model;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_modelSchema
|
|
/// The schema of this repository's model.
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Object.defineProperty(this, 'modelSchema', {
|
|
configurable: false,
|
|
enumerable: true,
|
|
get() {
|
|
return this.model.prototype.schema;
|
|
}
|
|
});
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_prefix
|
|
/// The prefix of the application. This is useful if you want to construct AQL
|
|
/// queries for example.
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
this.prefix = this.options.prefix;
|
|
|
|
// Undocumented, unfinished graph feature
|
|
this.graph = this.options.graph;
|
|
|
|
if (this.indexes) {
|
|
_.each(this.indexes, function (index) {
|
|
this.collection.ensureIndex(index);
|
|
}, this);
|
|
}
|
|
|
|
EventEmitter.call(this);
|
|
|
|
_.each(this.model, function (listener, eventName) {
|
|
if (EVENTS.indexOf(eventName) === -1 || typeof listener !== 'function') {
|
|
return;
|
|
}
|
|
this.on(eventName, listener.bind(this.model));
|
|
}, this);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- Methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SUBSECTION-- Adding Entries
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_save
|
|
/// `FoxxRepository#save(model)`
|
|
///
|
|
/// Saves a model into the database.
|
|
/// Expects a model. Will set the ID and Rev on the model.
|
|
/// Returns the model.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.save(my_model);
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
save(model) {
|
|
if (!model.forDB) {
|
|
model = new this.model(model);
|
|
}
|
|
this.emit('beforeCreate', model);
|
|
model.emit('beforeCreate');
|
|
this.emit('beforeSave', model);
|
|
model.emit('beforeSave');
|
|
var id_and_rev;
|
|
if (this.collection.type() === 3) {
|
|
id_and_rev = this.collection.save(model.get('_from'), model.get('_to'), model.forDB());
|
|
} else {
|
|
id_and_rev = this.collection.save(model.forDB());
|
|
}
|
|
model.set(id_and_rev);
|
|
this.emit('afterSave', model);
|
|
model.emit('afterSave');
|
|
this.emit('afterCreate', model);
|
|
model.emit('afterCreate');
|
|
return model;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SUBSECTION-- Finding Entries
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_byId
|
|
/// `FoxxRepository#byId(id)`
|
|
///
|
|
/// Returns the model for the given ID ("collection/key") or "key".
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// var byIdModel = repository.byId('test/12411');
|
|
/// byIdModel.get('name');
|
|
///
|
|
/// var byKeyModel = repository.byId('12412');
|
|
/// byKeyModel.get('name');
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
byId(id) {
|
|
var data = this.collection.document(id);
|
|
return new this.model(data);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_byExample
|
|
/// `FoxxRepository#byExample(example)`
|
|
///
|
|
/// Returns an array of models for the given ID.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// var myModel = repository.byExample({ amazing: true });
|
|
/// myModel[0].get('name');
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
byExample(example) {
|
|
var rawDocuments = this.collection.byExample(example).toArray();
|
|
return _.map(rawDocuments, function (rawDocument) {
|
|
return new this.model(rawDocument);
|
|
}, this);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_firstExample
|
|
/// `FoxxRepository#firstExample(example)`
|
|
///
|
|
/// Returns the first model that matches the given example.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// var myModel = repository.firstExample({ amazing: true });
|
|
/// myModel.get('name');
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
firstExample(example) {
|
|
var rawDocument = this.collection.firstExample(example);
|
|
return rawDocument ? new this.model(rawDocument) : null;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_all
|
|
/// `FoxxRepository#all()`
|
|
///
|
|
/// Returns an array of models that matches the given example. You can provide
|
|
/// both a skip and a limit value.
|
|
///
|
|
/// **Warning:** ArangoDB doesn't guarantee a specific order in this case, to make
|
|
/// this really useful we have to explicitly provide something to order by.
|
|
///
|
|
/// *Parameter*
|
|
///
|
|
/// * *options* (optional):
|
|
/// * *skip* (optional): skips the first given number of models.
|
|
/// * *limit* (optional): only returns at most the given number of models.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// var myModel = repository.all({ skip: 4, limit: 2 });
|
|
/// myModel[0].get('name');
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
all(options) {
|
|
if (!options) {
|
|
options = {};
|
|
}
|
|
var rawDocuments = this.collection.all();
|
|
if (options.skip) {
|
|
rawDocuments = rawDocuments.skip(options.skip);
|
|
}
|
|
if (options.limit) {
|
|
rawDocuments = rawDocuments.limit(options.limit);
|
|
}
|
|
return _.map(rawDocuments.toArray(), function (rawDocument) {
|
|
return new this.model(rawDocument);
|
|
}, this);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_any
|
|
/// `FoxxRepository#any()`
|
|
///
|
|
/// Returns a random model from this repository (or null if there is none).
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.any();
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
any() {
|
|
var data = this.collection.any();
|
|
if (!data) {
|
|
return null;
|
|
}
|
|
return new this.model(data);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SUBSECTION-- Removing Entries
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_remove
|
|
/// `FoxxRepository#remove(model)`
|
|
///
|
|
/// Remove the model from the repository.
|
|
/// Expects a model.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.remove(myModel);
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
remove(model) {
|
|
this.emit('beforeRemove', model);
|
|
model.emit('beforeRemove');
|
|
var id = model.get('_id'),
|
|
result = this.collection.remove(id);
|
|
this.emit('afterRemove', model);
|
|
model.emit('afterRemove');
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_removeById
|
|
/// `FoxxRepository#removeById(id)`
|
|
///
|
|
/// Remove the document with the given ID ("collection/key") or "key".
|
|
/// Expects an ID or key of an existing document.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.removeById('test/12121');
|
|
/// repository.removeById('12122');
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
removeById(id) {
|
|
return this.collection.remove(id);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_removeByExample
|
|
/// `FoxxRepository#removeByExample(example)`
|
|
///
|
|
/// Find all documents that fit this example and remove them.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.removeByExample({ toBeDeleted: true });
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
removeByExample(example) {
|
|
return this.collection.removeByExample(example);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SUBSECTION-- Replacing Entries
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_replace
|
|
/// `FoxxRepository#replace(model)`
|
|
///
|
|
/// Find the model in the database by its *_id* and replace it with this version.
|
|
/// Expects a model. Sets the revision of the model.
|
|
/// Returns the model.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// myModel.set('name', 'Jan Steemann');
|
|
/// repository.replace(myModel);
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
replace(model) {
|
|
var id = model.get("_id") || model.get("_key"),
|
|
data = model.forDB(),
|
|
id_and_rev = this.collection.replace(id, data);
|
|
model.set(id_and_rev);
|
|
return model;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_replaceById
|
|
/// `FoxxRepository#replaceById(id, object)`
|
|
///
|
|
/// Find the item in the database by the given ID ("collection/key") or "key"
|
|
/// and replace it with the given object's attributes.
|
|
///
|
|
/// If the object is a model, updates the model's revision and returns the model.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.replaceById('test/123345', myNewModel);
|
|
/// repository.replaceById('123346', myNewModel);
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
replaceById(id, data) {
|
|
if (data instanceof Model) {
|
|
var id_and_rev = this.collection.replace(id, data.forDB());
|
|
data.set(id_and_rev);
|
|
return data;
|
|
}
|
|
return this.collection.replace(id, data);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_replaceByExample
|
|
/// `FoxxRepository#replaceByExample(example, object)`
|
|
///
|
|
/// Find every matching item by example and replace it with the attributes in
|
|
/// the provided object.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.replaceByExample({ replaceMe: true }, myNewModel);
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
replaceByExample(example, data) {
|
|
return this.collection.replaceByExample(example, data);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SUBSECTION-- Updating Entries
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_update
|
|
/// `FoxxRepository#update(model, object)`
|
|
///
|
|
/// Find the model in the database by its *_id* and update it with the given object.
|
|
/// Expects a model. Sets the revision of the model and updates its properties.
|
|
/// Returns the model.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.update(myModel, {name: 'Jan Steeman'});
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
update(model, data) {
|
|
this.emit('beforeUpdate', model, data);
|
|
model.emit('beforeUpdate', data);
|
|
this.emit('beforeSave', model, data);
|
|
model.emit('beforeSave', data);
|
|
var id = model.get("_id") || model.get("_key"),
|
|
id_and_rev = this.collection.update(id, data);
|
|
model.set(data);
|
|
model.set(id_and_rev);
|
|
this.emit('afterSave', model, data);
|
|
model.emit('afterSave', data);
|
|
this.emit('afterUpdate', model, data);
|
|
model.emit('afterUpdate', data);
|
|
return model;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_updateById
|
|
/// `FoxxRepository#updateById(id, object)`
|
|
///
|
|
/// Find an item by ID ("collection/key") or "key" and update it with the
|
|
/// attributes in the provided object.
|
|
///
|
|
/// If the object is a model, updates the model's revision and returns the model.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.updateById('test/12131', { newAttribute: 'awesome' });
|
|
/// repository.updateById('12132', { newAttribute: 'awesomer' });
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
updateById(id, data) {
|
|
if (data instanceof Model) {
|
|
var id_and_rev = this.collection.update(id, data.forDB());
|
|
data.set(id_and_rev);
|
|
return data;
|
|
}
|
|
return this.collection.update(id, data);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_updateByExample
|
|
/// `FoxxRepository#updateByExample(example, object)`
|
|
///
|
|
/// Find every matching item by example and update it with the attributes in
|
|
/// the provided object.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.updateByExample({ findMe: true }, { newAttribute: 'awesome' });
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
updateByExample(example, data) {
|
|
return this.collection.updateByExample(example, data);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_exists
|
|
/// `FoxxRepository#exists(id)`
|
|
///
|
|
/// Checks whether a model with the given ID or key exists.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.exists(model.get('_id'));
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
exists(id) {
|
|
return this.collection.exists(id);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SUBSECTION-- Counting Entries
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_count
|
|
/// `FoxxRepository#count()`
|
|
///
|
|
/// Returns the number of entries in this collection.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.count();
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
count() {
|
|
return this.collection.count();
|
|
}
|
|
}
|
|
|
|
var indexPrototypes = {
|
|
skiplist: {
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_range
|
|
/// `FoxxRepository#range(attribute, left, right)`
|
|
///
|
|
/// Returns all models in the repository such that the attribute is greater
|
|
/// than or equal to *left* and strictly less than *right*.
|
|
///
|
|
/// For range queries it is required that a skiplist index is present for the
|
|
/// queried attribute. If no skiplist index is present on the attribute, the
|
|
/// method will not be available.
|
|
///
|
|
/// *Parameter*
|
|
///
|
|
/// * *attribute*: attribute to query.
|
|
/// * *left*: lower bound of the value range (inclusive).
|
|
/// * *right*: upper bound of the value range (exclusive).
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.range("age", 10, 13);
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
range(attribute, left, right) {
|
|
var rawDocuments = this.collection.range(attribute, left, right).toArray();
|
|
return _.map(rawDocuments, function (rawDocument) {
|
|
return new this.model(rawDocument);
|
|
}, this);
|
|
}
|
|
},
|
|
geo: {
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_near
|
|
/// `FoxxRepository#near(latitude, longitude, options)`
|
|
///
|
|
/// Finds models near the coordinate *(latitude, longitude)*. The returned
|
|
/// list is sorted by distance with the nearest model coming first.
|
|
///
|
|
/// For geo queries it is required that a geo index is present in the
|
|
/// repository. If no geo index is present, the methods will not be available.
|
|
///
|
|
/// *Parameter*
|
|
///
|
|
/// * *latitude*: latitude of the coordinate.
|
|
/// * *longitude*: longitude of the coordinate.
|
|
/// * *options* (optional):
|
|
/// * *geo* (optional): name of the specific geo index to use.
|
|
/// * *distance* (optional): If set to a truthy value, the returned models
|
|
/// will have an additional property containing the distance between the
|
|
/// given coordinate and the model. If the value is a string, that value
|
|
/// will be used as the property name, otherwise the name defaults to *"distance"*.
|
|
/// * *limit* (optional): number of models to return. Defaults to *100*.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.near(0, 0, {geo: "home", distance: true, limit: 10});
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
near(latitude, longitude, options) {
|
|
var collection = this.collection,
|
|
rawDocuments;
|
|
if (!options) {
|
|
options = {};
|
|
}
|
|
if (options.geo) {
|
|
collection = collection.geo(options.geo);
|
|
}
|
|
rawDocuments = collection.near(latitude, longitude);
|
|
if (options.distance) {
|
|
rawDocuments = rawDocuments.distance();
|
|
}
|
|
if (options.limit) {
|
|
rawDocuments = rawDocuments.limit(options.limit);
|
|
}
|
|
return _.map(rawDocuments.toArray(), function (rawDocument) {
|
|
var model = (new this.model(rawDocument)),
|
|
distance;
|
|
if (options.distance) {
|
|
delete model.attributes._distance;
|
|
distance = typeof options.distance === 'string' ? options.distance : 'distance';
|
|
model[distance] = rawDocument._distance;
|
|
}
|
|
return model;
|
|
}, this);
|
|
},
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_within
|
|
/// `FoxxRepository#within(latitude, longitude, radius, options)`
|
|
///
|
|
/// Finds models within the distance *radius* from the coordinate
|
|
/// *(latitude, longitude)*. The returned list is sorted by distance with the
|
|
/// nearest model coming first.
|
|
///
|
|
/// For geo queries it is required that a geo index is present in the
|
|
/// repository. If no geo index is present, the methods will not be available.
|
|
///
|
|
/// *Parameter*
|
|
///
|
|
/// * *latitude*: latitude of the coordinate.
|
|
/// * *longitude*: longitude of the coordinate.
|
|
/// * *radius*: maximum distance from the coordinate.
|
|
/// * *options* (optional):
|
|
/// * *geo* (optional): name of the specific geo index to use.
|
|
/// * *distance* (optional): If set to a truthy value, the returned models
|
|
/// will have an additional property containing the distance between the
|
|
/// given coordinate and the model. If the value is a string, that value
|
|
/// will be used as the property name, otherwise the name defaults to *"distance"*.
|
|
/// * *limit* (optional): number of models to return. Defaults to *100*.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.within(0, 0, 2000 * 1000, {geo: "home", distance: true, limit: 10});
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
within(latitude, longitude, radius, options) {
|
|
var collection = this.collection,
|
|
rawDocuments;
|
|
if (!options) {
|
|
options = {};
|
|
}
|
|
if (options.geo) {
|
|
collection = collection.geo(options.geo);
|
|
}
|
|
rawDocuments = collection.within(latitude, longitude, radius);
|
|
if (options.distance) {
|
|
rawDocuments = rawDocuments.distance();
|
|
}
|
|
if (options.limit) {
|
|
rawDocuments = rawDocuments.limit(options.limit);
|
|
}
|
|
return _.map(rawDocuments.toArray(), function (rawDocument) {
|
|
var model = (new this.model(rawDocument)),
|
|
distance;
|
|
if (options.distance) {
|
|
delete model.attributes._distance;
|
|
distance = typeof options.distance === 'string' ? options.distance : 'distance';
|
|
model[distance] = rawDocument._distance;
|
|
}
|
|
return model;
|
|
}, this);
|
|
}
|
|
},
|
|
fulltext: {
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @startDocuBlock JSF_foxx_repository_fulltext
|
|
/// `FoxxRepository#fulltext(attribute, query, options)`
|
|
///
|
|
/// Returns all models whose attribute *attribute* matches the search query
|
|
/// *query*.
|
|
///
|
|
/// In order to use the fulltext method, a fulltext index must be defined on
|
|
/// the repository. If multiple fulltext indexes are defined on the repository
|
|
/// for the attribute, the most capable one will be selected.
|
|
/// If no fulltext index is present, the method will not be available.
|
|
///
|
|
/// *Parameter*
|
|
///
|
|
/// * *attribute*: model attribute to perform a search on.
|
|
/// * *query*: query to match the attribute against.
|
|
/// * *options* (optional):
|
|
/// * *limit* (optional): number of models to return. Defaults to all.
|
|
///
|
|
/// @EXAMPLES
|
|
///
|
|
/// ```javascript
|
|
/// repository.fulltext("text", "word", {limit: 1});
|
|
/// ```
|
|
/// @endDocuBlock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
fulltext(attribute, query, options) {
|
|
if (!options) {
|
|
options = {};
|
|
}
|
|
var rawDocuments = this.collection.fulltext(attribute, query);
|
|
if (options.limit) {
|
|
rawDocuments = rawDocuments.limit(options.limit);
|
|
}
|
|
return _.map(rawDocuments.toArray(), function (rawDocument) {
|
|
return new this.model(rawDocument);
|
|
}, this);
|
|
}
|
|
}
|
|
};
|
|
|
|
function addIndexMethods(prototype) {
|
|
_.each(prototype.indexes, function (index) {
|
|
var protoMethods = indexPrototypes[index.type];
|
|
if (!protoMethods) {
|
|
return;
|
|
}
|
|
_.each(protoMethods, function (method, key) {
|
|
if (prototype[key] === undefined) {
|
|
prototype[key] = method;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
Repository.extend = function (protoProps, staticProps) {
|
|
var constructor = extend.call(this, protoProps, staticProps);
|
|
if (constructor.prototype.hasOwnProperty('indexes')) {
|
|
addIndexMethods(constructor.prototype);
|
|
}
|
|
return constructor;
|
|
};
|
|
|
|
exports.Repository = Repository;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- END-OF-FILE
|
|
// -----------------------------------------------------------------------------
|
|
|
|
/// Local Variables:
|
|
/// mode: outline-minor
|
|
/// outline-regexp: "/// @brief\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}\\|/\\*jslint"
|
|
/// End:
|