* added hint to aggregate * added support for hint in query * added else clause to aggregate * fixed tests * updated tests * Add tests and clean up * Add support for explain Co-authored-by: Diamond Lewis <findlewis@gmail.com>
This commit is contained in:
committed by
Diamond Lewis
parent
5a1d94ed88
commit
9842c6ee42
170
spec/ParseQuery.hint.spec.js
Normal file
170
spec/ParseQuery.hint.spec.js
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
'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 config.database.schemaCache.clear();
|
||||||
|
await TestUtils.destroyAllDataPermanently(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('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('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('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].stages[0].$cursor;
|
||||||
|
expect(queryPlanner.winningPlan.stage).toBe('COLLSCAN');
|
||||||
|
|
||||||
|
result = await collection.aggregate([{ $group: { _id: '$foo' } }], {
|
||||||
|
hint: '_id_',
|
||||||
|
explain: true,
|
||||||
|
});
|
||||||
|
queryPlanner = result[0].stages[0].$cursor.queryPlanner;
|
||||||
|
expect(queryPlanner.winningPlan.stage).toBe('FETCH');
|
||||||
|
expect(queryPlanner.winningPlan.inputStage.indexName).toBe('_id_');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('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].stages[0].$cursor;
|
||||||
|
expect(queryPlanner.winningPlan.stage).toBe('COLLSCAN');
|
||||||
|
|
||||||
|
result = await collection.aggregate([{ $group: { _id: '$foo' } }], {
|
||||||
|
hint: { _id: 1 },
|
||||||
|
explain: true,
|
||||||
|
});
|
||||||
|
queryPlanner = result[0].stages[0].$cursor.queryPlanner;
|
||||||
|
expect(queryPlanner.winningPlan.stage).toBe('FETCH');
|
||||||
|
expect(queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('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('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({ objectId: '$foo' }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let response = await request(options);
|
||||||
|
let { queryPlanner } = response.data.results[0].stages[0].$cursor;
|
||||||
|
expect(queryPlanner.winningPlan.stage).toBe('COLLSCAN');
|
||||||
|
|
||||||
|
options = Object.assign({}, masterKeyOptions, {
|
||||||
|
url: Parse.serverURL + '/aggregate/TestObject',
|
||||||
|
qs: {
|
||||||
|
explain: true,
|
||||||
|
hint: '_id_',
|
||||||
|
group: JSON.stringify({ objectId: '$foo' }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
response = await request(options);
|
||||||
|
queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner;
|
||||||
|
expect(queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -13,7 +13,10 @@ export default class MongoCollection {
|
|||||||
// none, then build the geoindex.
|
// none, then build the geoindex.
|
||||||
// This could be improved a lot but it's not clear if that's a good
|
// This could be improved a lot but it's not clear if that's a good
|
||||||
// idea. Or even if this behavior is a good idea.
|
// idea. Or even if this behavior is a good idea.
|
||||||
find(query, { skip, limit, sort, keys, maxTimeMS, readPreference } = {}) {
|
find(
|
||||||
|
query,
|
||||||
|
{ skip, limit, sort, keys, maxTimeMS, readPreference, hint, explain } = {}
|
||||||
|
) {
|
||||||
// Support for Full Text Search - $text
|
// Support for Full Text Search - $text
|
||||||
if (keys && keys.$score) {
|
if (keys && keys.$score) {
|
||||||
delete keys.$score;
|
delete keys.$score;
|
||||||
@@ -26,6 +29,8 @@ export default class MongoCollection {
|
|||||||
keys,
|
keys,
|
||||||
maxTimeMS,
|
maxTimeMS,
|
||||||
readPreference,
|
readPreference,
|
||||||
|
hint,
|
||||||
|
explain,
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
// Check for "no geoindex" error
|
// Check for "no geoindex" error
|
||||||
if (
|
if (
|
||||||
@@ -54,18 +59,24 @@ export default class MongoCollection {
|
|||||||
keys,
|
keys,
|
||||||
maxTimeMS,
|
maxTimeMS,
|
||||||
readPreference,
|
readPreference,
|
||||||
|
hint,
|
||||||
|
explain,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_rawFind(query, { skip, limit, sort, keys, maxTimeMS, readPreference } = {}) {
|
_rawFind(
|
||||||
|
query,
|
||||||
|
{ skip, limit, sort, keys, maxTimeMS, readPreference, hint, explain } = {}
|
||||||
|
) {
|
||||||
let findOperation = this._mongoCollection.find(query, {
|
let findOperation = this._mongoCollection.find(query, {
|
||||||
skip,
|
skip,
|
||||||
limit,
|
limit,
|
||||||
sort,
|
sort,
|
||||||
readPreference,
|
readPreference,
|
||||||
|
hint,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (keys) {
|
if (keys) {
|
||||||
@@ -76,10 +87,10 @@ export default class MongoCollection {
|
|||||||
findOperation = findOperation.maxTimeMS(maxTimeMS);
|
findOperation = findOperation.maxTimeMS(maxTimeMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return findOperation.toArray();
|
return explain ? findOperation.explain(explain) : findOperation.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
count(query, { skip, limit, sort, maxTimeMS, readPreference } = {}) {
|
count(query, { skip, limit, sort, maxTimeMS, readPreference, hint } = {}) {
|
||||||
// If query is empty, then use estimatedDocumentCount instead.
|
// If query is empty, then use estimatedDocumentCount instead.
|
||||||
// This is due to countDocuments performing a scan,
|
// This is due to countDocuments performing a scan,
|
||||||
// which greatly increases execution time when being run on large collections.
|
// which greatly increases execution time when being run on large collections.
|
||||||
@@ -96,6 +107,7 @@ export default class MongoCollection {
|
|||||||
sort,
|
sort,
|
||||||
maxTimeMS,
|
maxTimeMS,
|
||||||
readPreference,
|
readPreference,
|
||||||
|
hint,
|
||||||
});
|
});
|
||||||
|
|
||||||
return countOperation;
|
return countOperation;
|
||||||
@@ -105,9 +117,9 @@ export default class MongoCollection {
|
|||||||
return this._mongoCollection.distinct(field, query);
|
return this._mongoCollection.distinct(field, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
aggregate(pipeline, { maxTimeMS, readPreference } = {}) {
|
aggregate(pipeline, { maxTimeMS, readPreference, hint, explain } = {}) {
|
||||||
return this._mongoCollection
|
return this._mongoCollection
|
||||||
.aggregate(pipeline, { maxTimeMS, readPreference })
|
.aggregate(pipeline, { maxTimeMS, readPreference, hint, explain })
|
||||||
.toArray();
|
.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -620,7 +620,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
className: string,
|
className: string,
|
||||||
schema: SchemaType,
|
schema: SchemaType,
|
||||||
query: QueryType,
|
query: QueryType,
|
||||||
{ skip, limit, sort, keys, readPreference }: QueryOptions
|
{ skip, limit, sort, keys, readPreference, hint, explain }: QueryOptions
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
schema = convertParseSchemaToMongoSchema(schema);
|
schema = convertParseSchemaToMongoSchema(schema);
|
||||||
const mongoWhere = transformWhere(className, query, schema);
|
const mongoWhere = transformWhere(className, query, schema);
|
||||||
@@ -652,13 +652,18 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
keys: mongoKeys,
|
keys: mongoKeys,
|
||||||
maxTimeMS: this._maxTimeMS,
|
maxTimeMS: this._maxTimeMS,
|
||||||
readPreference,
|
readPreference,
|
||||||
|
hint,
|
||||||
|
explain,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.then(objects =>
|
.then(objects => {
|
||||||
objects.map(object =>
|
if (explain) {
|
||||||
|
return objects;
|
||||||
|
}
|
||||||
|
return objects.map(object =>
|
||||||
mongoObjectToParseObject(className, object, schema)
|
mongoObjectToParseObject(className, object, schema)
|
||||||
)
|
);
|
||||||
)
|
})
|
||||||
.catch(err => this.handleError(err));
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -712,7 +717,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
className: string,
|
className: string,
|
||||||
schema: SchemaType,
|
schema: SchemaType,
|
||||||
query: QueryType,
|
query: QueryType,
|
||||||
readPreference: ?string
|
readPreference: ?string,
|
||||||
|
hint: ?mixed
|
||||||
) {
|
) {
|
||||||
schema = convertParseSchemaToMongoSchema(schema);
|
schema = convertParseSchemaToMongoSchema(schema);
|
||||||
readPreference = this._parseReadPreference(readPreference);
|
readPreference = this._parseReadPreference(readPreference);
|
||||||
@@ -721,6 +727,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
collection.count(transformWhere(className, query, schema, true), {
|
collection.count(transformWhere(className, query, schema, true), {
|
||||||
maxTimeMS: this._maxTimeMS,
|
maxTimeMS: this._maxTimeMS,
|
||||||
readPreference,
|
readPreference,
|
||||||
|
hint,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.catch(err => this.handleError(err));
|
.catch(err => this.handleError(err));
|
||||||
@@ -760,7 +767,9 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
className: string,
|
className: string,
|
||||||
schema: any,
|
schema: any,
|
||||||
pipeline: any,
|
pipeline: any,
|
||||||
readPreference: ?string
|
readPreference: ?string,
|
||||||
|
hint: ?mixed,
|
||||||
|
explain?: boolean
|
||||||
) {
|
) {
|
||||||
let isPointerField = false;
|
let isPointerField = false;
|
||||||
pipeline = pipeline.map(stage => {
|
pipeline = pipeline.map(stage => {
|
||||||
@@ -791,6 +800,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
collection.aggregate(pipeline, {
|
collection.aggregate(pipeline, {
|
||||||
readPreference,
|
readPreference,
|
||||||
maxTimeMS: this._maxTimeMS,
|
maxTimeMS: this._maxTimeMS,
|
||||||
|
hint,
|
||||||
|
explain,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.then(results => {
|
.then(results => {
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ export type QueryOptions = {
|
|||||||
distinct?: boolean,
|
distinct?: boolean,
|
||||||
pipeline?: any,
|
pipeline?: any,
|
||||||
readPreference?: ?string,
|
readPreference?: ?string,
|
||||||
|
hint?: ?mixed,
|
||||||
|
explain?: Boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpdateQueryOptions = {
|
export type UpdateQueryOptions = {
|
||||||
@@ -92,7 +94,8 @@ export interface StorageAdapter {
|
|||||||
schema: SchemaType,
|
schema: SchemaType,
|
||||||
query: QueryType,
|
query: QueryType,
|
||||||
readPreference?: string,
|
readPreference?: string,
|
||||||
estimate?: boolean
|
estimate?: boolean,
|
||||||
|
hint?: mixed
|
||||||
): Promise<number>;
|
): Promise<number>;
|
||||||
distinct(
|
distinct(
|
||||||
className: string,
|
className: string,
|
||||||
@@ -104,7 +107,9 @@ export interface StorageAdapter {
|
|||||||
className: string,
|
className: string,
|
||||||
schema: any,
|
schema: any,
|
||||||
pipeline: any,
|
pipeline: any,
|
||||||
readPreference: ?string
|
readPreference: ?string,
|
||||||
|
hint: ?mixed,
|
||||||
|
explain?: boolean
|
||||||
): Promise<any>;
|
): Promise<any>;
|
||||||
performInitialization(options: ?any): Promise<void>;
|
performInitialization(options: ?any): Promise<void>;
|
||||||
|
|
||||||
|
|||||||
@@ -1289,13 +1289,14 @@ class DatabaseController {
|
|||||||
distinct,
|
distinct,
|
||||||
pipeline,
|
pipeline,
|
||||||
readPreference,
|
readPreference,
|
||||||
|
hint,
|
||||||
|
explain,
|
||||||
}: any = {},
|
}: any = {},
|
||||||
auth: any = {},
|
auth: any = {},
|
||||||
validSchemaController: SchemaController.SchemaController
|
validSchemaController: SchemaController.SchemaController
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const isMaster = acl === undefined;
|
const isMaster = acl === undefined;
|
||||||
const aclGroup = acl || [];
|
const aclGroup = acl || [];
|
||||||
|
|
||||||
op =
|
op =
|
||||||
op ||
|
op ||
|
||||||
(typeof query.objectId == 'string' && Object.keys(query).length === 1
|
(typeof query.objectId == 'string' && Object.keys(query).length === 1
|
||||||
@@ -1333,7 +1334,15 @@ class DatabaseController {
|
|||||||
sort.updatedAt = sort._updated_at;
|
sort.updatedAt = sort._updated_at;
|
||||||
delete sort._updated_at;
|
delete sort._updated_at;
|
||||||
}
|
}
|
||||||
const queryOptions = { skip, limit, sort, keys, readPreference };
|
const queryOptions = {
|
||||||
|
skip,
|
||||||
|
limit,
|
||||||
|
sort,
|
||||||
|
keys,
|
||||||
|
readPreference,
|
||||||
|
hint,
|
||||||
|
explain,
|
||||||
|
};
|
||||||
Object.keys(sort).forEach(fieldName => {
|
Object.keys(sort).forEach(fieldName => {
|
||||||
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
|
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
@@ -1406,7 +1415,9 @@ class DatabaseController {
|
|||||||
className,
|
className,
|
||||||
schema,
|
schema,
|
||||||
query,
|
query,
|
||||||
readPreference
|
readPreference,
|
||||||
|
undefined,
|
||||||
|
hint
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (distinct) {
|
} else if (distinct) {
|
||||||
@@ -1428,9 +1439,18 @@ class DatabaseController {
|
|||||||
className,
|
className,
|
||||||
schema,
|
schema,
|
||||||
pipeline,
|
pipeline,
|
||||||
readPreference
|
readPreference,
|
||||||
|
hint,
|
||||||
|
explain
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if (explain) {
|
||||||
|
return this.adapter.find(
|
||||||
|
className,
|
||||||
|
schema,
|
||||||
|
query,
|
||||||
|
queryOptions
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return this.adapter
|
return this.adapter
|
||||||
.find(className, schema, query, queryOptions)
|
.find(className, schema, query, queryOptions)
|
||||||
|
|||||||
@@ -118,6 +118,8 @@ function RestQuery(
|
|||||||
case 'includeAll':
|
case 'includeAll':
|
||||||
this.includeAll = true;
|
this.includeAll = true;
|
||||||
break;
|
break;
|
||||||
|
case 'explain':
|
||||||
|
case 'hint':
|
||||||
case 'distinct':
|
case 'distinct':
|
||||||
case 'pipeline':
|
case 'pipeline':
|
||||||
case 'skip':
|
case 'skip':
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import * as middleware from '../middlewares';
|
|||||||
import Parse from 'parse/node';
|
import Parse from 'parse/node';
|
||||||
import UsersRouter from './UsersRouter';
|
import UsersRouter from './UsersRouter';
|
||||||
|
|
||||||
const BASE_KEYS = ['where', 'distinct', 'pipeline'];
|
const BASE_KEYS = ['where', 'distinct', 'pipeline', 'hint', 'explain'];
|
||||||
|
|
||||||
const PIPELINE_KEYS = [
|
const PIPELINE_KEYS = [
|
||||||
'addFields',
|
'addFields',
|
||||||
@@ -46,6 +46,14 @@ export class AggregateRouter extends ClassesRouter {
|
|||||||
if (body.distinct) {
|
if (body.distinct) {
|
||||||
options.distinct = String(body.distinct);
|
options.distinct = String(body.distinct);
|
||||||
}
|
}
|
||||||
|
if (body.hint) {
|
||||||
|
options.hint = body.hint;
|
||||||
|
delete body.hint;
|
||||||
|
}
|
||||||
|
if (body.explain) {
|
||||||
|
options.explain = body.explain;
|
||||||
|
delete body.explain;
|
||||||
|
}
|
||||||
options.pipeline = AggregateRouter.getPipeline(body);
|
options.pipeline = AggregateRouter.getPipeline(body);
|
||||||
if (typeof body.where === 'string') {
|
if (typeof body.where === 'string') {
|
||||||
body.where = JSON.parse(body.where);
|
body.where = JSON.parse(body.where);
|
||||||
@@ -96,7 +104,6 @@ export class AggregateRouter extends ClassesRouter {
|
|||||||
*/
|
*/
|
||||||
static getPipeline(body) {
|
static getPipeline(body) {
|
||||||
let pipeline = body.pipeline || body;
|
let pipeline = body.pipeline || body;
|
||||||
|
|
||||||
if (!Array.isArray(pipeline)) {
|
if (!Array.isArray(pipeline)) {
|
||||||
pipeline = Object.keys(pipeline).map(key => {
|
pipeline = Object.keys(pipeline).map(key => {
|
||||||
return { [key]: pipeline[key] };
|
return { [key]: pipeline[key] };
|
||||||
|
|||||||
@@ -173,6 +173,8 @@ export class ClassesRouter extends PromiseRouter {
|
|||||||
'readPreference',
|
'readPreference',
|
||||||
'includeReadPreference',
|
'includeReadPreference',
|
||||||
'subqueryReadPreference',
|
'subqueryReadPreference',
|
||||||
|
'hint',
|
||||||
|
'explain',
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const key of Object.keys(body)) {
|
for (const key of Object.keys(body)) {
|
||||||
@@ -219,6 +221,15 @@ export class ClassesRouter extends PromiseRouter {
|
|||||||
if (typeof body.subqueryReadPreference === 'string') {
|
if (typeof body.subqueryReadPreference === 'string') {
|
||||||
options.subqueryReadPreference = body.subqueryReadPreference;
|
options.subqueryReadPreference = body.subqueryReadPreference;
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
body.hint &&
|
||||||
|
(typeof body.hint === 'string' || typeof body.hint === 'object')
|
||||||
|
) {
|
||||||
|
options.hint = body.hint;
|
||||||
|
}
|
||||||
|
if (body.explain) {
|
||||||
|
options.explain = body.explain;
|
||||||
|
}
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -499,6 +499,10 @@ export function maybeRunQueryTrigger(
|
|||||||
restOptions = restOptions || {};
|
restOptions = restOptions || {};
|
||||||
restOptions.excludeKeys = jsonQuery.excludeKeys;
|
restOptions.excludeKeys = jsonQuery.excludeKeys;
|
||||||
}
|
}
|
||||||
|
if (jsonQuery.explain) {
|
||||||
|
restOptions = restOptions || {};
|
||||||
|
restOptions.explain = jsonQuery.explain;
|
||||||
|
}
|
||||||
if (jsonQuery.keys) {
|
if (jsonQuery.keys) {
|
||||||
restOptions = restOptions || {};
|
restOptions = restOptions || {};
|
||||||
restOptions.keys = jsonQuery.keys;
|
restOptions.keys = jsonQuery.keys;
|
||||||
@@ -507,6 +511,10 @@ export function maybeRunQueryTrigger(
|
|||||||
restOptions = restOptions || {};
|
restOptions = restOptions || {};
|
||||||
restOptions.order = jsonQuery.order;
|
restOptions.order = jsonQuery.order;
|
||||||
}
|
}
|
||||||
|
if (jsonQuery.hint) {
|
||||||
|
restOptions = restOptions || {};
|
||||||
|
restOptions.hint = jsonQuery.hint;
|
||||||
|
}
|
||||||
if (requestObject.readPreference) {
|
if (requestObject.readPreference) {
|
||||||
restOptions = restOptions || {};
|
restOptions = restOptions || {};
|
||||||
restOptions.readPreference = requestObject.readPreference;
|
restOptions.readPreference = requestObject.readPreference;
|
||||||
|
|||||||
Reference in New Issue
Block a user