MongoDB: find if any field is true - mongodb

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}
]
})

Related

Bulk insert.update in mongodb

Currently in my MongoDB collection I have this data
currentDataList = [{'id':1,'name':test1},{'id':2,'name':test2}]
but now I have a new data list with huge amount of data like
newDataList = [{'id':1,'name':a1},{'id':2,'name':a2},{'id':3,'name':a3}.....{'id':9999,'name':a9999}]
and I need to update this newDataList in my collection with insert if not exist/update if exist manner.
I do not want to update the documents one by one because it will take time.
Can anyone please suggest some solution for bulk insert/update in this case.
Update in bulk:
If you want to update in bulk you can use db.runCommand() with update command.
Command example:
db.runCommand({
update: "collectionName",
updates: [
{
q: { id: 3 },
u: { $set: { id: 3, name: "a3", newFields: "newField" } },
multi: true,
upsert: true
}, // the list goes here
{
q: { id: 11 },
u: { $set: { id: 11, name: "a11" } },
multi: true,
upsert: true
}
],
ordered: false,
writeConcern: { w: "majority", wtimeout: 5000 }
});
Update one by one:
Alternately you can run the query for each item in the list using the .update() method with upsert set to true.
mongodb playground example
operation example:
db.collection.update({
id: 3
},
{
$set: {
id: 3,
name: "a3",
newField: "newField",
}
},
{
upsert: true,
multi: true
})

Aggregate match documents using nested documents

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

Return all documents only if all matches conditions

Looking for help with a query that returns either true or false (or an empty array when false or similar) I need to query a couple of documents by id in a collection and only return true if all documents match the query, if one or more documents don't match I need a false value returned.
If the documents looks like below with the checked value both true and false I would like a false/empty array value back from the query but if the checked are true in all I want a true or the whole array back.
If a regular find is more suitable i could use that.
I've tried with a regular $match but it only return the matched documents.
I do like this now but feels it could be done in a better way?
const coupons = await CouponModel.find({ id }, { checked: 1, _id: 0 });
const everyCouponIsChecked = coupons.every(data => data.checked === true);
Thanks.
Sample data:
[ { _id: 5e43e7831bc81503efa54c61,
id: 'foo',
checked: true,
},
{ _id: 5e43e7831bc81503efa54c61,
id: 'foo',
checked: true,
},{ _id: 5e43e7831bc81503efa54c61,
id: 'foo',
checked: false,
}]
const result = await MyModel.aggregate([
{
$match: {
id: 'foo',
checked: true,
},
},
]);
You can use a $group stage with null _id, then check if all elements checked field are true with $allElementsTrue operator.
Here's the query :
db.collection.aggregate([
{
$group: {
_id: null,
docs: {
$push: "$$ROOT"
}
}
},
{
$addFields: {
allTrue: {
$allElementsTrue: "$docs.checked"
}
}
},
{
$project: {
result: {
$cond: {
if: {
$eq: [
true,
"$allTrue"
]
},
then: "$docs",
else: "$allTrue"
}
}
}
}
])
If any checked field is false, result will equal to false, else result will be equal to the array of documents.
You can test it here
You could do with a query like this
const coupons = await CouponModel.find({ _id:id, checked:true });
const everyCouponIsChecked = coupons.length > 0;

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"}}
])