lemon-core
Version:
Lemon Serverless Micro-Service Platform
212 lines • 8.37 kB
JavaScript
// origin from: https://github.com/baseprime/dynamodb @20191106
// Copyright (c) 2016 Ryan Fitzgerald
/**
* `expressions.ts`
* - expressions
*
*
* @author Steve Jung <steve@lemoncloud.io>
* @date 2019-11-20 refactoring to ts via origin
*
* @copyright (C) lemoncloud.io 2019 - All Rights Reserved.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildFilterExpression = exports.stringify = exports.serializeUpdateExpression = exports.parse = void 0;
const lodash_1 = __importDefault(require("lodash"));
const utils = __importStar(require("./utils"));
const serializer_1 = __importDefault(require("./serializer"));
const internals = {};
internals.actionWords = ['SET', 'ADD', 'REMOVE', 'DELETE'];
internals.regexMap = lodash_1.default.reduce(internals.actionWords, function (result, key) {
result[key] = new RegExp(key + '\\s*(.+?)\\s*(SET|ADD|REMOVE|DELETE|$)');
return result;
}, {});
// explanation http://stackoverflow.com/questions/3428618/regex-to-find-commas-that-arent-inside-and
internals.splitOperandsRegex = new RegExp(/\s*(?![^(]*\)),\s*/);
internals.match = function (actionWord, str) {
const match = internals.regexMap[actionWord].exec(str);
if (match && match.length >= 2) {
return match[1].split(internals.splitOperandsRegex);
}
else {
return null;
}
};
const parse = function (str) {
return lodash_1.default.reduce(internals.actionWords, function (result, actionWord) {
result[actionWord] = internals.match(actionWord, str);
return result;
}, {});
};
exports.parse = parse;
const serializeUpdateExpression = function (schema, item) {
const datatypes = schema._modelDatatypes;
const data = utils.omitPrimaryKeys(schema, item);
const memo = {
expressions: {},
attributeNames: {},
values: {},
};
memo.expressions = lodash_1.default.reduce(internals.actionWords, function (result, key) {
result[key] = [];
return result;
}, {});
const result = lodash_1.default.reduce(data, function (result, value, key) {
const valueKey = ':' + key;
const nameKey = '#' + key;
if (lodash_1.default.isNull(value) || (lodash_1.default.isString(value) && lodash_1.default.isEmpty(value))) {
result.expressions.REMOVE.push(nameKey);
result.attributeNames[nameKey] = key;
}
else if (lodash_1.default.isPlainObject(value) && value.$add) {
result.expressions.ADD.push(nameKey + ' ' + valueKey);
result.values[valueKey] = serializer_1.default.serializeAttribute(value.$add, datatypes[key]);
result.attributeNames[nameKey] = key;
}
else if (lodash_1.default.isPlainObject(value) && value.$del) {
result.expressions.DELETE.push(nameKey + ' ' + valueKey);
result.values[valueKey] = serializer_1.default.serializeAttribute(value.$del, datatypes[key]);
result.attributeNames[nameKey] = key;
}
else {
result.expressions.SET.push(nameKey + ' = ' + valueKey);
result.values[valueKey] = serializer_1.default.serializeAttribute(value, datatypes[key]);
result.attributeNames[nameKey] = key;
}
return result;
}, memo);
return result;
};
exports.serializeUpdateExpression = serializeUpdateExpression;
const stringify = function (expressions) {
return lodash_1.default.reduce(expressions, function (result, value, key) {
if (!lodash_1.default.isEmpty(value)) {
if (lodash_1.default.isArray(value)) {
result.push(key + ' ' + value.join(', '));
}
else {
result.push(key + ' ' + value);
}
}
return result;
}, []).join(' ');
};
exports.stringify = stringify;
internals.formatAttributeValue = function (val) {
if (lodash_1.default.isDate(val)) {
return val.toISOString();
}
return val;
};
internals.isFunctionOperator = function (operator) {
return lodash_1.default.includes([
'attribute_exists',
'attribute_not_exists',
'attribute_type',
'begins_with',
'contains',
'NOT contains',
'size',
], operator);
};
internals.uniqAttributeValueName = function (key, existingValueNames) {
let potentialName = ':' + key;
let idx = 1;
while (lodash_1.default.includes(existingValueNames, potentialName)) {
idx++;
potentialName = ':' + key + '_' + idx;
}
return potentialName;
};
const buildFilterExpression = function (key, operator, existingValueNames, val1, val2) {
// IN filter expression is unlike all the others where val1 is an array of values
if (operator === 'IN') {
return internals.buildInFilterExpression(key, existingValueNames, val1);
}
let v1 = internals.formatAttributeValue(val1);
const v2 = internals.formatAttributeValue(val2);
if (operator === 'attribute_exists' && v1 === false) {
operator = 'attribute_not_exists';
v1 = null;
}
else if (operator === 'attribute_exists' && v1 === true) {
v1 = null;
}
const path = '#' + key;
const v1ValueName = internals.uniqAttributeValueName(key, existingValueNames);
const v2ValueName = internals.uniqAttributeValueName(key, [v1ValueName].concat(existingValueNames));
let statement = '';
if (internals.isFunctionOperator(operator)) {
if (!lodash_1.default.isNull(v1) && !lodash_1.default.isUndefined(v1)) {
statement = operator + '(' + path + ', ' + v1ValueName + ')';
}
else {
statement = operator + '(' + path + ')';
}
}
else if (operator === 'BETWEEN') {
statement = path + ' BETWEEN ' + v1ValueName + ' AND ' + v2ValueName;
}
else {
statement = [path, operator, v1ValueName].join(' ');
}
const attributeValues = {};
if (!lodash_1.default.isNull(v1) && !lodash_1.default.isUndefined(v1)) {
attributeValues[v1ValueName] = v1;
}
if (!lodash_1.default.isNull(v2) && !lodash_1.default.isUndefined(v2)) {
attributeValues[v2ValueName] = v2;
}
const attributeNames = {};
attributeNames[path] = key;
return {
attributeNames: attributeNames,
statement: statement,
attributeValues: attributeValues,
};
};
exports.buildFilterExpression = buildFilterExpression;
internals.buildInFilterExpression = function (key, existingValueNames, values) {
const path = '#' + key;
const attributeNames = {};
attributeNames[path] = key;
const attributeValues = lodash_1.default.reduce(values, function (result, val) {
const existing = lodash_1.default.keys(result).concat(existingValueNames);
const p = internals.uniqAttributeValueName(key, existing);
result[p] = internals.formatAttributeValue(val);
return result;
}, {});
return {
attributeNames: attributeNames,
statement: path + ' IN (' + lodash_1.default.keys(attributeValues) + ')',
attributeValues: attributeValues,
};
};
//# sourceMappingURL=expressions.js.map
;