I'm a newbie in MongoDB and mongoose. and I'm trying to create a simple messenger using MongoDB.
Subscription model:
const SubscriptionSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User',
index: { unique: true }
},
conversations: [
{
conversation: {
type: Schema.Types.ObjectId,
ref: 'Conversation'
},
lastSeenMessage: {
type: Schema.Types.ObjectId,
ref: 'Message'
}
}
]
});
Conversation model:
const ConversationSchema = new Schema(
...,
{ timestamps: true, toJSON: { virtuals: true } }
);
ConversationSchema.virtual('messages', {
ref: 'Message',
localField: '_id',
foreignField: 'conversation'
});
Message model:
const MessageSchema = new Schema(
{
content: {
type: String,
maxlength: [1440, 'Text too long.']
},
type: {
type: String
},
conversation: {
type: Schema.Types.ObjectId,
ref: 'Conversation'
},
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
}
)
I need to retrieve m latest conversations of user and n messages within each conversation when they are written after last seen message. So I used mongoose like this:
let subscription = await Subscription.findOne(
{ user }
//{ ObjectArray: { $slice: [(page - 1) * limit, (page - 1) * limit + limit] } }
).populate({
path: 'conversations.conversation',
model: 'Conversation',
populate: {
path: 'messages',
match: { _id: { $gt: 'conversations.lastSeenMessage' } },
options: {
skip: 0,
limit: 50,
sort: { updatedAt: -1 }
}
}
});
the match condition doesn't works in above query. how can use upper model in match condition in the deep population ?
Is it possible or I must try another way?
Related
I am building an e-commerce application and this is my orders schema.
const mongoose = require("mongoose");
const orderSchema = new mongoose.Schema({
buyer: {
type: mongoose.Schema.Types.ObjectId,
ref: "buyer",
required: true
},
items: [{
item: {
type: mongoose.Schema.Types.ObjectId,
ref: "item",
},
quantity: {
type: Number,
default: 1
}}
],
seller: {
type: mongoose.Schema.Types.ObjectId,
ref: "seller",
required: true
},
location: {
type: {
type: "String",
enum:['Point']
},
coordinates: {
type: [Number],
index: '2dsphere'
}
},
sendAt:{
type: Date,
default: Date.now
}
});
const orderModel = mongoose.model("orders", orderSchema);
module.exports = orderModel;
I want to have an array having item-reference-id and quantity.
But with the above schema when i enter data, each item is acting as an another sub-document and having _id. Query response image.
I have found solution:
order: [
{
_id: false,
item: {
type: mongoose.Schema.Types.ObjectId,
ref: "items",
required: true,
},
quantity: { type: Number },
},
],
_id: false will stop the subdocument from creating another id for the subdocument.
This is what I have so far. This is my AnswerSchema with a comments array nested within that I am trying to update.
const AnswerSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'user',
},
question: {
type: Schema.Types.ObjectId,
ref: 'question',
},
text: {
type: String,
required: true,
},
name: {
type: String,
},
avatar: {
type: String,
},
views: {
type: Number,
},
date: {
type: Date,
default: Date.now,
},
answerLikes: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'user',
},
},
],
comments: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'user',
},
text: {
type: String,
required: true,
},
name: {
type: String,
},
avatar: {
type: String,
},
commentLikes: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'user',
},
},
],
date: {
type: Date,
default: Date.now,
},
},
],
})
and here is my update route that I am trying to use to update the comments array text field
try {
const updatedAnswer = await Answer.findOneAndUpdate(
{ _id: req.params.answer_id },
{
$set: { 'comments.$[comment].text': formattedAnswer },
},
{
arrayFilters: [{'comment._id': req.params.comment_id }],
},
{ new: true }
)
res.json(updatedAnswer)
I keep getting the error 'Callback must be a function, got [object Object]' and cant figure out a fix.
Any ideas?
Thanks!
The problem in your code is that you are passing 4 parameters to the findOneAndUpdate function.
The 4th argument is a callback which accepts a function:
(err /* an error if occurred */, doc /* the updated document */) => {}
In order to solve that you need to combine your last 2 arguments into one object like:
{
arrayFilters: [{'comment._id': req.params.comment_id }],
new: true
}
Final query:
const updatedAnswer = await Answer.findOneAndUpdate(
{ _id: req.params.answer_id },
{
$set: { 'comments.$[comment].text': formattedAnswer },
},
{
arrayFilters: [{'comment._id': req.params.comment_id }],
new: true
}
)
The 4th argument in findOneAndUpdate function takes in a callback function that was where your error was.
Try this
try{
const updatedAnswer = await Answer.findOneAndUpdate(
{ _id: req.params.answer_id },
{
$set: { 'comments.$[comment].text': formattedAnswer },
},
{
arrayFilters: [{'comment._id': req.params.comment_id }],
new: true
}
);
res.json(updatedAnswer);
}catch(err){
//console.log(err)
}
How to use populate api to get inbox data populated by user model?
Here shcemas:
const inboxSchema = Schema({
messageList: [{
from: { type: Schema.Types.ObjectId, require: true, ref: 'user' },
to: { type: Schema.Types.ObjectId, require: true, ref: 'user' },
}]
})
const userSchema = Schema({
name: { type: String, require: true },
})
expected result, eg:
{
"messageList": [
{
from: {
_id: xxxxxxxxxxxxxxx,
name: 'Smith'
},
to: {
_id: zzzzzzzzzzzzzzz,
name: 'John'
}
}
]
}
You could also use The .populate() Query like Below."
.populate([{
path:"messageList.from"
select:"_id name"
},{
path:"messageList.to"
select:"_id name"
}])
Suppose I have the following schemas:
var QuizSchema = new mongoose.Schema({
name: { type: String, required: true },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }],
questionGroups: [{ type: mongoose.Schema.Types.ObjectId, ref: 'QuestionGroup' }]
});
var QuestionSchema = new mongoose.Schema({
number: { type: String, required: true }, // e.g. 1, a, i, anything
question: { type: String, required: true },
type: { type: String, enum: ['multiple choice', 'multiple select', 'short answer'] },
choices: [String],
answers: [String]
});
var QuestionGroupSchema = new mongoose.Schema({
number: { type: String, required: true }, // e.g. 1, a, i, anything
prompt: { type: String },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }]
});
I am trying to design a way that will allow me to order questions and question groups together.
I was thinking maybe of adding a new field order
var QuizSchema = new mongoose.Schema({
// ...
order: [
{
type: { type: String, enum: ['Question', 'QuestionGroup'] },
id: mongoose.Schema.Types.ObjectId // reference
}
]
});
such that in the database, the field would contain something such as
[
{ type: 'Question', id: ObjectId('57867a34567g67790') },
{ type: 'Question', id: ObjectId('57867a34567g67765') },
{ type: 'QuestionGroup', id: ObjectId('69864b64765y45645') },
{ type: 'Question', id: ObjectId('57867a34567g67770') },
{ type: 'QuestionGroup', id: ObjectId('69864b64767y45647') }
]
This may mean that I would need to "populate" the ordered list of questions and question groups as
quiz.populate('questions questionGroups').exec(function (err, quiz) {
// sort questions and groups by the order
quiz.order = quiz.order.map(function (o) {
if (o.type === 'QuestionGroup') {
return quiz.questionGroups.id(o.id);
}
return quiz.questions.id(o.id);
});
});
So my question: is there a better way to design this?
Virtuals can come in handy here; without persisting order field in db and doing calculations on client each time:
var QuizSchema = new mongoose.Schema({
name: { type: String, required: true },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }],
questionGroups: [{ type: mongoose.Schema.Types.ObjectId, ref: 'QuestionGroup' }]
},
{
toObject: {
virtuals: true
},
toJSON: {
virtuals: true
}
}
);
QuizSchema
.virtual('order')
.get(function() {
return this.questions.concat(this.questionGroups); //questions followed by questionGroups
});
Sort on createdAt is of course optional, but for that you need to have this field in Question and QuestionGroup:
Quiz.find({}, function (err, quiz) {
//...
})
.populate({path : 'questions', options: {sort: { 'createdAt': 1 }}})
.populate({path : 'questionGroups', options: {sort: { 'createdAt': 1 }}});
I am trying to get the below query to work which I found in this post:
Remove array entries containing an empty array
However, I get the error
Error: Can't use $exists with ObjectId.
I am not sure if it is a version thing, I am using Mongo DB version 3.2.6
Thanks
var ProductRateSchema = new Schema({
product: {
type: Schema.ObjectId,
ref: 'products'
},
user: {
type: Schema.ObjectId,
ref: 'User'
},
rates: [{
type: Schema.ObjectId,
ref: 'rates'
}]
});
var InventorySchema = new Schema({
name: {
type: String,
default: '',
required: 'Please enter in a name',
trim: true
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
},
products: [productRateSchema]
});
var inventoryId = req.body.inventoryId;
var productId = req.body.productId;
Inventory.update(
{ "products.rates.0": { "$exists": false } },
{
"$pull": {
"products": { "rates.0": { "$exists": false } }
}
},
{ "multi": true },
function(err,numAffected) {
}
)