I would like to create an expired_bool
virtual column.
I have the following 3 tables:
subscriptions:
id | duration | price |
---|---|---|
1 | 30 | 0.99 |
2 | 360 | 1.99 |
invoices:
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 |
This is how the User
, Invoice
, and InvoiceItems
tables are modeled in 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",
},
},
};
}
}
And this is how I'm Querying for the user and his/her purchases:
async function getUserAllWithPasswordByIdAsync(userId) {
try {
const query = await User.query()
.findById(userId)
.withGraphFetched("invoiceItems.subscription")
.withGraphFetched("invoices")
return query;
}
catch (error) {
console.log(error)
}
}
Just as an aside, the expired_bool
virtual column is determined by checking the activation date
of invoiceItems and adding the duration
from the subscriptions table, and making sure that date is in the future from today's date.
So in summary, how can I determine if the invoiceItem
is expired automatically (by use of a virtual column, which should be added to the invoiceItems
table), and ensure that this is included with my query of User
?
Apparently, you can't use virtual field to do async operations. Nor can you query based on virtual fields. They are defined entirely in javascript.
So it can be done as follows (using raw
imported from Objection.js):
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")
This uses Postgres functions, but the idea remains the same for other databases, though the code inside raw
may need to be slightly adjusted.