UNPKG

dynamodb-toolbox

Version:

Lightweight and type-safe query builder for DynamoDB and TypeScript.

180 lines (179 loc) 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.queryParams = void 0; const index_js_1 = require("../../../../entity/actions/parseCondition/index.js"); const index_js_2 = require("../../../../entity/actions/parsePaths/index.js"); const index_js_3 = require("../../../../entity/utils/index.js"); const index_js_4 = require("../../../../errors/index.js"); const capacity_js_1 = require("../../../../options/capacity.js"); const consistent_js_1 = require("../../../../options/consistent.js"); const entityAttrFilter_js_1 = require("../../../../options/entityAttrFilter.js"); const index_js_5 = require("../../../../options/index.js"); const limit_js_1 = require("../../../../options/limit.js"); const maxPages_js_1 = require("../../../../options/maxPages.js"); const noEntityMatchBehavior_js_1 = require("../../../../options/noEntityMatchBehavior.js"); const rejectExtraOptions_js_1 = require("../../../../options/rejectExtraOptions.js"); const select_js_1 = require("../../../../options/select.js"); const showEntityAttr_js_1 = require("../../../../options/showEntityAttr.js"); const tableName_js_1 = require("../../../../options/tableName.js"); const expressCondition_js_1 = require("../../../../schema/actions/parseCondition/expressCondition/expressCondition.js"); const index_js_6 = require("../../../../schema/actions/parseCondition/index.js"); const deduper_js_1 = require("../../../../schema/actions/utils/deduper.js"); const schema_js_1 = require("../../../../schema/any/schema.js"); const isEmpty_js_1 = require("../../../../utils/isEmpty.js"); const isBoolean_js_1 = require("../../../../utils/validation/isBoolean.js"); const parseQuery_js_1 = require("./parseQuery.js"); const defaultAnySchema = new schema_js_1.AnySchema({ required: 'never' }); const queryParams = (table, entities = [], query, options = {}) => { var _a; const { entityAttributeSavedAs, indexes } = table; const { index } = query; const { capacity, consistent, exclusiveStartKey, limit, maxPages, reverse, select, filter, filters: _filters, attributes: _attributes, tableName, entityAttrFilter = entities.every(entity => (0, index_js_3.isEntityAttrEnabled)(entity.entityAttribute)) && !isKeyAttribute(index !== undefined ? (_a = indexes[index]) !== null && _a !== void 0 ? _a : table : table, entityAttributeSavedAs), showEntityAttr, tagEntities, noEntityMatchBehavior, ...extraOptions } = options; (0, rejectExtraOptions_js_1.rejectExtraOptions)(extraOptions); const filters = (_filters !== null && _filters !== void 0 ? _filters : {}); const attributes = _attributes; if (tableName !== undefined) { (0, tableName_js_1.parseTableNameOption)(tableName); } const commandOptions = { TableName: tableName !== null && tableName !== void 0 ? tableName : table.getName() }; if (capacity !== undefined) { commandOptions.ReturnConsumedCapacity = (0, capacity_js_1.parseCapacityOption)(capacity); } if (index !== undefined) { commandOptions.IndexName = (0, index_js_5.parseIndexOption)(table, index); } if (consistent !== undefined) { commandOptions.ConsistentRead = (0, consistent_js_1.parseConsistentOption)(consistent, index !== undefined ? table.indexes[index] : undefined); } if (exclusiveStartKey !== undefined) { commandOptions.ExclusiveStartKey = exclusiveStartKey; } if (limit !== undefined) { commandOptions.Limit = (0, limit_js_1.parseLimitOption)(limit); } if (maxPages !== undefined) { // maxPages is a meta-option, validated but not used here (0, maxPages_js_1.parseMaxPagesOption)(maxPages); } if (reverse !== undefined) { if (!(0, isBoolean_js_1.isBoolean)(reverse)) { throw new index_js_4.DynamoDBToolboxError('queryCommand.invalidReverseOption', { message: 'Invalid "reverse" options: Must be a boolean', payload: { reverse } }); } commandOptions.ScanIndexForward = !reverse; } if (select !== undefined) { commandOptions.Select = (0, select_js_1.parseSelectOption)(select, { index, attributes }); } // entityAttrFilter is a meta-option, validated but not used here (0, entityAttrFilter_js_1.parseEntityAttrFilterOption)(entityAttrFilter, entities, filters); if (showEntityAttr !== undefined) { // showEntityAttr is a meta-option, validated but not used here (0, showEntityAttr_js_1.parseShowEntityAttrOption)(showEntityAttr); } if (tagEntities !== undefined) { // tagEntities is a meta-option, validated but not used here if (!(0, isBoolean_js_1.isBoolean)(tagEntities)) { throw new index_js_4.DynamoDBToolboxError('queryCommand.invalidTagEntitiesOption', { message: `Invalid 'tagEntities' option: '${String(tagEntities)}'. 'tagEntities' must be a boolean.`, payload: { tagEntities } }); } } if (noEntityMatchBehavior !== undefined) { // noEntityMatchBehavior is a meta-option, validated but not used here (0, noEntityMatchBehavior_js_1.parseNoEntityMatchBehavior)(noEntityMatchBehavior); } const expressionAttributeNames = {}; const expressionAttributeValues = {}; // --- PROJECTION --- if (attributes !== undefined && attributes.length > 0) { if (entities.length === 0) { // TODO: Handle projections even without entities } else { const transformedPaths = new deduper_js_1.Deduper({ serializer: value => value }); for (const entity of entities) { const entityTransformedPaths = entity .build(index_js_2.EntityPathParser) .transform(attributes, { strict: false }); if (entityTransformedPaths.length === 0) { throw new index_js_4.DynamoDBToolboxError('queryCommand.invalidProjectionExpression', { message: `Unable to match any expression attribute path with entity: ${entity.entityName}`, payload: { entity: entity.entityName } }); } for (const transformedPath of entityTransformedPaths) { transformedPaths.push(transformedPath); } } const expression = index_js_2.EntityPathParser.express(transformedPaths.values); Object.assign(expressionAttributeNames, expression.ExpressionAttributeNames); // include the entityAttrSavedAs for faster formatting const { entityAttributeSavedAs } = table; if (!Object.values(expression.ExpressionAttributeNames).includes(entityAttributeSavedAs)) { commandOptions.ProjectionExpression = [expression.ProjectionExpression, '#_et'].join(', '); expressionAttributeNames['#_et'] = entityAttributeSavedAs; } else { commandOptions.ProjectionExpression = expression.ProjectionExpression; } } } // --- KEY CONDITION --- const { KeyConditionExpression, ExpressionAttributeNames: keyConditionExpressionAttributeNames, ExpressionAttributeValues: keyConditionExpressionAttributeValues } = (0, parseQuery_js_1.parseQuery)(table, query); commandOptions.KeyConditionExpression = KeyConditionExpression; Object.assign(expressionAttributeNames, keyConditionExpressionAttributeNames); Object.assign(expressionAttributeValues, keyConditionExpressionAttributeValues); // --- FILTERS --- if (entities.length === 0 && filter !== undefined) { const expression = new index_js_6.ConditionParser(defaultAnySchema).parse(filter, { // Distinguish from KeyConditionExpression expressionId: '1' }); Object.assign(expressionAttributeNames, expression.ExpressionAttributeNames); Object.assign(expressionAttributeValues, expression.ExpressionAttributeValues); commandOptions.FilterExpression = expression.ConditionExpression; } if (entities.length > 0) { const transformedFilters = []; for (const entity of entities) { const entityOptionsFilter = filters[entity.entityName]; const entityNameFilter = entityAttrFilter ? // NOTE: We validated that all entities have entityAttr enabled { attr: (0, index_js_3.getEntityAttrOptionValue)(entity.entityAttribute, 'name'), eq: entity.entityName } : undefined; if (entityOptionsFilter === undefined && entityNameFilter === undefined) { continue; } const transformedFilter = entity.build(index_js_1.EntityConditionParser).transform({ and: [entityNameFilter, entityOptionsFilter].filter(Boolean) }); transformedFilters.push(transformedFilter); } if (transformedFilters.length > 0) { // Prefix w. '1' to distinguish from KeyConditionExpression const expression = (0, expressCondition_js_1.expressCondition)({ or: transformedFilters }, '1'); Object.assign(expressionAttributeNames, expression.ExpressionAttributeNames); Object.assign(expressionAttributeValues, expression.ExpressionAttributeValues); commandOptions.FilterExpression = expression.ConditionExpression; } } if (!(0, isEmpty_js_1.isEmpty)(expressionAttributeNames)) { commandOptions.ExpressionAttributeNames = expressionAttributeNames; } if (!(0, isEmpty_js_1.isEmpty)(expressionAttributeValues)) { commandOptions.ExpressionAttributeValues = expressionAttributeValues; } return commandOptions; }; exports.queryParams = queryParams; const isKeyAttribute = ({ partitionKey, sortKey }, attrSavedAs) => (partitionKey === null || partitionKey === void 0 ? void 0 : partitionKey.name) === attrSavedAs || (sortKey === null || sortKey === void 0 ? void 0 : sortKey.name) === attrSavedAs;