how to use spel to represent $cond of mongo - spring-data-mongodb

I have a collection with documents like below
{
"_id" : ObjectId("5946360fdab24b1d05fac7e6"),
"name" : "aaa",
"createdAt" : NumberLong("1497773583563"),
"segmentedStatus" : 0
}
I want to stat how many documents with segmentedStatus = 1,
db.foo.aggregate(
{$project: {_id:0, segmentedCount:{$cond: [{$eq:["$segmentedStatus",1]}, 1, 0]} } },
{$group: {_id:null, count:{$sum:"$segmentedCount"}}}
)
In spring data mongo
Aggregation aggregation = newAggregation(project().and("segmentedCount").applyCondition(when(where("segmentedStatus").is(1)).then(1).otherwise(0)),
group().sum("segmentedCount").as("count")
);
but I feel above manner a little cumbersome so want to know if could use spel in this case , I tried below manner
Aggregation aggregation = newAggregation(project().andExpression("segmentedStatus == 1 ? 1 : 0").as("segmentedCount"),
group().sum("segmentedCount").as("count")
);
but it throws exception
Exception in thread "main" java.lang.IllegalArgumentException: Unsupported Element: org.springframework.data.mongodb.core.spel.ExpressionNode#4d5d943d Type: class org.springframework.data.mongodb.core.spel.ExpressionNode You probably have a syntax error in your SpEL expression!

The short hand ternary operator syntax for $cond is currently not supported. Still you can reference the $cond operator via cond(if, then, else)
project()
.and(AggregationSpELExpression.expressionOf("cond(segmentedStatus == 1, 1, 0)"))
.grou...
which will create the following:
{ "$cond" : { "if" : { "$eq" : ["$segmentedStatus", 1] }, "then" : 1, "else" : 0 } }

Related

Mongo shell aggregation pipeline error: "unknown operator: $nor"

I'm trying aggregation pipelines for the first time on Mongo Shell following this course. The idea is to create a search query with multiple conditions and use it within a $match aggregation stage.
My aproach was writing the conditions individually and combining them in an object like this:
let fullQuery = {
"languagesQuery" : {
"languages" : {
"$all" : [
"English",
"Japanese"
]
}
},
"genresQuery" : {
"$nor" : [
{
"genres" : "Crime"
},
{
"genres" : "Horror"
}
]
},
"imdbRatingQuery" : {
"imdb.rating" : {
"$gte" : 7
}
},
"ratedQuery" : {
"rated" : {
"$in" : [
"PG",
"G"
]
}
}
}
The thing is that, while individually all the queries seem to work fine, when I run the pipeline or even db.movies.find(fullQuery) I get the following error:
Error: error: {
"operationTime" : Timestamp(1618483194, 1),
"ok" : 0,
"errmsg" : "unknown operator: $nor",
"code" : 2,
"codeName" : "BadValue",
"$clusterTime" : {
"clusterTime" : Timestamp(1618483194, 1),
"signature" : {
"hash" : BinData(0,"LPVJIin4JoThWZbFICVnzHOnJKU="),
"keyId" : NumberLong("6902062171803353090")
}
}
}
Any clue as to what may be happening?
https://docs.mongodb.com/manual/reference/operator/query/nor/ is an expression-level operator, e.g. you can give it on top level of a query to combine clauses. It is not defined as a field-level operator.
For your query, use https://docs.mongodb.com/manual/reference/operator/query/nin/.
The rest of your query looks problematic also in that you should probably be using dot notation for conditions on nested fields and not nesting the fields in the query as you have done.

Can I convert a string field with space delimiter into array in mongoDB aggregate

I would like to convert a document with a string like this :
{ myField : 'bar foo boo' }
Into something like this :
{ myField : ['bar', 'foo', 'boo'] }
using space as delimiter.
Is it possible with aggregate or should I do it programmatically ?
You need the $split operator. It's run inside an aggregation stage. In this example
we make a new field using the $project stage. To pick a specific document put a $match stage at the front of your aggregation pipeline.
> use test
switched to db test
> db.test.insert({ myField : 'bar foo boo' })
WriteResult({ "nInserted" : 1 })
> db.test.aggregate([{"$project" :{ "mySplitField" :{"$split" : ["$myField", " "]}}}])
{ "_id" : ObjectId("5ebe77d0ca404d65eed0c4a8"), "mySplitField" : [ "bar", "foo", "boo" ] }
>

update nested array element of basis of selecting array element in mongodb

Below is MongoDB document.
`{
"_id" : ObjectId("588f09c8d466d7054114b456"),
"phonebook" : [
{
"pb_name_first" : "Aasu bhai",
"pb_phone_number" : [
{
"ph_id" : 2,
"ph_no" : "+91111111",
"ph_type" : "Mobile"
}
],
"pb_email_id" : [
{
"email_id" : "temp#gmail.com",
"email_type" : "Home",
"em_id" :1
},
{
"email_id" : "test#gmail.com",
"email_type" : "work",
"em_id" :2
}
],
"pb_name_prefix" : "MR."
}
]
}`
I want mongodb query that will update email_id data in pb_email_id array on basis of em_id. If i select em_id=1 then that record temp#gmail.com will update.if i select em_id=2 then test#gmail.com will update.
I don't think you can apply if-else logic in update call, you can run two separate update calls
db.collection.update({'pb_email_id.em_id':1},{$set : {'pb_email_id.$.email_id' : 'temp#gmail.com'}},{multi:true});
db.collection.update({'pb_email_id.em_id':2},{$set : {'pb_email_id.$.email_id' : 'test#gmail.com'}},{multi:true});
However you can run a script on collection to apply multiple logic
db.collection.find({}).forEach(function(doc){
if(doc.pb_email_id && doc.pb_email_id.length>0){
for(var i in doc.pb_email_id){
if(doc.pb_email_id[i].em_id === 1){
doc.pb_email_id[i].email_id = "temp#gmail.com"}
else if(doc.pb_email_id[i].em_id === 2){doc.pb_email_id[i].email_id = "test#gmail.com"}
db.collection.save(db)
}
}
})
If you have to apply multiple logic, you can run script, otherwise two update calls if that's as much as needed.
P.S - since you didn't mentioned collection name, I used db.collection.update it should be collection name like db.phonebook.find etc.

MongoDB search using $in array not working

I'm using MongoDB shell version: 2.4.8, and would simply like to know why a nested array search doesn't work quite as expected.
Assume we have 2 document collections, (a) Users:
{
"_id" : ObjectId("u1"),
"username" : "user1",
"org_ids" : [
ObjectId("o1"),
ObjectId("o2")
]
}
{
"_id" : ObjectId("u2"),
"username" : "user2",
"org_ids" : [
ObjectId("o1")
]
}
and (b) Organisations:
{
"_id" : ObjectId("o1"),
"name" : "Org 1"
}
{
"_id" : "ObjectId("o2"),
"name" : "Org 2"
}
Collections have indexes defined for
Users._id, Users.org_id, Organisations._id
I would like to find all Organisations a specific user is a member of.
I've tried this:
> myUser = db.Users.find( { _id: ObjectId("u1") })
> db.Organisations.find( { _id : { $in : [myUser.org_ids] }})
yet it yields nothing as a result. I've also tried this:
> myUser = db.Users.find( { _id: ObjectId("u1") })
> db.Organisations.find( { _id : { $in : myUser.org_ids }})
but it outputs the error:
error: { "$err" : "invalid query", "code" : 12580 }
(which basically says you need to pass $in an array) ... but that's what I thought I was doing originally ? baffled.
Any ideas what I'm doing wrong?
db.collection.find() returns a cursor - according to documentation. Then myUser.org_ids is undefined, but $in field must be an array. Let's see the solution!
_id is unique in a collection. So you can do findOne:
myUser = db.Users.findOne( { _id: ObjectId("u1") })
db.Organisations.find( { _id : { $in : myUser.org_ids }})
If you are searching for a non-unique field you can use toArray:
myUsers = db.Users.find( { username: /^user/ }).toArray()
Then myUsers will be an array of objects matching to the query.

mongodb - adding the value in a field to the value in an embedded array

I have a document in MongoDB as below.
{
"CorePrice" : 1,
"_id" : 166,
"partno" : 76,
"parttype" : "qpnm",
"shipping" :
[
{
"shippingMethod1" : "ground",
"cost1" : "10"
},
{
"shippingMethod2" : "air",
"cost2" : "11"
},
{
"shippingMethod3" : "USPS",
"cost3" : "3"
},
{
"shippingMethod4" : "USPS",
"cost4" : 45
}
]
}
My goal is to add CorePrice (1) to cost4 (45) and retrieve the computed value as a new column "dpv". I tried using the below query. However I receive an error exception: $add only supports numeric or date types, not Array. I'm not sure why. Any kind of help will be greatly appreciated.
db.Parts.aggregate([
{
$project: {
partno: 1,
parttype: 1,
dpv: {$add: ["$CorePrice","$shipping.cost1"]}
}
},
{
$match: {"_id":{$lt:5}}
}
]);
When you refer to the field shipping.cost1 and shipping is an array, MongoDB does not know which entry of the shipping-array you are referring to. In your case there is only one entry in the array with a field cost1, but this can't be guaranteed. That's why you get an error.
When you are able to change your database schema, I would recommend you to turn shipping into an object with a field for each shipping-type. This would allow you to address these better. When this is impossible or would break some other use-case, you could try to access the array entry by numeric index (shipping.0.cost1).
Another thing you could try is to use the $sum-operator to create the sum of all shipping.cost1 fields. When there is only one element in the array with a field cost1, the result will be its value.
I am able to achieve this by divorcing the query into two as below.
var pipeline1 = [
{
"$unwind": "$shipping"
},
{
$project:{
partno:1,
parttype:1,
dpv:{
$add:["$CorePrice","$shipping.cost4"]
}
}
},
{
$match:{"_id":5}
}
];
R = db.tb.aggregate( pipeline );

Resources