File

src/core/interceptors/page.interceptor.ts

Description

Pagination-implementing interceptor

Index

Properties
Methods

Constructor

constructor(httpService: HttpService, cacheManager: Cache)

Injects needed dependencies and instantiates the storage object

Parameters :
Name Type Optional
httpService HttpService No
cacheManager Cache No

Methods

Async deletePIT
deletePIT(pitID: string)

Deletes the PIT specified by provided ID

Parameters :
Name Type Optional Description
pitID string No

, ID of the PIT, that would be deleted

Returns : Promise<boolean>

true/false, depending on the result of deletion of the PIT

Public Async getPIT
getPIT(alive: number, unit: EsTime)

Acquires a PIT ID from Elasticsearch, needed for a request

Parameters :
Name Type Optional Default value Description
alive number No

, amount of time in minutes (defaults to 1). If time unit is not specified - defaults to minutes.

unit EsTime No EsTime.min
Returns : Promise<EsPit>

PIT object containing PIT ID and keep_alive value

Async intercept
intercept(context: ExecutionContext, next: CallHandler)

Override of intercept() method, specified in NestInterceptor interface

Parameters :
Name Type Optional
context ExecutionContext No
next CallHandler<any> No

Page with content and metadata

Properties

Private Readonly ES_IP
Default value : process.env.ES_CONTAINER_NAME

Elastichsearch IP address

Private Readonly ES_PORT
Default value : process.env.ES_PORT

Elastichsearch server port-number

import { HttpService } from "@nestjs/axios";
import { CACHE_MANAGER, CallHandler, ExecutionContext, Inject, Injectable, NestInterceptor } from "@nestjs/common";
import { Observable, map, take, switchMap, of } from "rxjs";
import { PageDto } from "../domain/dtos";
import { EsQueryDto } from "../domain/dtos/elastic/es-query.dto";
import { RequestDto } from "../domain/dtos/request.dto";
import { SearchQueryDto } from "../domain/dtos/search-q.dto";
import { EsTime } from "../domain/enums/es-time.enum";
import { Order, toOrder } from "../domain/enums/page-order.enum";
import { EsPit } from "../domain/interfaces/elastic/es-pit.interface";
import { Cache } from 'cache-manager'
import { PageMetaDto } from "../domain/dtos/page-meta.dto";

/**
 * Pagination-implementing interceptor
 */
@Injectable()
export class PageInterceptor implements NestInterceptor {
    /**
     * Injects needed dependencies and instantiates the storage object
     * @param httpService
     * @param searchService
     */
    constructor(
        private readonly httpService: HttpService,
        @Inject(CACHE_MANAGER) private cacheManager: Cache
    ) {}

    /**
     * Elastichsearch server port-number
     */
    private readonly ES_PORT = process.env.ES_PORT;

    /**
     * Elastichsearch IP address
     */
     private readonly ES_IP = process.env.ES_CONTAINER_NAME;

    /**
     * Override of intercept() method, specified in NestInterceptor interface
     * @param context
     * @param next
     * @returns Page with content and metadata
     */
    async intercept(context: ExecutionContext, next: CallHandler<any>): Promise<Observable<PageDto>> {
        const request: RequestDto = context.switchToHttp().getRequest<RequestDto>();
        const query: SearchQueryDto = request.query;

        const offset = !query.offset ? 0 : query.offset;
        const limit = !query.limit ? 10 : query.limit; 
        const order = !query.order ? Order.DESC : query.order;

        const prev_page = await this.cacheManager.get('prev_page'); 
        if (prev_page) {
            if (offset == prev_page[1] && limit == prev_page[2] && order == prev_page[3]) return of(prev_page[0]);
        }

        // Contruct a body for querying Elasticsearch
        request.es_query = new EsQueryDto();
        request.es_query.query = {
            query_string: {
                query: query.query,
                default_field: 'content',
            }
        };
        request.es_query.from = offset;
        request.es_query.size = limit;

        return next.handle().pipe(
            switchMap(async (res) => {
                // Setting the page meta-data
                let meta: PageMetaDto = {
                    total: res.hits.total.value,
                    order: toOrder(order),
                };

                // Check if the performed search is a backwards search
                let data = res.hits.hits;
                // Omitting the redundant info and leaving only the document
                data = data.map((el) => el._source);
                // Change the order if set
                if (order == Order.ASC) data.reverse();

                // Cache and return the page
                const page: PageDto = new PageDto(data, meta);
                await this.cacheManager.set('prev_page', [page, offset, limit, order]);
                return page;
            })
        );
    }

    /**
     * Acquires a PIT ID from Elasticsearch, needed for a request
     * @param alive, amount of time in minutes (defaults to 1). If time unit is not specified - defaults to minutes.
     * @returns PIT object <EsPit> containing PIT ID and keep_alive value
     */
    public async getPIT(alive: number, unit: EsTime = EsTime.min): Promise<EsPit> {
       return new Promise((resolve, reject) => {
           try {
               this.httpService.post<EsPit>(`http://${this.ES_IP}:${this.ES_PORT}/papers/_pit?keep_alive=${alive+unit}`)
                   .pipe(take(1), map(axiosRes => axiosRes.data))
                   .subscribe((res: EsPit) => {
                       res.keep_alive = alive + unit;
                       resolve(res);
                   });
           } catch (error) {
               reject(error);
           }
       });
    }

    /**
     * Deletes the PIT specified by provided ID
     * @param pitID, ID of the PIT, that would be deleted
     * @returns true/false, depending on the result of deletion of the PIT
     */
    async deletePIT(pitID: string): Promise<boolean> {
       return new Promise((resolve, reject) => {
           try {
               this.httpService.delete(`http://${this.ES_IP}:${this.ES_PORT}/_pit`, {
                   data: { id: pitID },
                   headers: { 'Content-Type': 'application/json' },
               })
               .pipe(take(1), map(axiosRes => axiosRes.data))
               .subscribe((res) => {
                   resolve(res.succeeded);
               });
           } catch (error) {
               reject(error);
           }
       })
    }
}

results matching ""

    No results matching ""