MongoDB: how to get specific elements from array? - mongodb

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

Related

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

how to modify every field of a nested document mongo?

so imagine I have the following document:
{
"_id":...,
"data":{"a":[],"b":[],"x":[]}
}
I don't know beforehand which fields the subdocument data may have. I just know that every field in that subdocument will be an array
How do I make an update so that the object results like:
{
"_id":...,
"data":{"a":[1],"b":[1],"x":[1]}
}
Constraint: Using only mongodb operators. One single update. Without knowing the fields inside the 'data' subdocument
db.collection.update({},
[
{
$set: {
data: {
$arrayToObject: {
$map: {
input: { $objectToArray: "$data" },
as: "d",
in: { k: "$$d.k", v: [ 1 ] }
}
}
}
}
}
])
mongoplayground

Merge/Combine two object arrays from different fields

I have an array in_cart which has product_id(s) and the amount of the individual items in the cart document
"in_cart":[
{
"product_id":"12345",
"amount":2
}
]
What I want is to do is insert the amount field into the details array. $lookup operator is done on the product_id so there will always be an equal amount of items in both arrays.
"details":[
{
"_id":"12345",
"name":"test",
"price":1110,
// ...more data...
}
]
$map to iterate over the details array.
$filter to filter out the document from in_cart array that has the same value of product_id field as the current item from details array have in _id field
$arrayElemAt to get the first element of the filtered array (since it will always have only one element)
$getField to get only amount property of filtered item
db.collection.aggregate([
{
$set: {
details: {
$map: {
input: "$details",
in: {
_id: "$$this._id",
name: "$$this.name",
price: "$$this.price",
amount: {
$getField: {
field: "amount",
input: {
$arrayElemAt: [
{
$filter: {
input: "$in_cart",
as: "cart_item",
cond: {
$eq: [
"$$cart_item.product_id",
"$$this._id"
]
}
}
},
0
]
}
}
}
}
}
}
}
}
])
Working example

How to use find for nested object of objects in MongoDB

I have nested object of objects. Each document in collection looks like this:
{
anything: "whatever",
something: {
// find inside of these document
a: { getThis: "wow" },
b: { getThis: "just wow" },
c: { getThis: "another wow" }
}
}
I would like to find in every getThis from each document in something.
For example I would like to find document which has getThis: "wow".
I've tried to use something like wildcard with *:
{"something.*.getThis": "wow" }
I've also tried $elemMatch but it seems it works only with array;
{ something: { $elemMatch: { getThis: "wow" } } }
You can try using $objectToArray,
$addFields to convert something to array in somethingArr
$match condition getThis is wow or not
$project to remove somethingArr
db.collection.aggregate([
{
$addFields: {
somethingArr: { $objectToArray: "$something" }
}
},
{ $match: { "somethingArr.v.getThis": "wow" } },
{ $project: { somethingArr: 0 } }
])
Playground
Second possible way
$filter input something as array, convert using $objectToArray
filter will check condition getThis is equal to wow or not
db.collection.aggregate([
{
$match: {
$expr: {
$ne: [
[],
{
$filter: {
input: { $objectToArray: "$something" },
cond: { $eq: ["$$this.v.getThis", "wow"] }
}
}
]
}
}
}
])
Playground

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.