在 Objection.js 中,如何在查询中包含虚拟列?

In Objection.js, how can I include a virtual column with my query?

我想创建一个 expired_bool 虚拟列。

我有以下 3 个 tables:

订阅

id duration price
1 30 0.99
2 360 1.99

发票:

id user_id purchase_date
1 34 2020-01-01 01:21:01
2 42 2021-02-19 19:19:19

invoice_items:

id user_id invoice_id subscription_id activation_date
1 34 1 1 2020-05-15 12:51:51
2 34 1 2 2021-02-20 20:20:12
3 42 2 1 NULL
4 42 2 2 2021-02-20 20:20:12

这就是 UserInvoiceInvoiceItems table 在 Objection.js 中的建模方式:

class User extends Model {
  static get tableName() {
    return "users";
  }

  static get relationMappings() {
    return {
 
      invoices: {
        relation: Model.HasManyRelation,
        modelClass: Invoice,
        join: {
          from: "users.id",
          to: "invoices.user_id",
        },
      },

      invoiceItems: {
        relation: Model.HasManyRelation,
        modelClass: InvoiceItem,
        join: {
          from: "users.id",
          to: "invoice_items.user_id",
        },
      },

    };
  }
}

class Invoice extends Model {
  static get tableName() {
    return "invoices";
  }

  static get relationMappings() {
    return {
      user: {
        relation: Model.BelongsToOneRelation,
        modelClass: User,
        join: {
          from: "invoices.user_id",
          to: "users.id",
        },
      },

      invoiceItems: {
        relation: Model.HasManyRelation,
        modelClass: InvoiceItem,
        join: {
          from: "invoices.id",
          to: "invoice_items.invoice_id",
        },
      },
    };
  }
}

class InvoiceItem extends Model {
  static get tableName() {
    return "invoice_items";
  }

  static get relationMappings() {
    return {
      invoice: {
        relation: Model.BelongsToOneRelation,
        modelClass: Invoice,
        join: {
          from: "invoice_items.invoice_id",
          to: "invoice.id",
        },
      },

      user: {
        relation: Model.BelongsToOneRelation,
        modelClass: User,
        join: {
          from: "invoice_items.user_id",
          to: "users.id",
        },
      },

      subscription: {
        relation: Model.HasOneRelation,
        modelClass: Subscription,
        join: {
          from: "invoice_items.subscription_id",
          to: "subscriptions.id",
        },
      },
    };
  }
}

这就是我 查询 用户和 his/her 购买的方式:

async function getUserAllWithPasswordByIdAsync(userId) {
  try {
    const query = await User.query()
      .findById(userId)
      .withGraphFetched("invoiceItems.subscription")
      .withGraphFetched("invoices")

    return query;
  }
  catch (error) {
    console.log(error)
  }
}

顺便说一句,expired_bool 虚拟列是通过检查 invoiceItemsactivation date 并从中添加 duration 来确定的订阅 table,并确保该日期是今天的未来日期。

所以总而言之,我如何确定 invoiceItem 是否自动过期(通过使用虚拟列,应将其添加到 invoiceItems table),以及确保这包含在我的 User?

查询中

一种可能的解决方案是将 expiry_date 列添加到 invoice_items

activation_dateexpiry_date 都将在订阅激活期间设置

activation_date = today's date
expiry_date = activation_date + subscription duration

invoice_items:

id user_id invoice_id subscription_id activation_date expiry_date
1 34 1 1 2020-05-15 12:51:51 2020-06-14 12:51:51
2 34 1 2 2021-02-20 20:20:12 2022-02-15 20:20:12
3 42 2 1 NULL NULL
4 42 2 2 2021-02-20 20:20:12 2022-02-15 20:20:12

查询活动 invoice_items 将像

一样简单
select "invoice_items".* from "invoice_items"
  where "invoice_items"."expiry_date" >= [today's date]

假设现有订阅永远不会改变(并且只添加具有新 subscription_id 的新订阅),这种方法应该有效。

显然,您不能使用虚拟字段进行异步操作。也不能基于虚拟字段查询。它们完全在 javascript.

中定义

所以可以按如下方式完成(使用从Objection.js导入的raw):

import objection from "objection";
const { Model, raw } = objection;
...

class InvoiceItem extends Model {
  ...
  static modifiers = {
    expiryInfo(qb) {
      qb.select(
        'invoice_items.*', 
        raw('activation_date + make_interval(days => subscription.duration)').as('expiration_date'),
        raw('activation_date + make_interval(days => subscription.duration) < now()').as('expired_bool'),
      ).joinRelated('subscription')
    }
  }
  ...
}

    const query = await User.query()
      .findById(userId)
      .withGraphFetched("invoiceItems(expiryInfo).subscription")
      .withGraphFetched("invoices")

这使用了 Postgres 函数,但对于其他数据库,思路仍然相同,尽管 raw 中的代码可能需要稍作调整。