mirror of
https://github.com/fluxscape/fluxscape.git
synced 2026-01-12 15:22:54 +01:00
Initial commit
Co-Authored-By: Eric Tuvesson <eric.tuvesson@gmail.com> Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com> Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com> Co-Authored-By: Anders Larsson <64838990+anders-topp@users.noreply.github.com> Co-Authored-By: Johan <4934465+joolsus@users.noreply.github.com> Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com> Co-Authored-By: victoratndl <99176179+victoratndl@users.noreply.github.com>
This commit is contained in:
310
packages/noodl-runtime/src/api/queryutils.js
Normal file
310
packages/noodl-runtime/src/api/queryutils.js
Normal file
@@ -0,0 +1,310 @@
|
||||
const CloudStore = require('./cloudstore');
|
||||
const Model = require('../model');
|
||||
|
||||
function convertVisualFilter(query, options) {
|
||||
var inputs = options.queryParameters;
|
||||
|
||||
if (query.combinator !== undefined && query.rules !== undefined) {
|
||||
if (query.rules.length === 0) return;
|
||||
else if (query.rules.length === 1) return convertVisualFilter(query.rules[0], options);
|
||||
else {
|
||||
const _res = {};
|
||||
const _op = '$' + query.combinator;
|
||||
_res[_op] = [];
|
||||
query.rules.forEach((r) => {
|
||||
var cond = convertVisualFilter(r, options);
|
||||
if (cond !== undefined) _res[_op].push(cond);
|
||||
});
|
||||
|
||||
return _res;
|
||||
}
|
||||
} else if (query.operator === 'related to') {
|
||||
var value = query.input !== undefined ? inputs[query.input] : undefined;
|
||||
if (value === undefined) return;
|
||||
|
||||
return {
|
||||
$relatedTo: {
|
||||
object: {
|
||||
__type: 'Pointer',
|
||||
objectId: value,
|
||||
className: query.relatedTo
|
||||
},
|
||||
key: query.relationProperty
|
||||
}
|
||||
};
|
||||
} else {
|
||||
const _res = {};
|
||||
var cond;
|
||||
var value = query.input !== undefined ? inputs[query.input] : query.value;
|
||||
|
||||
if (query.operator === 'exist') {
|
||||
_res[query.property] = { $exists: true };
|
||||
return _res;
|
||||
}
|
||||
else if (query.operator === 'not exist') {
|
||||
_res[query.property] = { $exists: false };
|
||||
return _res;
|
||||
}
|
||||
|
||||
if (value === undefined) return;
|
||||
|
||||
if (CloudStore._collections[options.collectionName])
|
||||
var schema = CloudStore._collections[options.collectionName].schema;
|
||||
|
||||
var propertyType =
|
||||
schema && schema.properties && schema.properties[query.property]
|
||||
? schema.properties[query.property].type
|
||||
: undefined;
|
||||
|
||||
if (propertyType === 'Date') {
|
||||
if (!(value instanceof Date)) value = new Date(value.toString());
|
||||
value = { __type: 'Date', iso: value.toISOString() };
|
||||
}
|
||||
|
||||
if (query.operator === 'greater than') cond = { $gt: value };
|
||||
else if (query.operator === 'greater than or equal to') cond = { $gte: value };
|
||||
else if (query.operator === 'less than') cond = { $lt: value };
|
||||
else if (query.operator === 'less than or equal to') cond = { $lte: value };
|
||||
else if (query.operator === 'equal to') cond = { $eq: value };
|
||||
else if (query.operator === 'not equal to') cond = { $ne: value };
|
||||
else if (query.operator === 'points to') {
|
||||
var targetClass =
|
||||
schema && schema.properties && schema.properties[query.property]
|
||||
? schema.properties[query.property].targetClass
|
||||
: undefined;
|
||||
|
||||
cond = {
|
||||
$eq: { __type: 'Pointer', objectId: value, className: targetClass }
|
||||
};
|
||||
} else if (query.operator === 'contain') {
|
||||
cond = { $regex: value, $options: 'i' };
|
||||
}
|
||||
|
||||
|
||||
_res[query.property] = cond;
|
||||
|
||||
return _res;
|
||||
}
|
||||
}
|
||||
|
||||
function matchesQuery(model, query) {
|
||||
var match = true;
|
||||
|
||||
if (query === undefined) return true;
|
||||
|
||||
if (query['$and'] !== undefined) {
|
||||
query['$and'].forEach((q) => {
|
||||
match &= matchesQuery(model, q);
|
||||
});
|
||||
} else if (query['$or'] !== undefined) {
|
||||
match = false;
|
||||
query['$or'].forEach((q) => {
|
||||
match |= matchesQuery(model, q);
|
||||
});
|
||||
} else {
|
||||
var keys = Object.keys(query);
|
||||
keys.forEach((k) => {
|
||||
if (k === 'objectId') {
|
||||
if (query[k]['$eq'] !== undefined) match &= model.getId() === query[k]['$eq'];
|
||||
else if (query[k]['$in'] !== undefined) match &= query[k]['$in'].indexOf(model.getId()) !== -1;
|
||||
} else if (k === '$relatedTo') {
|
||||
match = false; // cannot resolve relation queries locally
|
||||
} else {
|
||||
var value = model.get(k);
|
||||
if (query[k]['$eq'] !== undefined && query[k]['$eq'].__type === 'Pointer')
|
||||
match &= value === query[k]['$eq'].objectId;
|
||||
else if (query[k]['$eq'] !== undefined) match &= value == query[k]['$eq'];
|
||||
else if (query[k]['$ne'] !== undefined) match &= value != query[k]['$ne'];
|
||||
else if (query[k]['$lt'] !== undefined) match &= value < query[k]['$lt'];
|
||||
else if (query[k]['$lte'] !== undefined) match &= value <= query[k]['$lt'];
|
||||
else if (query[k]['$gt'] !== undefined) match &= value > query[k]['$gt'];
|
||||
else if (query[k]['$gte'] !== undefined) match &= value >= query[k]['$gte'];
|
||||
else if (query[k]['$exists'] !== undefined) match &= value !== undefined;
|
||||
else if (query[k]['$in'] !== undefined) match &= query[k]['$in'].indexOf(value) !== -1;
|
||||
else if (query[k]['$nin'] !== undefined) match &= query[k]['$in'].indexOf(value) === -1;
|
||||
else if (query[k]['$regex'] !== undefined)
|
||||
match &= new RegExp(query[k]['$regex'], query[k]['$options']).test(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
function compareObjects(sort, a, b) {
|
||||
for (var i = 0; i < sort.length; i++) {
|
||||
let _s = sort[i];
|
||||
if (_s[0] === '-') {
|
||||
// Descending
|
||||
let prop = _s.substring(1);
|
||||
if (a.get(prop) > b.get(prop)) return -1;
|
||||
else if (a.get(prop) < b.get(prop)) return 1;
|
||||
} else {
|
||||
// Ascending
|
||||
if (a.get(_s) > b.get(_s)) return 1;
|
||||
else if (a.get(_s) < b.get(_s)) return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function convertVisualSorting(sorting) {
|
||||
return sorting.map((s) => {
|
||||
return (s.order === 'descending' ? '-' : '') + s.property;
|
||||
});
|
||||
}
|
||||
|
||||
function _value(v) {
|
||||
if (v instanceof Date && typeof v.toISOString === 'function') {
|
||||
return {
|
||||
__type: 'Date',
|
||||
iso: v.toISOString()
|
||||
};
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
function convertFilterOp(filter, options) {
|
||||
const keys = Object.keys(filter);
|
||||
if (keys.length === 0) return {};
|
||||
if (keys.length !== 1) return options.error('Filter must only have one key found ' + keys.join(','));
|
||||
|
||||
const res = {};
|
||||
const key = keys[0];
|
||||
if (filter['and'] !== undefined && Array.isArray(filter['and'])) {
|
||||
res['$and'] = filter['and'].map((f) => convertFilterOp(f, options));
|
||||
} else if (filter['or'] !== undefined && Array.isArray(filter['or'])) {
|
||||
res['$or'] = filter['or'].map((f) => convertFilterOp(f, options));
|
||||
} else if (filter['idEqualTo'] !== undefined) {
|
||||
res['objectId'] = { $eq: filter['idEqualTo'] };
|
||||
} else if (filter['idContainedIn'] !== undefined) {
|
||||
res['objectId'] = { $in: filter['idContainedIn'] };
|
||||
} else if (filter['relatedTo'] !== undefined) {
|
||||
var modelId = filter['relatedTo']['id'];
|
||||
if (modelId === undefined) return options.error('Must provide id in relatedTo filter');
|
||||
|
||||
var relationKey = filter['relatedTo']['key'];
|
||||
if (relationKey === undefined) return options.error('Must provide key in relatedTo filter');
|
||||
|
||||
var m = (options.modelScope || Model).get(modelId);
|
||||
res['$relatedTo'] = {
|
||||
object: {
|
||||
__type: 'Pointer',
|
||||
objectId: modelId,
|
||||
className: m._class
|
||||
},
|
||||
key: relationKey
|
||||
};
|
||||
} else if (typeof filter[key] === 'object') {
|
||||
const opAndValue = filter[key];
|
||||
if (opAndValue['equalTo'] !== undefined) res[key] = { $eq: _value(opAndValue['equalTo']) };
|
||||
else if (opAndValue['notEqualTo'] !== undefined) res[key] = { $ne: _value(opAndValue['notEqualTo']) };
|
||||
else if (opAndValue['lessThan'] !== undefined) res[key] = { $lt: _value(opAndValue['lessThan']) };
|
||||
else if (opAndValue['greaterThan'] !== undefined) res[key] = { $gt: _value(opAndValue['greaterThan']) };
|
||||
else if (opAndValue['lessThanOrEqualTo'] !== undefined)
|
||||
res[key] = { $lte: _value(opAndValue['lessThanOrEqualTo']) };
|
||||
else if (opAndValue['greaterThanOrEqualTo'] !== undefined)
|
||||
res[key] = { $gte: _value(opAndValue['greaterThanOrEqualTo']) };
|
||||
else if (opAndValue['exists'] !== undefined) res[key] = { $exists: opAndValue['exists'] };
|
||||
else if (opAndValue['containedIn'] !== undefined) res[key] = { $in: opAndValue['containedIn'] };
|
||||
else if (opAndValue['notContainedIn'] !== undefined) res[key] = { $nin: opAndValue['notContainedIn'] };
|
||||
else if (opAndValue['pointsTo'] !== undefined) {
|
||||
var m = (options.modelScope || Model).get(opAndValue['pointsTo']);
|
||||
if (CloudStore._collections[options.collectionName])
|
||||
var schema = CloudStore._collections[options.collectionName].schema;
|
||||
|
||||
var targetClass =
|
||||
schema && schema.properties && schema.properties[key] ? schema.properties[key].targetClass : undefined;
|
||||
var type = schema && schema.properties && schema.properties[key] ? schema.properties[key].type : undefined;
|
||||
|
||||
if (type === 'Relation') {
|
||||
res[key] = {
|
||||
__type: 'Pointer',
|
||||
objectId: opAndValue['pointsTo'],
|
||||
className: targetClass
|
||||
};
|
||||
} else {
|
||||
if (Array.isArray(opAndValue['pointsTo']))
|
||||
res[key] = {
|
||||
$in: opAndValue['pointsTo'].map((v) => {
|
||||
return { __type: 'Pointer', objectId: v, className: targetClass };
|
||||
})
|
||||
};
|
||||
else
|
||||
res[key] = {
|
||||
$eq: {
|
||||
__type: 'Pointer',
|
||||
objectId: opAndValue['pointsTo'],
|
||||
className: targetClass
|
||||
}
|
||||
};
|
||||
}
|
||||
} else if (opAndValue['matchesRegex'] !== undefined) {
|
||||
res[key] = {
|
||||
$regex: opAndValue['matchesRegex'],
|
||||
$options: opAndValue['options']
|
||||
};
|
||||
} else if (opAndValue['text'] !== undefined && opAndValue['text']['search'] !== undefined) {
|
||||
var _v = opAndValue['text']['search'];
|
||||
if (typeof _v === 'string') res[key] = { $text: { $search: { $term: _v, $caseSensitive: false } } };
|
||||
else
|
||||
res[key] = {
|
||||
$text: {
|
||||
$search: {
|
||||
$term: _v.term,
|
||||
$language: _v.language,
|
||||
$caseSensitive: _v.caseSensitive,
|
||||
$diacriticSensitive: _v.diacriticSensitive
|
||||
}
|
||||
}
|
||||
};
|
||||
// Geo points
|
||||
} else if (opAndValue['nearSphere'] !== undefined) {
|
||||
var _v = opAndValue['nearSphere'];
|
||||
res[key] = {
|
||||
$nearSphere: {
|
||||
__type: "GeoPoint",
|
||||
latitude: _v.latitude,
|
||||
longitude: _v.longitude,
|
||||
},
|
||||
$maxDistanceInMiles:_v.$maxDistanceInMiles,
|
||||
$maxDistanceInKilometers:_v.maxDistanceInKilometers,
|
||||
$maxDistanceInRadians:_v.maxDistanceInRadians
|
||||
};
|
||||
} else if (opAndValue['withinBox'] !== undefined) {
|
||||
var _v = opAndValue['withinBox'];
|
||||
res[key] = {
|
||||
$within:{
|
||||
$box: _v.map(gp => ({
|
||||
__type:"GeoPoint",
|
||||
latitude:gp.latitude,
|
||||
longitude:gp.longitude
|
||||
}))
|
||||
}
|
||||
};
|
||||
} else if (opAndValue['withinPolygon'] !== undefined) {
|
||||
var _v = opAndValue['withinPolygon'];
|
||||
res[key] = {
|
||||
$geoWithin:{
|
||||
$polygon: _v.map(gp => ({
|
||||
__type:"GeoPoint",
|
||||
latitude:gp.latitude,
|
||||
longitude:gp.longitude
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
} else {
|
||||
options.error('Unrecognized filter keys ' + keys.join(','));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
convertVisualFilter,
|
||||
compareObjects,
|
||||
matchesQuery,
|
||||
convertVisualSorting,
|
||||
convertFilterOp
|
||||
};
|
||||
Reference in New Issue
Block a user