New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Virtual entity does not work with cursor pagination #4628
Comments
Please don't ignore the issue template. Use discussions if you want to have a chat about something instead of reporting a bug. |
Reformated with the template. |
Thanks! The formatting is not super important, the reproduction was clear enough but the specific version number is often very important, same with the driver (seeing the quoting, it's postgres, right?). Not sure how to approach this, but I'd guess the same problem will be there with regular edit: or maybe its actually very simple - we can simply skip the ordering for the count query, IIRC we are already doing that somewhere |
Exactly, the findAndCount/count will fail as of today. The absence of |
@B4nan For dynamic expressions returning something other than a QueryBuilder (e.g. a string, a knex query, etc.), the virtual expression is not wrapped in a subquery. In this case, count does not work and there is no way to know (from inside the expression function) when it's called for a count or a find. Additionally, filtering, sorting, pagination, etc. have to be handled manually inside the expression, which I think kinda defeats the purpose of a virtual entity (but I may be making wrong assumptions). I'm wondering what the rational behind wrapping only QueryBuilder expressions? |
Not following, maybe provide a repro for your problem? A string expression is handled a few lines above that: https://github.com/mikro-orm/mikro-orm/blob/master/packages/knex/src/AbstractSqlDriver.ts#L150 You are not supposed to be putting a knex qb instance in there, that's why it's not explicitly handled anyhow (not saying it's a bad idea, we can support that too). The expression expects either an ORM qb, a string SQL, or an array (for mongodb aggregation). |
But in this case it must be a hard-coded string (i.e. not a function returning a string). I would like to rely on both the ORM for typing / field mapping benefits and Knex for the abstraction (and generating more complex SQL).
Looks like that returning a Knex qb also works but maybe not on purpose. There is no strong typing about what should return the expression (
Yes, sorry for recycling a closed issue without even providing more context. I can't easily share a repro and I'm not sure it worth the time to build one since I may actually just mis/overuse virtual entities. Please let me know if the following example is enough to draw some conclusions? @Entity()
class Book {
@PrimaryKey() id!: number;
@Property() name! string;
// ...
}
@Entity()
class BookDownload {
@PrimaryKey() id!: number;
@ManyToOne() book! Book;
@Property() downloadedAt!: Date;
// ...
}
// VIRTUAL ENTITY
@Entity({
expression: (em: EntityManager) => {
const knex = em.qb(Book).select(["id", "name"]).getKnexQuery();
// ... join with BookDownload, COUNT FILTER on downloadAt, etc.
// ... and some other advanced SQL that can't be done via the ORM qb
return knex;
}
})
class BookAnalytic {
// Fields from Book
@Property() id!: string;
@Property() name!: string;
// Generated fields from BookDownload
@Property() downloads7Days!: number;
@Property() downloads30Days!: number;
@Property() lastDownloadedAt!: Date;
} The reason I would like to rely on a virtual entity is to benefit from the smart filtering automatically converted to SQL but also working on generated fields (such as em.findAndCount(BookAnalytic, {
downloads7Days: { $gt: 42 },
}, {
offset: 10,
limit: 10,
orderBy: {
lastDownloadedAt: "DESC"
}
} |
I think it's just an omission, we can surely support both string and knex qb in the callback, it just wasn't expected/tested in there (but I do see a value for both). |
Actually, the idea behind the loose implementation was that you are free to return anything from the callback, including the results - you can await the knex qb and just do that. @Entity({
expression: (em: EntityManager) => {
const knex = em.qb(Book).select(["id", "name"]).getKnexQuery();
// ... join with BookDownload, COUNT FILTER on downloadAt, etc.
// ... and some other advanced SQL that can't be done via the ORM qb
return await knex; // this should be enough
}
}) But I will add support for the knex qb so it's less confusing. |
Thanks @B4nan for your replies!
That doesn't solve the "count" issue, right? It also doesn't automatically apply query options such as filter, limit, offset, etc.
Thank you! I would wrap the result of the expression if it's either an ORM qb, a knex qb or a string (to be consistent with the |
#4740 should help |
Thank you @B4nan for acting so quickly on this! |
Describe the bug
Cursor pagination currently does not work with virtual entities.
To Reproduce
Considering an entity:
Cursor pagination won't work and will trigger an error:
I tried to apply my "includeCount" branch and set it to false, which removes the error, but then the
after
orbefore
cursor has no effect (no where conditions are applied).I don't have yet sufficient knowledge of the code base to tackle this myself, but in case this would be an easy fix that someone would like to try :)
Expected behavior
Cursor pagination should work the same with virtual entities.
Versions
The text was updated successfully, but these errors were encountered: