import { UpdateCommand, isUpdateCommand, UPDATE_COMMANDS_LITERAL } from '../commands/update'; import { SYMBOL_UNSET_FIELD_NAME } from '../helper/symbol'; import { getType, isArray } from '../utils/type'; import { operatorToString } from '../operator-map'; import { flattenQueryObject, encodeInternalDataType, mergeConditionAfterEncode } from './common'; import { stringifyByEJSON } from '../utils/utils'; export class UpdateSerializer { constructor() { } static encode(query) { const stringifier = new UpdateSerializer(); return stringifier.encodeUpdate(query); } static encodeEJSON(query) { const stringifier = new UpdateSerializer(); return stringifyByEJSON(stringifier.encodeUpdate(query)); } encodeUpdate(query) { if (isUpdateCommand(query)) { return this.encodeUpdateCommand(query); } else if (getType(query) === 'object') { return this.encodeUpdateObject(query); } else { return query; } } encodeUpdateCommand(query) { if (query.fieldName === SYMBOL_UNSET_FIELD_NAME) { throw new Error('Cannot encode a comparison command with unset field name'); } switch (query.operator) { case UPDATE_COMMANDS_LITERAL.PUSH: case UPDATE_COMMANDS_LITERAL.PULL: case UPDATE_COMMANDS_LITERAL.PULL_ALL: case UPDATE_COMMANDS_LITERAL.POP: case UPDATE_COMMANDS_LITERAL.SHIFT: case UPDATE_COMMANDS_LITERAL.UNSHIFT: case UPDATE_COMMANDS_LITERAL.ADD_TO_SET: { return this.encodeArrayUpdateCommand(query); } default: { return this.encodeFieldUpdateCommand(query); } } } encodeFieldUpdateCommand(query) { const $op = operatorToString(query.operator); switch (query.operator) { case UPDATE_COMMANDS_LITERAL.REMOVE: { return { [$op]: { [query.fieldName]: '' } }; } default: { return { [$op]: { [query.fieldName]: query.operands[0] } }; } } } encodeArrayUpdateCommand(query) { const $op = operatorToString(query.operator); switch (query.operator) { case UPDATE_COMMANDS_LITERAL.PUSH: { let modifiers; if (isArray(query.operands)) { modifiers = { $each: query.operands.map(encodeInternalDataType) }; } else { modifiers = query.operands; } return { [$op]: { [query.fieldName]: modifiers } }; } case UPDATE_COMMANDS_LITERAL.UNSHIFT: { const modifiers = { $each: query.operands.map(encodeInternalDataType), $position: 0 }; return { [$op]: { [query.fieldName]: modifiers } }; } case UPDATE_COMMANDS_LITERAL.POP: { return { [$op]: { [query.fieldName]: 1 } }; } case UPDATE_COMMANDS_LITERAL.SHIFT: { return { [$op]: { [query.fieldName]: -1 } }; } default: { return { [$op]: { [query.fieldName]: encodeInternalDataType(query.operands) } }; } } } encodeUpdateObject(query) { const flattened = flattenQueryObject(query); for (const key in flattened) { if (/^\$/.test(key)) continue; let val = flattened[key]; if (isUpdateCommand(val)) { flattened[key] = val._setFieldName(key); const condition = this.encodeUpdateCommand(flattened[key]); mergeConditionAfterEncode(flattened, condition, key); } else { flattened[key] = val = encodeInternalDataType(val); const $setCommand = new UpdateCommand(UPDATE_COMMANDS_LITERAL.SET, [val], key); const condition = this.encodeUpdateCommand($setCommand); mergeConditionAfterEncode(flattened, condition, key); } } return flattened; } }