UNPKG

enju

Version:

An elasticsearch on node.js written in CoffeeScript.

398 lines (358 loc) 13.6 kB
config = require 'config' q = require 'q' utils = require './utils' Query = require './query' properties = require './properties' exceptions = require './exceptions' module.exports = class Document ### 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} ### constructor: (args={}) -> for propertyName, property of @constructor._properties @[propertyName] = property.toJs args[propertyName] # ----------------------------------------------------- # private methods # ----------------------------------------------------- @getIndexName = -> ### Get the index name with prefix. @returns: {string} ### "#{utils.getIndexPrefix()}#{@_index}" @getDocumentType = -> ### Get the document type. @returns {string} ### @_type ? @name # ----------------------------------------------------- # public methods # ----------------------------------------------------- @define = -> ### 1. define properties with class name. @param className: {string} @param properties: {object} @returns {constructor} 2. define properties for this document. @param properties: {object} ### @_properties = id: new properties.StringProperty(dbField: '_id') version: new properties.IntegerProperty(dbField: '_version') @_es = utils.getElasticsearch() if arguments.length is 2 and typeof(arguments[0]) is 'string' and arguments[1]? and typeof(arguments[1]) is 'object' # 1. define properties with class name. defined = arguments[1] defined._type ?= arguments[0] class DynamicClass extends @ @define defined return DynamicClass else if arguments.length is 1 and arguments[0]? and typeof(arguments[0]) is 'object' # 2. define properties for this document. defined = arguments[0] if '_index' of defined @_index = defined._index delete defined._index if '_settings' of defined @_settings = defined._settings delete defined._settings if '_type' of defined @_type = defined._type delete defined._type for propertyName, property of defined property.propertyName = propertyName @_properties[propertyName] = property return throw exceptions.ArgumentError('Argument error for enju.Document.define()') @get = (ids, fetchReference=yes) -> ### 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() # the empty document if not ids? or ids is '' deferred.resolve null return deferred.promise # empty documents if ids.constructor is Array ids = (x for x in ids when x) if ids.length is 0 deferred.resolve [] return deferred.promise # fetch documents if ids.constructor is Array @_es.mget index: @getIndexName() type: @getDocumentType() body: ids: ids , (error, response) => if error deferred.reject error return result = [] for doc in response.docs when doc.found item = id: doc._id version: doc._version for propertyName, property of @_properties dbField = property.dbField ? propertyName if dbField of doc._source item[propertyName] = doc._source[dbField] result.push new @(item) # call resolve() if fetchReference Query.updateReferenceProperties(result).then -> deferred.resolve result else deferred.resolve result return deferred.promise # fetch the document @_es.get index: @getIndexName() type: @getDocumentType() id: ids , (error, response) => if error if error.status is 404 deferred.resolve null return deferred.reject error return args = id: response._id version: response._version for propertyName, property of @_properties dbField = property.dbField ? propertyName if dbField of response._source args[propertyName] = response._source[dbField] # call resolve() document = new @(args) if fetchReference Query.updateReferenceProperties([document]).then -> deferred.resolve document else deferred.resolve document deferred.promise @exists = (id) -> ### Is the document exists? @param id {string} The documents' id. @returns {promise<bool>} ### deferred = q.defer() @_es.exists index: @getIndexName() type: @getDocumentType() id: id , (error, response) -> if error deferred.reject error return deferred.resolve response deferred.promise @all = -> ### Generate a query for this document. @returns {Query} ### new Query(@) @where = (field, operation) -> ### Generate the query for this document. Please via Query.intersect(). ### query = new Query(@) query.intersect field, operation @updateMapping = -> ### 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} ### createIndex = => deferred = q.defer() @_es.indices.create index: @getIndexName() , (error, response) -> if error and error.status isnt 400 deferred.reject error return setTimeout -> deferred.resolve response , 1000 deferred.promise closeIndex = => deferred = q.defer() @_es.indices.close index: @getIndexName() , (error, response) -> if error deferred.reject error return deferred.resolve response deferred.promise putSettings = => deferred = q.defer() if not @_settings deferred.resolve() return deferred.promise @_es.indices.putSettings index: @getIndexName() body: settings: index: @_settings , (error, response) -> if error deferred.reject error return deferred.resolve response deferred.promise putMapping = => deferred = q.defer() mapping = {} for propertyName, property of @_properties if property.dbField in ['_id', '_version'] # don't set the mapping to _id and _version continue if property.mapping # there is an object in this field mapping[property.dbField ? propertyName] = properties: property.mapping continue field = {} switch property.constructor when properties.StringProperty field.type = 'string' when properties.BooleanProperty field.type = 'boolean' when properties.IntegerProperty field.type = 'long' when properties.FloatProperty field.type = 'double' when properties.DateProperty field.type = 'date' field.format = 'strict_date_optional_time||epoch_millis' when properties.ReferenceProperty field.type = 'string' field.analyzer = 'keyword' when properties.ListProperty switch property.itemClass when properties.StringProperty field.type = 'string' when properties.BooleanProperty field.type = 'boolean' when properties.IntegerProperty field.type = 'long' when properties.FloatProperty field.type = 'double' when properties.DateProperty field.type = 'date' field.format = 'strict_date_optional_time||epoch_millis' when 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[property.dbField ? propertyName] = field @_es.indices.putMapping index: @getIndexName() type: @getDocumentType() body: properties: mapping , (error, response) -> if error deferred.reject error return deferred.resolve response deferred.promise openIndex = => deferred = q.defer() @_es.indices.open index: @getIndexName() , (error, response) -> if error deferred.reject error return deferred.resolve response deferred.promise deferred = q.defer() createIndex() .then closeIndex .then putSettings .then putMapping .then openIndex .then => console.log "updated mapping [#{@getIndexName()}]" deferred.resolve() , (error) -> console.error error deferred.reject error deferred.promise save: (refresh=no) -> ### Save this document. @param refresh {bool} Refresh the index after performing the operation. @returns {promise<Document>} ### deferred = q.defer() document = {} # it will be written to database for propertyName, property of @constructor._properties when property.dbField not in ['_id', '_version'] dbFieldName = property.dbField ? propertyName try document[dbFieldName] = property.toDb @ catch error deferred.reject error return deferred.promise @constructor._es.index index: @constructor.getIndexName() type: @constructor.getDocumentType() refresh: refresh id: @id version: if @version? then @version + 1 else 0 versionType: 'external' body: document , (error, response) => if error deferred.reject error return @id = response._id @version = response._version deferred.resolve @ deferred.promise delete: (refresh=no) -> ### Delete this document. @returns {promise<Document>} ### deferred = q.defer() @constructor._es.delete index: @constructor.getIndexName() type: @constructor.getDocumentType() refresh: refresh id: @id , (error) => if error deferred.reject error return deferred.resolve @ deferred.promise