Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: task 521; long response time of filters #167

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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": "unique-marketplace-backend",
"version": "3.0.241",
"version": "3.0.243",
"description": "Backend project for unique marketplace",
"author": "Unique Network",
"private": true,
Expand Down
7 changes: 7 additions & 0 deletions packages/common/modules/config/load.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ export function loadFileStorageConfig(): FileStorageConfig {
};
}

const createAppCacheConfig = () => {
return {
offersAttributesTtlMs: +process.env.OFFERS_ATTRIBUTES_TTL_MS || 3_600_000,
};
};

export const loadConfig = (): Config => ({
environment: process.env.ENVIRONMENT || 'development',
port: parseInt(process.env.PORT, 10) || 3000,
Expand All @@ -30,6 +36,7 @@ export const loadConfig = (): Config => ({
signatureKey: process.env.SIGNATURE_KEY || '', // Sign and Verify key (sign the following data)

cache: createCacheConfig(process.env),
appCache: createAppCacheConfig(),

releaseVersion: process.env.npm_package_version,

Expand Down
5 changes: 5 additions & 0 deletions packages/common/modules/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export interface FileStorageConfig {
secretKey: string;
}

export type AppCacheConfig = {
offersAttributesTtlMs: number;
};

export type Config = {
environment: string;
port: number;
Expand All @@ -16,6 +20,7 @@ export type Config = {

market: MarketSwaggerOptions;
cache: CacheConfig;
appCache: AppCacheConfig;

signer?: SignerConfig;
signatureKey?: string;
Expand Down
5 changes: 4 additions & 1 deletion packages/market/src/offers/dto/offers.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,13 @@ export class OffersResultDto {
itemsCount: number;
@ApiProperty({ required: false })
items: OfferEntityDto[];
}

export class OffersAttributesResultDto {
@ApiProperty({ required: false, type: 'array' })
attributes?: Array<any>;
@ApiProperty({ required: false, type: 'array' })
attributesCount?: Array<any>;
counts?: Array<any>;
}

export class OfferAttributes {
Expand Down
11 changes: 10 additions & 1 deletion packages/market/src/offers/offers.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ApiOperation, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger';
import { OffersService } from './offers.service';
import { readApiDocs } from '../utils/utils';
import { PaginationRouting } from '@app/common/src/lib/base.constants';
import { OfferEntityDto, OffersFilter, OffersResultDto } from './dto/offers.dto';
import { OfferEntityDto, OffersAttributesResultDto, OffersFilter, OffersResultDto } from './dto/offers.dto';
import { ParseOffersFilterPipe } from './pipes/offer-filter.pipe';
import { SortingOfferRequest } from '@app/common/modules/types/requests';
import { ParseSortFilterPipe } from './pipes/sort-order.pipe';
Expand Down Expand Up @@ -32,6 +32,15 @@ export class OffersController {
return await this.offersService.getOffers(offerFilter, pagination, sortFilter);
}

@Get('/attributes')
@ApiOperation({
summary: 'Get offers attributes',
})
@ApiResponse({ type: OffersAttributesResultDto, status: HttpStatus.OK })
async getAttributes() {
return await this.offersService.getOfferAttributes();
}

@Get(':id')
@ApiOperation({
summary: 'Get offer by ID',
Expand Down
20 changes: 18 additions & 2 deletions packages/market/src/offers/offers.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export class OffersService extends BaseService<OfferEntity, OffersDto> {

items = this.parseItems(offers.items, propertiesFilter, collections) as any as Array<ViewOffers>;
} catch (e) {
console.log(e);
this.logger.error(e.message);

throw new BadRequestException({
Expand All @@ -60,11 +61,26 @@ export class OffersService extends BaseService<OfferEntity, OffersDto> {
return {
...offers.meta,
items: items.map(OfferEntityDto.fromOffersEntity),
attributes: offers.attributes as Array<TraitDto>,
attributesCount: offers.attributesCount,
};
}

async getOfferAttributes(): Promise<any> {
let attributes;
try {
attributes = await this.viewOffersService.getAttributes();
} catch (e) {
this.logger.error(e.message);

throw new BadRequestException({
statusCode: HttpStatus.BAD_REQUEST,
message: 'Something went wrong!',
error: e.message,
});
}

return attributes;
}

/**
* Show one selected offer
* @param { collectionId: number; tokenId: number } filter - The ID of the collection and token
Expand Down
48 changes: 39 additions & 9 deletions packages/market/src/offers/view-offers.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BadRequestException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { BadRequestException, CACHE_MANAGER, HttpStatus, Inject, Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { OfferEntity, ViewOffers } from '@app/common/modules/database';
import { DataSource, Repository, SelectQueryBuilder, ValueTransformer } from 'typeorm';
Expand All @@ -11,6 +11,9 @@ import { GetOneFilter, SortingOrder, SortingParameter } from './interfaces/offer
import { HelperService } from '@app/common/src/lib/helper.service';
import { PaginationRouting } from '@app/common/src/lib/base.constants';
import { SortingOfferRequest } from '@app/common/modules/types/requests';
import { Cache } from 'cache-manager';
import { ConfigService } from '@nestjs/config';
import { AppCacheConfig } from '@app/common/modules/config/types';

const offersMapping = {
priceRaw: 'price_raw',
Expand All @@ -36,13 +39,18 @@ const priceTransformer: ValueTransformer = {
export class ViewOffersService {
private logger = new Logger(ViewOffersService.name);
private readonly offersSorts: Record<string, string>;
private appCache: AppCacheConfig;

constructor(
configService: ConfigService,
private connection: DataSource,
@InjectRepository(ViewOffers) private viewOffersRepository: Repository<ViewOffers>,
private readonly bundleService: BundleService,
@Inject(CACHE_MANAGER)
private readonly cache: Cache,
) {
this.offersSorts = this.prepareMapping(offersMapping, connection.getMetadata(OfferEntity).columns);
this.appCache = configService.getOrThrow<AppCacheConfig>('appCache');
}

prepareMapping = (input: Record<string, string>, columnMetadata: ColumnMetadata[]): Record<string, string> => {
Expand Down Expand Up @@ -166,39 +174,61 @@ export class ViewOffersService {
return this.bundleService.bundle(collectionId, tokenId);
}

public async getAttributes(): Promise<any> {
let response = await this.cache.get('offers-attributes');

if (response) {
return response;
} else {
const queryFilter = this.viewOffersRepository.createQueryBuilder('view_offers');
const counts = await this.byAttributesCount(queryFilter);
const attributes = await this.byAttributes(queryFilter).getRawMany();
const attributesParsed = this.parseAttributes(attributes);

response = {
counts,
attributes: attributesParsed,
};

await this.cache.set('offers-attributes', response, this.appCache.offersAttributesTtlMs);

return response;
}
}

public async filter(offersFilter: OffersFilter, pagination: PaginationRouting, sort: SortingOfferRequest): Promise<any> {
let queryFilter = this.viewOffersRepository.createQueryBuilder('view_offers');

// Filert by collection id
queryFilter = this.byCollectionId(queryFilter, offersFilter.collectionId);

// Filter by max price
queryFilter = this.byMaxPrice(queryFilter, offersFilter.maxPrice);

// Filter by min price
queryFilter = this.byMinPrice(queryFilter, offersFilter.minPrice);

// Filter by seller address
queryFilter = this.bySeller(queryFilter, offersFilter.seller);

// Filter by search
queryFilter = this.bySearch(queryFilter, offersFilter.searchText, offersFilter.searchLocale);

// Filter by traits
queryFilter = this.byFindAttributes(queryFilter, offersFilter.collectionId, offersFilter.attributes);
// Does not contain a search by the number of attributes
const attributesCount = await this.byAttributesCount(queryFilter);

// Exceptions to the influence of the search by the number of attributes
queryFilter = this.byNumberOfAttributes(queryFilter, offersFilter.numberOfAttributes);

const attributes = await this.byAttributes(queryFilter).getRawMany();

queryFilter = this.prepareQuery(queryFilter);

queryFilter = this.sortBy(queryFilter, sort);
//

const itemQuery = await paginateRaw(queryFilter, pagination);
//

return {
meta: itemQuery.meta,
items: itemQuery.items,
attributes: this.parseAttributes(attributes),
attributesCount,
};
}

Expand Down
Loading