Advertisement
aldikhan13

custom builder sequelize new

May 13th, 2025
449
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
TypeScript 10.23 KB | Source Code | 0 0
  1. import { Injectable } from '@nestjs/common'
  2. import sequelize, {
  3.   FindOptions,
  4.   Transaction,
  5.   TransactionOptions,
  6.   Optional,
  7.   WhereOptions,
  8.   Includeable,
  9.   Op,
  10.   IncludeOptions,
  11.   CreateOptions,
  12.   UpdateOptions,
  13.   DestroyOptions,
  14.   FindAttributeOptions,
  15. } from 'sequelize'
  16. import { Repository, Sequelize, Model } from 'sequelize-typescript'
  17. import { MakeNullishOptional, NullishPropertiesOf } from 'sequelize/lib/utils'
  18. import { EPGRelationAction } from '~/domain/enums/common.enum'
  19. export { InjectModel as InjectRepository } from '@nestjs/sequelize'
  20. export { Repository } from 'sequelize-typescript'
  21.  
  22. import { response } from '~/infrastructure/common/helpers/response.helper'
  23.  
  24. /**
  25.  * A service for handling Sequelize transactions and common operations.
  26.  */
  27. @Injectable()
  28. export class SequelizeService {
  29.   constructor(private sequelize: Sequelize) {}
  30.  
  31.   /**
  32.    * Executes a database transaction.
  33.    * @param operation The operation to perform within the transaction.
  34.    * @param options Transaction options.
  35.    * @returns The result of the operation, or null if an error occurs.
  36.    */
  37.   async transaction<T = any>(
  38.     operation: (transaction: Transaction) => Promise<T>,
  39.     options?: TransactionOptions,
  40.   ): Promise<T | null> {
  41.     try {
  42.       const transaction = await this.sequelize.transaction({ autocommit: false, ...options })
  43.       try {
  44.         const result: T = await operation(transaction)
  45.         await transaction.commit()
  46.         return result
  47.       } catch (e: any) {
  48.         await transaction.rollback()
  49.         response(e) // Assuming response logs or handles the error
  50.         return null
  51.       }
  52.     } catch (e: any) {
  53.       response(e)
  54.       return null
  55.     }
  56.   }
  57.  
  58.   /**
  59.    * Finds a record and updates it if found.
  60.    * @param repository The repository to use.
  61.    * @param data The data to update the record with.
  62.    * @param options The find options.
  63.    * @returns The updated record's data values, or null if not found or an error occurs.
  64.    */
  65.   async findOneAndUpdate<T = any>(
  66.     repository: Repository<Model<T, T>>,
  67.     data: Optional<T, NullishPropertiesOf<T>>,
  68.     options: FindOptions<T>,
  69.   ): Promise<T | null> {
  70.     try {
  71.       const repositoryModel: Model<T, T> | null = await repository.findOne(options)
  72.       if (!repositoryModel) return null
  73.  
  74.       repositoryModel.set(data) // Use set for safer attribute updates
  75.       const updatedModel = await repositoryModel.save(options)
  76.       return updatedModel?.dataValues || null
  77.     } catch (e: any) {
  78.       response(e)
  79.       return null
  80.     }
  81.   }
  82. }
  83.  
  84. /**
  85.  * A fluent query builder for constructing Sequelize queries.
  86.  */
  87. export class SequelizeQueryBuilder<M extends Model> {
  88.   private model: Repository<M>
  89.   private options: FindOptions<M>
  90.  
  91.   constructor(model: Repository<M>) {
  92.     this.model = model
  93.     this.options = { where: {}, attributes: { include: [], exclude: [] } }
  94.   }
  95.  
  96.   /**
  97.    * Specifies columns to include or exclude in the query.
  98.    * @param columns Columns to select.
  99.    */
  100.   select(columns: FindAttributeOptions): this {
  101.     this.options.attributes = columns
  102.     return this
  103.   }
  104.  
  105.   /**
  106.    * Sets the WHERE condition for the query.
  107.    * @param condition The where condition.
  108.    */
  109.   where(condition: WhereOptions<M>): this {
  110.     this.options.where = condition
  111.     return this
  112.   }
  113.  
  114.   /**
  115.    * Adds an AND condition to the existing WHERE clause.
  116.    * @param condition The condition to add.
  117.    */
  118.   andWhere(condition: WhereOptions<M>): this {
  119.     if (!this.options.where) {
  120.       this.options.where = {}
  121.     }
  122.     Object.assign(this.options.where, condition)
  123.     return this
  124.   }
  125.  
  126.   /**
  127.    * Adds an OR condition to the existing WHERE clause.
  128.    * @param condition The condition to add.
  129.    */
  130.   orWhere(condition: WhereOptions<M>): this {
  131.     if (!this.options.where[Op.or]) {
  132.       this.options.where[Op.or] = []
  133.     }
  134.     this.options.where[Op.or].push(condition)
  135.     return this
  136.   }
  137.  
  138.   /**
  139.    * Adds an OR WHERE IN condition.
  140.    * @param field The field to check.
  141.    * @param values The values to match.
  142.    */
  143.   orWhereIn<T = any>(field: keyof M, values: T[]): this {
  144.     if (!this.options.where[Op.or]) {
  145.       this.options.where[Op.or] = []
  146.     }
  147.     this.options.where[Op.or].push({ [field]: { [Op.in]: values } })
  148.     return this
  149.   }
  150.  
  151.   /**
  152.    * Adds an AND WHERE IN condition.
  153.    * @param field The field to check.
  154.    * @param values The values to match.
  155.    */
  156.   andWhereIn<T = any>(field: keyof M, values: T[]): this {
  157.     this.options.where[String(field)] = { [Op.in]: values }
  158.     return this
  159.   }
  160.  
  161.   /**
  162.    * Adds a NOT condition to the WHERE clause.
  163.    * @param condition The condition to negate.
  164.    */
  165.   whereNot(condition: WhereOptions<M>): this {
  166.     this.options.where[Op.not] = condition
  167.     return this
  168.   }
  169.  
  170.   /**
  171.    * Adds a WHERE NOT IN condition.
  172.    * @param field The field to check.
  173.    * @param values The values to exclude.
  174.    */
  175.   whereNotIn<T = any>(field: keyof M, values: T[]): this {
  176.     this.options.where[String(field)] = { [Op.notIn]: values }
  177.     return this
  178.   }
  179.  
  180.   /**
  181.    * Adds a WHERE IN condition.
  182.    * @param field The field to check.
  183.    * @param values The values to match.
  184.    */
  185.   whereIn<T = any>(field: keyof M, values: T[]): this {
  186.     this.options.where[String(field)] = { [Op.in]: values }
  187.     return this
  188.   }
  189.  
  190.   /**
  191.    * Sets the order of the results.
  192.    * @param field The field to order by.
  193.    * @param direction The sort direction (ASC or DESC).
  194.    */
  195.   orderBy(field: keyof M, direction: 'ASC' | 'DESC' = 'ASC'): this {
  196.     this.options.order = [[String(field), direction]]
  197.     return this
  198.   }
  199.  
  200.   /**
  201.    * Limits the number of results.
  202.    * @param limit The maximum number of records to return.
  203.    */
  204.   limit(limit: number): this {
  205.     this.options.limit = limit
  206.     return this
  207.   }
  208.  
  209.   /**
  210.    * Sets the offset for pagination.
  211.    * @param offset The number of records to skip.
  212.    */
  213.   offset(offset: number): this {
  214.     this.options.offset = offset
  215.     return this
  216.   }
  217.  
  218.   /**
  219.    * Includes related models (eager loading).
  220.    * @param include The relations to include.
  221.    */
  222.   include(include: Includeable | Includeable[]): this {
  223.     this.options.include = Array.isArray(include) ? include : [include]
  224.     return this
  225.   }
  226.  
  227.   /**
  228.    * Configures a join with specified action (INNER, LEFT, RIGHT).
  229.    * @param options The include options for the join.
  230.    * @param action The type of join.
  231.    * @param alias Optional alias for the relation.
  232.    */
  233.   join(options: IncludeOptions | IncludeOptions[], action: EPGRelationAction, alias?: string): this {
  234.     if (Array.isArray(options)) {
  235.       options = options.map((option: IncludeOptions) => {
  236.         if (alias) option.as = alias
  237.         if (action === EPGRelationAction.INNER) option.required = true
  238.         else if (action === EPGRelationAction.LEFT) option.required = false
  239.         else if (action === EPGRelationAction.RIGH) {
  240.           option.required = true
  241.           option.right = true
  242.         }
  243.         return option
  244.       })
  245.     } else {
  246.       if (alias) options.as = alias
  247.       if (action === EPGRelationAction.INNER) options.required = true
  248.       else if (action === EPGRelationAction.LEFT) options.required = false
  249.       else if (action === EPGRelationAction.RIGH) {
  250.         options.required = true
  251.         options.right = true
  252.       }
  253.     }
  254.     this.include(options)
  255.     return this
  256.   }
  257.  
  258.   /**
  259.    * Inserts a new record.
  260.    * @param values The data to insert.
  261.    * @param options Creation options.
  262.    */
  263.   insert(values: MakeNullishOptional<M>, options?: CreateOptions): Promise<M> {
  264.     return this.model.create(values, options)
  265.   }
  266.  
  267.   /**
  268.    * Updates records matching the query.
  269.    * @param values The data to update.
  270.    * @param options Update options.
  271.    */
  272.   update(
  273.     values: Partial<M>,
  274.     options?: UpdateOptions & { returning?: boolean | Array<keyof M> },
  275.   ): Promise<[number, M[]]> {
  276.     return this.model.update(values, { ...options, where: this.options.where, returning: options?.returning || true })
  277.   }
  278.  
  279.   /**
  280.    * Deletes records matching the query.
  281.    * @param options Destroy options.
  282.    */
  283.   delete(options?: DestroyOptions & { returning?: boolean | Array<keyof M> }): Promise<number> {
  284.     return this.model.destroy({ ...options, where: this.options.where })
  285.   }
  286.  
  287.   /**
  288.    * Adds a LIKE condition.
  289.    * @param field The field to match.
  290.    * @param value The pattern to search for.
  291.    */
  292.   like(field: keyof M, value: string): this {
  293.     this.options.where = sequelize.where(sequelize.literal(`${String(field)}::text`), {
  294.       [Op.iLike]: `%${value}%`,
  295.     })
  296.     return this
  297.   }
  298.  
  299.   /**
  300.    * Adds an OR LIKE condition.
  301.    * @param field The field to match.
  302.    * @param value The pattern to search for.
  303.    */
  304.   orLike(field: keyof M, value: string): this {
  305.     if (!this.options.where[Op.or]) this.options.where[Op.or] = []
  306.     this.options.where[Op.or].push(
  307.       sequelize.where(sequelize.literal(`${String(field)}::text`), { [Op.iLike]: `%${value}%` }),
  308.     )
  309.     return this
  310.   }
  311.  
  312.   /**
  313.    * Adds an AND LIKE condition.
  314.    * @param field The field to match.
  315.    * @param value The pattern to search for.
  316.    */
  317.   andLike(field: keyof M, value: string): this {
  318.     if (!this.options.where) this.options.where = {}
  319.     this.options.where = sequelize.where(sequelize.literal(`${String(field)}::text`), {
  320.       [Op.iLike]: `%${value}%`,
  321.     })
  322.     return this
  323.   }
  324.  
  325.   /**
  326.    * Retrieves one record matching the query.
  327.    */
  328.   getOne(): Promise<M | null> {
  329.     return this.model.findOne({ ...this.options, raw: false })
  330.   }
  331.  
  332.   /**
  333.    * Retrieves all records matching the query.
  334.    */
  335.   getMany(): Promise<M[]> {
  336.     return this.model.findAll({ ...this.options, raw: false })
  337.   }
  338.  
  339.   /**
  340.    * Retrieves one raw record matching the query.
  341.    */
  342.   getOneRaw(): Promise<M | null> {
  343.     return this.model.findOne({ ...this.options, raw: true })
  344.   }
  345.  
  346.   /**
  347.    * Retrieves all raw records matching the query.
  348.    */
  349.   getManyRaw(): Promise<M[]> {
  350.     return this.model.findAll({ ...this.options, raw: true })
  351.   }
  352.  
  353.   /**
  354.    * Counts the number of records matching the query.
  355.    */
  356.   getCount(): Promise<number> {
  357.     delete this.options.attributes
  358.     return this.model.count(this.options)
  359.   }
  360. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement