loopback-connector-gclouddatastore
Version:
Google Cloud Datastore Connector for Loopback.io
223 lines (204 loc) • 7.3 kB
JavaScript
/* eslint-disable no-unused-vars */
var debug = require('debug')('loopback:connector:gdatastore');
const Datastore = require('@google-cloud/datastore');
const _ = require('./util');
module.exports = Query;
function Query(ds, model, idName, definition) {
this.ds = ds;
this.model = model;
this.idName = idName;
this.definition = definition;
this.query = ds.createQuery(model);
}
Query.prototype.filter = function (filters) {
// Where clauses (including conditions on primary key)
if (filters !== undefined && filters.where !== undefined) {
_.forin(filters.where, (val, key) => {
this.where(val, key);
});
}
// Limit restriction
if (undefined !== filters.limit) {
debug('find: adding limit %d', filters.limit);
this.query.limit(filters.limit);
}
// Offset restriction
if (undefined !== filters.offset) {
debug('find: adding offset %d', filters.offset);
this.query.offset(filters.offset);
}
};
Query.prototype.order = function (filters) {
if (filters !== undefined && filters.order !== undefined) {
if(Array.isArray(filters.order))// si tenemos varios criterios de ordenación
{
for(let i = 0; i < filters.order.length; i++)
{
let filter_order = filters.order[i]
debug('find: adding order %d', filter_order);
let value_order = filter_order[0];
let key_order, desc_order;
if(value_order == '-')
{
desc_order = {descending: true};
key_order = filter_order.substring(1);
}
else
{
desc_order = {descending: false};
key_order = filter_order;
}
this.query.order(key_order, desc_order);
}
}
else
{
// Sort restriction
debug('find: adding order %d', filters.order);
let filter_order = filters.order;
let value_order = filter_order[0];
let key_order, desc_order;
if(value_order == '-')
{
desc_order = {descending: true};
key_order = filter_order.substring(1);
}
else
{
desc_order = {descending: false};
key_order = filter_order;
}
this.query.order(key_order, desc_order);
}
}
};
// aquí también hay que contemplar los operadores > <
Query.prototype.where = function (data, name) {
data = data === undefined || data === null ? "" : data;
// How to handle?
if (this.idName == name) {
debug('find: adding filter by __key__ = %s', data);
let key = this.ds.key([this.model, data]);
this.query.filter('__key__', '=', key);
} else if ('and' === name) {
_.forin(data, (val, key) => {
this.where(val, key);
});
} else if ('or' === name) {
debug('find: UNSUPPORTED OR %s', JSON.stringify(data));
} else {
debug('find: adding filter %s = %s', name, JSON.stringify(data));
this.query.filter(name, '=', data);
}
};
Query.prototype.select = function (callback) {
this.ds.runQuery(this.query, (error, result, cursor) => {
if(result !== undefined) {
if (result.length > 0) {
result = result.map((entity) => {
let key = entity[Datastore.KEY];
entity[this.idName] = key.name;// como los datos son de tipo string en lugar de key.id ponemos key.name
return entity;
});
result[result.length] = cursor;
} else {
result[0] = cursor;
}
}
callback(error, JSON.stringify(result)); // (*) se hace stringify para que se conserve el cursor en el array
});
};
Query.prototype.findById = function (id, callback) {
let filter = { where: {}, limit: 1 };
filter.where[this.idName] = id;
this.filter(filter);
this.select((errors, result) => {
if (errors) {
return callback(errors);
}
result = JSON.parse(result);// (*) antes de devolver el resultado se parsea
callback(null, (result != null && result.length)
? result[0] : undefined);
});
};
Query.prototype.create = function (data, callback) {
var id = data[this.idName];
var key;
if (id) {
debug('create: using preset: %s %s', this.idName, id);
key = this.ds.key([this.model, id]);
delete data[this.idName];
} else {
debug('create: no id found on %s, will be auto-generated on insert', this.idName);
key = this.ds.key(this.model);
}
// Convert to a proper DB format, with indexes off as needed
data = this.toDatastoreFormat(data);
this.ds.save({
key: key,
data: data
}, (errors, result) => {
if (errors) {
return callback(errors);
}
if (!key.path && !key.path[1]) {
var err = new Error('Datastore error: missing key.path');
return callback(err);
}
callback(null, key.path[1]);
});
};
Query.prototype.update = function (id, data, callback) {
this.findById(id, (error, entity) => {
if (error) {
return callback(error);
}
let _data = _.merge(entity, data);
_data = this.toDatastoreFormat(_data);
if (typeof _data.find(x => x.name === 'id') !== 'undefined') {
let index = _data.indexOf(_data.find(x => x.name === 'id'));
if (index >= 0) {
_data.splice(index, 1);
}
}
let key = this.ds.key([this.model, id]);
this.ds.update({
key: key,
data: _data
}, (errors, result) => {
if (errors) {
return callback(errors);
}
callback(null, id);
});
});
}
Query.prototype.deleteById = function (id, callback) {
let key = this.ds.key([this.model, id]);
this.ds.delete(key, (errors, result) => {
if (errors) {
return callback(errors, null);
}
callback(null, result.indexUpdates); // se modifica el callback para que en lugar de devolver el id devuelva un parametro que indique si se ha borrado o no el registro
});
}
Query.prototype.toDatastoreFormat = function (data) {
var properties = this.definition.properties;
return _.map(data, (value, name) => {
var excluded;
var property = properties[name];
if (!property) {
excluded = true;
if (this.definition.settings.strict) {
return undefined;
}
} else {
excluded = property.index === false;
}
return {
name: name,
value: value,
excludeFromIndexes: excluded
};
});
};