Unable to sort mongoose query - node.js

Note
I went through other answers before posting this, so expecting help here.
Mongoose model :
const projectSchema = new mongoose.Schema({
user: { type: String, required: true },
profile: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Profile'
},
title: { type: String, required: true },
image: { type: String },
description: { type: String, required: true, minlength: 300, maxlength: 3000 },
comments: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}],
state: { type: Boolean, default: true },
requests: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Request'
}],
createdAt: { type: Date, default: Date.now() }, // <== created at
Query:
const projects = await Project.find({
state: true
})
.limit(10)
.sort({ createdAt: -1 }) // <== have tried +1 and
// also ({ date: -1 }),
// it returns in ascending order
return res.send(projects || [])
I have tried +1 in place of 1 and also ({ date: -1 }), it sends in ascending order.
I want the latest projects to be on top of array.

I was able to resolve it.
I needed to sort before I put the limit.
const projects = await Project.find({
state: true
})
.sort({ createdAt: -1 }) // here
.limit(10)
return res.send(projects || [])

Related

How to limit one field only in mongoose

I am creating something like fb... I want to show 3 comments only on home page... How to limit one field only... my schema is this:
const postSchema = new Schema({
admin: { type: Types.ObjectId, ref: 'users', required: true },
text: { type: String, required: true },
comments: [{
postId: { type: Types.ObjectId, ref: 'posts', required: true },
admin: { type: Types.ObjectId, ref: 'users', required: true },
comment: { type: String, required: true },
time: { type: Date, default: Date.now }
}],
createdAt: { type: Date, default: Date.now },
modified: { type: Boolean, default: false }
});
I have all comments in an array... I want to limit them... Please help
Try using $slice (projection) in MongoDB. The $slice operator controls the number of items of an array that a query returns.
If you want to fetch first 3 comments only then your query will be as below:
db.slice.find( {}, { comments: { $slice: 3 } } )
In case if you want last 3 comments then your query will be:
db.slice.find( {}, { comments: { $slice: -3 } } )

Mongoose – querying subdocuments referenced by ID

I have 2 schemas connected like this:
const Brand = new mongoose.Schema({
_id: { type: String, required: true },
name: {
type: String,
required: true,
},
products: [{
type: String,
ref: 'Product',
}],
});
const Product = new mongoose.Schema({
_id: { type: String, required: true },
name: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
});
I want to find brands that have certain types of products, so I wrote a query (not including async/await and promises in the code below for simplicity)
const docs = Brand.find({ 'products.type': 'my-type-here' })
.populate([
{
path: 'products',
},
])
.sort({ index: 1 })
.exec();
This gives me 0 results, yet I know that there are brand with the type of products. What am I doing wrong here? Is it connected with the fact, that products are only referenced by their ids when I invoke find method?

Mongoose's populate() does not return data from database

I have the following Schema, and Base is the one-for-all collector of info:
const BaseSchema = mongoose.Schema({
creatorId: { type: mongoose.Schema.Types.ObjectId, required: true },
title: { type: String, required: true },
created: { type: Date, default: Date.now() },
users: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }],
messages: [{ type: mongoose.Schema.Types.ObjectId, ref: "Message" }]
});
And:
const BaseUserSchema = mongoose.Schema({
userId: { type: String },
baseId: { type: mongoose.Schema.Types.ObjectId },
created: { type: Date, default: Date.now() },
acceptedMembership: { type: Boolean, default: false },
isCreator: { type: Boolean, default: false }
});
(I have one for Message which looks about the same)
The latter one is referred to as User in const User = mongoose.model("User", UserSchema);
When I create a new Base I automatically add a user to a list within. In the DB i can see that the user does exist, but when I call the following the field does not populate:
Base.find({ creatorId: req.params.id })
.populate("users", "messages")
.exec()
.then(data => console.log(data));
I get the following from the console.log:
[ { created: 2018-09-05T03:41:45.416Z,
users: [],
messages: [],
_id: 5b8f508b2760c5329c13a9be,
creatorId: 5b86f7970cd98b2004969bf0,
title: 'testBase1',
__v: 1 } ]
When I first create the base via React front-end, and the base gets added to a list, I see that the users.length is 1, or the length of the automatically created user. When I refresh the page, however, the userlist is empty in the console.
Adding:
Forget to show how I populate the userlist upon creation:
router.post("/add", jwtAuth, (req, res) => {
Base.create({
creatorId: req.body.userId,
title: req.body.title
}).then(baseInfo => {
BaseUser.create({
userId: req.body.username,
baseId: baseInfo._id,
created: Date.now(),
acceptedMembership: true,
isCreator: true
})
.then(baseuser => {
baseInfo.users.push(baseuser);
return baseInfo.save();
})
.then(base => res.json(base.serialize()));
});
});
Answer:
.populate("users", "messages")
to begin with will return messages from users. It has to be two separate entries, like so:
.populate("users")
.populate("messages")
Then, in the model
users: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }],
refers to the wrong ref. It has to be
users: [{ type: mongoose.Schema.Types.ObjectId, ref: "BaseUser" }],
from
const BaseUser = mongoose.model("BaseUser", BaseUserSchema);
At last, I am not sure if this is needed, but I added a ref to each of the to-be-populated items:
baseId: { type: mongoose.Schema.Types.ObjectId, required: true, ref: "Base" },
baseId: { type: mongoose.Schema.Types.ObjectId, ref: "Base" },
Now it works like a charm, all without additional fetch

Mongoose: Populate referenced SubDocument

I've the following schemas:
Occurence that references a competence in the CostCenter subdocuments competences.
const OccurrenceSchema = new mongoose.Schema({
date: {
type: Date,
default: Date.now,
},
competence: {
type: mongoose.Schema.Types.ObjectId,
ref: 'CostCenter.competences',
},
...
})
CostCenter where I've an array of subdocuments that can be ref by Occurence.
const CostCenterSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
},
competences: [CompetenceSchema],
});
And finally the Competence.
const CompetenceSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
},
});
When I try to populate the competence, I get "Schema hasn't been registered for model \"CostCenter.competences\".\nUse mongoose.model(name, schema)".
const occurrence_list = (request, response) => {
Occurrence.find()
.populate('occurrence origin tag entity method priority competence')
.then(occurrences => response.send(occurrences))
.catch(e => response.send(e));
};
How can I possibly populate the occurrence when referencing a subdocument?
First, you need to change your Occurrence model to this
const OccurrenceSchema = new mongoose.Schema({
date: { type: Date, default: Date.now },
competence: { type: mongoose.Schema.Types.ObjectId, ref:'CostCenter' }
});
mongoose.model('Occurrence', OccurrenceSchema);
and CostCenter model:
const CostCenterSchema = new mongoose.Schema({
name: { type: String, required: true, trim: true },
competences:[{ type: mongoose.Schema.Types.ObjectId, ref: 'Competence' }],
});
mongoose.model('CostCenter', CostCenterSchema);
finally Competence model:
const CompetenceSchema = new mongoose.Schema({
name: { type: String, required: true, trim: true }
});
mongoose.model('Competence', CompetenceSchema);
To populate the competence from Occurrence, you can do so:
Occurrence.find({ your_query })
.populate('competence')
.then(occurrences => response.send(occurrences))
.catch(e => response.send(e));
Hope it helps!

mongoose is not able to populate ref id with default value empty object

My schema is as shown below:
const order = new Schema({
order_status: Number,
foodtruck_id: { type: Schema.Types.ObjectId, ref: 'foodtruck' },
customer_id: { type: Schema.Types.ObjectId, ref: 'user' },
items: [{ type: Schema.Types.ObjectId, ref: 'items' }],
user_type: Boolean,
order_time: Date,
order_rating: { type: Number, default: 5.0 },
order_issue_comments: String,
order_special_instruction: String,
order_total: Number,
order_location: String,
order_coupon_code: String,
payment_id: { type: Schema.Types.ObjectId, ref: 'payment' },
order_meta: { type: Schema.Types.Mixed, ref: 'order_sub_info', default: {} }
}, { versionKey: false }, { minimize: false });
my query is as shown below:
order.find({
'foodtruck_id': foodtruck_id.trim()
}).populate('customer_id', {
'_id': 1,
'user_name': 1,
'email_id': 1,
'ph_no': 1,
'login_type': 1
}).populate('items').
populate('order_meta', 'order_otp').exec((err, orderList) => {
if (err) res.json({
status: '500',
message: err
});
else {
console.log("called");
res.json({
status: '200',
message: 'Order list',
data: orderList
});
}
});
For this query,it is giving me Cast to ObjectId failed for value at path _id as order_meta has default value {}. How to have effective populate query so that It can take care of this testcase?
It is not good idea to put empty object in a place, where reference id is expected. Both - for having problem with populate and for common sense too (if it is field which has reference, it should be null/undefined or reference itself).
It is common that you want to transform your data at some endpoint, but it should not interfere with database or business logic of application.
You can defined toJSON method that should be used for your model. In your case
const order = new Schema({
order_status: Number,
foodtruck_id: { type: Schema.Types.ObjectId, ref: 'foodtruck' },
customer_id: { type: Schema.Types.ObjectId, ref: 'user' },
items: [{ type: Schema.Types.ObjectId, ref: 'items' }],
user_type: Boolean,
order_time: Date,
order_rating: { type: Number, default: 5.0 },
order_issue_comments: String,
order_special_instruction: String,
order_total: Number,
order_location: String,
order_coupon_code: String,
payment_id: { type: Schema.Types.ObjectId, ref: 'payment' },
order_meta: { type: Schema.Types.ObjectId, ref: 'order_sub_info'}
}, { versionKey: false }, { minimize: false });
order.options.toJSON = {
transform(zipRequestDocument, ret, options) { // eslint-disable-line no-unused-vars
if (!ret.order_meta){
ret.order_meta = {};
}
},
};

Resources