I have an application that stores books
, and reviews
. It is a one-to-many
relationship. But i need to get data for the following scenarios.
-
Scenario 1 Given a
book
, I want to list all thereviews
for that book. -
Scenario 2 Given a query that returns a list of
books
, I need to get a count of all thereviews
associated with each book. -
Scenario 3 Given a query that returns a list of
reviews
(eg, all reviews from a user), I need to get the get thebook
associated with each of thosereviews
.
Here are are several approaches i have come up with. Approach 4
is the one that seems to fit my needs the most at the moment, but i am not entirely happy with it.
I would appreciate feedback if you think there is a solution to anything i have considered.
Approach 1
Approach 1. The review
object has a pointer to the book
.
-
PRO: This makes
Scenario 3
very easy. -
PRO:
Scenario 1
can also be easily accomplished with a query that filters for allreviews
that have a pointer to the givenbook
. -
CON: maybe inefficient to perform
Scenario 2
? Would i have to loop through eachbook
and run a separate query that filters forreviews
that have a pointer to thebook
?
// ----------------------------------------------
// ADD REVIEW TO EXISTING BOOK
// APPROACH 1. Review has pointer to book
// ----------------------------------------------
const user = Moralis.User.current();
const Book = Moralis.Object.extend("Book");
const Review = Moralis.Object.extend("Review");
// 1. Get book
const query = new Moralis.Query("Book")
query.equalTo("title", "Frankenstein")
const book = await query.first()
// 2. Create the review with pointer to the book
const review = new Comment();
review.set("content", "Amazing book!");
review.set("user", user);
review.set("book", book);
review.save();
Approach 2
Approach 2. The book
object has a list of pointers to the review
objects.
-
PRO: This makes
Scenario 1
, andScenario 2
very easy. -
CON: Might be inefficient to do
Scenario 3
? Would i have to loop through eachreview
and run a separate query that filters forbooks
that have a pointer to thereview
?
Approach 3
Apporach 3 review
has pointer to book
, and book
has relationship review
.
-
PRO: This makes
scenario 1
, andscenario 3
easy. - What about scenario 2? Would it require some subquery to get a count of reviews for each book? or will it require looping?
-
CON: requires two separate queries to
save()
the relationship on both ends. Might cause issues if connection is lost before second query is executed.
// ----------------------------------------------
// ADD REVIEW TO EXISTING BOOK
// APPROACH 3. Review has pointer to book, and book has relation to review
// ----------------------------------------------
const user = Moralis.User.current();
const Book = Moralis.Object.extend("Book");
const Review = Moralis.Object.extend("Review");
// 1. Get book
const query = new Moralis.Query("Book")
query.equalTo("title", "Frankenstein")
const book = await query.first()
// 2. Create the review with pointer to the book
const review = new Review();
review.set("content", "Amazing book");
review.set("user", user);
review.set("book", book);
review.save();
// 3. Add relation from book to review
const reviewRelation = book.relation("reviewRelation")
reviewRelation.add(review)
book.save()
Approach 4
Apporach 4 review
has pointer to book
, and book
has list of pointers to review
objects.
- PRO: This makes querying for all scenarios very easy.
- PRO: Only a single query is needed to save the relationships on both ends.
-
CON: For
scenario 2
, it might consume too much bandwidth, since the query that returns allbooks
also returns all the actualreview
objects. See my code below. Is there any way to prevent the actualreview
objects from being returned? And only return eg theobject id
instead? Or the aggregate count?
// ----------------------------------------------
// ADD REVIEW TO EXISTING BOOK
// APPROACH 4. Review has pointer to book, and book has list of pointers to reviews
// ----------------------------------------------
const user = Moralis.User.current();
const Book = Moralis.Object.extend("Book");
const Review = Moralis.Object.extend("Review");
// 1. Get book
const query = new Moralis.Query("Book")
query.equalTo("title", "Frankenstein")
const book = await query.first()
// 2. Create the review with pointer to the book
const review = new Review();
review.set("content", "Such amazing prose!");
review.set("user", user);
review.set("book", book);
book.get("reviewList").push(review)
book.save()
// SCENRARIO 2 QUERY
const query = new Moralis.Query("Book")
const books = await query.find()
// Peek inside the results
// Returns the actual full review object
books[0].get("reviewList")[0]
Replicating
Below is a full set of queries to get data on the database to replicate Approach 4
above.
const user = Moralis.User.current();
const Book = Moralis.Object.extend("Book");
const Review = Moralis.Object.extend("Review");
// ------------------------------------
// ADDING BOOKS
// ------------------------------------
const book = new Book();
book.set("title", "Frankenstein");
book.set("author", "Mary Shelly");
book.set("reviewList", []);
book.save()
const book = new Book();
book.set("title", "1984");
book.set("author", "George Orwell");
book.set("reviewList", []);
book.save()
const book = new Book();
book.set("title", "Pygmalion");
book.set("author", "George Bernard Shaw");
book.set("reviewList", []);
book.save()
// ------------------------------------
// ADDING REVIEWS TO A BOOK
// ------------------------------------
const query = new Moralis.Query("Book")
query.equalTo("title", "Frankenstein")
const book = await query.first()
const review = new Review();
review.set("content", "Amazing book!");
review.set("user", user);
review.set("book", book);
book.get("reviewList").push(review)
book.save()
const review = new Review();
review.set("content", "So differnent from the hollywood movies");
review.set("user", user);
review.set("book", book);
book.get("reviewList").push(review)
book.save()
// ------------------------------------
// SCENARIO 2 QUERY
// ------------------------------------
const query = new Moralis.Query("Book")
const books = await query.find()
// Peek inside the results
// Returns the actual full review object
books[0].get("reviewList")[0]