'use strict'; const Config = require('../lib/Config'); const TestUtils = require('../lib/TestUtils'); const request = require('../lib/request'); let config; const masterKeyHeaders = { 'X-Parse-Application-Id': 'test', 'X-Parse-Rest-API-Key': 'rest', 'X-Parse-Master-Key': 'test', 'Content-Type': 'application/json', }; const masterKeyOptions = { headers: masterKeyHeaders, json: true, }; describe_only_db('mongo')('Parse.Query hint', () => { beforeEach(() => { config = Config.get('test'); }); afterEach(async () => { await TestUtils.destroyAllDataPermanently(false); }); it_only_mongodb_version('<5.1 || >=6 <8')('query find with hint string', async () => { const object = new TestObject(); await object.save(); const collection = await config.database.adapter._adaptiveCollection('TestObject'); let explain = await collection._rawFind({ _id: object.id }, { explain: true }); expect(explain.queryPlanner.winningPlan.stage).toBe('IDHACK'); explain = await collection._rawFind({ _id: object.id }, { hint: '_id_', explain: true }); expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); }); it_only_mongodb_version('>=8')('query find with hint string', async () => { const object = new TestObject(); await object.save(); const collection = await config.database.adapter._adaptiveCollection('TestObject'); let explain = await collection._rawFind({ _id: object.id }, { explain: true }); expect(explain.queryPlanner.winningPlan.stage).toBe('EXPRESS_IXSCAN'); explain = await collection._rawFind({ _id: object.id }, { hint: '_id_', explain: true }); expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); }); it_only_mongodb_version('<5.1 || >=6 <8')('query find with hint object', async () => { const object = new TestObject(); await object.save(); const collection = await config.database.adapter._adaptiveCollection('TestObject'); let explain = await collection._rawFind({ _id: object.id }, { explain: true }); expect(explain.queryPlanner.winningPlan.stage).toBe('IDHACK'); explain = await collection._rawFind({ _id: object.id }, { hint: { _id: 1 }, explain: true }); expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); expect(explain.queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1, }); }); it_only_mongodb_version('>=8')('query find with hint object', async () => { const object = new TestObject(); await object.save(); const collection = await config.database.adapter._adaptiveCollection('TestObject'); let explain = await collection._rawFind({ _id: object.id }, { explain: true }); expect(explain.queryPlanner.winningPlan.stage).toBe('EXPRESS_IXSCAN'); explain = await collection._rawFind({ _id: object.id }, { hint: { _id: 1 }, explain: true }); expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); expect(explain.queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1, }); }); it_only_mongodb_version('>=7')('query aggregate with hint string', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); const collection = await config.database.adapter._adaptiveCollection('TestObject'); let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { explain: true, }); let queryPlanner = result[0].queryPlanner; expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); result = await collection.aggregate([{ $group: { _id: '$foo' } }], { hint: '_id_', explain: true, }); queryPlanner = result[0].queryPlanner; expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); }); it_only_mongodb_version('>=7')('query aggregate with hint object', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); const collection = await config.database.adapter._adaptiveCollection('TestObject'); let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { explain: true, }); let queryPlanner = result[0].queryPlanner; expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); result = await collection.aggregate([{ $group: { _id: '$foo' } }], { hint: { _id: 1 }, explain: true, }); queryPlanner = result[0].queryPlanner; expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); it_only_mongodb_version('<5.1 || >=6')('query find with hint (rest)', async () => { const object = new TestObject(); await object.save(); let options = Object.assign({}, masterKeyOptions, { url: Parse.serverURL + '/classes/TestObject', qs: { explain: true, }, }); let response = await request(options); let explain = response.data.results; expect(explain.queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); options = Object.assign({}, masterKeyOptions, { url: Parse.serverURL + '/classes/TestObject', qs: { explain: true, hint: '_id_', }, }); response = await request(options); explain = response.data.results; expect(explain.queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); }); it_only_mongodb_version('>=7')('query aggregate with hint (rest)', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); let options = Object.assign({}, masterKeyOptions, { url: Parse.serverURL + '/aggregate/TestObject', qs: { explain: true, $group: JSON.stringify({ _id: '$foo' }), }, }); let response = await request(options); let queryPlanner = response.data.results[0].queryPlanner; expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); options = Object.assign({}, masterKeyOptions, { url: Parse.serverURL + '/aggregate/TestObject', qs: { explain: true, hint: '_id_', $group: JSON.stringify({ _id: '$foo' }), }, }); response = await request(options); queryPlanner = response.data.results[0].queryPlanner; expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); });