Giter Club home page Giter Club logo

typeorm-plus's Introduction

Introduction

TypeORM+ is an ORM that can run in NodeJS, Browser, Cordova, PhoneGap, Ionic, React Native, NativeScript, Expo, and Electron platforms and can be used with TypeScript and JavaScript (ES5, ES6, ES7, ES8).

TypeORM vs TypeORM+

TypeORM+ is a fork of TypeORM. TypeORM+ adds functionality to TypeORM intending to make the Repository and QueryBuilder more powerful. Since this is a fork we'll pull in changes from the original TypeORM regularly as they are released.

TypeORM+ is intended to replace TypeORM, so any changes in its interface are documented below.

The soft-delete feature has been merged to the TypeORM. It's recommended to use the TypeORM directly if you don't need the other features.

Differences from TypeORM

  • Soft Deleting
  • Query Scopes
  • Convenient Pagination
  • Conditional Clauses

Installation

  1. Install the npm package:

    yarn add typeorm-plus --save

  2. You need to install reflect-metadata shim, node typing, a database driver and so on. You can read more from here: http://typeorm.io.

Getting Started

After installed the npm packages, you can import modules from "typeorm-plus". But if you are using the third-party modules for TypeORM, such like nestjs/typeorm, you need to install typeorm-plus with the alias name typeorm:

yarn add typeorm@npm:typeorm-plus --save

Soft Deleting

1. Including Soft Deleted Entities

In addition to actually removing records from your database, TypeORM+ supports "soft delete". When entities are soft deleted, they are not actually removed from your database. Instead, an attribute that records the delete time is set on the entity and inserted into the database. If the attribute is a non-null value, the entity has been soft deleted. To enable soft deletes for an entity, use the @DeleteDateColumn on the entity:

import { DeleteDateColumn } from 'typeorm-plus'

export class Entity {

    @DeleteDateColumn({ name: 'deleted_at' })
    public deletedAt: Date

}

2. Applying Soft Delete To QueryBuilder

@DeleteDateColumn is a special column that is automatically set to the entity's delete time each time you call soft-delete of entity manager or repository. You don't need to set this column - it will be automatically set.

import {createConnection} from "typeorm-plus";
import {Entity} from "./entity";

createConnection(/*...*/).then(async connection => {

    await connection
      .getRepository(Entity)
      .createQueryBuilder()
      .softDelete()

    // And You can restore it using restore;
    await connection
      .getRepository(Entity)
      .createQueryBuilder()
      .restore()

}).catch(error => console.log(error));

3. Applying Soft Delete To Repository

import {createConnection} from "typeorm-plus";
import {Entity} from "./entity";

createConnection(/*...*/).then(async connection => {

    const repository = connection.getRepository(Entity);

    // Delete a entity
    await repository.softDelete(1);

    // And You can restore it using restore;
    await repository.restore(1);

    // Or You can soft-delete them using softRemove
    const entities = await repository.find();
    const entitiesAfterSoftRemove = await repository.softRemove(entities);

    // And You can recover them using recover;
    await repository.recover(entitiesAfterSoftRemove);

}).catch(error => console.log(error));

4. Cascading Soft Deletes

This example show what the cascading soft deletes behaves in TypeORM+.

const category1 = new Category();
category1.name = "animals";

const category2 = new Category();
category2.name = "zoo";

const question = new Question();
question.categories = [category1, category2];
const newQuestion =  await connection.manager.save(question);

await connection.manager.softRemove(newQuestion);

As you can see in this example we did not call save or softRemove for category1 and category2. But They will be automatically saved and soft-deleted when the cascade of relation options is set to true like this:

import {Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable} from "typeorm-plus";
import {Category} from "./Category";

@Entity()
export class Question {

    @PrimaryGeneratedColumn()
    id: number;

    @ManyToMany(type => Category, category => category.questions, {
        cascade: true
    })
    @JoinTable()
    categories: Category[];

}

Query Scopes

Query scopes allow you to add constraints to all queries for a given entity. You can register scopes in your entity:

1. Registering Scopes

import { DeleteDateColumn } from 'typeorm-plus'

export class Entity {

    static scope = {
      'default': {
        deletedAt: IsNull()
      },
      'myScope': {
        deletedAt: Not(IsNull())
      }
    }

}

2. Applying Scopes To QueryBuilder

When you are calling queryBuilder, you can also apply the scope.

import {createConnection} from "typeorm-plus";
import {Entity} from "./entity";

createConnection(/*...*/).then(async connection => {

    const repository = connection.getRepository(Entity);
    await repository.createQueryBuilder().setScope("myScope").getMany();

}).catch(error => console.log(error));

The param scope of the setScope function selects scope to apply to the repository. If it is false, none of the scopes will be applied. If it is undefined, the value will be "default".

3. Applying Scopes To Repository

When you are calling repository, you can apply the scope. The scope mode supports these methods of repository: find, findOne, findOneOrFail, countใ€findByIds And findAndCount.

import {createConnection} from "typeorm-plus";
import {Entity} from "./entity";

createConnection(/*...*/).then(async connection => {

    const repository = connection.getRepository(Entity);
    // Delete a entity
    await repository.find({
        scope: 'myScope'
    });

}).catch(error => console.log(error));

The property scope of the find options selects scope to apply to the repository. If it is false, none of the scopes will be applied. If it is undefined, the value will be "default".

4. Working with Soft Delete

TypeORM's own soft delete functionality utilizes global scopes to only pull "non-deleted" entities from the database.

If the @DeleteDateColumn is set, the default scope will be "non-deleted".

Convenient Pagination

TypeORM+'s paginator is integrated with the query builder and repository and provides convenient, easy-to-use pagination of database results out of the box.

1. Paginating Query Builder Results

In this example, the arguments passed to the paginate method is the current page number and the number of items you would like displayed "per page":

import {createConnection} from "typeorm-plus";
import {Entity} from "./entity";

createConnection(/*...*/).then(async connection => {

  await connection
    .getRepository(Entity)
    .createQueryBuilder()
    .paginate(1, 15)
    .getMany();

}).catch(error => console.log(error));

TypeORM+'s paginator also supports raw mode:

import {createConnection} from "typeorm-plus";
import {Entity} from "./entity";

createConnection(/*...*/).then(async connection => {

  await connection
    .getRepository(Entity)
    .createQueryBuilder()
    .paginateRaw(1, 15)
    .getRawMany();

}).catch(error => console.log(error));

2. Paginating Repository Results

You may also paginate queries with the repository.

import {createConnection} from "typeorm-plus";
import {Entity} from "./entity";

createConnection(/*...*/).then(async connection => {

  await connection
    .getRepository(Entity)
    .findAndCount({
      current: 1,
      size: 15
    })

}).catch(error => console.log(error));

The property current of the find options defines an offset page (paginated) where from entities should be taken. And The property size is the alias name for taking, just effected for the conditions that current and size are both defined.

Conditional Clauses

Sometimes you may want clauses to apply to a query only when something else is true. For instance, you may only want to apply a where statement if a given input value is present on the incoming request. You may accomplish this using the when method:

import {createConnection} from "typeorm-plus";
import {Entity} from "./entity";

createConnection(/*...*/).then(async connection => {

  await connection
    .getRepository(Entity)
    .createQueryBuilder("it")
    .when(true, qb => qb.where('it.id = 1'))
    .getMany();

}).catch(error => console.log(error));

The when method only executes the given Closure when the first parameter is true. If the first parameter is false, the Closure will not be executed.

License

TypeORM+ is MIT licensed.

typeorm-plus's People

Contributors

abingoal avatar alexmesser avatar bednic avatar championswimmer avatar chriskalmar avatar daniel-lang avatar eyalhakim avatar hajekj14 avatar hmil avatar iwinston avatar iz-iznogood avatar johannespfeiffer avatar juddling avatar kononnable avatar luchillo17 avatar matt-neal avatar michallytek avatar mingyang91 avatar mojodna avatar mrkmg avatar pleerock avatar pravdomil avatar rhymmor avatar rustamwin avatar salimlou avatar tjme avatar typokign avatar v1d3rm3 avatar webleaf avatar yehezkielsaputra avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

typeorm-plus's Issues

SoftDelete doesnt contain entity in subscriber events

Issue type:

[ ] question
[X ] bug report
[ ] feature request
[ ] documentation issue

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[ ] mysql / mariadb
[ ] oracle
[ X] postgres
[ ] cockroachdb
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo

TypeORM version:

[X ] latest
[ ] @next
[ ] 0.x.x (or put your version here)

Steps to reproduce or a small repository showing the problem:

Hi Im using softDelete functionality of your library but Im having problem when used with Subscribers, I cannot get the entity model that was updated ??

import { EntitySubscriberInterface, EventSubscriber, InsertEvent, UpdateEvent, RemoveEvent } from 'typeorm';
import { Subscriber } from './subscriber.entity';

@EventSubscriber()
export class RevisionSubscriber {
    /**
     * Indicates that this subscriber only listen to User events.
     */
    listenTo() {
        return Subscriber;
    }
    /**
     * Called after User update.
     */
    async afterUpdate(event: UpdateEvent<Subscriber>) {
        console.log('Eventtttt ');
        console.log(event.databaseEntity); // this is undefined
        console.log(event.entity)// this is also undefined
    }


    async beforeUpdate(event) {
        console.log('Eventtttt ');
        console.log(event.databaseEntity); // this is undefined
        console.log(event.entity)// this is also undefined
    }


}

Getting deleted posts when use relation.

Issue type:

[x] question
[x] bug report
[ ] feature request
[ ] documentation issue

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[x] mysql / mariadb
[ ] oracle
[ ] postgres
[ ] cockroachdb
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo

TypeORM version:

[x] latest
[ ] @next
[ ] 0.x.x (or put your version here)

Steps to reproduce or a small repository showing the problem:

I'm running this code and getting the reponse with deleted posts:

Find

const userRepository = await this.userRepository.findOne({
      where: {
        id: id,
      },
      relations: ['posts'],
    });

Response

{
  "id": 4,
  "name": "Michael Jordan",
  "createdAt": "2020-09-26T23:07:13.000Z",
  "updatedAt": "2020-09-26T23:07:13.000Z",
  "deletedAt": null,
  "posts": [
    {
      "id": 5,
      "name": "Post One",
      "createdAt": "2020-09-26T23:07:45.000Z",
      "updatedAt": "2020-09-26T23:08:27.000Z",
      "deletedAt": "2020-09-26T23:08:27.000Z"
    },
    {
      "id": 6,
      "name": "Post Two",
      "createdAt": "2020-09-26T23:14:09.000Z",
      "updatedAt": "2020-09-26T23:14:20.000Z",
      "deletedAt": null,
    }
  ]
}

How can i filter to get only not deleted posts?

Amazing

Issue type:

[x] question
[ ] bug report
[ ] feature request
[ ] documentation issue

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[x] mysql / mariadb
[ ] oracle
[ ] postgres
[ ] cockroachdb
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo

TypeORM version:

[x] latest
[ ] @next
[ ] 0.x.x (or put your version here)

Steps to reproduce or a small repository showing the problem:

I loved this initiative! I had some issues with TypeORM maintainer, as seen at typeorm/typeorm#4686.

I've started a fork seeking to fix some of the troubles I noted there, but it's kind sad to be alone. Now that I have found you, would you mind if I participate here? Let's join forces to grow this project, what do you thing?

I already have a project in production, circa 4k-6k simultaneous access sometimes during a day. I am willing to replace typeorm by typeorm-plus there, could be a showcase.

@iWinston congrats

Implement boolean soft deletes?

Issue type:

[ ] question
[ ] bug report
[x] feature request
[ ] documentation issue

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[ ] mysql / mariadb
[ ] oracle
[x] postgres
[ ] cockroachdb
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo

TypeORM version:

[x] latest
[ ] @next
[ ] 0.x.x (or put your version here)

I'd like to be able to use a decorator similar to @DeleteDateColumn (e.g. @DeleteColumn) which provides the same soft delete functionality when querying entities, but implemented with boolean type instead of Date.

This would be useful in the case where the typescript code doesn't manage the schema of the database, and the managing system uses booleans to implement soft deletes (e.g. Hibernate ORM in Java).

Soft delete for relations

Issue type:

[ ] question
[x] bug report
[ ] feature request
[ ] documentation issue

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[x] mysql / mariadb
[ ] oracle
[ ] postgres
[ ] cockroachdb
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo

TypeORM version:

[x] latest
[ ] @next
[ ] 0.x.x (or put your version here)

Steps to reproduce or a small repository showing the problem:

The soft delete scope of filtering out entries which have a deletedAt column set does not get applied to relations - eg:

this.wishlistRepository.findOne({
  id: wishlistItemId,
}, {
  relations: ['owner']
})

The SQL generated correctly filters based on the deletedAt column in the wishlist entity but doesn't also get pushed down into the join on the ownerentity

Preserving TypeORM whitespace rules for simpler rebasing

Issue type:

[x] question
[ ] bug report
[ ] feature request
[ ] documentation issue

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[ ] mysql / mariadb
[ ] oracle
[x] postgres
[ ] cockroachdb
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo

TypeORM version:

[x] latest
[ ] @next
[ ] 0.x.x (or put your version here)

Steps to reproduce or a small repository showing the problem:

Hey, love the project - been looking for soft deletes in TypeORM for a while!

I noticed that you're not preserving the whitespace rules that TypeORM uses so your diffs are really large as you make your customizations. Given you'll need to continuously pull in the latest from TypeORM , does It make sense to obey their whitespace rules by shutting auto-formatters off so that rebasing will be easier?

Also, are you planning on maintaining this project long-term?

Thanks!

`findOne` with a scope option

Issue type:

[ ] question
[x] bug report
[ ] feature request
[ ] documentation issue

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[x] mysql / mariadb
[ ] oracle
[ ] postgres
[ ] cockroachdb
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo

TypeORM version:

[x] latest
[ ] @next
[ ] 0.x.x (or put your version here)

Steps to reproduce or a small repository showing the problem:

Hi. I tried to get a deleted entity from the database. But it seems that findOne with scope doesn't work.

const deleteId = product.id;
// some codes for deleting the product...
// ...

const deletedProduct = await getRepository(Product).findOne(deleteId, {scope: 'deleted'});
expect(deletedProduct).toBeNull(); // because `deletedProduct` is undefined.

The query shows that the scope option is not properly applied to the query builder. (AND Product.deletedAt IS NULL)

    SELECT DISTINCT `distinctAlias`.`Product_id` as "ids_Product_id" FROM (SELECT `Product`.`id` AS `Product_id`, `Product`.`name` AS `Product_name`, `Product`.`description` AS `Product_description`, `Product`.`price` AS `Product_price`, `Product`.`detailLink` AS `Product_detailLink`, `Product`.`brandName` AS `Product_brandName`, `Product`.`imageLinks` AS `Product_imageLinks`, `Product`.`createdAt` AS `Product_createdAt`, `Product`.`updatedAt` AS `Product_updatedAt`, `Product`.`deletedAt` AS `Product_deletedAt`, `Product_brand`.`name` AS `Product_brand_name`, `Product_brand`.`url` AS `Product_brand_url`, `Product_brand`.`createdAt` AS `Product_brand_createdAt`, `Product_hashtags`.`name` AS `Product_hashtags_name`, `Product_hashtags`.`createdAt` AS `Product_hashtags_createdAt` FROM `product` `Product` LEFT JOIN `brand` `Product_brand` ON `Product_brand`.`name`=`Product`.`brandName`  LEFT JOIN `product_hashtags_hashtag` `Product_Product_hashtags` ON `Product_Product_hashtags`.`productId`=`Product`.`id` LEFT JOIN `hashtag` `Product_hashtags` ON `Product_hashtags`.`name`=`Product_Product_hashtags`.`hashtagName` WHERE `Product`.`id` IN ("1993bf98-e902-41cc-989c-d871ca0adbf4") AND `Product`.`deletedAt` IS NULL) `distinctAlias` ORDER BY `Product_id` ASC LIMIT 1

Here is my entity class

@Entity()
export class Product extends BaseEntity {
  public static scope = {
    deleted: {
      deletedAt: Not(IsNull()),
    },
  };

//some codes...
}

I don't know why the find works as expected but findOne doesn't.

[Feature Request] Polymorphism

I come from laravel background which has pretty nice ways to handle polymorphism. My use cases are going to be having comments, status, bookmarks that could apply for multiple things.. like you could bookmark a user, comment, or post.

It'd be nice to just have some reusable decorators to get this out of the box. Since soft deletes and query scopes are very familiar to laravel, seems maybe polymorphism could be something this package could add to typeorm and make it closer to eloquent.

Issue type:

[ ] question
[ ] bug report
[x] feature request
[ ] documentation issue

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[x] mysql / mariadb
[ ] oracle
[x] postgres
[ ] cockroachdb
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo

TypeORM version:

[x] latest
[ ] @next
[ ] 0.x.x (or put your version here)

Steps to reproduce or a small repository showing the problem:

Is it possible to apply multiple where clauses without overring previous one?

Issue type:

[*] question
[ ] bug report
[ ] feature request
[ ] documentation issue

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[ ] mysql / mariadb
[ ] oracle
[*] postgres
[ ] cockroachdb
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo

TypeORM version:

[*] latest
[ ] @next
[ ] 0.x.x (or put your version here)

TypeORM's original query builder requires andWhere or orWhere after the first where call. Otherwise it overrides existing where condition. The first where call can differ depending on the request parameters so it's hard to keep track of the where calls (which one is first, which one is second etc...).

Cascade softRemove on relations

Issue type:

[x] question
[ ] bug report
[ ] feature request
[ ] documentation issue

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[ ] mysql / mariadb
[ ] oracle
[x] postgres
[ ] cockroachdb
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo

TypeORM version:

[x] latest
[ ] @next
[ ] 0.x.x (or put your version here)

Steps to reproduce or a small repository showing the problem:

I have an issue with a cascade softRemove on relations. This is the entry for a Client table

  @Field(_ => [Patient])
  @OneToMany(
    _ => Patient,
    patient => patient.owner,
    { lazy: true, cascade: true  },
  )
  public patients: Lazy<Patient[]>

So when i do softRemove on a client entry it should do softRemove on all patients. This is how I do softRemove on my service:

  async removeById(id: string) {
    const client = await this.getById(id)
    return this.clientRepository.softRemove(client)
  }

It soft removed a client correctly however all patients where client is the owner are not soft removed. Could you point where the error is? I have even tried this but it also didn't worked:

  @Field(_ => [Patient])
  @OneToMany(
    _ => Patient,
    patient => patient.owner,
    { lazy: true, cascade: true, onDelete: 'CASCADE'  },
  )
  public patients: Lazy<Patient[]>

or even on the Patient table which also didn't worked

  @Field(_ => Client)
  @ManyToOne(_ => Client, { lazy: true, cascade: true })
  public owner: Lazy<Client>

details of how soft delete works

Issue type:

[x] question
[ ] bug report
[ ] feature request
[ ] documentation issue

hi I was just taking a look at the diff here: iWinston:00d46e...master

curious why you deleted the package.lock file?

also just to verify my understanding - should softRemove work with cascades?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.