Skip to content

Commit d80ad2f

Browse files
committed
MLE-22707 : Vector base64 encode and decode functions in the MarkLogic Node.js client
1 parent e67bcb4 commit d80ad2f

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

lib/vector-util.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright © 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
3+
*/
4+
5+
'use strict';
6+
const { Buffer } = require('buffer');
7+
8+
const base64Encode = (vector) => {
9+
const dimensions = vector.length;
10+
const buffer = Buffer.alloc(8 + 4 * dimensions);
11+
12+
buffer.writeInt32LE(0, 0);
13+
buffer.writeInt32LE(dimensions, 4);
14+
15+
vector.forEach((value, index) => {
16+
buffer.writeFloatLE(value, 8 + index * 4);
17+
});
18+
19+
return buffer.toString('base64');
20+
};
21+
22+
const base64Decode = (encodedVector) => {
23+
24+
const buffer = Buffer.from(encodedVector, 'base64');
25+
const version = buffer.readInt32LE(0);
26+
27+
if (version !== 0) {
28+
throw new Error(`Unsupported vector version: ${version}`);
29+
}
30+
31+
const dimensions = buffer.readInt32LE(4);
32+
const vector = [];
33+
34+
for (let i = 0; i < dimensions; i++) {
35+
vector.push(buffer.readFloatLE(8 + i * 4));
36+
}
37+
38+
return vector;
39+
};
40+
41+
module.exports = {
42+
base64Encode : base64Encode,
43+
base64Decode: base64Decode
44+
};

test-basic/vector-util-test.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright © 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
3+
*/
4+
5+
'use strict';
6+
7+
const assert = require('assert');
8+
const testlib = require('../etc/test-lib');
9+
let serverConfiguration = {};
10+
const vectorUtil = require('../lib/vector-util');
11+
const pbb = require("./plan-builder-base");
12+
const marklogic = require("../lib/marklogic");
13+
const testconfig = require("../etc/test-config");
14+
const vector = [3.14, 1.59, 2.65];
15+
const delta = 0.0001;
16+
const p = marklogic.planBuilder;
17+
const testPlan = pbb.testPlan;
18+
const dbWriter = marklogic.createDatabaseClient(testconfig.restWriterConnection);
19+
20+
describe('tests for vector-util', function () {
21+
this.timeout(5000);
22+
before(function (done) {
23+
try {
24+
testlib.findServerConfiguration(serverConfiguration);
25+
setTimeout(() => {
26+
if (serverConfiguration.serverVersion < 12) {
27+
this.skip();
28+
}
29+
done();
30+
}, 3000);
31+
} catch (error) {
32+
done(error);
33+
}
34+
});
35+
36+
it('should encode the vector correctly using client side', function (done) {
37+
const encoded = vectorUtil.base64Encode(vector);
38+
try{
39+
assert.strictEqual(encoded, 'AAAAAAMAAADD9UhAH4XLP5qZKUA=');
40+
done();
41+
} catch(error){
42+
done(error);
43+
}
44+
});
45+
46+
it('should decode the vector correctly using client side without delta', function (done) {
47+
const input = 'AAAAAAMAAADD9UhAH4XLP5qZKUA=';
48+
const decoded = vectorUtil.base64Decode(input);
49+
try {
50+
assert(Object.getPrototypeOf(decoded) == Array.prototype);
51+
assert(decoded[0] == 3.140000104904175);
52+
assert(decoded[1] == 1.590000033378601);
53+
assert(decoded[2] == 2.6500000953674316);
54+
for (let i = 0; i < vector.length; i++) {
55+
assert(Math.abs(decoded[i] - vector[i]) < delta, `Value mismatch at index ${i}`);
56+
}
57+
done();
58+
} catch(error){
59+
done(error);
60+
}
61+
});
62+
63+
it('should encode using server-side vector function and decode using client side', function (done) {
64+
const vec1 = [0.002];
65+
testPlan([""],p.vec.base64Encode(p.vec.subvector(p.vec.vector(vec1),0)))
66+
.then(function(response) {
67+
const input = response.rows[0].t.value;
68+
const decoded = vectorUtil.base64Decode(input);
69+
assert(input =='AAAAAAEAAABvEgM7');
70+
assert(Object.getPrototypeOf(decoded) == Array.prototype);
71+
assert(decoded[0] == 0.0020000000949949026);
72+
assert(Math.abs(decoded[0] - vec1[0]) < delta, `Value mismatch`);
73+
done();
74+
}).catch(error => done(error));
75+
});
76+
77+
it('should encode using client-side vector function and decode using server side', done => {
78+
79+
const input = vectorUtil.base64Encode(vector);
80+
const vectorString = '[ '+vector[0].toString()+', '+vector[1].toString()+', '+vector[2].toString()+' ]';
81+
dbWriter.eval(`vec.base64Decode('${input}')`).result(res=>{
82+
assert(res[0].value == vectorString);
83+
done();
84+
}).catch(error=> done(error))
85+
});
86+
});

0 commit comments

Comments
 (0)