Skip to content

Commit

Permalink
Merge pull request #336 from lenneTech/fix/optimzed-processing-and-ma…
Browse files Browse the repository at this point in the history
…pping

Processing and mapping of database objects optimized
  • Loading branch information
kaihaase authored Nov 19, 2023
2 parents e94ca18 + f022f4e commit 94528cd
Show file tree
Hide file tree
Showing 10 changed files with 38 additions and 45 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lenne.tech/nest-server",
"version": "10.2.0",
"version": "10.2.1",
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
"keywords": [
"node",
Expand Down
2 changes: 1 addition & 1 deletion spectaql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ servers:
info:
title: lT Nest Server
description: Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).
version: 10.2.0
version: 10.2.1
contact:
name: lenne.Tech GmbH
url: https://lenne.tech
Expand Down
20 changes: 16 additions & 4 deletions src/core/common/helpers/db.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,21 +505,33 @@ export async function popAndMap<T extends CoreModel>(

// Map result
if (Array.isArray(result)) {
result = result.map(item => (modelClass as any).map(item));
} else {
result = result.map((item) => {
if (item && typeof item === 'object') {
return (modelClass as any).map(item);
}
return item;
});
} else if (result && typeof result === 'object') {
result = (modelClass as any).map(result);
}

// Process documents
} else {
if (Array.isArray(queryOrDocument)) {
await setPopulates(queryOrDocument, populateOptions, mongooseModel, { ignoreSelections });
result = queryOrDocument.map(item => (modelClass as any).map(item));
result = queryOrDocument.map((item) => {
if (item && typeof item === 'object') {
return (modelClass as any).map(item);
}
return item;
});

// Process document
} else {
await setPopulates(queryOrDocument, populateOptions, mongooseModel, { ignoreSelections });
result = (modelClass as any).map(queryOrDocument);
if (queryOrDocument && typeof queryOrDocument === 'object') {
result = (modelClass as any).map(queryOrDocument);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/core/common/helpers/input.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ export async function check(
}

// Return value if it is only a basic type
if (typeof value !== 'object') {
if (!value || typeof value !== 'object') {
return value;
}

Expand Down
4 changes: 2 additions & 2 deletions src/core/common/helpers/service.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export async function prepareInput<T = any>(
};

// Check input
if (typeof input !== 'object') {
if (!input || typeof input !== 'object') {
return input;
}

Expand Down Expand Up @@ -206,7 +206,7 @@ export async function prepareOutput<T = { [key: string]: any; map: (...args: any
};

// Check output
if (typeof output !== 'object') {
if (!output || typeof output !== 'object') {
return output;
}

Expand Down
2 changes: 1 addition & 1 deletion src/core/common/pipes/map-and-validate.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class MapAndValidatePipe implements PipeTransform {
async transform(value: any, metadata: ArgumentMetadata) {
const { metatype } = metadata;

if (typeof value !== 'object' || !metatype || isBasicType(metatype)) {
if (!value || typeof value !== 'object' || !metatype || isBasicType(metatype)) {
return value;
}

Expand Down
43 changes: 12 additions & 31 deletions src/core/common/services/crud.service.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { NotFoundException } from '@nestjs/common';
import { Document, FilterQuery, Model as MongooseModel, PipelineStage, Query, QueryOptions } from 'mongoose';
import { Document, FilterQuery, PipelineStage, Query, QueryOptions } from 'mongoose';
import { FilterArgs } from '../args/filter.args';
import { getStringIds, popAndMap } from '../helpers/db.helper';
import { getStringIds } from '../helpers/db.helper';
import { convertFilterArgsToQuery } from '../helpers/filter.helper';
import { mergePlain, prepareServiceOptionsForCreate } from '../helpers/input.helper';
import { ServiceOptions } from '../interfaces/service-options.interface';
import { CoreModel } from '../models/core-model.model';
import { ArrayElement } from '../types/array-element.type';
import { FieldSelection } from '../types/field-selection.type';
import { PlainObject } from '../types/plain-object.type';
import { ConfigService } from './config.service';
import { ModuleService } from './module.service';
Expand Down Expand Up @@ -478,38 +476,21 @@ export abstract class CrudService<
}

/**
* Populate, exec and map Mongoose query or document(s) with serviceOptions
* Execute, populate and map Mongoose query or document(s) with serviceOptions
* Generic T is the type of the returned object(s)
*
* @example const user = await this.populateAndProcessQuery<User>(User.findById(id), serviceOptions);
* @example const users = await this.populateAndProcessQuery<User[]>(User.find({name: {'$regex': 'ma'}}), serviceOptions);
* @example const users = await this.populateAndProcessQuery<User[]>(User.find({name: {'$regex': 'ma'}}), {...serviceOptions, populate:['contacts'], force: true});
*/
async populateAndProcessQuery<T extends CoreModel | CoreModel[] = CoreModel>(
async processQueryOrDocument<T extends CoreModel | CoreModel[] = CoreModel>(
queryOrDocument: Query<unknown, unknown> | Document | Document[],
options?: {
fieldSelection?: FieldSelection;
ignoreSelections?: boolean;
modelClass?: new (...args: any[]) => ArrayElement<T>;
populate?: FieldSelection;
mongooseModel?: MongooseModel<any>;
},
serviceOptions?: ServiceOptions,
): Promise<T> {
const config = {
modelClass: this.mainModelConstructor,
mongooseModel: this.mainDbModel,
...options,
};
let result: T;
if (config.fieldSelection || config.populate) {
result = (await popAndMap<ArrayElement<T>>(
queryOrDocument,
config.populate || config.fieldSelection,
config.modelClass as new (...args: any[]) => ArrayElement<T>,
config.mongooseModel)
) as T;
} else if (queryOrDocument instanceof Query) {
result = (await queryOrDocument.exec()) as T;
}
return result;
return this.process(() => {
if (queryOrDocument instanceof Query) {
return queryOrDocument.exec();
}
return queryOrDocument;
}, { serviceOptions });
}
}
2 changes: 1 addition & 1 deletion src/core/modules/auth/core-auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class CoreAuthController {
// Check if cookie handling is activated
if (this.configService.getFastButReadOnly('cookies')) {
// Set cookies
if (typeof result !== 'object') {
if (!result || typeof result !== 'object') {
res.cookie('token', '', { httpOnly: true });
res.cookie('refreshToken', '', { httpOnly: true });
return result;
Expand Down
2 changes: 1 addition & 1 deletion src/core/modules/auth/core-auth.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class CoreAuthResolver {
// Check if cookie handling is activated
if (this.configService.getFastButReadOnly('cookies')) {
// Set cookies
if (typeof result !== 'object') {
if (!result || typeof result !== 'object') {
ctx.res.cookie('token', '', { httpOnly: true });
ctx.res.cookie('refreshToken', '', { httpOnly: true });
return result;
Expand Down

0 comments on commit 94528cd

Please sign in to comment.