MongoDB: Is it possible to use $cond operator within Update query? - mongodb

The aim that I am trying to achieve:
Update a value in MongoDB based on its current value.
After googling I have found that $cond operator potentially allows me to solve the problem, so I've written the following query:
db.getCollection('product').update({_id: ObjectId("77b2a57556a5e634d57d9977")},
{"$set": {"availability" :
{$cond: [ { "$availability": { $eq: true } }, "Yes", "No" ] }}});
but for some reason, it doesn't work and MongoDB throws an exception.
So could you please advise: Is it technically the right approach to use $cond within the update query?
If yes, why the query doesn't work, semantically it looks good to me,
or possibly some other options on how to solve the problem available, please suggest.

So it seems like you are trying to use Update with Aggregation pipeline which is supported in MongoDB >=4.2. And also if your DB version is >=4.2 the correct syntax would be
db.getCollection("product").update(
{ _id: ObjectId("77b2a57556a5e634d57d9977") },
[
{
$set: {
availability: {
$cond: [
{
$eq: ["$availability", true],
},
"Yes",
"No",
],
},
},
}
]
);

Related

Push an object to a nested array within a document but use a value from a field in the root document

I've been scratching my head with this problem. I've attempted to search for a solution but I didn't find anything relating to my specific use case.
Would anyone be able to help me out?
Say I have a collection of "discount" documents, and importantly they have an "amount" field to say how much the discount is worth. Whenever a discount is redeemed I currently want to track what the worth was at the time of the redemption.
To do this I've been attempting to use the following code:
await datastore.collection('discounts').updateOne(
{
$expr: { $gt: [ '$maxUses', '$uses' ] },
...criteria
},
{
$set: {
uses: 1
},
$push: {
redemptions: {
name: concatNames(user),
email: user.email,
amount: '$amount', // <-- use amount from root document
when: new Date()
}
}
}
)
Unfortunately $amount does not pull the value from the root document, instead it just becomes "$amount" as a string. I've also attempted to convert this update to use a pipeline but $push is not a valid pipeline stage.
Here's a quick Mongo playground link.
Thanks in advance.
In order to refer to another fields value, you'll need to use the aggregation pipeline form of update. However, '$push' is an update operator, not an aggregation operator.
$concatArrays gets most of the way there like
{$set: {redepmtions: {$concatArrays: [ "$redemptions", [{amount: "$amount"}]}}
That will throw an error if $redemptions doesn't already exist, so use $cond to subsitute an empty array in that case:
.updateOne(
{ ...criteria },
[{$set: {
redemptions: {$concatArrays: [
{$cond: [{$eq: ["array", {$type: "$redemptions"}]}, "$redemptions", []]},
[{amount: "$amount"}]
]}
}}]
)
Playground

How to match two different Object ids using MongoDB find() query?

I have an entry like below,
[
{
"_id":ObjectId("59ce020caa87df4da0ee2c78"),
"name": "Tom",
"owner_id": ObjectId("59ce020caa87df4da0ee2c78")
},
{
"_id":ObjectId("59ce020caa87df4da0ee2c79"),
"name": "John",
"owner_id": ObjectId("59ce020caa87df4da0ee2c78")
}
]
now, I need to find the person whose _id is equal to owner_id using find() in MongoDB.
Note, we can't not use $match (aggregation) due to some reason.
I am using this query,
db.people.find({ $where: "this._id == this.owner_id" })
but, it's not returning the expected output. Can anyone help me with this.
Thanks.
Using $expr and $eq you can get desired values avoiding the use of $where into a find stage (not aggregation necessary).
db.collection.find({
"$expr": {
"$eq": [
"$_id",
"$owner_id"
]
}
})

Morphia Aggregation Lookup syntax?

I'm trying to achieve a join on three String fields between two collections on MongoDB v. 4.4.3: one containing the original documents, the other the translations.
Both document types look like this:
{
"_id" : ObjectId("60644367b521563be8044f07"),
"dsId" : "2051918",
"lcId" : "data_euscreenXL_EUS_15541BBE705033639D4E06691D7A5D2E",
"pgId" : "1",
(...)
This MongoDB query does what I need, embedding the Translations in the result:
db.Original.aggregate([
{ $match: { query parameters } },
{ $lookup:
{
from: "Translation",
let: { "origDsId": "$dsId", origLcId: "$lcId", "origPgId": "$pgId" },
pipeline: [
{ $match:
{ $expr:
{ $and:
[
{ $eq: [ "$dsId", "$$origDsId" ] },
{ $eq: [ "$lcId", "$$origLcId" ] },
{ $eq: [ "$pgId", "$$origPgId" ] }
]
}
}
},
{ $project: { dsId: 0, _id: 0 } }
],
as: "translations"
}
}])
However, I can't figure out how to write the equivalent Morphia query. I updated to Morphia v.2.2, which adds the required features, but it's all very new and hasn't yet been documented on morphia.dev; I couldn't find much more on Javadoc either. This Morphia unit test on Github looked interesting and I tried copying that approach:
Aggregation<Original> query = datastore.aggregate(Original.class)
.match(eq("dsId", datasetId), eq("lcId", localId))
.lookup(Lookup.lookup(Translation.class)
.let("origDsId", value("$dsId"))
.let("origLcId", value("$lcId"))
.let("origPgId", value("$pgId"))
.pipeline(Match.match(expr(Expressions.of()
.field("$and",
array(Expressions
.of().field("$eq",
array(field("dsId"),
field("$origDsId"))),
Expressions
.of().field("$eq",
array(field("lcId"),
field("$origLcId"))),
Expressions
.of().field("$eq",
array(field("pgId"),
field("$origPgId"))))))))
.as("translations"));
...
This returns the Original documents, but fails to join the Translations.
The problem is that the syntax of the pipeline stage is rather puzzling. I wonder if anyone can shed some light on this?
the unit test example does not use (or need?) the double-$ form seen in "$$origDsId"? From the MongoDB documentation I understand that this form is used to refer to externally defined variables (eg in the "let" assignment before the "pipeline") but they don't work in the quoted example either;
what is the role of the static ArrayExpression "array"? It looks as if it's a kind of assignment container, where Expressions.of().field("$eq", array(field("dsId"), field("$origDsId"))) might mean something like "dsId" = "$origDsId" - which would be what I need (if it would work ;) )
I tried all sorts of combinations, using field("$origDsId"), value("$origDsId"), field("$$origDsId"), value("$$origDsId"), etcetera, but having no luck so far.
Thanks in advance!

Nested Mongodb Query needing multi seareshes

Hi I'm new with mongodb and I have to write a query to check if each field in database has a parent, if yes change the hasChild field to "true" else leave it be the default value of false.
Here is sample of one Document.
{
"_id":"5e19ef611052250ba51abb7b",
"name":"shampoo",
"price":12.99,
"hasChild":true
}
{
"_id":"5e1b268d25fe046b3518dcb9",
"name":"conditioner",
"price":"13.99",
"parent":"5e19ef611052250ba51abb7b",
"hasChild":false
}
I though a query which could ,while updating a filed search in other documents, check whether they have a parent id same as current documents id and set true/false would work out. but I couldn't find a way to write it. I would appreciate any ideas.
note that "_id" and "parent" are ObjectId.
You can do it with aggregation framework.
Something like this with $addFields and $cond:
db.getCollection("yourcollection").aggregate(
[
{
$addFields: {
"hasChild": {
$cond: { if: { $eq: [ "$parent", undefined ] }, then: false, else: true }
}
}
},
]
);

Optimized way of Querying in MongoDB using $in vs $or

Implementing an application that looks up a table for mail id presence from a list of around 10 email ids. Tried using $or and $in.
$in seems to give better performance but not significant. Is one more optimized than other?
MongoDB docs have the answer:
"When using $or with <expressions> that are equality checks for the value of the same field, choose the $in operator over the $or operator."
$or operator is logical operator where you can define your own login but $in operator is Comparison operator where you can compare you can not put your on logic.
Syntax of $in:
{ field: { $in: [<value1>, <value2>, ... <valueN> ] } }
Example:
db.account.find( { qty: { $in: [ 5, 15 ] } } )
Syntax of $or:
{ $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] }
Example:
db.account.find( { $or: [ { quantity: { $lt: 20 } }, { price: 10 } ] } )
Note: Account is your collection name
"While "$or"will always work, use "$in"whenever possible as the query optimizer
handles it more efficiently."
Moreover "$in" has more readability.
Ref: MongoDB: The Definitive Guide
Well that will insure no indecis to be ensured if you use $in, however i prefer to format it to $or as it will ensure index (readability won't concern me at is being handled in application logic in which i prefer to consume the memory of app rather than mongodb server)