diff --git a/simple/run-big-all-runs1.js b/simple/run-big-all-runs1.js index 73eb5da..1dc4219 100644 --- a/simple/run-big-all-runs1.js +++ b/simple/run-big-all-runs1.js @@ -14,7 +14,8 @@ function main () { crud: true, crudSearch: true, subqueryTests: true, - mditests: true + mditests: true, + vectorTests: true }); return GLOBAL.returnValue; } diff --git a/simple/run-big-all.js b/simple/run-big-all.js index b718d4b..572ced5 100644 --- a/simple/run-big-all.js +++ b/simple/run-big-all.js @@ -14,7 +14,8 @@ function main () { crud: true, crudSearch: true, subqueryTests: true, - mditests: true + mditests: true, + vectorTests: true }); print('oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo') print(GLOBAL.returnValue) diff --git a/simple/run-medium-all-runs1.js b/simple/run-medium-all-runs1.js index 01c95aa..bf06e55 100644 --- a/simple/run-medium-all-runs1.js +++ b/simple/run-medium-all-runs1.js @@ -14,7 +14,8 @@ function main () { crud: true, crudSearch: true, subqueryTests: true, - mditests: true + mditests: true, + vectorTests: true }); return GLOBAL.returnValue; } diff --git a/simple/run-medium-all.js b/simple/run-medium-all.js index 43998ed..accaeb3 100644 --- a/simple/run-medium-all.js +++ b/simple/run-medium-all.js @@ -14,7 +14,8 @@ function main () { crud: true, crudSearch: true, subqueryTests: true, - mditests: true + mditests: true, + vectorTests: true }); return GLOBAL.returnValue; } diff --git a/simple/run-small-all-junit.js b/simple/run-small-all-junit.js index 6ce7b02..7de9ee3 100644 --- a/simple/run-small-all-junit.js +++ b/simple/run-small-all-junit.js @@ -13,7 +13,8 @@ function main () { phrase: true, crud: true, crudSearch: true, - mditests: true + mditests: true, + vectorTests: true }); return GLOBAL.returnValue; } diff --git a/simple/run-small-all.js b/simple/run-small-all.js index f085b27..46e42df 100644 --- a/simple/run-small-all.js +++ b/simple/run-small-all.js @@ -13,7 +13,8 @@ function main () { noMaterializationSearch: true, crud: true, crudSearch: true, - mditests: true + mditests: true, + vectorTests: true }); return GLOBAL.returnValue; } diff --git a/simple/run-tiny-all.js b/simple/run-tiny-all.js index dd6808b..9e1c629 100644 --- a/simple/run-tiny-all.js +++ b/simple/run-tiny-all.js @@ -15,7 +15,8 @@ function main () { crud: true, crudSearch: true, subqueryTests: true, - mditests: true + mditests: true, + vectorTests: true }); return GLOBAL.returnValue; } diff --git a/simple/run-vector.js b/simple/run-vector.js new file mode 100644 index 0000000..9f20996 --- /dev/null +++ b/simple/run-vector.js @@ -0,0 +1,23 @@ +function main () { + global.returnValue = 0; + require("./simple/test").test({ + outputCsv: false, + small: true, + runs: 3, + documents: false, + ioless: false, + edges: false, + search: false, + phrase: false, + noMaterializationSearch: false, + crud: false, + crudSearch: false, + subqueryTests: false, + mditests: false, + vectorTests: true + }); + return global.returnValue; +} +if (typeof arango !== "undefined") { + process.exit(main()); +} diff --git a/simple/test.js b/simple/test.js index 1aad5d7..95bcc1d 100644 --- a/simple/test.js +++ b/simple/test.js @@ -19,6 +19,19 @@ function sum (values) { } } +function randomNumberGeneratorFloat(seed) { + const rng = (function* (seed) { + while (true) { + const nextVal = Math.cos(seed++); + yield nextVal; + } + })(seed); + + return function () { + return rng.next().value; + }; +} + function calc (values, options) { values.sort((a, b) => a - b); @@ -136,6 +149,7 @@ exports.test = function (testParams) { // Substring first 5 characters to limit to A.B.C format and not use any `nightly`, `rc`, `preview` etc. const serverVersion = (((typeof arango) !== "undefined") ? arango.getVersion() : internal.version).split("-")[0]; testParams.zkdMdiRenamed = semver.satisfies(serverVersion, ">3.11.99") ; + testParams.vectorTests = testParams.vectorTests && semver.satisfies(serverVersion, ">3.12.3") ; const isEnterprise = internal.isEnterprise(); const isCluster = internal.isCluster(); @@ -156,6 +170,10 @@ exports.test = function (testParams) { if (options.hasOwnProperty("iterations")) { params.iterations = options.iterations; } + if (options.hasOwnProperty("extras")) { + params.extras = options.extras; + } + return params; }; @@ -3333,6 +3351,59 @@ exports.test = function (testParams) { }, ]; + function vectorTest (params) { + let bindParam = { "@col": params.collection, "qp": params.extras.queryPoint }; + if ("bindParamModifier" in params) { + params.bindParamModifier(params, bindParam); + } + db._query( + params.queryString, + bindParam, + ); + } + + function vectorTestNoParams (params) { + let bindParam = { "@col": params.collection }; + if ("bindParamModifier" in params) { + params.bindParamModifier(params, bindParam); + } + db._query( + params.queryString, + bindParam, + ); + } + + let VectorTests = [ + { + name: "aql-vector-top-k", + params: { + func: vectorTest, + queryString: ` + FOR d IN @@col + SORT APPROX_NEAR_L2(d.vector, @qp) + LIMIT 5 + RETURN d` + } + }, + { + name: "aql-vector-subquery-10-points", + params: { + func: vectorTestNoParams, + queryString: ` + FOR docOuter IN @@col + LIMIT 10 + LET neibhours = ( + FOR docInner IN @@col + LET dist = APPROX_NEAR_L2(docInner.vector, docOuter.vector) + SORT dist + LIMIT 10 + RETURN {dist, doc: docInner._key} + ) + RETURN {doc: docOuter._key, neibhours: neibhours}` + } + }, + ]; + const runSatelliteGraphTests = (testParams.satelliteGraphTests && isEnterprise && isCluster); if (testParams.documents || testParams.edges || testParams.noMaterializationSearch || testParams.subqueryTests || runSatelliteGraphTests) { @@ -3441,6 +3512,68 @@ exports.test = function (testParams) { runTestSuite("MDI", MdiTests, options); } + // vector tests + if (testParams.vectorTests) { + const dimension = 500; + let gen = randomNumberGeneratorFloat(3243758343); + let randomPoint = Array.from({ length: dimension }, () => gen()); + + options = { + runs: testParams.runs, + digits: testParams.digits, + setup: function (params) { + db._drop(params.collection); + let col = db._create(params.collection); + + const batchSize = Math.round(params.batchSize / 4); // we have big docs + const n = Math.round(params.collectionSize / batchSize); + for (let i = 0; i < n / batchSize ; ++i) { + internal.wait(0, true); // garbage collect... + let docs = []; + for (let j = 0; j < batchSize; ++j) { + const vector = Array.from({ length: dimension }, () => gen()); + if (i * batchSize + j === 2000) { + randomPoint = vector; + } + docs.push({_key: "test_" + (j + i* batchSize), vector: vector }); + } + col.insert(docs); + } + + col.ensureIndex({ + name: "vector_l2", + type: "vector", + fields: ["vector"], + inBackground: false, + params: { metric: "l2", dimension: dimension, nLists: params.extras.nLists }, + }); + + }, + teardown: function () {}, + collections: [], + removeFromResult: 1 + }; + + let extras = { queryPoint: randomPoint }; + + if (testParams.tiny) { + options.collections.push({ name: "Vectorvalues1000", label: "1k", size: 1000 }); + extras["nLists"]= 10; + } else if (testParams.small) { + options.collections.push({ name: "Vectorvalues10000", label: "10k", size: 10000 }); + extras["nLists"]= 10; + } else if (testParams.medium) { + options.collections.push({ name: "Vectorvalues100000", label: "100k", size: 100000 }); + extras["nLists"]= 100; + } else if (testParams.big) { + options.collections.push({ name: "Vectorvalues1000000", label: "1000k", size: 1000000 }); + extras["nLists"]= 100; + } + options.extras = extras; + + runTestSuite("Vector", VectorTests, options); + } + if (testParams.ioless) { options = { runs: testParams.runs,