Mongoose populate by field not _id - node.js

I have these two schema
const User = new mongoose.Schema({
username: {
type: String,
unique: true,
}
})
const Product = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
//...Other product information
})
and when I query product I can populate the product user like this
await database.Product.findOne({}).populate("user")
this will populate the user by his id but I want to populate the user by his username since it never gonna repeat and it's unique. I there is a way to reference a model by another field other than the _id

Do this:
await database.Product.findOne({}).populate('user',{path: 'username'})

Related

Mongoose, upsert an item in array in referenced subdocument

I have the following schemas and models:
const shelfSchema = new mongoose.Schema({
name: {
type: String,
required: true,
default: 'New Shelf'
},
books: {
type: [bookSchema],
required: false
}
}, {autoCreate: false})
const shelfModel = mongoose.model('Shelf', shelfSchema)
const librarySchema= new mongoose.Schema({
shelves: {
type: [shelfSchema],
required: false,
}
})
const libraryModel = mongoose.model('library', librarySchema)
const userSchema = new mongoose.Schema({
username: {
type: String,
required:true,
unique:true
}
library: {
type: mongoose.Schema.Types.ObjectId,
ref: 'library',
required: true
}
})
const userModel = mongoose.model('User', userSchema );
Every User has a unique username and a Library reference, and every library has one or more Shelves, each one with one or more Book.
When I add a book, I pass also the information of the shelf name I want to insert the book into, and if the shelf with that name is missing, it should be created.
Since I come from a sql mentality I'm having a bit of difficulties in understanding if I can manage an upsert the same way.
I thought that I could insert the book using at most two queries: one to create the self if it's missing and one to insert the book in the shelf.
My approach was then to use
UserModel.findOneAndUpdate({username: user.username, "library.shelves.name": shelfName},{}, {upsert: true})
but since it's a query in the UserModel, if it doesn't find a user with a shelf with that name it tries to create a new user, duplicating the username.
Am I right to assume that I have to split this first query in two parts, "Find a user with a shelf with that name in the library" and in case it's not found "Create that shelf in the library"?
Or is it possible to unite the queries in some way?
What you are doing right now is trying to update a UserModel that matches the user name, has the shelve name, that is why upsert: true creates a new user entry.
What you should do is to find out the library _id and then $push the book to the shelf you are searching for.

How to connect schemas with each other in mongooseJS?

Basically what I am trying to achieve is the following:
In my iOS application a QR-Code can be scanned to get the ID of a specific lounge and with that its JSON data (only ID and name for now) saved in MongoDB.
Every lounge is able (but doesn't have to) to add products to their lounge, so that in the end, after scanning the QR-Code, a customer is able to see which products are offered.
A product contains only a name and imageURL for now, but not a price, because the price can variate and be set by owners of a lounge. So a price of one and the same product can be different.
I would like to know, what the correct and best way is for implementing the schemas for that approach, so that I can easily fetch all products of a specific lounge, maybe based on its ID.
What I got so far:
lounge.js:
const mongoose = require('mongoose');
const loungeSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true }
});
module.exports = mongoose.model('Lounge', loungeSchema);
product.js
const mongoose = require('mongoose');
const productSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true },
imageURL: { type: String, required: true, unique: true }
});
module.exports = mongoose.model('Product', productSchema);
Basically you can use mongoose populate. You can achieve this by storing the products in separate collection and basically populate an array on the lounge schema using the _id for the lounge let me show you an example:
Your lounge schema would be something like this
const mongoose = require('mongoose');
const loungeSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true }
products: [type: Schema.Types.ObjectId, ref: 'Products' ]
});
module.exports = mongoose.model('Lounge', loungeSchema);
And your product schema
const mongoose = require('mongoose');
const productSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true },
imageURL: { type: String, required: true, unique: true }
});
module.exports = mongoose.model('Product', productSchema);
When saving creating a lounge just add the _id of the product to products array that apart of the lounge schema.
So basically you find the product and retrieve its _id.
To run a find query it would be something like this:
lounge.find({}).populate('products').exec();
products array will then have the related products for each lounge

How to dynamically populate array of objects in mongoose?

I am trying to dynamically populate array of objects in mongoose. On my user model I want an array that contains all posts that user made. The problem is that I have multiple types of posts.
Different post models:
const ImagePost = mongoose.model('ImagePost', new Schema({ url: String }))
const TextPost = mongoose.model('TextPost', new Schema({ text: String }))
My user model looks like this:
const userSchema = new Schema({
userName: {
type: String,
required: true
},
posts: [{
postId: {
type: Schema.Types.ObjectId,
required: true,
refPath: "postModel"
},
postModel: {
type: String,
required: true,
enum: ['ImagePost', 'TextPost']
}
}]
})
const User = mongoose.model('User', userSchema)
How can I get the user from my database and automatically populate the posts the user made?
The whey I think it should work is this but for some reason it doesn't do anything:
User.findById('5d302c7caf1b8906ccb611b6').populate('posts.postId')
Changing your refPath from postModel to posts.postModel may solve your problem.

Match specific value in mongoose populate

Following is the schema of a user collection:
const Mongoose = require('mongoose')
const Schema = Mongoose.Schema
const userSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String
},
supporterOf: [{
type: Schema.Types.ObjectId,
ref: 'individual',
required: false
}],
})
module.exports = Mongoose.model('user', userSchema);
I want to populate 'supporterOf' which is a collection of individual (ref: individual).
The 'supporterOf' contains an array of ObjectId.
I am having the problem of matching the specific objectId with that ObjectId array.
Can anyone suggest me how I can match specific ObjectId with an array of ObjectIds in populate function?
You have a reference of 'individual' in supporterOf and you want to populate only relevant object from the array of individuals?
If this is right case then do the following:
YourUserModel.findById(userId, function (err, user) {
console.log(user)
}).populate({
path: 'supporterOf',
match: {
yourObjectOfIndividualDocument: yourMatchingIdOfIndividual
}
})
.exec()
Replace yourObjectOfIndividualDocument: yourMatchingIdOfIndividual by name: 'abcd'.
Here name is the field of Individual document not of User document.
You add condition in populate
if you wanted to populate fans array based on their age, and return, at most, any 5 of them
.populate('fans', null, { age: { $gte: 21 }}, { limit: 5 })

Individual nested subdocument Mongoose

I'm Trying to embed a subdocument into my main document,like this:
This is the main document.js
var mongoose = require('../../db/mongodb.connector'),
Schema = mongoose.Schema;
require('./document.model');
var Document= mongoose.model('Document');
require('./alert.model');
var Alert = mongoose.model('Alert');
var userSchema = new Schema({
name: { type: String }
created: { type: Date, default: Date.now()},
alerts: {type: Schema.ObjectId,ref: 'Alert'},
documents: [{type: Schema.ObjectId,ref: 'Document'}],
});
module.exports = mongoose.model('User', userSchema);
This is the embed document.js
var mongoose = require('../../db/mongodb.connector'),
Schema = mongoose.Schema;
var alertsSchema = new Schema({
push: {type: String, default: "true"},
email: {type: String, default: "false"},
sms: {type: String, default: "false"}
});
module.exports = mongoose.model('Alert', alertsSchema);
When I Insert a new User document like this:
exports.insertUser = function (userData, res) {
var user = new User({
name: userData.name,
alerts: {push: "true", email:"false", sms: "false"}
});
user.save...
...
The returned data is this:
{ name: 'name',
documents: [],
created: 2017-04-14T10:22:05.612Z
}
The problem is that I don't know if I'm doing correctly the sintax of embed document because the insert doesn't return any error but the alerts object doesn't appear into the inserted new document.
What would be wrong?
You are doing it wrong. You need to first save the alert document and then use its id in the user document.
let alertDoc = await new Alert({push: "true", email:"false", sms: "false"}).save();
// now use the id in the user doc
await new User({name: userData.name,alerts: alertDoc._id }).save()
In case you want to embed the whole document instead of just storing the ref. You could modify schema of user model. Define your schema like this.
var alertsSchema = new Schema({
push: {type: String, default: "true"},
email: {type: String, default: "false"},
sms: {type: String, default: "false"}
});
....
var userSchema = new Schema({
name: { type: String }
created: { type: Date, default: Date.now()},
alerts: alertsSchema,
documents: [{type: Schema.ObjectId,ref: 'Document'}],
});
....
// now this should work
var user = new User({
name: "<some name>",
alerts: {push: "true", email:"false", sms: "false"}
});
There is a small issue in userSchema. From your schema definition, it looks like you want to store only references to alerts and documents. The right syntax here would be alerts: {type: Schema.Types.ObjectId,ref: 'Alert'}. Please notice that extra Types in it.
Another issue here is, you are trying to store complete alert object inside user document. Mongoose can't allow that, as in your schema, you have told mongoose to save only references to the alert document. So what you need to do here is, create an alert document, get it's _id and then store it in alert field of user document.
Whenever you want to fetch the complete user schema, you can just populate alert and documents.
Hope this answer improves your understanding of how mongoose schema works.

Resources