Add filter based on condition in MongDB - python-3.x

I am new to MongoDB and I have a collection student. I need to add the student.name filter in the query only when is_rep=true.
My document structure.
{
{
"student": {
"name": "arun",
"dept": "bio",
"subject": "bot"
},
"is_rep": true
},
{
"student": {
"name": "div",
"dept": "csc",
"subject": "program"
},
"is_rep": false
}
}
Can anyone please guide me to achieve this in pymongo

You can use aggregations like
db.collection.aggregate([
{
$match: {
is_rep: true,
"student.name": "arun"
}
}
])
Working Mongo playground
Or you can use find query
db.collection.find({
is_rep: true,
"student.name": "arun"
})
Working Mongo playground

Related

How to filter sub values from the object using ExpressJS and MongoDB?

I have a data from MongoDB where I'm getting all the data of the agent, and I want to get the testimonials with isDisplayed is true only. Is there a way in ExpressJS & MongoDB where I can filter the testimonial key?
Here's what I tried
const getSingleAgent = expressAsync(async (req, res) => {
const agent = await Agents.findOne({
_id: req.params.id,
});
res.json(agent);
} else {
res.status(404);
throw new Error("Agent not found.");
}
});
Actual Result
{
"_id": "63ea901a85d4fbd62fb887b3",
"name": "test namee",
"isDeclined": false,
"testimonials": [
{
"isDisplayed": true,
"name": "test123123123123123",
},
{
"isDisplayed": false,
"name": "test123123123123123",
},
{
"isDisplayed": false,
"name": "test#gmail.com",
},
],
}
Expected Result
{
"_id": "63ea901a85d4fbd62fb887b3",
"name": "test namee",
"isDeclined": false,
"testimonials": [
{
"isDisplayed": true,
"name": "test123123123123123",
},
],
}
You can use $elemMatch into a projection stage like this:
db.collection.find({
"_id": "63ea901a85d4fbd62fb887b3",
"testimonials.isDisplayed": true
},
{
"name": 1,
"isDeclined": 1,
"testimonials": {
"$elemMatch": {
"isDisplayed": true
}
}
})
Example here
But be careful. using $elemMatch will only return the first match. Look at this example where there are two true but only one is returned.
As explained into docs:
The $elemMatch operator limits the contents of an field from the query results to contain only the first element matching the $elemMatch condition.
So if you have or want more than one value you can use and aggregation pipelina and $filter into a $project stage:
db.collection.aggregate([
{
"$match": {
"_id": "63ea901a85d4fbd62fb887b3",
"testimonials.isDisplayed": true
}
},
{
"$project": {
"name": 1,
"isDeclined": 1,
"testimonials": {
"$filter": {
"input": "$testimonials",
"cond": {
"$eq": [
"$$this.isDisplayed",
true
]
}
}
}
}
}
])
Example here
Note how in this example if exists two true values, both are displayed.

How to use $pull to delete a particular data from a document in mongoDB

I'm pretty new in MongoDB,I want to delete a particular object from an array of objects in a document. I tried a lot and finds a lot of questions in StackOverflow.But none of them worked (maybe it's my fault)
following is a document from a collection that is matched by the user with userId
61f15af01a3c53dfa87e38e6
The Document
{
"_id": "61f15af01a3c53dfa87e38ed",
"user": "61f15af01a3c53dfa87e38e6",
"transactions": [
{
"amount": 50,
"category": "Bills",
"type": "Expense",
"_id": "61f15af01a3c53dfa87e38ee"
},
{
"amount": 100,
"category": "Lottery",
"type": "Income",
"_id": "61f15af01a3c53dfa87e38ef"
},
{
"amount": 200,
"category": "Salary",
"type": "Income",
"_id": "61f15af01a3c53dfa87e38f0"
},
]
}
I want to delete the transaction with an Id of 61f15af01a3c53dfa87e38ef
I tried the below one (I don't know whether it is correct)
const allTransactions = await Transactions.findOne({ user: req.user._id });
const updatedTransactions = await allTransactions.update(
{ },
{ $pull: { transactions: { id: req.params.id } } },
);
Can anyone Spot the mistake and how can I achieve it ?
Thanks in advance :)
You can do it in one database call using below statement:
await Transactions.update(
{ user: "61f15af01a3c53dfa87e38e6" },
{ $pull: { "transactions": { _id: "61f15af01a3c53dfa87e38f0" } } }
)
Mongo Playground
Also make sure types match. If _id is of type ObjectId instead of string you should convert req.params.id to ObjectId first. See below:
await Transactions.update(
{ user: mongoose.Types.ObjectId(req.user._id) },
{ $pull: { "transactions": { _id: mongoose.Types.ObjectId(req.params.id) } } }
)

Mongodb project one field as collective array in one document as result

This is data
[ {
"_id": "5c75802b1312ca10e63d2ca7",
"external_user_id": "5cbc86081e06c111f9b16fbf"
},
{
"_id": "5c75a35a3cd9af224c0622c1",
"external_user_id": "5cbc86081e06c111f9b16fbf"
},
{
"_id": "5c82c3bede451c0fd74e6739",
"external_user_id": "5cbc86081e06c111f9b16fd5"
},
{
"_id": "5c85432c1a515a17f2d7a2e7",
"external_user_id": "5cbc86081e06c111f9b16fbc"
},
{
"_id": "5c8e8132bfda140998c2f1c4",
"external_user_id": "5cbc86081e06c111f9b16fbf"
}]
I want mongodb query of something like which results different document fields into one field array according to query following:
{
external_user_id: ["5cbc86081e06c111f9b16fbc", "5cbc86081e06c111f9b16fbf", "5cbc86081e06c111f9b16fd5"]
}
You can use below aggregation
db.collection.aggregate([
{ "$group": {
"_id": null,
"external_user_id": {
"$addToSet": "$external_user_id"
}
}}
])

manage and remove objectId from array - mongoose

i have this schema structure
{
"_id": {
"$oid": "571251dae4b065a8c4d70ce1"
},
"email": "somthing12345#gmail.com",
"events": [
{
"$oid": "57125378e4b065a8c4d70d10"
},
{
"$oid": "571253b8e4b065a8c4d70d1b"
}
],
"valid": true,
}
and my problem is this part
"events": [
{
"$oid": "57125378e4b065a8c4d70d10"
},
{
"$oid": "571253b8e4b065a8c4d70d1b"
}
]
how to remove object in this structure?
can i add fields to each one of thos "events" objects like-
"title" : "some string".
how should i add this?
thanks.
For removing:
events.update({
_id: "571251dae4b065a8c4d70ce1"
}, {
$pull: {
events: {
$oid: "57125378e4b065a8c4d70d10"
}
}
}, {
safe: true
}, function(err, obj) {
// code goes here
});
And what about adding field title: "some string", mondoDB at this moment doesn't allow multiple update for embedded documents, so the only one way to achieve your goal is select document from db, then take this document events array and add title to each object of array. save whole document whith changed events array. Hope you understand everything :)

Mongoose (MongoDB): Exclude Properties in a Dictionary Like Schema Type

I have a Schema of the following structure:
var schema = mongoose.Schema({
answers: {type: mongoose.Schema.Types.Mixed}
});
I use the answers field as an object (associative array to implement something like a dictionary). Here is an example:
{
"__v": 0,
"_id": {
"$oid": "53a0251c50d0536c1bfc6006"
},
"answers": {
"fea": {
"viewed": false
},
"3d2": {
"viewed": true,
"value": true
},
"4fr": {
"viewed": true,
"value": true
},
"84h": {
"viewed": false
},
...
}
}
In a query I want to select only the "value" field of each entry. How is that possible through the select syntax? This of course doesn't work:
XY.find(...)
.select({'answers': true, 'answers.*.value': false})
.exec(...);
Maybe I have to design the data in another fashion?
Best regards,
Kersten
You should never model with "explicit values" as the "key" names. This is very bad practice. Consider what you would do in a SQL database. Would you create "fields/columns" for the different "names" of the things you want?
No you would not. You have a generic field that specifies a "type" and then you have others that hold the data. Nothing changes here:
{
"_id": {
"$oid": "53a0251c50d0536c1bfc6006"
},
"answers": [
{ "type": "fea", "viewed": false },
{ "type": "3d2", "viewed": true, "value": true },
{ "type": "4fr", "viewed": true, "value": true },
{ "type": "84h", "viewed": false },
...
]
}
Now this is easy to use something like the aggregation framework to make the projection of the content like you want:
With Modern MongoDB 2.6 and onwards you can use $map and $setDifference to filter the array without using $unwind:
Model.aggregate(
[
{ "$project": {
"answers": {
"$setDifference": [
{
"$map": {
"input": "$answers",
"as": "el",
"in": {
"$cond": [
1,
{
"type": "$$el.type",
"value": { "$ifNull": [ "$$el.value", false ] }
},
false
]
}
}
},
[false]
]
}
}}
],
function(err,result) {
}
);
Or with older versions pre 2.6:
Model.aggregate(
[
{ "$unwind": "$answers" },
{ "$group": {
"_id": "$_id",
"answers": {
"$push": {
"type": "$answers.type",
"value": { "$ifNull": [ "$answers.value", false ] }
}
}
}}
],
function(err,result) {
}
);
Of course you can "filter" the array results to certain conditions by either adding a logical evaluation as the first argument to the $cond operator in the $map implementation. Or by using a $match pipeline stage in between the $unwind and $group stages.
Either form allows you to re-shape the result without problem and is the fastest way to process this, which is a strong advantage of using arrays as opposed to embedded objects whose keys are actually a "data" item.
If you are stuck with this then you need to process with JavaScript evaluation like mapReduce. This runs much slower than the aggregation framework due to the need to invoke and run in a JavaScript interpreter process:
Model.mapReduce(
{
"map": function() {
for ( var k in this.answers ) {
this.answers[k] = this.answers[k].hasOwnProperty("value")
? this.answers[k].value : false;
}
var id = this._id;
delete this._id;
emit( id, this );
},
"reduce": function(){}
},
function(err,docs) {
}
)
But really, consider changing your structure as it makes things much more flexible for queries and other operations.

Resources