Find in nested array with compare on last field - mongodb

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

Related

after aggregation how to check two fields are equal inside a document in mongodb

{
id: 1,
name: "sree",
userId: "001",
paymentData: {
user_Id: "001",
amount: 200
}
},
{
id: 1,
name: "sree",
userId: "001",
paymentData: {
user_Id: "002",
amount: 200
}
}
I got this result after unwind in aggregation any way to check user_Id equal to userId
Are you looking to only retrieve the results when they are equal (meaning you want to filter out documents where the values are not the same) or are you looking to add a field indicating whether the two are equal?
In either case, you append subsequent stage(s) to the aggregation pipeline to achieve your desired result. If you want to filter the documents, the new stage may be:
{
$match: {
$expr: {
$eq: [
"$userId",
"$paymentData.user_Id"
]
}
}
}
See how it works in this playground example.
If instead you want to add a field that compares the two values, then this stage may be what you are looking for:
{
$addFields: {
isEqual: {
$eq: [
"$userId",
"$paymentData.user_Id"
]
}
}
}
See how it works in this playground example.
You could also combine the two as in:
{
$addFields: {
isEqual: {
$eq: [
"$userId",
"$paymentData.user_Id"
]
}
}
},
{
$match: {
isEqual: true
}
}
Playground demonstration here

MongoDB: how to get specific elements from array?

I have objects in the document that look's like:
I want to get only the elements from the 'transactions' array that their inside field 'isMatched' == false
I tried:
db.myCollection.find(
{ "transactions.isMatched": false } ,
{ "transactions.isMatched": 1
})
But i got all the array elements:
What is the appropriate query for this?
You can achieve this with aggregation. $filter helps to eliminate unwanted objects.
db.collection.aggregate([
{
$project: {
company: 1,
transaction: {
$filter: {
input: "$transaction",
cond: {
$eq: [
"$$this.isMatched",
false
]
}
}
}
}
}
])
Working Mongo playground

I have a mongodb query and I want to find the exact match word "Approved"

I have a Mongodb query and I want to find the exact match word "Approved" in Array named "Sales.Action".
But the array contains also other values other then the value "Approve" which also return when i query.
I want only one action to return.
Sample:
{
Sales: [
{Action: "Approve"},
{Action: "Other"}
]
}
Required output:
{
Sales: [
{Action: "Approve"},
]
}
If you want only one match you should use the positional operator $
The positional $ operator limits the contents of an from the query results to contain only the first element matching the query document.
Use it like so:
db.collection.find(
{
"Sales.Action": "Approve"
},
{
"Sales.$": 1
}
)
If you want to array in each document to possibly contain more than 1 result use $filter:
db.collection.aggregate([
{
$match: {
"Sales.Action": "Approve"
}
},
{
$project: {
Sales: {
$filter: {
input: "$Sales",
as: "obj",
cond: {$setIsSubset: [["Approve"], "$$obj.Action"]}
}
}
}
}
])

Mongodb comparing using a field with another field in an array of Objects

I have the below structure for my collection:
{
"price":123,
"totalPrices": [
{
"totPrice":123
}
]
}
I am trying to query for all the documents in my collection where price is not equals to totalPrice.totPrice (so above should not be returned).
But it keeps returning the documents which have equal prices as well (such as above sample).
This is the query I'm using:
{
$where : "this.price!== this.totalPrices.totPrice",
totalPrice:{$size:1}
}
What am I doing wrong :(
First, you need to match the size of the array totalPrices is equal to 1. Second, you need to unwind the totalPrices, since it's an array field. Last, you should match the equality of price and totalPrices.totPrice. Try the below code:
db.collection.aggregate([
{
$match: {
$expr: {
$eq: [
{
$size: "$totalPrices"
},
1
]
}
}
},
{
$unwind: "$totalPrices"
},
{
$match: {
$expr: {
$ne: [
"$price",
"$totalPrices.totPrice"
]
}
}
}
])
MongoPlayGroundLink

Find all entries where one of attributes within array is empty

I've following mongodb query:
db
.getCollection("entries")
.find({
$and: [
{
"array.attribute_1": {
$exists: true,
$not: {
$size: 0
}
}
},
{
$or: [
{ "array.attribute_2": { $exists: true, $size: 0 } },
{ "array.attribute_2": { $exists: true, $eq: {} } }
]
},
]
})
And example of my document:
{
_id: 'foo',
array: [
{attribute_1: [], attribute_2: []},
{attribute_1: ['bar'], attribute_2: []}
]
}
In my understanding my query should find all entries that have at least one element within array that has existent and not empty attribute_1 and existent empty array or empty object attribute_2. However, this query finds all entries that has all elements within array that has existent and not empty attribute_1 and existent empty array or empty object attribute_2. As such, my foo entry won't be found.
What should be the correct formula for my requirements?
$find would find the first document with the matching criteria and in your case that first document contains all the arrays. You either need to use $project with $filter or aggregation with $unwind and $match.
Something like this:
db.collection.aggregate([
{ $unwind: "$array" },
{
$match: {
$and: [
{ "array.attribute_1.0": { $exists: true }},
{
$or: [
{ "array.attribute_2.0": { $exists: false } },
{ "array.attribute_2.0": { $eq: {} } }
]
}
]
}
}
])
You can see it working here
Also since you are trying to find out if array is empty and exists at the same time using .0 with $exists is a quick and one statement way to get the same result as with both $exists and $size.