Aggregate match documents using nested documents - mongodb

I have a document structure as so:
{
"name": "Bob",
"configurations": [
{
"version": 0,
"isValid": true,
"isPublished": false
},
{
"version": 1,
"isValid": false,
"isPublished": true
}
]
}
I wish to find all such document where there exists a configuration where both isValid is true and isPublished is true. For such a query, the example I gave above should not be returned since the none of the configurations have both of the flags set as true.
I first tried something like:
coll.aggregate([
{
$match: {
"configurations.isValid": true,
"configurations.isPublished": true
}
}
])
This isn't what I want because this simply checks if there exists a configuration with isValid set to true and if there also exists another configuration with isPublished set to true. I tried fixing this via:
coll.aggregate([
{
$match: {
"permissions": {
"isValid": true,
"isPublished": true
}
}
}
])
but this returns zero results. Any ideas?
EDIT:
Figured it out:
coll.aggregate([
{$match: { configurations: { $elemMatch: { $and: [ {isValid: true}, {isPublished: true} ] } } }}
])

Playground
$elemMatch will help you to find arrays with condition matches.
db.collection.find({
configurations: {
"$elemMatch": {
"isValid": true,
"isPublished": true
}
}
})
For aggregation, Example, Simply use the above in $match

Related

MongoDB - How to add a field to all object within an array

I have a document with a field called info, and info has a field inside it called data. data is an array of objects. I want to add a new boolean field, isActive: false, to each object in data, with updateMany.
This is how it looks now
{
info: {
data: [{
"name": "Max"
},
{
"name": "Brian"
},
...
]
}
}
This is what I want:
{
info: {
data: [{
"name": "Max",
"isActive": false
},
{
"name": "Brian",
"isActive": false
},
...
]
}
}
How do I do that?
Add the isActive field with all positional operator $[].
db.collection.update({},
{
$set: {
"info.data.$[].isActive": false
}
},
{
multi: true
})
Consider applying { multi: true } if you want to update multiple documents.

MongoDB: find if any field is true

I have documents structured like this. Notice that this is ONE document, not collection:
{
"1": {
"end_flag": false
}
"2": {
"end_flag": false
}
...
}
Problem: In the end, I want to count documents that have a end_flag: true in any of its fields. How can I match documents that any of X.end_flag field is true?
I have tried:
[
{
"$match": {
"1.end_flag": true,
"2.end_flag": true,
"3.end_flag": true,
"4.end_flag": true,
"5.end_flag": true,
"6.end_flag": true,
"7.end_flag": true,
"8.end_flag": true,
"9.end_flag": true,
"10.end_flag": true,
}
}
]
but I know this only works if all conditions are true. How can I make it any?
I also tried (desperately):
[
{
"$match": {
"$in": [ true, [
"$1.end_flag",
"$2.end_flag",
"$3.end_flag",
"$4.end_flag",
"$5.end_flag",
"$6.end_flag",
"$7.end_flag",
"$8.end_flag",
"$9.end_flag",
"$10.end_flag"
] ]
}
}
]
But none of them worked. I know the documents are structured poorly for this task but this is what I have.
A- while $or operation is costly in mongodb, the best solutions is to structure your data as array like :
[
0: {
"number": 1
"end_flag": false
}
1: {
"number": 2
"end_flag": true
}
...
}
then you can simply query :
db.collection.find({"fieldName.end_flag":true});
this is the power of MongoDB
B- if there is no way to restructure your data, then you have to use $or operation like this :
db.collection.find({
$or:[
{"1.end_flag": true},
{"2.end_flag": true},
{"3.end_flag": true},
{"4.end_flag": true},
{"5.end_flag": true},
{"6.end_flag": true},
{"7.end_flag": true}
]
})

Mongodb Update Elements from Multiple Arrays on a Document

I'd like to update a property from objects located in multiple lists on a MongoDB collection's document.
In a MongoDB collection I have documents like this one:
{
"firstArray": [
{
"name": "john",
"updated": false // I wanna switch that to true
},
{
"name": "bob",
"updated": false
}
],
"secondArray": [
{
"name": "eric",
"updated": false
},
{
"name": "john",
"updated": false // I wanna switch that to true
}
]
}
My goal is to update every element with "name":"john" from document's properties firstArray and secondArray to updated:true.
As they can be parallel accesses to a document, I shall only use update operators (a read and replace method like that one would add the risk of dropping changes made by another process)
It looks like the filtered positional operator $[] as mentioned in that question is the way to go to update an object's property from a document's Array BUT it appears that it doesn't support multiple $set with the same operator nor using two operators (one by array) in two different $set!
As this mongo shell command only update the secondArray:
db['collection'].update(
{},
{
"$set": { "firstArray.$[elem].updated": true },
"$set": { "secondArray.$[elem].updated": true }
},
{ "arrayFilters": [{ "elem.name": "john" }], "multi": true })
And this command returns The array filter for identifier 'a' was not used in the update { $set: { secondArray.$[b].updated: true } }:
db['collection'].update(
{},
{
"$set": { "firstArray.$[a].updated": true },
"$set": { "secondArray.$[b].updated": true }
},
{ "arrayFilters": [{ "a.name": "john" }, { "b.name": "john" }], "multi": true })
So, would you have any idea how to do that update? (Bonus point for any documentation quotes about the above limitations )
Thanks!
The second argument to that function in an object. A given field name can only have one value in an object. To show what is being sent to the server, I assigned the value you are using to a variable:
> var o={
"$set": { "firstArray.$[a].updated": true },
"$set": { "secondArray.$[b].updated": true }
};
> printjson(o)
{ "$set" : { "secondArray.$[b].updated" : true } }
As you can see, the first value was overwritten by the second because both fields were named $set.
Try combining these into a single field:
{
"$set": {
"firstArray.$[elem].updated": true,
"secondArray.$[elem].updated": true
}
}

How to reverse boolean in mongodb with mongoose

I have a data as below.
[
{
"id": 1,
"exist": true
},
{
"id": 2,
"exist": false
},
{
"id": 3,
"exist": false
}
]
Only one object can have exist true. So when I findOneAndUpdate({_id:2}),{exist:true}), I hope that exist of 'id:1' is changed to false automatically in one query using aggregate or etc.
could you recommend some idea fot it? Thank you so much for reading my question.
Starting in MongoDB 4.2, you can use the aggregation pipeline for updates so you can do something like this:
db.your_collection.update(
{
$or: [
{
id: 2,
exist: false
},
{
id: {$ne: 2},
exist: true
}
]
},
[{$set: {exist: {$eq: [ "$exist", false ] }}}],
{multi: true}
)
Explain:
The filter will find records that has id you want and not exist or don't have the id but exist is true. In this case, it will find:
[
{
"id": 1,
"exist": true
},
{
"id": 2,
"exist": false
}
]
The update reverse exist field of found records.

MongoDB - Returning Specific Document Fields

I have the following structure to my Mongodb documents, and as you'll see, I have 3 URLs, each with crawled set to True or False.
{
"_id": {
"$oid": "573b8e70e1054c00151152f7"
},
"domain": "example.com",
"good": [
{
"crawled": true,
"added": {
"$date": "2016-05-17T21:34:34.485Z"
},
"link": "/threads/11005-Cheap-booze!"
},
{
"crawled": false,
"added": {
"$date": "2016-05-17T21:34:34.485Z"
},
"link": "/threads/9445-This-week-s-voucher-codes"
},
{
"crawled": false,
"added": {
"$date": "2016-05-17T21:34:34.485Z"
},
"link": "/threads/9445-This-week-s-voucher-codes_2"
}
],
"link_found": false,
"subdomain": "http://www."
}
I'm trying to return specific fields where only those URL with crawled set to False are returned, for this I have the following query:
.find({'good.crawled' : False}, {'good.link':True, 'domain':True, 'subdomain':True})
However, what is returned vs what is expected is different as it's returning all the URLs, irrespective of whether they have a crawled status of True or False
What is returned is:
{
u'domain': u'cashquestions.com',
u'_id': ObjectId('573b8e70e1054c00151152f7'),
u'subdomain': u'http://www.',
u'good': [
{
u'link': u'/threads/11005-Cheap-booze!'
},
{
u'link': u'/threads/9445-This-week-s-voucher-codes'
},
{
u'link': u'/threads/9445-This-week-s-voucher-codes_2'
}
]
}
What is expected:
{
u'domain': u'cashquestions.com',
u'_id': ObjectId('573b8e70e1054c00151152f7'),
u'subdomain': u'http://www.',
u'good': [
{
u'link': u'/threads/9445-This-week-s-voucher-codes'
},
{
u'link': u'/threads/9445-This-week-s-voucher-codes_2'
}
]
}
How can I specify that only the links with crawled set to False is returned?
You'll want to use the aggregation framework (this will work in MongoDB 3.0 and later):
db.yourcolleciton.aggregate([
// optional: only those with at least one false
{$match: {'good.crawled': false}},
// get just the fields you need (plus _id)
{$project: {good:1, domain:1, subdomain: 1}},
// get each in a separate temporary document
{$unwind: {'good': 1}},
// limit to false
{$match: {'good.crawled': false}},
// undoes the $unwind
{$group: {_id: "$_id", domain: {"$first": "$domain"}, 'subdomain' : {$first, '$subdomain'}, good: {"$push":"$good"}}
])