From 3635d75dbf74b6a6c060c9c1c5c33c8205f04aed Mon Sep 17 00:00:00 2001 From: asinha Date: Thu, 10 Jul 2025 12:14:20 -0700 Subject: [PATCH] MLE-20906 : Update Node Client based on changes to functions in Optic --- lib/plan-builder-base.js | 14 +++++- lib/plan-builder-generated.js | 2 +- test-basic/annTopK.js | 82 ++++++++++++++++++++++++++++++----- 3 files changed, 86 insertions(+), 12 deletions(-) diff --git a/lib/plan-builder-base.js b/lib/plan-builder-base.js index 8a63b089..2d661632 100644 --- a/lib/plan-builder-base.js +++ b/lib/plan-builder-base.js @@ -401,6 +401,18 @@ function castArg(arg, funcName, paramName, argPos, paramTypes) { } }); 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; } @@ -428,7 +440,7 @@ function castArg(arg, funcName, paramName, argPos, paramTypes) { break; case 'string': if (isProtoChained(paramTypes, [types.XsAnyAtomicType, types.TextNode, types.JsonContentNode, types.XmlContentNode, - types.XsString])) { + types.XsString, types.ServerType])) { return arg; } break; diff --git a/lib/plan-builder-generated.js b/lib/plan-builder-generated.js index 6daed766..e5e5dee2 100755 --- a/lib/plan-builder-generated.js +++ b/lib/plan-builder-generated.js @@ -8089,7 +8089,7 @@ shortestPath(...args) { */ annTopK(...args) { const namer = bldrbase.getNamer(args, 'inputK'); - const paramdefs = [['inputK', [types.XsInteger, PlanParam], true, false], ['vectorColumn', [PlanExprCol, PlanColumn, types.XsString], true, false], ['queryVector', [PlanParam, types.VecVector], true, false], ['distanceColumn', [PlanExprCol, PlanColumn, types.XsString], false, false], ['options', [PlanAnnTopKOptions], false, false]]; + const paramdefs = [['inputK', [types.XsInteger, PlanParam], true, false], ['vectorColumn', [PlanExprCol, PlanColumn, types.XsString], true, false], ['queryVector', [PlanParam, types.VecVector], true, false], ['distanceColumn', [PlanExprCol, PlanColumn, types.XsString], false, false], ['options', [PlanAnnTopKOptions], false, true]]; const checkedArgs = (namer !== null) ? bldrbase.makeNamedArgs(namer, 'PlanModifyPlan.annTopK', 3, new Set(['inputK', 'vectorColumn', 'queryVector', 'distanceColumn', 'options']), paramdefs, args) : bldrbase.makePositionalArgs('PlanModifyPlan.annTopK', 3, false, paramdefs, args); diff --git a/test-basic/annTopK.js b/test-basic/annTopK.js index 738d0609..35d683eb 100644 --- a/test-basic/annTopK.js +++ b/test-basic/annTopK.js @@ -13,6 +13,7 @@ let serverConfiguration = {}; const execPlan = pbb.execPlan; describe('tests for annTopK', function () { + this.timeout(5000) before(function (done) { try { testlib.findServerConfiguration(serverConfiguration); @@ -27,22 +28,83 @@ describe('tests for annTopK', function () { } }); - it('happy path', function (done) { + it('annTopK without PlanAnnTopKOptions', function (done) { execPlan(p .fromView('vectors', 'persons', '') - .annTopK(10, p.col('embedding'), p.vec.vector([1.1, 2.2, 3.3]), p.col('distance'), 0.5) + .annTopK(10, p.col('embedding'), p.vec.vector([1.1, 2.2, 3.3]), p.col('distance')) .orderBy(p.col('name')) ) .then(function (response) { - const rows = response.rows; - assert(rows.length === 2, 'Expecting both rows in the view to be returned.'); - assert(rows[0].name.value === 'Alice'); - assert(rows[0].distance.type === 'xs:float', 'Verifying that the distance column was populated.'); - assert(rows[1].name.value === 'Bob'); - assert(rows[1].distance.type === 'xs:float', 'Verifying that the distance column was populated.'); - done(); + verifyResults(response.rows, done); + }) + .catch(error => done(error)); + }); + + it('annTopK with PlanAnnTopKOptions as a single string', function (done) { + execPlan(p + .fromView('vectors', 'persons', '') + .annTopK(10, p.col('embedding'), p.vec.vector([1.1, 2.2, 3.3]), p.col('distance'), 'onlyIndex') + .orderBy(p.col('name')) + ) + .then(function (response) { + verifyResults(response.rows, done); }) - .catch(done); + .catch(error => done(error)); + }); + + it('annTopK with PlanAnnTopKOptions as an array of string', function (done) { + execPlan(p + .fromView('vectors', 'persons', '') + .annTopK(10, p.col('embedding'), p.vec.vector([1.1, 2.2, 3.3]), p.col('distance'), + ['onlyIndex', "maxDistance=0.15", "searchFactor=1.0"]) + .orderBy(p.col('name')) + ).then(function (response) { + verifyResults(response.rows, done); + }).catch(error => done(error)); }); + it('annTopK with PlanAnnTopKOptions as a map', function (done) { + const planAnnTopKOptionsMap = new Map(); + planAnnTopKOptionsMap.set("maxDistance", 0.158454656600952); + planAnnTopKOptionsMap.set("searchFactor", 10.0); + execPlan(p + .fromView('vectors', 'persons', '') + .annTopK(10, p.col('embedding'), p.vec.vector([1.1, 2.2, 3.3]), p.col('distance'), + planAnnTopKOptionsMap) + .orderBy(p.col('name')) + ) + .then(function (response) { + verifyResults(response.rows, done); + }) + .catch(error => done(error)); + }); + + it('annTopK with invalid PlanAnnTopKOptions', function (done) { + const planAnnTopKOptionsMap = new Map(); + planAnnTopKOptionsMap.set('invalid', 10.0); + try{ + execPlan(p + .fromView('vectors', 'persons', '') + .annTopK(10, p.col('embedding'), p.vec.vector([1.1, 2.2, 3.3]), p.col('distance'), + planAnnTopKOptionsMap) + .orderBy(p.col('name')) + ); + } catch(error){ + assert(error.message.toString().includes('options argument at 4 of PlanModifyPlan.annTopK() has invalid key- invalid')) + done(); + } + }); + + function verifyResults(rows, done){ + try { + assert(rows.length === 2, 'Expecting both rows in the view to be returned.'); + assert(rows[0].name.value === 'Alice'); + assert(rows[0].distance.type === 'xs:float', 'Verifying that the distance column was populated.'); + assert(rows[1].name.value === 'Bob'); + assert(rows[1].distance.type === 'xs:float', 'Verifying that the distance column was populated.'); + done(); + } catch (error){ + done(error) + } + } }); \ No newline at end of file