-
Notifications
You must be signed in to change notification settings - Fork 122
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
Add tests for virtual population with mongoose adapter #354
Conversation
@icebob can you take a look at this PR and let me know if everything is ok ? Thanks a lot 🙂 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My problem with this PR is that it's a breaking change. Because the adapter.entityToObject
is changed from sync to async. And also this method exists in all adapters, so you should change to async in all adapters as well. And it causes a major version update for all adapters.
Can you change that so it doesn't affect this method?
I fully understand your concerns. Another solution is the second one I mentionned in the PR:
The implementation I made is strongly bonded to mongoose workflow and I can't think of anything else less cumbersome. Feel free to let me know if you have better implementation ideas and I'll try to modify my PR to implement it properly. |
I don't know why, but the tests are freezing on CI, locally it's running fine. |
I saw that and I was also trying to figure out why. I'm gonna investigate as deep as I can and try to understand why. My first guess would be an issue related to bluebird or promises in general. |
Thanks, plz share if you find something. |
Ok I think I'm getting it 🤔 If I'm correct there is no mongodb server started on any workflows cause no docker container is poped in the workflow and all mongoose methods are mocked. I imagine that I have to rewrite all the tests with mocked methods instead unless you're okay to start a mongodb instance by adding a step in the github actions like so: - name: Start MongoDB
uses: supercharge/[email protected]
with:
mongodb-version: 4.4 |
@icebob any thoughts on this one ? |
Ohh, you are really right! I thought we have integrated tests as well (because the new database repo has), but now I'm checked and really, every test only unit tests. If you have time and you can modify it, we should do the following steps:
|
@Freezystem . How did you implement it in moleculer service ? I'm interested to know, how, following the "one database per service" pattern1, and the actual moleculer implementation you can use this ? You are initializing your service with one model (and only one), right ? Actually, I think this PR doesn't work with pattern "one database per service" pattern Also, in you're test, you are really linked to the "global mongoose connection" (that can't fit with the pattern, because differents services need to allow different connection) (you are creating models directly from mongoose like here - documentation ) So, maybe I miss something (it can be possible), or this logic need to be rethinked to follow the "one database per service" pattern . Edit after more searches, I think the populate function need to populate only ID if the virtual is a reference, and the normal populate function will populate the rest by calling the actions . ( so, setting Also, not sure to understand why you want to select only _id ? you already have the distant _id in the field, right ? so, you are asking mongodb, to load object, and just return _id, from _id ? Footnotes
|
@thib3113 You're right, this fix can't work with the pattern one connection, one service (if your don't redeclare all the needed models in the created connection). You'll generally wants your related models to be on the same db to be able to populate between them otherwise mongoose will not be able to make those links. The fact is, in my case, I was using the same mongoose instance for all my services. I see two "unsolvable" cases:
So.. In order to be able to populate virtuals properly we need two things:
They are built in mechanism to use multiple DBs with mongoose, but as far as I understand it, you can't use populate from one DB to another with the same connection. You have to choose before requesting what DB you want to use for the query. related docs: Also you can't use the internal moleculer-db adapter mechanism to populate the virtuals because virtuals are a mongoose internal feature so documents must be in the mongoose document type to be able to retrieve those said virtuals. If you envision other possibilities I'd be glad to assist you in the making of another PR concerning this issue. |
Hello @Freezystem . About the one database per service . This is the choice of @icebob when creating the moleculer-db ... supporting it on other adapters, and not on mongoose one seems strange . About the rest, my idea can be to map virtuals manually ? ( so, lost the possibility to use justOne / options ), just, if found the functionnality like justOne / options can be done by the propagate ( choosing the correct action ), and will also allow to use other moleculer functionnalities ( like cache ) . Actually, I start a draft PR trying to correct the use of the pattern #370 . I plan to really support it . So, can't use the virtuals from mongoose, because models can be in different DB . I'm not using virtuals a lot, so maybe I'm wrong on some points |
In the mongoose sense it means that if you don't instanciate all your schema as models in that newly created connection, mongoose will not know what you're talking about even if you're using the same DB to store your collections. So yes, thanks to But here we are talking about virtuals, which is mongoose specific. Important note on virtuals: they can be other things than basic references to a model, they can be computations base on references or computation without any reference: e.g: Let's imagine that I have two models: So.. It's a mongoose specific case and as long as you are using virtuals, documents needs to be stored in the same DB and connections (if your using more than one) must know each other model definitions. Be aware that in the mongoose case, that's the connection that knows about and enforce model definitions as opposed to SQL DBs that are enforcing data structures directly in the DB itself as table definition. |
https://moleculer.services/docs/0.14/moleculer-db => the documentation talk about "one db per service" . But, yes it should allow to use the same DB for multiples services ( this is planned . my goal is to allow the original pattern, without blocking the other ) .
What I understand from virtuals, is that there are "virtual properties", populated from some configurations . and so, we can just map some of them to moleculer-db population ( so just replacing mongoose internal calls, by moleculer internal calls ), and so, no needs of storing the object in the same db . Everything will pass through moleculer, and so can use all the db you want . About your example, in my opinion, you need to don't use it like that . But more with "moleculer" . If you want to do some calculs, you need to call an action, that will do it, and return the result . So, ok, I understand all functions from virtuals are not handled . But, in my opinion, this adapter need to works more in "moleculer" way than in "mongoose" way . |
The example was fake and not a particularly good case indeed. Virtual is like a subrequest in SQL, when you want to link one or several models to another one. Could be a simple join or a join with computation. Typically, you have a user, that have cars. You don't want to reference all cars in the user document as the list will be hard to maintain. But you wanna be able to retrieve cars on demand if needed. |
yes, ok I understand . But, finally, what not using a moleculer-db with population ? When not modifying the cars, the cache will handle it . if you update the cars of this user, you can update the cache ( in my moleculer, when I will update a cache, I will regenerate it at the end of the action ), when I update the cars, the next request will directly hit the cache, without calling mongo (and without knowing what is the DB handling the cars) . or I don't understand ? |
Yes you're are right for the cache part. |
@Freezystem Hum ... yes ok I better understand ... About, this my opinion will be the same "moleculer first" . Because this app is for moleculer . But I understand the use-case ... So, because we will need to create a new major version (to use mongoose 7), what about an option like "replaceVirtualsRefById" default to true, and you can set to false to use in your use-case ? so, if true, the virtual containing a ref will be replaced manually by the id, if false, it will call mongoose populate without modification ( like now ) ? |
Using a dedicated option is fine as long as I can still use virtuals in that way 😇 |
Can you elaborate on it ? You are talking about this part moleculer-db/packages/moleculer-db-adapter-mongoose/src/index.js Lines 69 to 85 in 77d3619
hum, are your talking about what I fix in my last PR ? #367 |
fix #355
This PR allows the pre-population of virtuals before converting the entity to an object when using mongoose adapter.
It compares the service model virtual field with request fields to populate.
If a field is in both lists it will be pre-populated with _id fields only in order to be expanded later in the
populateDocs
method.Note:
The pre-population part could have been done in another function call before
entityToObject
methods but it would have required a lot more modification impacting other adapters.Either one of this solution could have been done:
findById
,findByIds
, etc...) but it would have require to pass the context to all those functions and add pre-population condition in everyone of them resulting in a risky refacto.entityToObject
, but it would only servemongoose
purpose and be useless for other adapters adding useless no-op function in the workflow.