$addfield in spesific condiiton MongoDB - mongodb

I have this data when get by aggregation
[{_id: 1,
name:'Abraham',
class:'V',
question_answered:[{
id:'quest1',
answer:'A',
score:10,
question:{
soal:'apa judul lagu?',
correct_answer:'A',
type_question:'Essay'
}
},
{
id:'quest2'
answer:'C',
score:null,
question:{
soal:'apa judul lagu B?',
correct_answer:'B',
type_question:'Essay'
}
},
{
id:'quest3'
answer:'C',
score:10,
question:{
soal:'apa judul lagu C?',
correct_answer:'C',
type_question:'essay_pg'
}
},
]
}
]
Now i want to addfield with condition
addfield with name
formated_status_evaluation_essay and formated_status_evaluation_essay_option,
with condition:
IF(question_answer.question.type_question === 'Essay' && score !== null)
formated_status_evaluation_essay = 'already scoring'
Elseif(question_answer.question.type_question === 'Essay' && score === null)
formated_status_evaluation_essay = 'haven't scoring'
Elseif(question_answer.question.type_question === 'Essay' is not exists
formated_status_evaluation_essay = 'No essay question'
formated_status_evaluation_essay_option is have same condition with evaluation_essay
Soo expected output would be like
[{_id: 1,
name:'Abraham',
class:'V',
question_answered:[.........]
formated_status_evaluation_essay:'havent Scoring',
formated_status_evaluation_essay_option:'Already Scoring'
}
]
How to write a correct addfield with condition in mongodb to make an output like that?
thanks before i've tried soo many ways but still got no answer.
i tried this but seems i put a wrong syntax
{
'$addFields': {
'formated_status_evaluation_essay': {
'$cond': [
{
'$and': [
{'$$question_answer.question.type_soal ':
'essay'},
{'$$question_answer.nilai':{$ne:null}},
]
},
'already scoring',
'havent scoring'
]
}
}
}

db.collection.aggregate([
{
$set: {
formated_status_evaluation_essay: {
$cond: [
{
$and: [
{
$in: [
"Essay",
"$question_answered.question.type_question"
]
},
{
$ne: [
"$question_answered.score",
null
]
}
]
},
"already scoring",
"havent scoring"
]
}
}
}
])
mongoplayground

Related

MongoDB search by first attr with value

Is it possible do same filtering as in js
const list = [
{
a: 1,
"mostImportant": "qwer",
"lessImportant": "rty"
},
{
a: 2,
"lessImportant": "weRt",
"notImportant": "asd",
},
{
a: 3,
"mostImportant": "qwe2",
"notImportant": "asd",
}
];
list.filter((data) => {
data.attrToSearch = data.mostImportant || data.lessImportant || data.notImportant;
return data.attrToSearch.match(/wer/i);
});
in MongoDB?
Loot at example:
https://mongoplayground.net/p/VQdfoQ-HQV4
So I want to attrToSearch contain value of first not blank attr with next order mostImportant, lessImportant, notImportant
and then match by regex.
Expected result is receive first two documents
Appreciate your help
Approach 1: With $ifNull
Updated
$ifNull only checks whether the value is null but does not cover checking for the empty string.
Hence, according to the attached JS function which skips for null, undefined, empty string value and takes the following value, you need to set the field value as null if it is found out with an empty string via $cond.
db.collection.aggregate([
{
$addFields: {
mostImportant: {
$cond: {
if: {
$eq: [
"$mostImportant",
""
]
},
then: null,
else: "$mostImportant"
}
},
lessImportant: {
$cond: {
if: {
$eq: [
"$lessImportant",
""
]
},
then: null,
else: "$lessImportant"
}
},
notImportant: {
$cond: {
if: {
$eq: [
"$notImportant",
""
]
},
then: null,
else: "$notImportant"
}
}
}
},
{
"$addFields": {
"attrToSearch": {
$ifNull: [
"$mostImportant",
"$lessImportant",
"$notImportant"
]
}
}
},
{
"$match": {
attrToSearch: {
$regex: "wer",
$options: "i"
}
}
}
])
Demo Approach 1 # Mongo Playground
Approach 2: With $function
Via $function, it allows you to write a user-defined function (UDF) with JavaScript support.
db.collection.aggregate([
{
"$addFields": {
"attrToSearch": {
$function: {
body: "function(mostImportant, lessImportant, notImportant) { return mostImportant || lessImportant || notImportant; }",
args: [
"$mostImportant",
"$lessImportant",
"$notImportant"
],
lang: "js"
}
}
}
},
{
"$match": {
attrToSearch: {
$regex: "wer",
$options: "i"
}
}
}
])
Demo Approach 2 # Mongo Playground

Filtering dollar values with mongodb

I'm using mongodb and I want to select objects with AppraisedValueDisplay between 2 dollar values. I have tried filtering by :
{AppraisedValueDisplay: {$gt: 5000,$lt: 50000}}
This gives no results
:
I then realized that (screenshot), The value is saved in a string format like:
$35,000
How do I filter by dollar values in mongodb?
I would also like to preface this by saying that storing numeric values in your database formatted for presentation as strings is a bad idea, which you no doubt already know.
With that out of the way, here is the aggreagation you're looking for:
db.collection.aggregate([
{
"$project": {
"AppraisedValueDisplay": {
$replaceAll: {
input: "$AppraisedValueDisplay",
find: {
$literal: "$"
},
replacement: ""
}
}
}
},
{
"$project": {
"AppraisedValueDisplay": {
"$toInt": {
$replaceAll: {
input: "$AppraisedValueDisplay",
find: ",",
replacement: ""
}
}
}
}
},
{
$match: {
AppraisedValueDisplay: {
$gt: 30000,
$lt: 40000
}
}
}
])
The idea is to replace the $ and , with empty strings and then cast the resulting strings to integers. From that point, it's just a simple matter of matching the numeric values.
Playground: https://mongoplayground.net/p/YU65M-q1QCM
You should consider saving data in a format that you usually need to retrive as other users said in the comments.
But for this specific usecase you can use aggregate query to filter the results.
First convert the value into double and then match, I tested this query with mongodb version 4.0.23
db.tempCol.aggregate([
{
'$addFields':{
'signRemoved': {
'$reduce': {
'input': { '$split': [ '$AppraisedValueDisplay', { $literal: '$' } ] },
'initialValue': '',
'in': {
'$concat': [
'$$value',
{'$cond': [{'$eq': ['$$value', '']}, '', ', ']},
'$$this'
]
}
}
},
}
},
{
'$addFields':{
'commaRemoved': {
'$reduce': {
'input': { '$split': [ '$signRemoved', ',' ] },
'initialValue': '',
'in': {
'$concat': [
'$$value',
{'$cond': [{'$eq': ['$$value', '']}, '', '']},
'$$this'
]
}
}
},
}
},
{
'$addFields':{
matchPrice: { '$toDouble': '$commaRemoved' }
}
},
{
'$match':{
matchPrice: { '$gt': 5000, '$lt': 50000}
}
},
])

How to use '$let' in MongoDB Aggregation Query in Scala?

I am trying to write a mongoDB aggregation query in Scala.
How do I write Scala code to use "$let" in '$project' stage?
I am wondering if Variable should be used. Not sure how?
'$project': {
'myprojitem' :{
'$let': {
'vars' : { 'myVariable1': { '$or': [...] } }
'in' : {
'$cond': [
'$$myVariable1',
{ ... },
{ ... },
]
}
}
I figured out the answer. Hopefully it helps someone.
val doc : Document = Document("{
'$let': {
'vars' : { 'myVariable1': { '$or': [...] } },
'in' : { '$cond': ['$$myVariable1',{ ... },{ ... } ]
}
}")
var pipeline = mutable.Buffer[Bson]()
pipeline += Aggregates.project(Projections.fields(
Projections.computed("myprojitem",doc)
))
Basically, every { name : expression } can be written as :
Document("name" -> expression)
Or
Document( "{name : expression}")
$let is used to bind variables together to a results obj. The syntax follows the rule:
{
$let:
{
vars: { <var1>: <expression>},
in: <expression>
}
}
for mere details you should take a look at $let (aggregation) definition from mongodb manual
Here is a text book example just to make more sense:
Consider the following data:
{ _id: 1, price: 10, tax: 0.50, applyDiscount: true }
{ _id: 2, price: 10, tax: 0.25, applyDiscount: false }
And imagine that we want to generate a result for the finalTotal in a way that:
Where Disc = 10% if applyDiscount: true and 0 otherwise.
So we need now to create the aggregation on the data to construct this equation. So we can get a results like:
{ _id: 1, finalTotal: 9.45 }
{ _id: 2, finalTotal: 10.25 }
We can do this by doing:
$project: {
finalTotal: {
$let: {
vars: {
total: { $add: [ '$price', '$tax' ] },
discounted: { $cond: { if: '$applyDiscount', then: (0.9, else: 1 } }
},
in: { $multiply: [ "$$total", "$$discounted" ] }
}
}
}
We can break this down:
Step 1. adding price to tax together to a variable called total
total: { $add: [ '$price', '$tax' ] },
Step 2. transforming the condition in numbers (variable discounted)
discounted: { $cond: { if: '$applyDiscount', then: 0.9, else: 1 } }
Step 3. performing the operation $multiply operation between the constructed $$total and $$discounted
in: { $multiply: [ "$$total", "$$discounted" ] }

mongodb aggregation if condition - if true then execute filter

I'm trying to do a query that get arrays of profession and subProfession, and return all Therapist that has a matching profession/subProfession
But if both arrays of profession and subProfession are empty, then return all therapists.
here is a pseudo code of the logic I'm trying to get:
professionsFlag = professions.length != 0;
subProfessionsFlag = subProfessions.length != 0;
Therapist.aggregate([
{
$match: {
$cond: {if: professionsFlag || subProfessionsFlag,
then:
{$or: [
{ profession: { $in: professions } },
{
subProfession: {
$elemMatch: { $in: subProfessions }
}
}
]}
},
}
},
this code fails with unknown top level operator: $cond
what is the correct why to do so?
NOTE: The $or part of the query works as expected.
I ended up with this code:
let filter = {
$match: {
image: { $exists: true, $ne: null },
'clinic.city': params.place.value
}
};
if ( professions.length != 0 || subProfessions.length != 0) {
filter.$match.$or = [
{ profession: { $in: professions } },
{
subProfession: {
$elemMatch: { $in: subProfessions }
}
}
];
}
Therapist.aggregate([
filter,
], callback);

Filtering a .find() based on another document returns [ ]

Here is my attempt at the query AFTER Sergiu Zaharie's answer was posted below:
db.collection('users').find({_id:ObjectID('561f0cbd3ed5b852e95cab6a')}).limit(1).toArray(function(error, result) {
if((!error) && (result[0] !== undefined) && result.length) {
var user = result[0];
console.log("User ID: ", user._id);
console.log("Blocked users: ", user.blocked_users);
console.log("passed quizzes: ", user.passed_quizzes);
console.log("failed quizzes: ", user.failed_quizzes);
db.collection('users').find({
$and: [
{ _id: { $in: [user._id] } },
{ blocked_users: { $nin: [user.blocked_users] } },
{ passed_quizzes: { $nin: [user.passed_quizzes] } },
{ failed_quizzes: { $nin: [user.failed_quizzes] } }
]
}).toArray(function(error, doc) {
console.log("Results: ", doc);
});
}
});
There's three users in the table, one with ObjectID('561f0cbd3ed5b852e95cab6a') (me), one with ObjectID('561f0bbaff803840e917568d') (michael) and one with ObjectID('561f0b68d83c293fe960a25d') (thomas).
The query above executed should have returned the account with the ObjectID('561f0bbaff803840e917568d') (Michael) instead query returned [] 0 results.
Data output:
User ID: 561f0cbd3ed5b852e95cab6a
Block users: [ 561f0b68d83c293fe960a25d ]
passed quizzes: []
failed quizzes: []
Results: []
That would make the query executed this:
db.collection.find({
$and: [
// Void out myself, because I'm looking for other people.
{ _id: { $nin: [ ObjectID('561f0cbd3ed5b852e95cab6a') ] } },
// Void out blocked users, because I blocked them.
{ blocked_users: { $nin: [ObjectID('561f0b68d83c293fe960a25d')] } },
// More array based filtering: (Blank arrays, shouldn't matter right now)
{ passed_quizzes: { $nin: [] } },
{ failed_quizzes: { $nin: [] } }
]
});
As stated, this leaves room for the document with ObjectID('561f0bbaff803840e917568d') to be returned by the find() statement. As it's not filtered per requirements above.
Try this query:
db.collection.find(
{ [
{_id: { $nin: [id] }},
{ "settings.min_rating": { $lt: rating }},
{ "settings.max_rating": { $gt: rating }},
{ blockedUsers: { $nin: [id] }},
{ discoveredUsers: { $nin: [id] }}
]
}
)