MongoDB mongoose $elemMatch for multiple results [duplicate] - mongodb

This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 4 years ago.
I have a query that should return multiple subdocuments from an array in a document, based on a timestamp range criteria. I first choose the main documents by specifying some id's:
In the Mongo Shell it is:
db.fps.find({"_id": {$in: [15,24] }}, {someArray: {$elemMatch: {Timestamp: {$gt: "2018-06-06T18:00:00", $lt:"2018-06-07"}}}}).pretty()
Because of $elemMatch, it returns only the first document that matches my query.
However, I want all relevant documents returned that match the criteria.
How would I have to do the query in mongoose?

Let's say you have a document like this:
db.fps.save({_id: 15, someArray: [ { Timestamp: "2018-06-06T19:00:00" }, { Timestamp: "2018-06-06T19:00:00" }, { Timestamp: "2018-06-07T00:00:00" } ]});
To filter nested array you need aggregation framework, where $match will represent your matching condition and $filter will apply Timestamps comparison. $addFields simply overwrites someArray in result set here
db.fps.aggregate([
{
$match: { "_id": {$in: [15,24] } }
},
{
$addFields: {
someArray: {
$filter: {
input: "$someArray",
as: "doc",
cond: {
$and: [
{ $gt: [ "$$doc.Timestamp", "2018-06-06T18:00:00" ] },
{ $lt: [ "$$doc.Timestamp", "2018-06-07" ] }
]
}
}
}
}
}
])

Related

what is syntactically wrong with this query in MongoDB? [duplicate]

This question already has answers here:
Syntax of $or in mongoDB
(1 answer)
MongoDB aggregation framework match OR
(2 answers)
Closed last month.
{a: {b: 1, c: 2}}
db.getCollection("col").aggregate([
{ $match: { "a.b": { $or: [2, 3] } } },
])
It is complaining that it doesn't recognize the $or operator.
The documentation for the $match stage states that:
The query syntax is identical to the read operation query syntax
If we inspect the documentation for the $or operator, you need to pass it expressions, or more specifically, expression objects. Expression objects have the form { <field1>: <expression1>, ... }.
So the correct way to perform this query using $or would be to do:
db.collection.aggregate([
{
"$match": {
"$or": [
{
"a.b": 2
},
{
"a.b": 3
}
]
}
}
])
Or as the other answer suggested, if both expressions are inspecting the same field, you can use $in. The syntax you would use for $in is more like what you tried initially: { field: { $in: [<value1>, <value2>, ... <valueN> ] } }. Put together it might look like:
db.collection.aggregate([
{
"$match": {
"a.b": {
$in: [
2,
3
]
}
}
}
])

Find in nested array with compare on last field

I have a collection with documents like this one:
{
f1: {
firstArray: [
{
secondArray: [{status: "foo1"}, {status: "foo2"}, {status: "foo3"}]
}
]
}
}
My expected result includes documents that have at least one item in firstArray, which is last object status on the secondArray is included in an input array of values (eg. ["foo3"]).
I don't must use aggregate.
I tried:
{
"f1.firstArray": {
$elemMatch: {
"secondArray.status": {
$in: ["foo3"],
},
otherField: "bar",
},
},
}
You can use an aggregation pipeline with $match and $filter, to keep only documents that their size of matching last items are greater than zero:
db.collection.aggregate([
{$match: {
$expr: {
$gt: [
{$size: {
$filter: {
input: "$f1.firstArray",
cond: {$in: [{$last: "$$this.secondArray.status"}, ["foo3"]]}
}
}
},
0
]
}
}
}
])
See how it works on the playground example
If you know that the secondArray have always 3 items you can do:
db.collection.find({
"f1.firstArray": {
$elemMatch: {
"secondArray.2.status": {
$in: ["foo3"]
}
}
}
})
But otherwise I don't think you can check only the last item without an aggregaation. The idea is that a regular find allows you to write a query that do not use values that are specific for each document. If the size of the array can be different on each document or even on different items on the same document, you need to use an aggregation pipeline

MongoDB map filtered array inside another array with aggregate $project

I am using Azure Cosmos DB's API for MongoDB with Pymongo. My goal is to filter array inside array and return only filtered results. Aggregation query works for the first array, but returns full inside array after using map, filter operations. Please find Reproducible Example in Mongo Playground: https://mongoplayground.net/p/zS8A7zDMrmK
Current query use $project to filter and return result by selected Options but still returns every object in Discount_Price although query has additional filter to check if it has specific Sales_Week value.
Let me know in comments if my question is clear, many thanks for all possible help and suggestions.
It seemed you troubled in filtering nested array.
options:{
$filter: {
input: {
$map: {
input: "$Sales_Options",
as: 's',
in: {
City: "$$s.City",
Country: "$$s.Country",
Discount_Price: {
$filter: {
input: "$$s.Discount_Price",
as: "d",
cond: {
$in: ["$$d.Sales_Week", [2, 7]]
}
}
}
}
}
},
as: 'pair',
cond: {
$and: [{
$in: [
'$$pair.Country',
[
'UK'
]
]
},
{
$in: [
'$$pair.City',
[
'London'
]
]
}
]
}
}
}
Working Mongo plaground. If you need price1, you can use $project in next stage.
Note : If you follow the projection form upper stage use 1 or 0 which is good practice.
I'd steer you towards the $unwind operator and everything becomes a lot simpler:
db.collection.aggregate([
{$match: {"Store": "AB"}},
{$unwind: "$Sales_Options"},
{$unwind: "$Sales_Options.Discount_Price"},
{$match: {"Sales_Options.Country": {$in: [ "UK" ]},
"Sales_Options.City": {$in: [ "London" ]},
"Sales_Options.Discount_Price.Sales_Week": {$in: [ 2, 7 ]}
}
}
])
Now just $project the fields as appropriate for your output.

MongoDB, finding documents by matching sub elements in an array by several Date conditions [duplicate]

This question already has answers here:
Specify Multiple Criteria for Array Elements
(2 answers)
MongoDB: find value in Array with multiple criteria
(1 answer)
Closed 3 years ago.
I have documents like this:
{
"_id": ID,
"seen_at" : [
ISODate("2018-12-27T17:00:00.000Z"),
ISODate("2019-01-01T01:00:00.000Z")
]
}
I try to select document based on a query into the seen_at elements:
db.collection.aggregate(
[
{
"$match": {
seen_at: {
"$gt": ISODate("2019-01-01T00:00:00.000Z"),
"$lt": ISODate('2019-01-01T00:00:00.001Z')
}
}
}
]
)
I was expecting this query to find only documents that have elements in the seen_at that matche both conditions.
But the above query returns the top-above document (among others also not matching both conditions)
Use $elemMatch if you have multiple criteria to find from array:
db.collection.find({
seen_at: {
$elemMatch: {
"$gt": ISODate("2019-01-01T00:00:00.000Z"),
"$lt": ISODate("2019-01-01T00:00:00.001Z")
}
}
})
Checkout the results in Mongo Playground for find.
If you have to use Aggregate, the $unwind operator can be used:
db.collection.aggregate([
{
$unwind : "$seen_at"
},
{
"$match": {
seen_at: {
"$gt": ISODate("2019-01-01T00:00:00.000Z"),
"$lt": ISODate('2019-01-01T00:00:00.001Z')
}
}
},
{
$group : {
"_id" : "$_id",
"seen_at" : {$push : "$seen_at"}
}
}
])
Checkout the results in Mongo Playground for Aggregate.

Mongo DB aggregation array size greater than match [duplicate]

This question already has answers here:
Query for documents where array size is greater than 1
(14 answers)
Closed 5 years ago.
I have a collection where investments is an array inside the mongodb document. Now using aggregation I am trying to filter results where investments length is more than 5 times and then do the next processing using match query.
Collection{
_id:000000
---------------------
"investments" : [ {
hhhhhhhhhhhhhh
},
{
hhhhhhhhhhhhhh
} }]
-----------------
The match query I wrote like below which isn't working. Any suggestions:
db.companies.aggregate( [
{ $match: {"founded_year" : 2004},
{ "investments" : {$size: : { $gte: 5 } } } },
----------------------------------
--------------------------------
]}
With aggregate:
db.companies.aggregate([
{ $match: { "founded_year":2004 } },
{ $project: { founded_year:1,
moreThanFive: { $gt: [ {$size: "$external_links" }, 5 ] } } },
{ $match: { moreThanFive : true }} ,
])
You will need to:
1. Include a $project stage, to find the number of investement (the size of the array), and check if that greater than 5.
2. and then do another $match stage to filter those with moreThanFive equals to true.
With find:
db.companies.find({'investments.5': {$exists: true}})
You ask if the position number 6 in the investments array exists.