Query/sort reference of reference in mongoose - node.js

Hopefully I can explain this well.
I have 3 Model types in play here: Users, Products, and Stores. What I'm after is a sorted list of Stores, per user, based on how many Products they've added from that Store. So basically "show me this User's top Stores".
pseudo-schemas:
var User = {
name: String
};
var Store = {
name: String
};
var Product = {
title: String,
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
}
store: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Store'
}
};
So how can I find which Stores the User has added the most Products to? This may be obvious, it's late. :-P
Thanks!

You can try to use Aggregation framework to solve it.
And especially $group pipeline:
// aggregate whole `Product` collection
Product.aggregate([
// count products by `user` and `store` and save result to `products_count`
{$group: {
_id: {user_id:"$user", store_id:"$store"},
products_count: {$sum: 1}
}},
// sort by most products count
{$sort: {products_count: -1}}
])
There are also $limit and $skip pipelines, that help to paginate all this stuff.

Related

How to get categories and sub-categories in single API response

I have two collections Categories and Subcategories inside a Categories collection. I have an array that is storing the ids of subcategories that are stored in Subcategories collection. Below is my document structure:
Categories collection
{
id:65,
title:"Automotive",
active:true,
subcategories:[35,28,30]
}
subcategories collection
{
id:35,
title:"Automotive technology",
category_id:65,
active:true
},
{
id:28,
title:"Automotive coatings",
category_id:65,
active:true
},
{
id:30,
title:"Machinery and equipments",
category_id:65,
active:true
}
As seen in above collection 3 documents from subcategories collection have been associated with the category document. I want to fetch data in below format on single API hit.
API response should be in below format:
{
data:{
category:{
id:65,
title:"Automotive",
subcategories:[{
id:35,
name:"Automotive technology",
},
{
id:28,
name:"Automotive coatings",
},
{
id:30,
name:"Machinery and equipments",
}]
},
category:{
id:66,
title:"Food",
subcategories:[{
id:23,
name:"Drinks",
},
{
id:2,
name:"Additives",
}]
},
},
messsage:"Success",
code:200
}
As of now I am able to get data in 2 api hits that is like first getting all the categories
const category = await db.categories.find({});
Then on click of some particular category fetching all the sub categories based on the category id.
const subCategories = await db.SubCategories.find({category_id:id});
How can I get the above data in desired format in single API hit?
You need something like this, also, if you use mongoose, you can use .populate()
To format data you can use $project stage in aggregation pipeline or projection in .find()
If you want to use Mongoose with populate:
CategorySchema:
const CategorySchema= new mongoose.Schema({
...
subCategories: [{ type: mongoose.Schema.Types.ObjectId, ref: 'SubCategory' }],
...
});
need _id column on reference table
ref content is must be equal to model name like
module.exports = mongoose.model('SubCategory', SubCategorySchema);
Controller:
const categories = await Category.find({})
.populate({
path: 'subCategories'
})
path content is must be equal to column name
If you want to write with Mongo query:
db.getCollection("categories").aggregate([
{
$lookup:
{
from: 'subCategories',
localField: 'subCategories',
foreignField: 'id',
as: 'subCategories'
}
}
])
You get empty object because you are not using "_id" field for join schemas. If you want to use "id" field u need to use virtuals on Mongoose:
https://mongoosejs.com/docs/tutorials/virtuals.html#populate

Require a mongodb db model deign. Whether to choose Refs or Embeded doc

I'm designing a backend for a Talent hunt application. Initially I designed the DB using refs. Like I followed the primary key forign key using refs to join my collections. But as the collection requirement increases it's being hard to join all the collections. Then I come across the one to many collections. So I'm thinking to get a suggestion from experts. Let me tell the requirements.
I have the following collections initially.
User.js
{
_id: "5ecfdc903165f709b49a4a14",
name: "Lijo",
email: "lijo#gmail.com"
}
then I have a category table for serving the categories
Category.js
{
_id: 5ecfdc903165f709b49a5a18,
title: "Acting",
code: "ACT"
}
If one user adds his talents to profile. I used one another collection for storing. Rmember the user can save multilple talents. So i created,
UserTalents.js
{
_id: "5ecfdc903165f709b49a6c87",
categoryId: "5ecfdc903165f709b49a5a18",
userId: "5ecfdc903165f709b49a4a14",
level: "beginner"
}
For each catgeory need to upload atleast one media along with description Soagain I created a new collection for that.
Media.js
{
_id: "5ecfdc903165f709b49a8a14",
talentId: "5ecfdc903165f709b49a6c87",
userId: "5ecfdc903165f709b49a4a14"
media: "5ecfdc903165f709b49a4a14_1.jpg"
}
And I need to have these users connected. For that craeted.
Friends.js
{
_id: "5ecfdc903165f709b49a8a18",
sender: "5ecfdc903165f709b49a4a14",
receiver: "5ecfdc903165f709b49a4a15"
status: "accepted"
}
Is this good to continue??? Expecting a huge amount of users. Or can I follow like:
User.js
{
_id: "5ecfdc903165f709b49a4a14",
name: "Lijo",
email: "lijo#gmail.com",
talents: [
{
_id: "5ecfdc903165f709b49a5a18", // _id from Category
title: "Acting",
code: "ACT",
level: "beginner",
media: "5ecfdc903165f709b49a4a14_1.jpg"
}
],
friends: [
{
_id: "5ecfdc903165f709b49a4a15",
name: "Test",
status: "approved"
}
]
}
I I follow this, then how do I update the name fileds in talents and friends array either one of its original name is changed?
Which is better approach?

get length of array field in mongoose _ Nodejs

suppose we have a User model that contains an array of other User objects.
let UserSchema = mongoose.Schema({
followers: [{
type: mongoose.Schema.ObjectId,
ref: 'User',
}]
})
I need a count of this objectIds.
the first solution is to get length of followers.
req.user.followers.length
but I think it's not relevant to get all the followers that contains many of objectIds. and in my query i dont need all of this objectIds.
I tried to use virtuals but in virtuals I have many unnecessary pieces of stuff.I'm looking for the best and uncosted way for this type of situations.
because of misunderstanding your question, so I update my answer: you can use $size of mongo aggregate.
db.users.aggregate(
[
{
$project: {
id: 1,
total_followers: { $size: "$followers" }
}
}
]
)
In case of you want to find any document with specific number of length (eg: 0), you can do this :
db.users.find({ followers: { $size: 0 } } )

MongoDB Subdocument Query Performance on Large Dataset

I have created a schema for Conversations in MongoDB where Messages are stored as an Array of Objects in Conversations Object.
Conversation {
company_id: { type:ObjectId, index: true },
messages: [{
_id: { type: ObjectId, index: true }
}]
}
There is a query I have in place that looks up a Conversation based on the company_id and _id of the first message sent in the array (that is being sent from another part of the application).
Conversation.findOne({ company_id: c_id, messages._id: firstMessage })
Theoretically, if a company has 100 million conversations, and each of those conversations has 1 million messages, how much of a performance issue will I have for the query of the subdocument, rather then me storing the first message id in the main Document and querying just the base object?
Conversation {
company_id: { type:ObjectId, index: true },
firstMessage_id: { type:ObjectId, index: true },
messages: [{
_id: { type: ObjectId, index: true }
}]
}
Conversation.findOne({ company_id: c_id, firstMessage_id: firstMessage })
Thanks in advance for the help.
If your individual messages are 1 million, I would keep them in a separate collection altogether and use the aggregation framework's $lookup in version 3.4 to give you the outcome. Of course I would assume that the proper indices are in place in both collections and proper filters are used to MATCH the company.

How to sort array of embedded documents via Mongoose query?

I'm building a node.js application with Mongoose and have a problem related to sorting embedded documents. Here's the schema I use:
var locationSchema = new Schema({
lat: { type: String, required: true },
lon: { type: String, required: true },
time: { type: Date, required: true },
acc: { type: String }
})
var locationsSchema = new Schema({
userId: { type: ObjectId },
source: { type: ObjectId, required: true },
locations: [ locationSchema ]
});
I'd like to output the locations embedded in the userLocations documented sorted by their time attribute. I currently do the sorting in JavaScript after I retrieved the data from MongoDb like so:
function locationsDescendingTimeOrder(loc1, loc2) {
return loc2.time.getTime() - loc1.time.getTime()
}
LocationsModel.findOne({ userId: theUserId }, function(err, userLocations) {
userLocations.locations.sort(locationsDescendingTimeOrder).forEach(function(location) {
console.log('location: ' + location.time);
}
});
I did read about the sorting API provided by Mongoose but I couldn't figure out if it can be used for sorting arrays of embedded documents and if yes, if it is a sensible approach and how to apply it to this problem. Can anyone help me out here, please?
Thanks in advance and cheers,
Georg
You're doing it the right way, Georg. Your other options are either to sort locations by time upon embedding in the first place, or going the more traditional non-embedded route (or minimally embedded route so that you may be embedding an array of ids or something but you're actually querying the locations separately).
This also can be done using mongoose sort API as well.
LocationsModel.findOne({ userId: theUserId })
// .sort({ "locations.time": "desc" }) // option 1
.sort("-locations.time") // option 2
.exec((err, result) => {
// compute fetched data
})
Sort by field in nested array with Mongoose.js
More methods are mentioned in this answer as well
Sorting Options in mogoose
Mongoose Sort API

Resources