Cast error when using $gt ,$gte, $lt ,$lte in mongodb - node.js

I have a document like this
{
"_id":"5dfa6e23ef4b260e8c23a70c",
"username":"foo",
"count":4,
"log":[
{"description":"Eat sandwich","duration":30,"date":1576454400000,"_id":"5dfa6e49ef4b260e8c23a70e"},
{"description":"Eat sandwich","duration":300,"date":1576458700000,"_id":"5dfa6f1c2924c010b35c6b60"},
{"description":"Eat sandwich","duration":400,"date":1576459000000,"_id":"5dfa6f3a2924c010b35c6b61"},
{"description":"Eat sandwich","duration":400,"date":1576457400000,"_id":"5dfa6f702924c010b35c6b62"}
]
}
I'm trying to get the activities of a user's activities from a date range like this.
Activities.findOne({username:"foo",log:{date:{$gt: 1576454400000, $lt: 1576458700000 }}},function(err,activities){
//Do something
});
I always get a cast error whether the type is date or number. I have tried almost everything. I don't know what I'm doing wrong.

For the given input document in the post, the following query matches and returns the document (NOTE: in mongo shell).
This query uses $elemMatch:
db.test.findOne( { log: { $elemMatch: { date: { $gt: 1576454400000, $lte: 1576458700000 } } } } )
In case you do not use $elemMatch, the document field in the array must be specified using the dot (.) notation; e.g., "log.date".
db.test.findOne( { "log.date": { $gt: 1576454400000, $lte: 1576458700000 } } )
The query you are using will not give any syntax errors, but return wrong results.

Related

MongoDB - Compare the field value with array size

I'm trying to return all documents which satisfy document.field.value > document.array.length. I'm trying to do this using MongoClient in ExpressJS and was not able to find answers on SO.
Here is what I have tried but this gives an empty list.
const db = ref()
const c = db.collection(t)
const docs = await c.find({
"stop.time":{$gt: new Date().toISOString()},
"stop.expect": {$gt: { $size: "stop.riders"}}
})
console.log(docs)
Also tried replacing
"stop.expect": {$gt: { $size: "stop.riders"}}
with the below code which does not compile
$expr: {$lt: [{$size: "stop.riders"}, "stop.expect"]}
Sample data:
{ "stop": { "expect":3, "riders": ["asd", "erw", "wer"] } },
{ "stop": { "expect":4, "riders": ["asq", "frw", "wbr"] } }
To filter the query with a complex query involving the calculation, you need to use the $expr operator, which allows the aggregation operators.
Next, within the $expr operator, to refer to the field, you need to add the prefix $.
db.collection.find({
$expr: {
$lt: [
{
$size: "$stop.riders"
},
"$stop.expect"
]
}
})
Demo # Mongo Playground
To compare fields within a document to each other, you must use $expr. It would look like this:
const docs = await c.find({
$expr: { $gt: ["$stop.expect", { $size: "$stop.riders" }] },
});
I omitted the stop.time condition because it's not in your sample data and it's unclear what type of field it is, if it actually is in your data (whether it's a Date or String would be very important).
Note that this sort of query is unable to use an index.

nodejs: $lte for date is not working as expected in mongo db

As the question explains, I am trying to query on dates and the result is not as expected. Here is the how the objects, that I am trying to query, look like:
{
"_id":{"$oid":"5f660cfcde436c3035b59648"},
"orderid":"2020-09-19-8939",
"orderdate":"2021-08-09T11:02:31.202+00:00",
"paycondition_ref":{"$oid":"5f211e9690e310990ea6aa5d"},
"receivedate":{"$date":"2020-09-22T05:59:38.211Z"},
"duedate":"2020-09-19T13:51:56.219Z",
"currency":{"$oid":"5f660cfcde436c3035b59647"},
"supplier_ref":{"$oid":"5f2e12286b15925440f03b56"},
"history":false,
"remark":"Clarance - Tuesday"
}
And here is how I am querying it:
"$match": {
"$and": [
{ 'purchaseorder.orderdate': { $gte: new Date(startDate), $lte: new Date(endDate) } },
{ 'purchaseorder.history': false },
]
}
And the startDate and endDate objects look like this:
startDate 2021-08-08T19:00:00.000Z
endDate 2021-08-08T19:00:00.000Z
This should return the object above, shouldn't it? But it doesn't! What am I doing wrong here? How can I resolve this?
use ISODate instead of new Date and remove "purchaseorder"
"$match": {
"$and": [
{ 'orderdate': { $gte: ISODate(startDate), $lte: ISODate(endDate) } },
{ 'history': false },
]
}
Looking at your data object and query I noticed two things,
Why are you fetching data using purchaseorder.orderdate and purchaseorder.history'. You can directly access field
You are storing the date fields as string instead of date type, that is the reason comparison won't work when you do new Date . Change that schema to hold date field as type of date
Also you are storing dates in local Time Zone, so when comparing you should not use UTC to match your existing date. To make it simple it is always recommended to store dates in UTC TZ.
The following comparisons work for me:
db.getCollection("test-code").aggregate({
"$match": {
'orderdate':
{ $gte: new Date("2021-08-08T16:32:31.202+05:30"), $lte: new Date("2021-08-10T16:32:31.202+05:30") },
'history': false,
}
})
Note: I have used local TZ and my fields were of type date in DB 2021-08-10T16:32:31.202+05:30

Mongo (Node Js , Keystone ) field selection and slice cannot be used with distinct Error:

How do we integrate both distinct and selects the documents where the value of the field is not equal to the specified value.in a query in mongo using nodejs (keystone framework) ? or just basically in mongo. I am receiving an error which is field selection and slice cannot be used with distinct Error:. Any idea? or solution? I did try to use Syntax: {field: {$ne: value} } and that is the error. Also how can we include a limit when limit cannot be used with distinct Error: limit cannot be used with distinct.
query
keystone.list('Customer').model.find({ customer_id: { $in: locals.data.customers } }, { vin: { $ne: vin } }).distinct('vin').limit(4) ....
You can add a query to distinct but not skip and limit
https://docs.mongodb.com/manual/reference/method/db.collection.distinct/#specify-query-with-distinct
Instead, you can use the aggregate pipeline as
db.customer.aggregate(
{ $match:{ customer_id: { $in: locals.data.customers } }},
{ $group:{_id:"$vin"}},
{ $skip: skip},
{ $limit: limit},
{ $group:{_id:null,vin:{$push:"$_id"}}}
);

I want to update sub document in array

I now use mongooses to pull and pull subdocuments to the array, and now I want to change the contents of the detail field of that subdocument with the _id of the subdocument.
{
subDocument: [{
_id: ObjectId('123'),
detail: 'I want update this part'
}]
}
I tried to use the $set method as shown below but it did not work as expected.
Model.findByIdAndUpdate(uid, { $Set: {subDocument: {_id: _id}}});
Looking at the for statement as shown below is likely to have a bad effect on performance. So I want to avoid this method.
const data = findById(uid);
for(...) {
if(data.subDocument[i]._id==_id) {
data.subDocument[i].detail = detail
}
}
Can you tell me some mongodb queries that I can implement?
And, Is it not better to use the 'for(;;)' statement shown above than to search using mongodb's query?
This should work:
Model.findOneAndUpdate({"subdocument._id": uid},
{
$set: {
"subdocument.$.detail ": "detail here"
}
},
).exec(function(err, doc) {
//code
});
To find subdocument by id, I am using something like this :
var subDocument = data.subDocument.id(subDocumentId);
if (subDocument) {
// Do some stuff
}
else {
// No subDocument found
}
Hope it helps.

In Mongoose, how to filter an array of objects

I have the following schema:
var sampleSchema = new Schema({
name: String,
dates: [{
date: Date,
duration: Number
}]
});
I'd need to filters the records according to the following rule: if one of dates is later than a given date date_begin, keep the record, otherwise, don't.
I have the impression that $gte or $lte are the function I need, but I can't find a way to use them correctly. I tried
sampleSchema.find({date_begin: {$gte: 'date'}});
or some variants of that, but I can't seem to be able to make it work. Anyone has an idea of how I am supposed to do this?
To do querying on elements inside arrays, $elemMatch is used :
SampleModel.find( { dates : { $elemMatch: { date : { $gte: 'DATE_VALUE' } } } } )
If you're using a single query condition, you can directly filter:
SampleModel.find( { 'dates.date': { $gte: 'DATE_VALUE' } } )

Resources