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.
Related
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
The following is my database schema update operation:
db.school_student.update({ _id: "003" }, {
$set: {
"result": [
{
"_id": "001",
"isPassed": false
},
{
"_id": "002",
"isPassed": false,
},
{
"_id": "003",
"isPassed": false
}
]
}
});
I want to change ALL the property values of "isPassed" to true. Is there any way to update this? I have been struggling with this the whole day :(
db.school_student.update({},{$Set :{"result.isPassed" : true}}
this should update all the documents in school_student collection and set isPassed to true.
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"}}
])
I have a mongoose Schema that looks likes this :
var AnswerSchema = new Schema({
author: {type: Schema.Types.ObjectId, ref: 'User'},
likes: [{type: Schema.Types.ObjectId, ref: 'User'}]
text: String,
....
});
and I have an API endpoint that allow to get answers posted by a specific user (which exclude the likes array). What I want to do is add a field (with "true/false" value for example) to the answer(s) returned by the mongoose query, when a specific user_id is (or is not) in the likes array of an answer. This way, I can display to the user requesting the answers if he already liked an answer or not.
How could I achieve this in an optimised way ? I would like to avoid fetching the likes array, then look into it myself in my Javascript code to check if specified userId is present in it, then remove it before sending it back to the client... because it sounds wrong to fetch all this data from mongoDB to my node app for nothing. I'm sure there is a better way by using aggregation but I never used it and am a bit confused on how to do it right.
The database might grow very large so it must be quick and optimised.
One approach you could take is via the aggregation framework which allows you to add/modify fields via the $project pipeline, applying a host of logical operators that work in cohort to achieve the desired end result. For instance, in your above case this would translate to:
Answer.aggregate()
.project({
"author": 1,
"matched": {
"$eq": [
{
"$size": {
"$ifNull": [
{ "$setIntersection": [ "$likes", [userId] ] },
[]
]
}
},
1
]
}
})
.exec(function (err, docs){
console.log(docs);
})
As an example to test in mongo shell, let's insert some few test documents to the test collection:
db.test.insert([
{
"likes": [1, 2, 3]
},
{
"likes": [3, 2]
},
{
"likes": null
},
{
"another": "foo"
}
])
Running the above aggregation pipeline on the test collection to get the boolean field for userId = 2:
var userId = 2;
db.test.aggregate([
{
"$project": {
"matched": {
"$eq": [
{
"$size": {
"$ifNull": [
{ "$setIntersection": [ "$likes", [userId] ] },
[]
]
}
},
1
]
}
}
}
])
gives the following output:
{
"result" : [
{
"_id" : ObjectId("564f487c7d3c273d063cd21e"),
"matched" : true
},
{
"_id" : ObjectId("564f487c7d3c273d063cd21f"),
"matched" : true
},
{
"_id" : ObjectId("564f487c7d3c273d063cd220"),
"matched" : false
},
{
"_id" : ObjectId("564f487c7d3c273d063cd221"),
"matched" : false
}
],
"ok" : 1
}
Suppose I have a collection that contains the following documents:
{ "_id": 1, name: "Apple" }
{ "_id": 2, name: "Banana", "is_reported": null }
{ "_id": 3, name: "Cherry", "is_reported": false }
{ "_id": 4, name: "Kiwi", "is_reported": true }
Is there a simpler query to select all documents where "is_reported" is in a falsy state; that is, either non-existent, null, or false? That is, a query that selects Apple, Banana, and Cherry, but not Kiwi?
According to the MongoDB FAQ, { "is_reported": null } will select documents where "is_reported" is either null or nonexistent, but it still doesn't select documents where "is_reported" is false.
Right now I have the following query, which works fine, but it just doesn't seem very elegant. If there are multiple fields that I need to select on, it gets messy very fast. Is there a better query that achieves the same end result?
db.fruits.find({ $or: [ { "is_reported": null }, { "is_reported": false } ] })
You can do this with $in:
db.fruits.find({is_reported: {$in: [null, false]}})
returns:
{
"_id": 1,
"name": "Apple"
}
{
"_id": 2,
"name": "Banana",
"is_reported": null
}
{
"_id": 3,
"name": "Cherry",
"is_reported": false
}
You could also flip things around logically and use $ne if you don't have any values besides true to exclude:
db.fruits.find({is_reported: {$ne: true}})