|
| 1 | +'use strict'; |
| 2 | + |
| 3 | +const { MongoClient } = require('mongodb'); |
| 4 | +const MongoCollection = require('../lib/Adapters/Storage/Mongo/MongoCollection').default; |
| 5 | +const { findGeoIndexField } = require('../lib/Adapters/Storage/Mongo/MongoCollection'); |
| 6 | + |
| 7 | +describe_only_db('mongo')('MongoCollection', () => { |
| 8 | + describe('findGeoIndexField', () => { |
| 9 | + it('extracts the field constrained by $nearSphere', () => { |
| 10 | + const query = { construct: 'line', location: { $nearSphere: [-121.5, 38.5], $maxDistance: 2.5 } }; |
| 11 | + expect(findGeoIndexField(query)).toBe('location'); |
| 12 | + }); |
| 13 | + |
| 14 | + it('extracts the field constrained by $near', () => { |
| 15 | + expect(findGeoIndexField({ region: { $near: [0, 0] } })).toBe('region'); |
| 16 | + }); |
| 17 | + |
| 18 | + it('recurses into $and to find the geo field', () => { |
| 19 | + const query = { $and: [{ a: 1 }, { loc: { $nearSphere: [0, 0] } }] }; |
| 20 | + expect(findGeoIndexField(query)).toBe('loc'); |
| 21 | + }); |
| 22 | + |
| 23 | + it('returns undefined when there is no geo operator', () => { |
| 24 | + expect(findGeoIndexField({ a: 1, b: { $gt: 2 } })).toBeUndefined(); |
| 25 | + }); |
| 26 | + |
| 27 | + it('returns undefined for empty / non-object queries', () => { |
| 28 | + expect(findGeoIndexField({})).toBeUndefined(); |
| 29 | + expect(findGeoIndexField(null)).toBeUndefined(); |
| 30 | + expect(findGeoIndexField(undefined)).toBeUndefined(); |
| 31 | + }); |
| 32 | + |
| 33 | + it('does not treat $geoWithin as requiring an index', () => { |
| 34 | + const query = { location: { $geoWithin: { $centerSphere: [[0, 0], 1] } } }; |
| 35 | + expect(findGeoIndexField(query)).toBeUndefined(); |
| 36 | + }); |
| 37 | + |
| 38 | + it('does not recurse into $or (MongoDB forbids $near inside $or)', () => { |
| 39 | + const query = { $or: [{ a: 1 }, { loc: { $nearSphere: [0, 0] } }] }; |
| 40 | + expect(findGeoIndexField(query)).toBeUndefined(); |
| 41 | + }); |
| 42 | + }); |
| 43 | + |
| 44 | + describe('lazy geo index creation', () => { |
| 45 | + const collectionName = 'MongoCollectionLazyGeoIndexTest'; |
| 46 | + let client; |
| 47 | + let rawCollection; |
| 48 | + |
| 49 | + const geoQuery = { location: { $nearSphere: [-121.5, 38.5], $maxDistance: 2.526 } }; |
| 50 | + |
| 51 | + beforeEach(async () => { |
| 52 | + client = new MongoClient(databaseURI); |
| 53 | + await client.connect(); |
| 54 | + rawCollection = client.db().collection(collectionName); |
| 55 | + // Start from a clean collection with NO geo index so the lazy-creation path is exercised. |
| 56 | + await rawCollection.drop().catch(() => {}); |
| 57 | + await rawCollection.insertMany([ |
| 58 | + { _id: '1', location: [-121, 38] }, |
| 59 | + { _id: '2', location: [-122, 39] }, |
| 60 | + ]); |
| 61 | + }); |
| 62 | + |
| 63 | + afterEach(async () => { |
| 64 | + await rawCollection.drop().catch(() => {}); |
| 65 | + await client.close(); |
| 66 | + }); |
| 67 | + |
| 68 | + it('creates a 2d index on demand and returns results for a $nearSphere query on an un-indexed field', async () => { |
| 69 | + const mongoCollection = new MongoCollection(rawCollection); |
| 70 | + const results = await mongoCollection.find(geoQuery); |
| 71 | + expect(results.length).toBe(2); |
| 72 | + const indexes = await rawCollection.indexes(); |
| 73 | + const hasGeoIndex = indexes.some(index => index.key && index.key.location === '2d'); |
| 74 | + expect(hasGeoIndex).toBe(true); |
| 75 | + }); |
| 76 | + |
| 77 | + it_only_mongodb_version('>=8.3')('MongoDB 8.3+ reports the geoNear "no index" error without the field name', async () => { |
| 78 | + let error; |
| 79 | + try { |
| 80 | + await rawCollection.find(geoQuery).toArray(); |
| 81 | + } catch (e) { |
| 82 | + error = e; |
| 83 | + } |
| 84 | + expect(error).toBeDefined(); |
| 85 | + expect(error.message).toMatch(/unable to find index for .geoNear/); |
| 86 | + expect(error.message).not.toMatch(/field=/); |
| 87 | + }); |
| 88 | + |
| 89 | + it_only_mongodb_version('<8.3')('older MongoDB reports the geoNear "no index" error with the field name', async () => { |
| 90 | + let error; |
| 91 | + try { |
| 92 | + await rawCollection.find(geoQuery).toArray(); |
| 93 | + } catch (e) { |
| 94 | + error = e; |
| 95 | + } |
| 96 | + expect(error).toBeDefined(); |
| 97 | + expect(error.message).toMatch(/unable to find index for .geoNear/); |
| 98 | + expect(error.message).toMatch(/field=location/); |
| 99 | + }); |
| 100 | + }); |
| 101 | +}); |
0 commit comments