typescript 如何使用typeORM创建复杂的嵌套where条件?

oyjwcjzk  于 2023-04-13  发布在  TypeScript
关注(0)|答案(5)|浏览(472)

我有多个嵌套的where条件,并且希望在使用typeORM生成它们时没有太多的代码重复。
SQL where条件应该是这样的:

WHERE "Table"."id" = $1
AND
"Table"."notAvailable" IS NULL
AND
(
  "Table"."date" > $2
  OR
  (
    "Table"."date" = $2
    AND
    "Table"."myId" > $3
  )
)
AND
(
  "Table"."created" = $2
  OR
  "Table"."updated" = $4
)
AND
(
  "Table"."text" ilike '%search%'
  OR
  "Table"."name" ilike '%search%'
)

但是对于FindConditions,似乎不可能使它们嵌套,所以我必须在FindConditions数组中使用AND的所有可能组合。并且不可能将其拆分为.where().andWhere(),因为andWhere不能使用Object Literal。
有没有其他的可能性来实现这个查询与typeORM不使用原始SQL?

zlwx9yxi

zlwx9yxi1#

当使用queryBuilder时,我建议使用Typeorm文档中所述的Brackets:https://typeorm.io/#/select-query-builder/adding-where-expression
你可以这样做:

createQueryBuilder("user")
    .where("user.registered = :registered", { registered: true })
    .andWhere(new Brackets(qb => {
        qb.where("user.firstName = :firstName", { firstName: "Timber" })
          .orWhere("user.lastName = :lastName", { lastName: "Saw" })
    }))

这将导致:

SELECT ... 
FROM users user
WHERE user.registered = true 
AND (user.firstName = 'Timber' OR user.lastName = 'Saw')
jhdbpxl9

jhdbpxl92#

我认为你混合了从TypeORM检索实体的两种方式,从存储库中查找和查询构建器。FindConditions用于查找函数。andWhere函数由查询构建器使用。当构建更复杂的查询时,通常使用查询构建器更好/更容易。

查询构建器

当使用查询构建时,你有更多的自由来确保查询是你所需要的。在这里你可以自由地添加任何SQL,只要你喜欢:

const desiredEntity = await connection
  .getRepository(User)
  .createQueryBuilder("user")
  .where("user.id = :id", { id: 1 })
  .andWhere("user.date > :date OR (user.date = :date AND user.myId = :myId)",
    { 
      date: specificCreatedAtDate,
      myId: mysteryId,
    })
  .getOne();

请注意,根据您使用的数据库,您在这里使用的实际SQL需要兼容。使用这种方法也可能会带来一个可能的缺点。您将把您的项目绑定到特定的数据库。确保阅读有关您可以设置的表的别名的信息,如果您使用关系,这将很方便。

仓库

您已经看到,这是不太舒服的。这是因为find函数或更具体的findOptions使用对象来构建where子句。这使得很难实现一个适当的接口来并行实现嵌套的ANDOR子句。(我假设)他们选择了拆分ANDOR子句。这使得接口更具声明性,这意味着你必须将OR子句拉到顶部:

const desiredEntity = await repository.find({
  where: [{
    id: id,
    notAvailable: Not(IsNull()),
    date: MoreThan(date)
  },{
    id: id,
    notAvailable: Not(IsNull()),
    date: date
    myId: myId
  }]
})

我无法想象看一个所需的查询的大小,这段代码将是非常高性能的。
或者,你可以使用Raw find helper。这需要你重写每个字段的子句,因为你一次只能访问一个别名。你可以猜测列名或别名,但这是非常糟糕的做法,而且非常不稳定,因为你不能直接控制它。

vbopmzt1

vbopmzt13#

如果要在满足条件时嵌套andWhere语句,以下是一个示例:

async getTasks(filterDto: GetTasksFilterDto, user: User): Promise<Task[]> {
const { status, search } = filterDto;

/* create a query using the query builder */
// task is what refer to the Task entity
const query = this.createQueryBuilder('task');

// only get the tasks that belong to the user
query.where('task.userId = :userId', { userId: user.id });

/* if status is defined then add a where clause to the query */
if (status) {
  // :<variable-name> is a placeholder for the second object key value pair
  query.andWhere('task.status = :status', { status });
}
/* if search is defined then add a where clause to the query */
if (search) {
  query.andWhere(
    /* 
    LIKE: find a similar match (doesn't have to be exact)
      - https://www.w3schools.com/sql/sql_like.asp
    Lower is a sql method
    - https://www.w3schools.com/sql/func_sqlserver_lower.asp

    * bug: search by pass where userId; fix: () whole addWhere statement
    because andWhere stiches the where class together, add () to make andWhere  with or and like into a single where statement
     */
    '(LOWER(task.title) LIKE LOWER(:search) OR LOWER(task.description) LIKE LOWER(:search))',
    // :search is like a param variable, and the search object is the key value pair. Both have to match
    { search: `%${search}%` },
  );
}
/* execute the query

- getMany means that you are expecting an array of results
 */
let tasks;
try {
  tasks = await query.getMany();
} catch (error) {
  this.logger.error(
    `Failed to get tasks for user "${
      user.username
    }", Filters: ${JSON.stringify(filterDto)}`,
    error.stack,
  );
  throw new InternalServerErrorException();
}
return tasks;

}

92dk7w1h

92dk7w1h4#

我有一份名单

{ 
   date: specificCreatedAtDate,
   userId: mysteryId
}

我的解决办法是

.andWhere(
      new Brackets((qb) => {
        qb.where(
          'userTable.date = :date0 AND userTable.type = :userId0',
          {
            date0: dates[0].date,
            userId0: dates[0].type,
          }
        );

        for (let i = 1; i < dates.length; i++) {
          qb.orWhere(
            `userTable.date = :date${i} AND userTable.userId = :userId${i}`,
            {
              [`date${i}`]: dates[i].date,
              [`userId${i}`]: dates[i].userId,
            }
          );
        }
      })
    )

会产生类似的东西

const userEntity = await repository.find({
  where: [{
    userId: id0,
    date: date0
  },{
    id: id1,
    userId: date1
  }
  ....
]
})
plicqrtu

plicqrtu5#

是的,如果我们使用querybuilder(),那么我们可以使用太多的选项。

别担心,我们可以像querybuilder()一样做下面有一个使用NOT的例子,还有LIKE用于搜索/查询。

const users = await User.find({
        where: {
          username: Like(`%${req.body?.search}%`),
          uuid: Not(req.user?.uuid),
        },
        select: {
          id: true,
          uuid: true,
          username: true,
          name: true,
          status: true,
          image: true,
        },
      });

之后,我们可以将response作为json数据发送。

res.status(200).json({message: "The query user got it.", users});

相关问题