Get newest object of each subset - mongodb

I'm working on an app based on mongodb and mongoose. One of my schemas has the following form:
var schema = new mongoose.Schema(
{
name: { type: String },
timestamp: { type: Number, default: Date.now },
// further properties
});
schema.index({ name: 1, timestamp: -1 });
I'd like to retrieve the newest object (i.e., largest timestamp) for a set of name-strings.
E.g., consider the set of names ['a','b']. How do I query the database such that I get returned a collection of objects that contains both, the newest entry where id=='a', and the newest entry where id=='b'?
[Update: The idea is to avoid having to query the database multiple times (i.e. once for each name).]

"How do I query the database such that I get returned a collection of objects that contains the newest where id=='a'"
db.collection.find({ name : 'a' }).sort({ timestamp : -1 })
"and the newest entry where id=='b'?"
db.collection.find({ name : 'b' }).sort({ timestamp : -1 }).limit(1)

Related

How to ignore schema default value when querying in mongoose?

eg.
created: {
type: Date,
default: Date.now,
}
, but in mongodb there exists some old data without "created" field. How can I get real data but not Date.now.
We can use $exists here to fetch the new documents which will have the created field.
Mongo Shell Query
db.collection.find( { created: { $exists: true} } )

How to query a document using mongoose and send the document to the client with only one relevant element from an array field to the client?

I have the following schema:
var lessonSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: String,
students: [{
_id: mongoose.Schema.Types.ObjectId,
attendance: {
type: Boolean,
default: false,
},
}],
});
The students array is an array of students who attended the particular lesson. I want to find a lesson using whether a particular user is present in the students array and then sent only that element of the students array which corresponds to the user making the request, along with all other fields as it is. For example, the query should return:
{
_id: 'objectid',
name: 'lesson-name'
students: [details of just the one student corresponding to req.user._id]
}
I tried using:
Lesson.find({'students._id': String(req.user._id)}, {"students.$": 1})
The query returns the document with just the id and the relevant element from the students array:
{
_id: 'objectid'
students: [details of the one student corresponding to req.user._id]
}
I tried using:
Lesson.find({'students._id': mongoose.Types.ObjectId(req.user._id)})
This returns the document with the details of all the students:
{
_id: 'objectid',
name: 'lesson-name'
students: [array containing details of all the students who attended the lesson]
}
How can I modify the query to return it the way I want?
You can return the name field by adding it to the projection object like this:
Lesson.find({ "students._id": String(req.user._id) }, { "name": 1, "students.$": 1 })
When you add a projection object (2nd parameter to find), the _id field is returned by default, plus whichever fields you set to 1.
Therefore, you were returning just the _id and the desired student but not the name field.
If you want to return all other fields and just limit the array to the matched item then you can make use of $slice in your projection:
Lesson.find({ "students._id": String(req.user._id) }, { "students.$": { $slice: 1 } })

What's the strategy for creating index for this query?

I have a comment model for each thread,
const CommentSchema = new mongoose.Schema({
author: { type: ObjectID, required: true, ref: 'User' },
thread: { type: ObjectID, required: true, ref: 'Thread' },
parent: { type: ObjectID, required: true, ref: 'Comment' },
text: { type: String, required: true },
}, {
timestamps: true,
});
Besides a single query via _id, I want to query the database via this way:
Range query
const query = {
thread: req.query.threadID,
_id: { $gt: req.query.startFrom }
};
CommentModel.find(query).limit(req.query.limit);
My intention here is to find comments which related to a thread then get part of the result. It seems this query works as expected. My questions are:
Is this the right way to fulfill my requirement?
How to proper index the fields? Is this a compound index or I need to separate indexing each field? I checked the result of explain(), it seems as long as one of the query fields contains an index, the inputStage.stage will always have IXSCAN rather than COLLSCAN? Is this the key information to check the performace of the query?
Does it mean that every time I need to find based on one field, I need to make an index for these fields? Let's say that I want to search all the comments that are posted by an author to a specific thread.
Code like this:
const query = {
thread: req.query.threadID,
author: req.query.authorID,
};
Do I need to create a compound index for this requirement?
If you want to query by multiple fields then you have to create compound index.
for example
const query = {
thread: req.query.threadID,
author: req.query.authorID,
};
if you want to use this query then you have to create compound index like :
db.comments.createIndex( { "thread": 1, "author": 1 } );
Then that is, the index supports queries on the item field as well as both item and stock fields:
db.comments.find( { thread: "threadName" } )
db.comments.find( { thread: "threadName", author: "authorName" }
but not supports this one
db.comments.find( { author: "authorName", thread: "threadName" }
say if you create index for { "thread": 1, "author": 1, "parent": 1 } then for bellow ordered query support index
the thread field,
the thread field and the author field,
the thread field and the author field and the parent field.
But not support for bellow order
the author field,
the parent field, or
the author and parent fields.
For more read this

Indexing mongoose timestamps

I have a mongoose schema like so:
let PictureSchema = mongoose.Schema({
...
...
}, {timestamps: true})
PictureSchema.index({"createdAt": 1});
PictureSchema.index({"updatedAt": 1});
I'm trying to get the fields "createdAt" and "updatedAt" indexed. When using
PictureSchema.index({"createdAt": 1});
PictureSchema.index({"updatedAt": 1});
It doesn't work and all other indexes except "_id_" also stop working.
I got a variation of this working by using the mongoose-timestamp plugin like so:
PictureSchema.plugin(timestamps, {
createdAt: {
name: 'createdAt',
type: Date,
index: true
},
updatedAt: {
name: 'updatedAt',
type: Date,
index: true
}
})
But the problem I have with this plugin is that it doesn't record UTC time but system time. A fix to my problem would also be to get mongoose-timestamp to record UTC time but preferably I would prefer to index the fields provided by the built in timestamps.
Turns out
PictureSchema.index({"createdAt": 1});
PictureSchema.index({"updatedAt": 1});
Works perfectly. Had to restart the database server a couple of times to get it working.
Wondering why is that?

Why does this query of a nested object not work?

Given this Job object structure, where users is an array of user documents:
{
_id: "56228b5ba851623018f88ff7",
created: "2015-10-17T17:54:35.475Z",
active: true,
workOrders: [{
startDate: "2015-10-18T05:00:00.000Z",
name: "Test1",
users: [{...}]
},{
startDate: "2015-10-20T05:00:00.000Z",
name: "Test2",
users: [{...}]
}]
}
Why does this query work:
Job.find({
'workOrders.users._id' : userId,
'created' : { '$gte' : new Date('10/17/2015'), '$lt': new Date('10/25/2015')},
'active' : true
}).exec(cb);
But this one does not:
Job.find({
'workOrders.users._id' : userId,
'workOrders.startDate' : { '$gte' : new Date('10/20/2015'), '$lt': new Date('10/25/2015')},
'active' : true
}).exec(cb);
Initial thought is that workOrder.startDate is not of type date, but it is.
Is there some reason I cannot query the nested workOrder object this way? I'm just confused why I can query the created date property on the Job document, I can query the array of nested user documents in the workOrders array, but then this last query doesn't work.
Note, there are no errors thrown, it simply returns no results. Even if I expand the date range to definitely be inclusive of all dates such as 01/01/2000 to 01/01/2016 I get nothing.
Can you replace both startDate's with:
created: {
type: Date,
default: Date.now
},
...to establish whether you can add multiple dates to a document, and that your startDate code is not incorrect?