Skip to content

Commit e8913ba

Browse files
committed
test(model): add Atlas CLI-based test cases for search indexes re: Automattic#15645
1 parent 603b287 commit e8913ba

File tree

2 files changed

+129
-11
lines changed

2 files changed

+129
-11
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
matrix:
4242
node: [16, 18, 20, 22, 24]
4343
os: [ubuntu-22.04, ubuntu-24.04]
44-
mongodb: [6.0.15, 7.0.12, 8.0.0]
44+
mongodb: [6.0.15, 7.0.12, 8.2.0]
4545
include:
4646
- os: ubuntu-22.04 # customize on which matrix the coverage will be collected on
4747
mongodb: 6.0.15

test/model.test.js

Lines changed: 128 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8742,30 +8742,148 @@ describe('Model', function() {
87428742
});
87438743

87448744
it('createSearchIndexes creates an index for each search index in schema (gh-15465)', async function() {
8745-
const sinon = require('sinon');
8745+
this.timeout(20000);
8746+
const version = await start.mongodVersion();
8747+
if (version[0] < 8 || !process.env.IS_ATLAS) {
8748+
this.skip();
8749+
return;
8750+
}
87468751
const schema = new mongoose.Schema({
87478752
name: String,
87488753
description: String
87498754
});
87508755

8751-
schema.searchIndex({ name: 'test', definition: { mappings: { dynamic: true } } });
8756+
schema.searchIndex({
8757+
name: 'test',
8758+
definition: {
8759+
mappings: {
8760+
dynamic: false,
8761+
fields: { name: { type: 'string' }, description: { type: 'string' } }
8762+
}
8763+
}
8764+
});
87528765

87538766
const TestModel = db.model('Test', schema);
87548767

8755-
const createSearchIndexStub = sinon.stub(TestModel, 'createSearchIndex').resolves({ acknowledged: true });
8768+
await TestModel.init();
8769+
const results = await TestModel.createSearchIndexes();
87568770

87578771
try {
8758-
const results = await TestModel.createSearchIndexes();
8759-
8760-
assert.equal(createSearchIndexStub.callCount, 1);
87618772
assert.equal(results.length, 1);
8762-
assert.deepEqual(results, [{ acknowledged: true }]);
8773+
assert.deepEqual(results, ['test']);
87638774

8764-
// Verify that createSearchIndex was called with the correct arguments
8765-
assert.ok(createSearchIndexStub.firstCall.calledWithMatch({ name: 'test', definition: { mappings: { dynamic: true } } }));
8775+
let indexes = await TestModel.listSearchIndexes();
8776+
assert.equal(indexes.length, 1);
8777+
assert.equal(indexes[0].name, 'test');
8778+
8779+
let isQueryable = indexes[0].queryable;
8780+
while (!isQueryable) {
8781+
await delay(100);
8782+
indexes = await TestModel.listSearchIndexes();
8783+
isQueryable = indexes[0].queryable;
8784+
}
8785+
8786+
// Insert a document to search.
8787+
await TestModel.create({ name: 'Atlas Search Example', description: 'This is a test for MongoDB Atlas Search.' });
8788+
8789+
// Retry aggregate up to 10 times every 500ms because Lucene index is not immediately queryable
8790+
let searchResults;
8791+
for (let tries = 0; tries < 10; ++tries) {
8792+
searchResults = await TestModel.aggregate([
8793+
{
8794+
$search: {
8795+
index: 'test',
8796+
text: {
8797+
query: 'Atlas',
8798+
path: 'name'
8799+
}
8800+
}
8801+
}
8802+
]);
8803+
if (searchResults.length > 0) {
8804+
break;
8805+
}
8806+
await delay(500);
8807+
}
8808+
assert.ok(searchResults.length > 0);
8809+
assert.strictEqual(searchResults[0].name, 'Atlas Search Example');
87668810
} finally {
8767-
sinon.restore();
8811+
await TestModel.dropSearchIndex('test');
8812+
}
8813+
});
8814+
8815+
it('can create a vector search index (gh-15465)', async function() {
8816+
this.timeout(10000);
8817+
const version = await start.mongodVersion();
8818+
if (version[0] < 8 || !process.env.IS_ATLAS) {
8819+
this.skip();
8820+
return;
87688821
}
8822+
const schema = new mongoose.Schema({
8823+
name: String,
8824+
myVector: [Number]
8825+
});
8826+
schema.searchIndex({
8827+
name: 'vector_index',
8828+
type: 'vectorSearch',
8829+
definition: {
8830+
fields: [
8831+
{
8832+
type: 'vector',
8833+
numDimensions: 2,
8834+
path: 'myVector',
8835+
similarity: 'dotProduct',
8836+
quantization: 'scalar'
8837+
}
8838+
]
8839+
}
8840+
});
8841+
8842+
const TestModel = db.model('Test', schema);
8843+
8844+
await TestModel.init();
8845+
const results = await TestModel.createSearchIndexes();
8846+
8847+
assert.equal(results.length, 1);
8848+
assert.deepEqual(results, ['vector_index']);
8849+
8850+
await TestModel.create([{ name: 'Test1', myVector: [0, 99] }, { name: 'Test2', myVector: [99, 0] }])
8851+
8852+
let indexes = await TestModel.listSearchIndexes();
8853+
let isQueryable = indexes[0].queryable;
8854+
while (!isQueryable) {
8855+
await delay(100);
8856+
indexes = await TestModel.listSearchIndexes();
8857+
isQueryable = indexes[0].queryable;
8858+
}
8859+
8860+
let [doc] = await TestModel.aggregate([
8861+
{
8862+
$vectorSearch: {
8863+
index: 'vector_index',
8864+
path: 'myVector',
8865+
queryVector: [0, 100],
8866+
numCandidates: 10,
8867+
limit: 1
8868+
}
8869+
}
8870+
]);
8871+
assert.strictEqual(doc.name, 'Test1');
8872+
8873+
[doc] = await TestModel.aggregate([
8874+
{
8875+
$vectorSearch: {
8876+
index: 'vector_index',
8877+
path: 'myVector',
8878+
queryVector: [100, 1],
8879+
numCandidates: 10,
8880+
limit: 1
8881+
}
8882+
}
8883+
]);
8884+
assert.strictEqual(doc.name, 'Test2');
8885+
8886+
await TestModel.dropSearchIndex('vector_index');
87698887
});
87708888
});
87718889

0 commit comments

Comments
 (0)