UNPKG

marklogic

Version:

The official MarkLogic Node.js client API.

665 lines (651 loc) 23.2 kB
/* * Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. */ 'use strict'; const types = require('./server-types-generated.js'); function checkMinArity(funcName, argsLen, minArity) { if (argsLen < minArity) { throw new Error( `${funcName} takes a minimum of ${minArity} arguments but received: ${argsLen}` ); } } function checkMaxArity(funcName, argsLen, maxArity) { if (argsLen > maxArity) { throw new Error( `${funcName} takes a maximum of ${maxArity} arguments but received: ${argsLen}` ); } } function checkArity(funcName, argsLen, minArity, maxArity) { checkMinArity(funcName, argsLen, minArity); checkMaxArity(funcName, argsLen, maxArity); } function checkArg(arg, funcName, argPos, paramName, paramTypes, isRequired, isMultiple) { if (arg === void 0) { if (isRequired) { throw new Error( `${argLabel(funcName, paramName, argPos)} is a required ${typeLabel(paramTypes)} value` ); } return null; } else if (Array.isArray(arg)) { if (!isMultiple) { throw new Error( `${argLabel(funcName, paramName, argPos)} must be one ${typeLabel(paramTypes)} value instead of an array` ); } else if (arg.length === 0 && isRequired) { throw new Error( `${argLabel(funcName, paramName, argPos)} array must have at least one ${typeLabel(paramTypes)} value` ); } return arg.map(val => castArg(val, funcName, paramName, argPos, paramTypes)); } const val = castArg(arg, funcName, paramName, argPos, paramTypes); const result = isMultiple ? [val] : val; return result; } function castArg(arg, funcName, paramName, argPos, paramTypes) { if (arg === void 0 || arg === null || !Array.isArray(paramTypes) || paramTypes.length === 0) { return arg; } else if (arg instanceof Object) { if (paramTypes.some(paramType => (arg instanceof paramType))) { return arg; } else if (arg instanceof Number || arg instanceof Boolean || arg instanceof String) { arg = arg.valueOf(); } else if (arg instanceof types.ServerType) { if(arg._ns === 'vec'){ return arg._args; } throw new Error( `${argLabel(funcName, paramName, argPos)} must have type ${typeLabel(paramTypes)}` ); } else if (paramTypes.some(paramType => { const paramClass = paramType.name; switch(paramClass) { case 'PlanCtsReferenceMap': return Object.keys(arg).every(key => { const value = arg[key]; if (value instanceof types.CtsReference) { return true; } throw new Error( `${argLabel(funcName, paramName, argPos)} has ${key} key without a CtsReference value for ${typeLabel(paramTypes)}` ); }); case 'PlanGroupConcatOption': return Object.keys(arg).every(key => { const value = arg[key]; switch(key) { case 'separator': if (typeof value === 'string' || value instanceof String) { return true; } throw new Error( `${argLabel(funcName, paramName, argPos)} separator must be a string for ${typeLabel(paramTypes)} options` ); case 'values': if (value === 'distinct') { return true; } throw new Error( `${argLabel(funcName, paramName, argPos)} values can only be "distinct" for ${typeLabel(paramTypes)} options` ); default: return false; }}); case 'PlanSampleByOption': return Object.keys(arg).every(key => { const value = arg[key]; switch(key) { case 'limit': if (typeof value === 'number' || value instanceof Number || typeof value === 'string' || value instanceof String) { return true; } throw new Error( `${argLabel(funcName, paramName, argPos)} limit must be a number or string for ${typeLabel(paramTypes)} options` ); default: return false; }}); case 'PlanSearchOption': return Object.keys(arg).every(key => { const value = arg[key]; switch(key) { case 'scoreMethod': if (['logtfidf', 'logtf', 'simple', 'bm25', 'zero', 'random'].includes(value)) { return true; } throw new Error( `${argLabel(funcName, paramName, argPos)} can only be 'logtfidf', 'logtf', 'simple', 'bm25', 'zero' or 'random'` ); case 'qualityWeight': if (typeof value === 'number' || value instanceof Number) { return true; } throw new Error( `${argLabel(funcName, paramName, argPos)} must be a number` ); case 'bm25LengthWeight': if (typeof value === 'number' || value instanceof Number) { return true; } throw new Error( 'bm25LengthWeight must be a number' ); default: return false; }}); case 'PlanSparqlOption': return Object.keys(arg).every(key => { const value = arg[key]; switch(key) { case 'dedup': switch(value) { case 'on': case 'off': return true; default: throw new Error( `${argLabel(funcName, paramName, argPos)} values for dedup can only be "on" or "off" for ${typeLabel(paramTypes)} options` ); } break; case 'base': if (typeof value === 'string' || value instanceof String) { return true; } throw new Error( `${argLabel(funcName, paramName, argPos)} base must be a URI string for ${typeLabel(paramTypes)} options` ); default: return false; }}); case 'PlanTripleOption': return Object.keys(arg).every(key => { const value = arg[key]; switch(key) { case 'dedup': switch(value) { case 'on': case 'off': return true; default: throw new Error( `${argLabel(funcName, paramName, argPos)} values for dedup can only be "on" or "off" for ${typeLabel(paramTypes)} options` ); } break; default: return false; }}); case 'PlanValueOption': return Object.keys(arg).every(key => { const value = arg[key]; switch(key) { case 'values': if (value === 'distinct') { return true; } throw new Error( `${argLabel(funcName, paramName, argPos)} values can only be "distinct" for ${typeLabel(paramTypes)} options` ); default: return false; }}); case 'PlanXsValueMap': const keys = Object.keys(arg); const columns = (keys.length === 2) ? arg.columnNames : null; const rows = Array.isArray(columns) ? arg.rowValues : null; if (Array.isArray(rows)) { if (columns.every(column => (typeof column === 'string' || column instanceof String))) { if (rows.every(row => (Array.isArray(row) && row.length <= columns.length))) { return true; } } return false; } return keys.every(key => { const value = arg[key]; const valtype = typeof value; switch (valtype) { case 'boolean': return true; case 'number': return true; case 'string': return true; case 'object': if (value === null || value instanceof String || value instanceof Number || value instanceof Boolean) { return true; } else if (value instanceof types.XsAnyAtomicType) { const valArgs = value._args; if (Array.isArray(valArgs) && valArgs.every(valArg => !(valArg instanceof types.ServerType))) { return true; } throw new Error( `${argLabel(funcName, paramName, argPos)} has ${key} key with expression value` ); } break; default: throw new Error( `${argLabel(funcName, paramName, argPos)} has ${key} key with ${valtype} instead of ${typeLabel(paramTypes)} literal value` ); }}); case 'PlanDocDescriptor': const expectedKeys = ['uri', 'doc', 'collections', 'metadata', 'permissions', 'quality', 'temporalCollection']; const objKeysDocDescriptor = Object.keys(arg); if(!objKeysDocDescriptor.length) { throw new Error( `${argLabel(funcName, paramName, argPos)} - called with objects without document column keys such as "uri" for {}` ); } return objKeysDocDescriptor.every(key => { const value = arg[key]; const type = typeof value; if(!expectedKeys.includes(key)) { throw new Error( `${argLabel(funcName, paramName, argPos)} - no typechecking info available for: ${key}` ); } else { if(key === 'uri' || key === 'temporalCollection') { if(type !== 'string') { throw new Error( `${argLabel(funcName, paramName, argPos)} - ${key} key has type ${type} instead of 'string'` ); } return true; } if(key === 'doc') { return true; } if(key === 'collections' || key === 'permissions') { if(!Array.isArray(value)) { throw new Error( `${argLabel(funcName, paramName, argPos)} - ${key} key must be array` ); } if(key === 'permissions'){ const permissionValue = []; for(let i=0; i<value.length; i++){ if(value[i].hasOwnProperty('role-name')){ for(let j=0; (value[i].capabilities && j<value[i].capabilities.length); j++){ const tempValue = {}; tempValue.roleName=value[i]['role-name']; tempValue.capability = value[i].capabilities[j]; permissionValue.push(tempValue); } } } arg.permissions = permissionValue; } return true; } if(key === 'metadata') { if (type !== 'object') { throw new Error( `${argLabel(funcName, paramName, argPos)} - ${key} key must be array or json object` ); } return true; } if(key === 'quality') { if(typeof value !== 'number') { throw new Error( `${argLabel(funcName, paramName, argPos)} - ${key} key must be type of number` ); } return true; } } }); case 'PlanDocColsIdentifier': const expectedWriteKeys = ['uri', 'doc', 'collections', 'metadata', 'permissions', 'quality', 'temporalCollection']; const objKeysDocCols = Object.keys(arg); return objKeysDocCols.every(key => { const value = arg[key]; const type = typeof value; if(!expectedWriteKeys.includes(key)) { throw new Error( `${argLabel(funcName, paramName, argPos)} - unknown document descriptor found: ${key}` ); } else { if(type !== 'string' && type !== 'object') { throw new Error( `${argLabel(funcName, paramName, argPos)} - invalid type: ${type} for: ${key}` ); } return true; } }); case 'PlanSchemaDef': const objKeysPlanSchemaDef = Object.keys(arg); if(!objKeysPlanSchemaDef.includes('kind')) { return false; } return objKeysPlanSchemaDef.every(key => { if(key === 'kind') { if(typeof arg[key] !== 'string') { throw new Error( `${argLabel(funcName, paramName, argPos)} has another type than string` ); } else { return true; } } if(key === 'mode') { if(typeof arg[key] !== 'string') { throw new Error( `${argLabel(funcName, paramName, argPos)} has another type than string` ); } return true; } if(key === 'schemaUri') { if(typeof arg[key] !== 'string') { throw new Error( `${argLabel(funcName, paramName, argPos)} has another type than string` ); } return true; } }); case 'PlanTransformDef': return Object.keys(arg).every(key => { const value = arg[key]; const type = typeof value; if(key === 'path') { if(type !== 'string') { throw new Error( `${argLabel(funcName, paramName, argPos)} should be a type of string` ); } return true; } if(key === 'kind') { if(type !== 'string') { throw new Error( `${argLabel(funcName, paramName, argPos)} should be a type of string` ); } return true; } if(key === 'params') { if(type !== 'object') { throw new Error( `${argLabel(funcName, paramName, argPos)} should be a type of object` ); } } return true; }); case 'PlanRowColTypes': const objKeys = Object.keys(arg); if(!objKeys.includes('column')) { return false; } objKeys.every(key => { if(key === 'column') { if(typeof arg[key] !== 'string') { throw new Error( `${argLabel(funcName, paramName, argPos)} has another type than string` ); } } }); return true; case 'PlanAnnTopKOptions': const planAnnTopKOptionsSet = new Set(['maxDistance', 'max-distance', 'searchFactor','search-factor']); if(Object.getPrototypeOf(arg) === Map.prototype){ arg.forEach((value, key) => { if(!planAnnTopKOptionsSet.has(key)) { throw new Error( `${argLabel(funcName, paramName, argPos)} has invalid key- ${key}` ); } }); } return true; default: return false; } })) { return arg; } else { throw new Error( `${argLabel(funcName, paramName, argPos)} has invalid argument for ${typeLabel(paramTypes)} value: ${arg}` ); } } const argType = typeof arg; switch(argType) { case 'boolean': if (isProtoChained(paramTypes, [types.XsBoolean, types.BooleanNode, types.JsonContentNode])) { return arg; } break; case 'number': if (isProtoChained(paramTypes, [types.XsDecimal, types.XsDouble, types.XsFloat, types.XsNumeric, types.NumberNode, types.JsonContentNode])) { return arg; } break; case 'string': if (isProtoChained(paramTypes, [types.XsAnyAtomicType, types.TextNode, types.JsonContentNode, types.XmlContentNode, types.XsString, types.ServerType])) { return arg; } break; default: throw new Error( `${argLabel(funcName, paramName, argPos)} must be a ${typeLabel(paramTypes)} instead of ${argType} value` ); } throw new Error( `${argLabel(funcName, paramName, argPos)} must be a ${typeLabel(paramTypes)} value` ); } function isProtoChained(declaredTypes, valueTypes) { return valueTypes.some(valueType => { const valueProto = valueType.prototype; return declaredTypes.some(declaredType => { const declaredProto = declaredType.prototype; return ( declaredProto === valueProto || declaredProto.isPrototypeOf(valueProto) || valueProto.isPrototypeOf(declaredProto) ); }); }); } function argLabel(funcName, paramName, argPos) { return `${paramName} argument at ${argPos} of ${funcName}()`; } function typeLabel(paramTypes) { // vulnerable if a type ever has a name property return paramTypes.map(paramType => paramType.name).join(' or '); } function getNamer(args, name) { const onlyArg = getOptionalArg(args); const namer = (onlyArg === null || onlyArg[name] === void 0) ? null : onlyArg; return namer; } function getOptionalArg(args) { const firstArg = (args.length === 1) ? args[0] : void 0; const onlyArg = (firstArg !== null && !Array.isArray(firstArg) && typeof firstArg === 'object') ? firstArg : null; return onlyArg; } function makePositionalArgs(funcName, minArity, isUnbounded, paramDefs, args) { const paramLen = paramDefs.length; if (isUnbounded) { checkMinArity(funcName, args.length, minArity); } else { checkArity(funcName, args.length, minArity, paramLen); } return args.map((arg, i) => { const paramNum = (isUnbounded && i >= paramLen) ? (paramLen - 1) : i; const paramDef = paramDefs[paramNum]; return checkArg(arg, funcName, i, paramDef[0], paramDef[1], paramDef[2], paramDef[3]); }); } function makeNamedArgs(namer, funcName, minArity, paramNames, paramDefs, args) { Object.keys(namer).forEach(name => { if (!paramNames.has(name)) { throw new Error(`${name} is not a named parameter of ${funcName}`); } }); args = []; let paramDef = null; let i = 0; let j = -1; for (const paramName of paramNames) { const val = namer[paramName]; if (val !== void 0) { paramDef = paramDefs[++j]; while (j < i) { args.push(checkArg(null, funcName, j, paramDef[0], paramDef[1], paramDef[2], paramDef[3])); paramDef = paramDefs[++j]; } args.push(checkArg(val, funcName, i, paramName, paramDef[1], paramDef[2], paramDef[3])); } i++; } while (++j < minArity) { paramDef = paramDefs[j]; args.push(checkArg(null, funcName, j, paramDef[0], paramDef[1], paramDef[2], paramDef[3])); } checkArity(funcName, args.length, minArity, paramNames.length); return args; } function makeSingleArgs(funcName, minArity, paramDef, args) { const argLen = args.length; checkArity(funcName, argLen, minArity, 1); if (argLen === 1) { const paramName = paramDef[0]; const namer = getNamer(args, paramName); if (namer === null || namer instanceof types.ServerType) { args[0] = checkArg(args[0], funcName, 0, paramName, paramDef[1], paramDef[2], paramDef[3]); } else if (Object.keys(namer).length > 1) { throw new Error(`named parameter object has keys other than ${paramName} for ${funcName}`); } else { args[0] = checkArg(namer[paramName], funcName, 0, paramName, paramDef[1], paramDef[2], paramDef[3]); } } return args; } function exportOperators(plan) { const operList = plan._operators; if (!Array.isArray(operList)) { throw new Error('operator list is not an array: '+operList); } return { ns: 'op', fn: 'operators', args: operList.map(oper => exportOperator(oper)) }; } function exportOperator(oper) { const ns = oper._ns; const fn = oper._fn; const args = oper._args; if (ns !== 'op' || fn === void 0 || !Array.isArray(args)) { throw new Error(`cannot export invalid operator: ${oper}`); } let exportedArgs = null; switch(fn) { case 'from-literals': exportedArgs = args.map((arg, i) => { if (i === 0) { const lit = (!Array.isArray(arg)) ? arg : (arg.length ===1) ? arg[0] : void 0; if (lit === void 0) { return exportArgs(arg); } else { const columnNames = lit.columnNames; const rowValues = lit.rowValues; if (columnNames === void 0 || rowValues === void 0) { return [exportArg(lit)]; } else if (Array.isArray(columnNames) && Array.isArray(rowValues)) { return rowValues.map((rowIn) => { if (!Array.isArray(rowIn)) { throw new Error(`literal row is not array: ${rowIn}`); } const rowOut = {}; rowIn.forEach((value, j) => { if (j > columnNames.length) { throw new Error(`more values than column names: ${rowIn}`); } rowOut[columnNames[j]] = value; }); return rowOut; }); } else { throw new Error(`no literals: ${oper}`); } } } else { return exportArg(arg); } }); break; case 'join-inner': case 'join-left-outer': case 'join-cross-product': case 'except': case 'intersect': case 'union': exportedArgs = args.map((arg, i) => { if (i === 0) { return exportOperators(arg); } return exportArg(arg); }); break; default: exportedArgs = exportArgs(args); break; } return {ns:ns, fn:fn, args:exportedArgs}; } function exportObject(obj) { const ns = obj._ns; const fn = obj._fn; const args = obj._args; if (ns !== void 0 && fn !== void 0 && Array.isArray(args)) { return { ns: ns, fn: fn, args: exportArgs(args) }; } return Object.getOwnPropertyNames(obj).reduce( (objCopy, key) => {objCopy[key] = exportArg(obj[key]); return objCopy;}, {} ); } function exportArgs(argList) { if (argList === void 0 || argList === null || argList.length === 0 || (argList.length === 1 && argList[0] === null) ) { return []; } return argList.map(arg => { return exportArg(arg); }); } function exportArg(arg) { if (arg === null || typeof arg !== 'object' || arg instanceof String || arg instanceof Number || arg instanceof Boolean ) { return arg; } else if (!Array.isArray(arg)) { return exportObject(arg); } else if (arg.length === 1) { return exportArg(arg[0]); } return exportArgs(arg); } function doExport(plan) { return {$optic:exportOperators(plan)}; } module.exports = { checkArity: checkArity, checkMaxArity: checkMaxArity, exportArg: exportArg, getNamer: getNamer, makeNamedArgs: makeNamedArgs, makePositionalArgs: makePositionalArgs, makeSingleArgs: makeSingleArgs, doExport: doExport };