arrest
Version:
OpenAPI v3 compliant REST framework for Node.js, with support for MongoDB and JSON-Schema
190 lines • 7.26 kB
JavaScript
import _ from 'lodash';
import rql from '../rql.js';
import { addConstraint } from '../util.js';
import { MongoOperation } from './base.js';
export class QueryMongoOperation extends MongoOperation {
constructor(resource, path, method, id = 'query') {
super(resource, path, method, id);
}
getCustomInfo() {
return {
summary: `Retrieve a list of ${this.resource.info.namePlural}`,
parameters: [
{
$ref: '#/components/parameters/limit',
},
{
$ref: '#/components/parameters/skip',
},
{
$ref: '#/components/parameters/fields',
},
{
$ref: '#/components/parameters/sort',
},
{
$ref: '#/components/parameters/query',
},
{
$ref: '#/components/parameters/format',
},
{
$ref: '#/components/parameters/csvFields',
},
{
$ref: '#/components/parameters/csvOptions',
},
{
$ref: '#/components/parameters/csvNames',
},
],
responses: {
'200': {
description: `List of matching ${this.resource.info.namePlural}`,
content: {
'application/json': {
schema: {
type: 'array',
items: this.responseSchema,
},
},
'text/csv': {
schema: {
type: 'string',
},
},
},
headers: {
Link: {
description: 'Data pagination links, as described in RFC5988. Currently only rel=next is supported',
schema: {
type: 'string',
},
},
'Results-Matching': {
description: 'Total number of resources matching the query',
schema: {
type: 'integer',
minimum: 0,
},
},
'Results-Skipped': {
description: 'Number of resources skipped to return the current batch of resources',
schema: {
type: 'integer',
minimum: 0,
},
},
},
},
},
};
}
async prepareQuery(job) {
job = await super.prepareQuery(job);
if (job.req.query.q) {
job.query = addConstraint(job.query, rql({}, job.opts, job.req.query.q, this.resource.info.idIsObjectId ? this.resource.info.id : undefined));
}
return job;
}
async prepareOpts(job) {
if (typeof job.req.query.limit !== 'undefined') {
job.opts.limit = job.req.query.limit;
}
if ((this.resource.info.queryLimit || 0) > 0 && !(job.opts.limit < this.resource.info.queryLimit)) {
job.opts.limit = this.resource.info.queryLimit;
}
if (typeof job.req.query.skip !== 'undefined') {
job.opts.skip = job.req.query.skip;
}
job.opts.projection = this.parseFields(job.req.query.fields);
if (job.req.query.sort) {
job.opts.sort = job.req.query.sort;
}
if (job.opts.sort) {
job.opts.sort = _.reduce(job.opts.sort, function (o, i) {
if (i[0] === '-') {
o[i.substr(1)] = -1;
}
else if (i[0] === '+') {
o[i.substr(1)] = 1;
}
else {
o[i] = 1;
}
return o;
}, {});
}
return job;
}
async runOperation(job) {
let matching;
if (Array.isArray(job.query)) {
if (job.opts.sort)
job.query.push({ $sort: job.opts.sort });
if (job.opts.projection)
job.query.push({ $project: job.opts.projection });
const innerQuery = [{ $match: {} }];
if (job.opts.skip)
innerQuery.push({ $skip: job.opts.skip });
if (job.opts.limit)
innerQuery.push({ $limit: job.opts.limit });
job.query.push({
$facet: {
count: [{ $count: 'count' }],
data: innerQuery,
},
});
let opts = undefined;
if (job.opts.readPreference) {
opts = { readPreference: job.opts.readPreference };
}
const cursor = job.coll.aggregate(job.query, opts);
const rawData = await cursor.toArray();
matching = rawData[0].count[0]?.count || 0;
job.data = rawData[0].data;
job.res.set('Results-Matching', matching + '');
}
else {
const cursor = job.coll.find(job.query);
if (job.opts.limit || job.opts.skip) {
matching = await job.coll.countDocuments(job.query, {});
}
if (job.opts.projection)
cursor.project(job.opts.projection);
if (job.opts.sort)
cursor.sort(job.opts.sort);
if (job.opts.limit)
cursor.limit(job.opts.limit);
if (job.opts.skip)
cursor.skip(job.opts.skip);
if (job.opts.readPreference)
cursor.withReadPreference(job.opts.readPreference);
job.data = await cursor.toArray();
if (typeof matching !== 'number') {
matching = job.data?.length || 0;
}
job.res.set('Results-Matching', matching + '');
}
if (job.opts.skip) {
job.res.set('Results-Skipped', job.opts.skip);
}
if (matching) {
if (job.opts.skip + job.opts.limit < matching) {
const host = `${job.req.protocol}://${job.req.headers['host']}`;
const q = new URL(`${host}${job.req.originalUrl}`).searchParams;
q.set('limit', job.opts.limit);
q.set('skip', job.opts.skip + job.opts.limit);
const fullURL = `${host}${job.req.baseUrl}${job.req.path}/?${q.toString()}`;
job.res.set('Link', '<' + fullURL + '>; rel="next"');
}
}
return job;
}
async redactResult(job) {
job = await super.redactResult(job);
job.data = job.data.filter((i) => Object.keys(i).length > 0);
return job;
}
}
//# sourceMappingURL=query.js.map