pubkey / rxdb

A fast, local first, reactive Database for JavaScript Applications https://rxdb.info/
https://rxdb.info/
Apache License 2.0
21k stars 1.02k forks source link

Feature Request: Mongoose-style population #170

Closed varbrad closed 7 years ago

varbrad commented 7 years ago

Case

New Feature Implement Mongoose-Style population joins.

Issue

Mongoose has a useful feature whereby a returned document from a query can have any specified paths replaced by the document that the path refers to (Sort of like JOINS in SQL, etc.).

Example;

Story
  .findOne({ title: /timex/i })
  .populate('_creator')
  .exec(...)

http://mongoosejs.com/docs/populate.html

As far as I understand it, rxdb does not quite offer the same functionality here, and the current population implementation only allows for population after the query has been executed (Or via the proxy-get by appending an underscore '_').

The current implementation also does not work with any query that returns more than one document, and attempting to 'populate' a multi-result query just results in an error (as it just an array?).

Would be cool to see this added maybe in the future, thanks guys/gals, keep up the good work. 😃

pubkey commented 7 years ago

Hi @varbrad I was thinking about your feature-request for the last few days. I have problems to understand how this is more usefull than taking a RxQuery-result and map over it for the population. Can you post some example code that describes how you query (with populate) and how the output of the query would look like? Does it spill out the _creators or does the result contain all documents including the creators etc..

varbrad commented 7 years ago

Say if you had a collection called "books" that literally just had an id, title and author. Lets say we had a document in that collection that looked like

{ id: 'mehwhatever', title: 'Cool Book', author: 'author-guy' }

If we just did the normal query, we would just get the author value ('author-guy') back and would have to do another request to get the author information (name, etc.). In Mongoose, using the populate method allows for this to be done in one-query, so would say get something back like;

{ id: 'mehwhatever', title: 'Cool Book', author: { id: 'author-guy', name: 'Guy Author', dob: '1980-05-04' } }

It's possible to do this for a single document or for multiple returned documents. If I get some free time over the weekend, I may even spend some time trying to implement this myself.

natew commented 7 years ago

I can see the value in having it both ways. This is helpful for handling one async function above a view, whereas sub-populations are good if you have a clean way to handle async code within each sub-item.

pubkey commented 7 years ago

One problem I see here is that the current implementation of RxDB relies on the paradigma A RxDocument is equal by the primary-key, no matter where it comes from This means that if you get the same document from different queries, it will still be exactly equal.

// assume myCollection has exactly one document
const doc1 = await myCollection.findOne().exec();
const doc2 = await myCollection.findOne().sort('anyField').exec();

console.log(doc1.anyField == doc2.anyField); // always true
console.log(doc1 == doc2); // always true

With populate:

// assume myCollection has exactly one document
const doc1 = await myCollection.findOne().exec();
const doc2 = await myCollection.findOne().populate('author').exec();

console.log(doc1.author == doc2.author); // false

Allowing a populate on a specific field would break this paradigma. This would make the implementation much more complex especially for the current usage of caching. I don't know if this is worth the benefits.

varbrad commented 7 years ago

That's fine and I understand your apprehension in adding it (I agree), it was just a nifty little method that I used a lot in previous projects working with MongoDB and Mongoose. 👍

pubkey commented 7 years ago

The main difference is that mongodb and mongoose return plain json-objects when you do queries. This was also the case in the first implementation for RxDB. But I found out that the api would be much easier to use when the 'reactive' behavior is implemented directly in the queries result-documents.

I'm closing this for now. Reopen if you have an idea to implement populate without breaking the paradigma.