enju
Version:
An elasticsearch on node.js written in CoffeeScript.
558 lines (522 loc) • 17.2 kB
JavaScript
(function() {
var Document, Query, config, exceptions, properties, q, utils,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
config = require('config');
q = require('q');
utils = require('./utils');
Query = require('./query');
properties = require('./properties');
exceptions = require('./exceptions');
module.exports = Document = (function() {
/*
You have to call define() to define your document.
@property _index {string} You can set index name by this attribute.
@property _type {string} You can set type of the document. The default is class name.
@property _settings {object} You can set index settings by this attribute.
@property id {string}
@property version {number}
@property _properties {object} {'propertyName': {Property}}
@property _es {Elasticsearch.Client}
*/
function Document(args) {
var property, propertyName, ref;
if (args == null) {
args = {};
}
ref = this.constructor._properties;
for (propertyName in ref) {
property = ref[propertyName];
this[propertyName] = property.toJs(args[propertyName]);
}
}
Document.getIndexName = function() {
/*
Get the index name with prefix.
@returns: {string}
*/
return "" + (utils.getIndexPrefix()) + this._index;
};
Document.getDocumentType = function() {
/*
Get the document type.
@returns {string}
*/
var ref;
return (ref = this._type) != null ? ref : this.name;
};
Document.define = function() {
/*
1. define properties with class name.
@param className: {string}
@param properties: {object}
@returns {constructor}
2. define properties for this document.
@param properties: {object}
*/
var DynamicClass, defined, property, propertyName;
this._properties = {
id: new properties.StringProperty({
dbField: '_id'
}),
version: new properties.IntegerProperty({
dbField: '_version'
})
};
this._es = utils.getElasticsearch();
if (arguments.length === 2 && typeof arguments[0] === 'string' && (arguments[1] != null) && typeof arguments[1] === 'object') {
defined = arguments[1];
if (defined._type == null) {
defined._type = arguments[0];
}
DynamicClass = (function(superClass) {
extend(DynamicClass, superClass);
function DynamicClass() {
return DynamicClass.__super__.constructor.apply(this, arguments);
}
DynamicClass.define(defined);
return DynamicClass;
})(this);
return DynamicClass;
} else if (arguments.length === 1 && (arguments[0] != null) && typeof arguments[0] === 'object') {
defined = arguments[0];
if ('_index' in defined) {
this._index = defined._index;
delete defined._index;
}
if ('_settings' in defined) {
this._settings = defined._settings;
delete defined._settings;
}
if ('_type' in defined) {
this._type = defined._type;
delete defined._type;
}
for (propertyName in defined) {
property = defined[propertyName];
property.propertyName = propertyName;
this._properties[propertyName] = property;
}
return;
}
throw exceptions.ArgumentError('Argument error for enju.Document.define()');
};
Document.get = function(ids, fetchReference) {
var deferred, x;
if (fetchReference == null) {
fetchReference = true;
}
/*
Fetch the document with id or ids.
If the document is not exist, it will return null.
@param ids {string|list}
@param fetchReference {bool} Fetch reference data of this document.
@returns {promise} (Document|null|list)
*/
deferred = q.defer();
if ((ids == null) || ids === '') {
deferred.resolve(null);
return deferred.promise;
}
if (ids.constructor === Array) {
ids = (function() {
var i, len, results;
results = [];
for (i = 0, len = ids.length; i < len; i++) {
x = ids[i];
if (x) {
results.push(x);
}
}
return results;
})();
if (ids.length === 0) {
deferred.resolve([]);
return deferred.promise;
}
}
if (ids.constructor === Array) {
this._es.mget({
index: this.getIndexName(),
type: this.getDocumentType(),
body: {
ids: ids
}
}, (function(_this) {
return function(error, response) {
var dbField, doc, i, item, len, property, propertyName, ref, ref1, ref2, result;
if (error) {
deferred.reject(error);
return;
}
result = [];
ref = response.docs;
for (i = 0, len = ref.length; i < len; i++) {
doc = ref[i];
if (!doc.found) {
continue;
}
item = {
id: doc._id,
version: doc._version
};
ref1 = _this._properties;
for (propertyName in ref1) {
property = ref1[propertyName];
dbField = (ref2 = property.dbField) != null ? ref2 : propertyName;
if (dbField in doc._source) {
item[propertyName] = doc._source[dbField];
}
}
result.push(new _this(item));
}
if (fetchReference) {
return Query.updateReferenceProperties(result).then(function() {
return deferred.resolve(result);
});
} else {
return deferred.resolve(result);
}
};
})(this));
return deferred.promise;
}
this._es.get({
index: this.getIndexName(),
type: this.getDocumentType(),
id: ids
}, (function(_this) {
return function(error, response) {
var args, dbField, document, property, propertyName, ref, ref1;
if (error) {
if (error.status === 404) {
deferred.resolve(null);
return;
}
deferred.reject(error);
return;
}
args = {
id: response._id,
version: response._version
};
ref = _this._properties;
for (propertyName in ref) {
property = ref[propertyName];
dbField = (ref1 = property.dbField) != null ? ref1 : propertyName;
if (dbField in response._source) {
args[propertyName] = response._source[dbField];
}
}
document = new _this(args);
if (fetchReference) {
return Query.updateReferenceProperties([document]).then(function() {
return deferred.resolve(document);
});
} else {
return deferred.resolve(document);
}
};
})(this));
return deferred.promise;
};
Document.exists = function(id) {
/*
Is the document exists?
@param id {string} The documents' id.
@returns {promise<bool>}
*/
var deferred;
deferred = q.defer();
this._es.exists({
index: this.getIndexName(),
type: this.getDocumentType(),
id: id
}, function(error, response) {
if (error) {
deferred.reject(error);
return;
}
return deferred.resolve(response);
});
return deferred.promise;
};
Document.all = function() {
/*
Generate a query for this document.
@returns {Query}
*/
return new Query(this);
};
Document.where = function(field, operation) {
/*
Generate the query for this document.
Please via Query.intersect().
*/
var query;
query = new Query(this);
return query.intersect(field, operation);
};
Document.updateMapping = function() {
/*
Update the index mapping.
https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-put-mapping.html
@returns {promise}
*/
var closeIndex, createIndex, deferred, openIndex, putMapping, putSettings;
createIndex = (function(_this) {
return function() {
var deferred;
deferred = q.defer();
_this._es.indices.create({
index: _this.getIndexName()
}, function(error, response) {
if (error && error.status !== 400) {
deferred.reject(error);
return;
}
return setTimeout(function() {
return deferred.resolve(response);
}, 1000);
});
return deferred.promise;
};
})(this);
closeIndex = (function(_this) {
return function() {
var deferred;
deferred = q.defer();
_this._es.indices.close({
index: _this.getIndexName()
}, function(error, response) {
if (error) {
deferred.reject(error);
return;
}
return deferred.resolve(response);
});
return deferred.promise;
};
})(this);
putSettings = (function(_this) {
return function() {
var deferred;
deferred = q.defer();
if (!_this._settings) {
deferred.resolve();
return deferred.promise;
}
_this._es.indices.putSettings({
index: _this.getIndexName(),
body: {
settings: {
index: _this._settings
}
}
}, function(error, response) {
if (error) {
deferred.reject(error);
return;
}
return deferred.resolve(response);
});
return deferred.promise;
};
})(this);
putMapping = (function(_this) {
return function() {
var deferred, field, mapping, property, propertyName, ref, ref1, ref2, ref3;
deferred = q.defer();
mapping = {};
ref = _this._properties;
for (propertyName in ref) {
property = ref[propertyName];
if ((ref1 = property.dbField) === '_id' || ref1 === '_version') {
continue;
}
if (property.mapping) {
mapping[(ref2 = property.dbField) != null ? ref2 : propertyName] = {
properties: property.mapping
};
continue;
}
field = {};
switch (property.constructor) {
case properties.StringProperty:
field.type = 'string';
break;
case properties.BooleanProperty:
field.type = 'boolean';
break;
case properties.IntegerProperty:
field.type = 'long';
break;
case properties.FloatProperty:
field.type = 'double';
break;
case properties.DateProperty:
field.type = 'date';
field.format = 'strict_date_optional_time||epoch_millis';
break;
case properties.ReferenceProperty:
field.type = 'string';
field.analyzer = 'keyword';
break;
case properties.ListProperty:
switch (property.itemClass) {
case properties.StringProperty:
field.type = 'string';
break;
case properties.BooleanProperty:
field.type = 'boolean';
break;
case properties.IntegerProperty:
field.type = 'long';
break;
case properties.FloatProperty:
field.type = 'double';
break;
case properties.DateProperty:
field.type = 'date';
field.format = 'strict_date_optional_time||epoch_millis';
break;
case properties.ReferenceProperty:
field.type = 'string';
field.analyzer = 'keyword';
}
}
if (property.type) {
field.type = property.type;
}
if (property.analyzer) {
field.analyzer = property.analyzer;
}
if (property.index) {
field.index = property.index;
}
if (Object.keys(field).length) {
mapping[(ref3 = property.dbField) != null ? ref3 : propertyName] = field;
}
}
_this._es.indices.putMapping({
index: _this.getIndexName(),
type: _this.getDocumentType(),
body: {
properties: mapping
}
}, function(error, response) {
if (error) {
deferred.reject(error);
return;
}
return deferred.resolve(response);
});
return deferred.promise;
};
})(this);
openIndex = (function(_this) {
return function() {
var deferred;
deferred = q.defer();
_this._es.indices.open({
index: _this.getIndexName()
}, function(error, response) {
if (error) {
deferred.reject(error);
return;
}
return deferred.resolve(response);
});
return deferred.promise;
};
})(this);
deferred = q.defer();
createIndex().then(closeIndex).then(putSettings).then(putMapping).then(openIndex).then((function(_this) {
return function() {
console.log("updated mapping [" + (_this.getIndexName()) + "]");
return deferred.resolve();
};
})(this), function(error) {
console.error(error);
return deferred.reject(error);
});
return deferred.promise;
};
Document.prototype.save = function(refresh) {
var dbFieldName, deferred, document, error, error1, property, propertyName, ref, ref1, ref2;
if (refresh == null) {
refresh = false;
}
/*
Save this document.
@param refresh {bool} Refresh the index after performing the operation.
@returns {promise<Document>}
*/
deferred = q.defer();
document = {};
ref = this.constructor._properties;
for (propertyName in ref) {
property = ref[propertyName];
if (!((ref1 = property.dbField) !== '_id' && ref1 !== '_version')) {
continue;
}
dbFieldName = (ref2 = property.dbField) != null ? ref2 : propertyName;
try {
document[dbFieldName] = property.toDb(this);
} catch (error1) {
error = error1;
deferred.reject(error);
return deferred.promise;
}
}
this.constructor._es.index({
index: this.constructor.getIndexName(),
type: this.constructor.getDocumentType(),
refresh: refresh,
id: this.id,
version: this.version != null ? this.version + 1 : 0,
versionType: 'external',
body: document
}, (function(_this) {
return function(error, response) {
if (error) {
deferred.reject(error);
return;
}
_this.id = response._id;
_this.version = response._version;
return deferred.resolve(_this);
};
})(this));
return deferred.promise;
};
Document.prototype["delete"] = function(refresh) {
var deferred;
if (refresh == null) {
refresh = false;
}
/*
Delete this document.
@returns {promise<Document>}
*/
deferred = q.defer();
this.constructor._es["delete"]({
index: this.constructor.getIndexName(),
type: this.constructor.getDocumentType(),
refresh: refresh,
id: this.id
}, (function(_this) {
return function(error) {
if (error) {
deferred.reject(error);
return;
}
return deferred.resolve(_this);
};
})(this));
return deferred.promise;
};
return Document;
})();
}).call(this);