nodejs mongodb check a range against a single value - node.js

I have some documents as following in db:
User A:
{
"_id" : ObjectId("5f1ec1869ea3e213cc2a159e"),
"age": 27,
"gender": "male",
"preference": {
"ageGroup": "25-35",
"gender": "female"
}
}
User X:
{
"_id" : ObjectId("378ec1869ea3e212333a159e"),
"age": 27,
"gender": "female",
"preference": {
"ageGroup": "20-30",
"gender": "male"
}
}
I am trying to filter docs based on :
other users' profile age and gender must match against the user's preference.
other users' preference also must match against the user's profile age and gender.
Here's what I am trying:
const getGsaMatches = async function (currentUser) {
const user: any = await User.findOne({ _id: currentUser._id });
const userPreference = user.preference;
const ageRange = userPreference.ageGroup.split("-");
const minAgeRange = ageRange[0];
const maxAgeRange = ageRange[1];
const matchResponse: any = await User.find({
"gender": userPreference.gender,
"age": { $gte: minAgeRange, $lte: maxAgeRange },
"preference.gender": user.gender,
"preference.ageGroup": user.age, // I'm stuck here
_id: { $ne: currentUser._id }
});
return matchResponse;
}
preference.ageGroup contains value in 25-30 string format.
How can I store this field so that it can be compared against a given single integer value?
I hope I made the problem clear.

A good way to start is to actually store it as an Integer. If you're using Mongo v4.0+ you can also use $toInt but if this is a query you're doing often then you might aswell save it in a structure like:
ageGroup: {
min: 20,
max: 30,
value: "20-30"
}
Now you can do something like this:
const matchResponse: any = await User.find({
_id: { $ne: currentUser._id },
"gender": userPreference.gender,
"age": { $gte: minAgeRange, $lte: maxAgeRange },
"preference.gender": user.gender,
"preference.ageGroup.min": {$lte: user.age},
"preference.ageGroup.max": {$gte: user.age}
});
Assuming you don't want to change the structure then you'll have to use an aggregation with $toInt as I suggested like so:
const matchResponse: any = await User.aggregate([
{
$addFields: {
tmpAgeField: {
$map: {
input: {$split: ["$preference.ageGroup", "-"]},
as: "age",
in: {$toInt: "$$age"}
}
}
}
},
{
$match: {
_id: { $ne: currentUser._id },
"gender": userPreference.gender,
"age": { $gte: minAgeRange, $lte: maxAgeRange },
"preference.gender": user.gender,
"tmpAgeField.0": {$lte: user.age},
"tmpAgeField.1": {$gte: user.age}
}
}
]);

Related

Nodejs Mongoose | Fetch the array of values for a given field

From the query below
let fields = { 'local.email': 1 };
UserModel.find({ '_id': { $in: userIds } }).select(fields).setOptions({ lean: true });
Result which we get is
[
{
"_id": "54bf2d7415eaaa570c9ed5a0",
"local": {
"email": "neo#q.com"
}
},
{
"_id": "54bfb753e4c9406112267056",
"local": {
"email": "test#q.com"
}
}
]
Is is possible to modify query itself to get below result
["neo#q.com", "test#q.com"]
Thanks in advance
You could use aggregate to return a list of objects with the emails and the map them to an array of strings:
const emailObjs = await UserModel.aggregate([
{
$match: {
_id: {
$in: userIds
}
}
},
{
$project: {
"_id": 0,
"email": "$local.email"
}
}
]);
const emails = emailObjs.map(obj => obj.email)
Link to playground for the query.

Get an element from an array of objects with an id in mongodb using nodejs and mongoose

I've a many documents like this
user:62e13ae4f9a38f7610e70bd7,
_id :62e13ae4f9a38f7610e70bdb
transactions:{
{
"amount": 50,
"category": "Bills",
"type": "Expense",
"date": "2022-01-20T00:00:00.000Z",
"_id": "62e13ae4f9a38f7610e70be0"
},
{
"amount": 100,
"category": "Lottery",
"type": "Income",
"date": "2022-01-20T00:00:00.000Z",
"_id": "62e13ae4f9a38f7610e70be1"
},
{
"amount": 200,
"category": "Salary",
"type": "Income",
"date": "2022-01-20T00:00:00.000Z",
"_id": "62e13ae4f9a38f7610e70be2"
}
}
And I want to retrieve an object from the transactions array with a particular id (I only want that object )
I tried some methods like
const transactions = await Transactions.find({
user: req.user._id,
"transactions._id": {$eq: req.params._id },
});
const transactions = await Transactions.find({
user: req.user._id,
"transactions": { _id: req.params._id },
});
const transactions = await Transactions.find({
user: req.user._id,
"transactions": { $elemMatch:{_id: req.params._id }},
});
but nothing seems to be working, can anyone help me to solve this And please mention the mistake I made.
Try to match the transactions._id directly:
"transactions._id": req.params._id
Example
const transactions = await Transactions.find({
user: req.user._id,
"transactions._id": req.params._id
});
Update
From the comment, it's possible to use projection as the second parameter of .find() to return only the object it found in the transactions.
const transactions = await Transactions.find({
user: req.user._id,
"transactions._id": req.params._id
}, { "transactions.$": 1 });
More information
If you only want that matching element, you have to use aggreation.
db.collection.aggregate([
{
$unwind: "$transactions"
},
{
$match: {
"transactions.id": "idGoesHere"
}
}
])
As you commented in the other answer, you could use positional operator to project the matching elements as well.

Mongodb mongoose agregation and getting length of array [duplicate]

I am VERY close to getting what I want out of this query... but I only want SOME of the fields returned and right now it is returning all of them
NOTE: This is a refinement : I am now asking how to return only certain fields, while my similar question asks how to return the data between a start and end date
In addition, can somebody please please provide an answer using the MongoDB Playground with MY data sets so I can try it out... I can't quite figure out how to "name" the data sets so they work in the playground !
Register Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const RegisterSchema = new Schema({
userId: {type: Schema.Types.ObjectId, required: true},
accessToken: {type:String, required: true, default: null},
})
module.exports = Register = mongoose.model( 'register', RegisterSchema)
Here is some register data
[
{
"_id": "5eac9e815fc57b07f5d0d29f",
"userId": "5ea108babb65b800172b11be",
"accessToken": "111"
},
{
"_id": "5ecaeba3c7b910d3276df839",
"userId": "5e6c2dddad72870c84f8476b",
"accessToken": "222"
}
]
The next document contains data that is related to the Register schema via the accessToken
Notifications
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const NotificationSchema = new Schema({
accessToken: {type:String, required: true},
summaryId: {type:Number, required: true},
dateCreated: {type: Date, default: Date.now},
// I don't want these returned in the final results
dontWantThis1: {type:Number, required: true},
dontWantThis2: {type:Number, required: true},
})
module.exports = Notification = mongoose.model( 'notification', NotificationSchema)
Here is some notification data
[{
"_id": "5ebf0390c719e60004f42e74",
"accessToken": "111",
"summaryId": 1111,
"dontWantThis1": 61,
"dontWantThis2": 62,
"dateCreated": "2020-04-17T00:00:00.000+00:00" },
{
"_id": "6ebf0390c719e60004f42e76",
"accessToken": "222",
"summaryId": 2221,
"dontWantThis1": 71,
"dontWantThis2": 72,
"dateCreated": "2020-04-18T00:00:00.000+00:00" },
{
"_id": "6ebf0390c719e60004f42e78",
"accessToken": "111",
"summaryId": 1112,
"dontWantThis1": 611,
"dontWantThis2": 622,
"dateCreated": "2020-05-25T00:00:00.000+00:00" },
{
"_id": "6ebf0390c719e60004f42e80",
"accessToken": "222",
"summaryId": 2222,
"dontWantThis1": 711,
"dontWantThis2": 722,
"dateCreated": "2020-05-26T00:00:00.000+00:00" }
]
Works, returns data between the two dates, but
This code returns everything, including the 'dontWantThis1' and 'dontWantThis2'
NOTE
I do not want the fields prefaced with 'dontWantThis' - but that is only to show which ones I don't want... I don't literally want to exclude fields prefaced with 'dontWantThis' ..... they could be named 'foo' or 'apple' or 'dog' they are just named that way to indicate that I don't want them
// make sure the input dates are REALLY date objects
// I only want to see notifications for the month of May (in this example)
var dateStart = new Date('2020-05-01T00:00:00.000+00:00');
var dateEnd = new Date('2020-05-30T00:00:00.000+00:00');
var match = {$match: { userId: mongoose.Types.ObjectId(userId) } };
var lookup ={
$lookup:
{
from: "my_Notifications",
localField: "accessToken",
foreignField: "accessToken",
as: "notifications"
}
};
var dateCondition = { $and: [
{ $gte: [ "$$item.dateCreated", dateStart ] },
{ $lte: [ "$$item.dateCreated", dateEnd ] }
]}
var project = {
$project: {
notifications: {
$filter: {
input: "$notifications",
as: "item",
cond: dateCondition
} } }
};
var agg = [
match,
lookup,
project
];
Register.aggregate(agg)
.then( ..... )
Try 1
I thought I could do something like this, but it still returns ALL of the notification fields
var project = {
$project: {
"_id": 1,
"userId": 1,
"accessToken":1,
"count":{$size:"$notifications"},
"notifications._id":1,
"notifications.summaryId": 1,
"notifications.dateCreated":1,
notifications : {
$filter: {
input: "$notifications",
as: "item",
cond: dateCondition
},
}}
};
SOLUTION
I created another projection and added that to the pipeline:
var project2 = {
$project: {
"_id": 1,
"userId": 1,
"accessToken":1,
"count":{$size:"$notifications"},
"notifications._id":1,
"notifications.summaryId": 1,
"notifications.dateCreated":1,
"notifications.dateProcessed":1,
}
};
var agg = [
match,
lookup,
project,
project2,
];
Thanks!!
https://stackoverflow.com/users/6635464/ngshravil-py was spot on.
I created another projection:
var project2 = {
$project: {
"_id": 1,
"userId": 1,
"accessToken":1,
"count":{$size:"$notifications"},
"notifications._id":1,
"notifications.summaryId": 1,
"notifications.dateCreated":1,
"notifications.dateProcessed":1,
}
};
Then added it to my aggregation pipeline:
var agg = [
match,
lookup,
project,
project2,
];
Worked ! -- thank you https://stackoverflow.com/users/6635464/ngshravil-py
You need to convert notifications.dateCreated to ISODate, as your date is in string, by using $dateFromString and $map operator. i suggest you to do this, because I don't think that you can do date comparison with string formats. Also, make sure that dateStart and dateEnd should also be in ISODate format.
And you need two $project operator in order to achieve this. Also, I don't see any field with userAccessToken, I assume, it's accessToken. Check the below query.
db.Register.aggregate([
{
$lookup: {
from: "my_Notifications",
localField: "accessToken",
foreignField: "accessToken",
as: "notifications"
}
},
{
$project: {
"_id": 1,
"userId": 1,
"accessToken": 1,
notifications: {
$map: {
input: "$notifications",
as: "n",
in: {
"_id": "$$n._id",
"summaryId": "$$n.summaryId",
"dateCreated": {
$dateFromString: {
dateString: "$$n.dateCreated"
}
}
}
}
}
}
},
{
$project: {
"userId": 1,
"accessToken": 1,
"notifications": {
$filter: {
input: "$notifications",
as: "item",
cond: {
$and: [
{
$gte: [
"$$item.dateCreated",
ISODate("2020-05-24T00:00:00Z")
]
},
{
$lte: [
"$$item.dateCreated",
ISODate("2020-05-26T00:00:00Z")
]
}
]
}
}
}
}
},
{
$set: {
"count": {
$size: "$notifications"
}
}
}
])
MongoPlayGroundLink

How to count all documents, count unique fields and sum in same query with Mongoose?

I have an addressmodel like this:
const AddressSchema = new Schema({
address: String,
city: String,
postal_code: String,
country: String,
user_id: String,
postalCodeMeters: Number,
});
And I want to know (for a certain user at a certain postal_code):
How many unique addresses have been visited
How many times have the user visited this postal code
How many meters have they traveled
This code works, but I want to do it in one query, not two:
addressrouter.get("/isexplored/:userid/:postalcode",
async (req, res) => {
console.log(req.params.userid)
try {
const addresses = await Address.aggregate(
[
{ $match:{
user_id: req.params.userid,
postal_code: req.params.postalcode
}},
{$group: {
_id: null,
meters: {$sum: "$postalCodeMeters"},
count: { $sum: 1 }
}
}]
)
console.log(addresses)
const uniqueAddresses = (await Address.find(
{
user_id: req.params.userid,
postal_code: req.params.postalcode
}
).distinct('address')).length
res.json({
time: addresses[0].count,
uniqueAddressesVisited: uniqueAddresses,
meters: addresses[0].meters
});
} catch (err) {
console.log(err)
res.json({ message: err });
}
}
)
If I've understood correctly you can do it in a single $group operation.
Check this query where using $addToSet in the $group stage we can get the unique values. And after that use $size into project to know the length.
Address.aggregate([
{
"$match": {
"user_id": req.params.userid,
"postal_code": req.params.postalcode
}
},
{
"$group": {
"_id": null,
"meters": {
"$sum": "$postalCodeMeters"
},
"count": {
"$sum": 1
},
"address": {
"$addToSet": "$address"
}
}
},
{
"$project": {
"_id": 0,
"count": 1,
"meters": 1,
"uniqueAddressesVisited": {
"$size": "$address"
}
}
}
])
Example here

Query for Matching Dates within Array

I have created a model schema with some nested fields in it, one of it is the Timestamp field:
{_id: Object_id,
name: string,
someArray: [{Timestamp: Date, otherFields: ...},{Timestamp: Date, otherFields...},...],
...,
}
the Timestamp has of type: Timestamp: Date
e.g (Timestamp: 2018-06-01T14:57:45.757647Z)
Now, I want to query only those documents from the array, which are between a start and end date that are received as parameters from a API url...
/link/Collection/:start.:end.:id
My router url (with the parameter strings as query) looks like this:
http://localhost:6000/link/Collection/2018-06-01T14:50:45.2018-06-01T15:17:45.29
My query function in mongoose / node (express) to retrieve the data looks like this:
exports.list_by_start_end_date_ID = function(req, res) {
console.log(req.body)
d_id=req.params.id;
start = req.params.start;
end = req.params.end;
console.log(d_id);
console.log(start)
console.log(end)
console.log(new Date(start));
console.log(new Date(end));
//SomeColl.findById(d_id, "myCollection").where("myCollection.Timestamp").gte(new Date(start)).lte(new Date(end))
SomeColl.findById(d_id, "myCollection",{"myCollection.Timestamp": {"$gte": new Date(start), "$lte": new Date(end)}})
.exec( function(err, fps) {
if (err)
res.send(err);
res.json(fps);
});
};
I get returned:
[{"Timestamp":"2018-06-01T14:57:45.757647Z"...},{"Timestamp":"2018-06-01T15:27:45.757647Z"...},{"Timestamp":"2018-06-01T15:12:45.757647Z"...}]
I don't get any error, I also can create new Date(start) from start and end parameters and it's correct, but as you can see, the document with 15:27 time shouldn't be returned...
I tried out both versions (also commented out version) of the query strings, and I also tried with the blank ISO Date format string that I passed as parameter (start / end) to the url.. but neither worked. How can I compare the dates in mongoose and get the correct documents passed back?
EDIT: I tried to find a workaround by ignoring db api operations, and just parsing the correct documents (subdocuments) of the array with javascript..:
myColl.findById(d_id)
.exec( function(err, fps) {
if (err) {
console.log(err);
res.send(err);
}
else {
//console.log(fps["someArray"])
laenge = fps["someArray"].length;
console.log(laenge);
startts = +new Date(start)
endts = +new Date(end)
TSarray = []
console.log(startts,endts)
for (let doc of fps["someArray"]) {
ts = +new Date(doc["Timestamp"])
//console.log(doc)
if ((ts >= startts) && (ts <= endts)){
TSarray.push(doc)
//console.log(TSarray)
}
}
res.json(TSarray)
//res.json(fps);
}
})//.then((res) => res.json())
};
However, when I want to get the results from the array, I get HTTP 304 error..
I did not find out yet, how to retrieve the corresponding subdocuments (based on a filter criteria) of an array field of one single document..
Do I have to use projection to get only the array field, and then use some filter criteria on that array to get the right subdocuments, or how does it generally work?
//EDIT2:
I tried with the mongoDB aggregation framework, but get returned []:
myColl.aggregate([{$match: {"id":d_id},
someArray: {
$filter: {
input: "$someArray",
as: "fp",
cond: {$and: [
{$gte: [ "$$fp.Timestamp", new Date(start)]},
{$lte: [ "$$fp.Timestamp", new Date(end)]}
]}
}
}
}
}]).exec( function(err, fps) {
if (err) {
console.log(err);
res.send(err);
}
else {
console.log(fps)
res.json(fps);
}
})}
;
This also does not work, is there anything wrong with that query? How can I specify a date range in mongoose with the filter criteria condition?
//EDIT3:
After 5 days of work, I finally managed to get the right documents returned, based on a timestamp. However, to get documents from 14:00:00 o'clock, I have to enter 16:00:00 as url parameter... I know it probably has something to do with UTC and timezones... my tz is Berlin, so I think its UTC +2 as MongoDB servers are in NY I think... How can I best accomodate to that problem?
Here is my function:
myColl.findById(d_id, "someArray")
.exec( function(err, fps) {
if (err) {
console.log(err);
res.send(err);
}
else {
startts = +new Date(start)
endts = +new Date(end)
TSarray = []
for (let doc of fps["Fahrplanabschnitte"]) {
ts = + new Date(doc["Timestamp"]
if ((ts >= startts) && (ts <= endts)){
TSarray.push(doc)
}
}
//for (let a of TSarray) {console.log(a)};
res.json(TSarray);
}
})
};
You're missing the $elemMatch operator on the basic query and the $filter you attempted with the aggregation framework actually has incorrect syntax.
So returning the document matching the dates being within that range in the array is:
// Simulating the date values
var start = new Date("2018-06-01"); // otherwise new Date(req.params.start)
var end = new Date("2018-07-01"); // otherwise new Date(req.params.end)
myColl.find({
"_id": req.params.id,
"someArray": {
"$elemMatch": { "$gte": start, "$lt": end }
}
}).then( doc => {
// do something with matched document
}).catch(e => { console.err(e); res.send(e); })
Filtering the actual array elements to be returned is:
// Simulating the date values
var start = new Date("2018-06-01");
var end = new Date("2018-07-01");
myColl.aggregate([
{ "$match": {
"_id": mongoose.Types.ObjectId(req.params.id),
"someArray": {
"$elemMatch": { "$gte": start, "$lt": end }
}
}},
{ "$project": {
"name": 1,
"someArray": {
"$filter": {
"input": "$someArray",
"cond": {
"$and": [
{ "$gte": [ "$$this.Timestamp", start ] }
{ "$lt": [ "$$this.Timestamp", end ] }
]
}
}
}
}}
]).then( docs => {
/* remember aggregate returns an array always, so if you expect only one
* then it's index 0
*
* But now the only items in 'someArray` are the matching ones, so you don't need
* the code you were writing to just pull out the matching ones
*/
console.log(docs[0].someArray);
}).catch(e => { console.err(e); res.send(e); })
The things to be aware of are that in the aggregate() you need to actually "cast" the ObjectId value, because Mongoose "autocasting" does not work here. Normally mongoose reads from the schema to determine how to cast the data, but since aggregation pipelines "change things" then this does not happen.
The $elemMatch is there because as the documentation says:
When specifying conditions on more than one field nested in an array of documents, you can specify the query such that either a single document meets these condition or any combination of documents (including a single document) in the array meets the conditions.
Use $elemMatch operator to specify multiple criteria on an array of embedded documents such that at least one embedded document satisfies all the specified criteria.
In short $gte and $lt are an AND condition and count as "two", therefore the simple "dot notation" form does not apply. It's also $lt and not $lte, since it makes more sense to be "less than" the "next day" rather than looking for equality up to the "last millisecond".
The $filter of course does exactly what it's name suggests and "filters" the actual array content so that only matching items are left behind.
Demonstration
Full demonstration listing creates two documents, one having only two array items which actually match the date range. The first query shows the correct document is matched with the range. The second shows the "filtering" of the array:
const { Schema, Types: { ObjectId } } = mongoose = require('mongoose');
const uri = 'mongodb://localhost/test';
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
const subSchema = new Schema({
timestamp: Date,
other: String
});
const testSchema = new Schema({
name: String,
someArray: [subSchema]
});
const Test = mongoose.model('Test', testSchema, 'filtertest');
const log = data => console.log(JSON.stringify(data, undefined, 2));
const startDate = new Date("2018-06-01");
const endDate = new Date("2018-07-01");
(function() {
mongoose.connect(uri)
.then(conn =>
Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()))
)
.then(() =>
Test.insertMany([
{
_id: "5b1522f5cdac0b6da18f7618",
name: 'A',
someArray: [
{ timestamp: new Date("2018-06-01"), other: "C" },
{ timestamp: new Date("2018-07-04"), other: "D" },
{ timestamp: new Date("2018-06-10"), other: "E" }
]
},
{
_id: "5b1522f5cdac0b6da18f761c",
name: 'B',
someArray: [
{ timestamp: new Date("2018-07-04"), other: "D" },
]
}
])
)
.then(() =>
Test.find({
"someArray": {
"$elemMatch": {
"timestamp": { "$gte": startDate, "$lt": endDate }
}
}
}).then(docs => log({ docs }))
)
.then(() =>
Test.aggregate([
{ "$match": {
"_id": ObjectId("5b1522f5cdac0b6da18f7618"),
"someArray": {
"$elemMatch": {
"timestamp": { "$gte": startDate, "$lt": endDate }
}
}
}},
{ "$addFields": {
"someArray": {
"$filter": {
"input": "$someArray",
"cond": {
"$and": [
{ "$gte": [ "$$this.timestamp", startDate ] },
{ "$lt": [ "$$this.timestamp", endDate ] }
]
}
}
}
}}
]).then( filtered => log({ filtered }))
)
.catch(e => console.error(e))
.then(() => mongoose.disconnect());
})()
Or a bit more modern with async/await syntax:
const { Schema, Types: { ObjectId } } = mongoose = require('mongoose');
const uri = 'mongodb://localhost/test';
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
const subSchema = new Schema({
timestamp: Date,
other: String
});
const testSchema = new Schema({
name: String,
someArray: [subSchema]
});
const Test = mongoose.model('Test', testSchema, 'filtertest');
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
try {
const startDate = new Date("2018-06-01");
const endDate = new Date("2018-07-01");
const conn = await mongoose.connect(uri);
// Clean collections
await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));
// Create test items
await Test.insertMany([
{
_id: "5b1522f5cdac0b6da18f7618",
name: 'A',
someArray: [
{ timestamp: new Date("2018-06-01"), other: "C" },
{ timestamp: new Date("2018-07-04"), other: "D" },
{ timestamp: new Date("2018-06-10"), other: "E" }
]
},
{
_id: "5b1522f5cdac0b6da18f761c",
name: 'B',
someArray: [
{ timestamp: new Date("2018-07-04"), other: "D" },
]
}
]);
// Select matching 'documents'
let docs = await Test.find({
"someArray": {
"$elemMatch": {
"timestamp": { "$gte": startDate, "$lt": endDate }
}
}
});
log({ docs });
let filtered = await Test.aggregate([
{ "$match": {
"_id": ObjectId("5b1522f5cdac0b6da18f7618"),
"someArray": {
"$elemMatch": {
"timestamp": { "$gte": startDate, "$lt": endDate }
}
}
}},
{ "$addFields": {
"someArray": {
"$filter": {
"input": "$someArray",
"cond": {
"$and": [
{ "$gte": [ "$$this.timestamp", startDate ] },
{ "$lt": [ "$$this.timestamp", endDate ] }
]
}
}
}
}}
]);
log({ filtered });
mongoose.disconnect();
} catch(e) {
console.error(e)
} finally {
process.exit()
}
})()
Both are the same and give the same output:
Mongoose: filtertest.remove({}, {})
Mongoose: filtertest.insertMany([ { _id: 5b1522f5cdac0b6da18f7618, name: 'A', someArray: [ { _id: 5b1526952794447083ababf6, timestamp: 2018-06-01T00:00:00.000Z, other: 'C' }, { _id: 5b1526952794447083ababf5, timestamp: 2018-07-04T00:00:00.000Z, other: 'D' }, { _id: 5b1526952794447083ababf4, timestamp: 2018-06-10T00:00:00.000Z, other: 'E' } ], __v: 0 }, { _id: 5b1522f5cdac0b6da18f761c, name: 'B', someArray: [ { _id: 5b1526952794447083ababf8, timestamp: 2018-07-04T00:00:00.000Z, other: 'D' } ], __v: 0 } ], {})
Mongoose: filtertest.find({ someArray: { '$elemMatch': { timestamp: { '$gte': new Date("Fri, 01 Jun 2018 00:00:00 GMT"), '$lt': new Date("Sun, 01 Jul 2018 00:00:00 GMT") } } } }, { fields: {} })
{
"docs": [
{
"_id": "5b1522f5cdac0b6da18f7618",
"name": "A",
"someArray": [
{
"_id": "5b1526952794447083ababf6",
"timestamp": "2018-06-01T00:00:00.000Z",
"other": "C"
},
{
"_id": "5b1526952794447083ababf5",
"timestamp": "2018-07-04T00:00:00.000Z",
"other": "D"
},
{
"_id": "5b1526952794447083ababf4",
"timestamp": "2018-06-10T00:00:00.000Z",
"other": "E"
}
],
"__v": 0
}
]
}
Mongoose: filtertest.aggregate([ { '$match': { _id: 5b1522f5cdac0b6da18f7618, someArray: { '$elemMatch': { timestamp: { '$gte': 2018-06-01T00:00:00.000Z, '$lt': 2018-07-01T00:00:00.000Z } } } } }, { '$addFields': { someArray: { '$filter': { input: '$someArray', cond: { '$and': [ { '$gte': [ '$$this.timestamp', 2018-06-01T00:00:00.000Z ] }, { '$lt': [ '$$this.timestamp', 2018-07-01T00:00:00.000Z ] } ] } } } } } ], {})
{
"filtered": [
{
"_id": "5b1522f5cdac0b6da18f7618",
"name": "A",
"someArray": [
{
"_id": "5b1526952794447083ababf6",
"timestamp": "2018-06-01T00:00:00.000Z",
"other": "C"
},
{
"_id": "5b1526952794447083ababf4",
"timestamp": "2018-06-10T00:00:00.000Z",
"other": "E"
}
],
"__v": 0
}
]
}

Resources