Bad Value needs an array in mongoDB - mongodb

I am trying to use $in inside $match condition of aggregate. I have SegmentList as literal array and Segment as a literal value. I am trying to match with $in condition as in query below.
db.collection.aggregate([
{'$match': {
'MaterialNumber': '867522'
}},
{'$project':{
'SegmentList': {'$literal':['A']},
'Segment':{'$literal':'A'},
'value':{'$literal':'B'},
}
},
{'$match': {
'Segment':{'$in':'$SegmentList'}
}
}
])
But I am getting an error.
assert: command failed: {
"ok" : 0,
"errmsg" : "bad query: BadValue: $in needs an array",
"code" : 16810
} : aggregate failed
I am not not able to understand what is problem

Not sure what you are intending to acheive here as all you are doing comparing if 'A' is in array with element 'A' which is always true.
$match in its regular form only takes a value not field reference.
No need to use $literal you can directly pass [].
You can use one of the following query based on mongo version.
3.4 & below:
{'$match': {'Segment':{'$in':['A']}}
3.6 ( not needed for your use case )
{'$match': {'$expr':{'$in':['$Segment', ['A']]}}}

Related

How to fetch value from inner object in mongo query result

I'm new to mongodb.
I have this object in my collection:
{
"_id" : ObjectId("5b549be38d9f1c00160117d3"),
"name" : "Name of object",
"a" : {
"b" : {
"c" : 100
}
}
}
What interests me is the 100 value, I want to fetch it from the object.
When I query the collection like this:
db.getCollection('myCollection').find({}, {'name':1, 'a.b.c':1})
I only get the same object with the inner objects.
Is there a way to query it so that I will get a result like this:
{"Name": "Name of object", "c":100}
By using Mongo aggregate query you can get the result. In $project stage of Mongo aggregate query you can add the conditions as per requirement.
Please try this query, might you will get the result:
db.myCollection.aggregate({
$project: {
"name": "$name",
"c": "$a.b.c",
_id: 0
}
})
As suggested by #Mayuri, You can achieve this by using .aggregate() but if you wanted to look at difference between $project in aggregation (Vs) projection in .find(), check out :
So one first thing with $project is it is much more powerful & can accept a lot more features that are helpful to transform the fields. But projection in .find() is pretty straight forward that can accept only few things : projection i.e; value to a field in projection can be one of these :
1 or true to include the field in the return documents.
0 or false to exclude the field.
Projection Operators : $, $elemMatch, $slice, $meta
Actual Issue :
In your query when you're doing 'a.b.c':1 in projection it means you're returning c field in the output, As field c is nested in b & a the output structure doesn't change & you'll be getting the c value under the same structure, but if you use aggregation { $project: { "c": "$a.b.c" } } it means you're assigning value of "$a.b.c" to a field named c. How ? : So when $ is used against a field in aggregation it will access field's value, which helps for assignment.

How can I traverse a nested document in MongoDB and return the sub-tree?

I have a document with the following structure in MongoDB:
{
"k1": {
"k11": {<extended-sub-document-11>},
"k12": {<extended-sub-document-12>}
},
"k2": {
"k21": {<extended-sub-document-21>}
}
}
How can I fetch the entire object at k12? The find mechanism requires me to provide a value against which to match. But here, I simply want to traverse the path k1/k12 and retrieve the entire sub-document at that key.
Thanks in advance.
Using projection in .find() you can try as like below :
db.collection.find({}, { "k1.k12": 1})
Test : mongoplayground
Note : You would only get values/object of k12 but as it's nested in k1, In the output you would see same structure with just k12 object in k1 like : {k1: {k12: {...}}}.
Using aggregation's $project stage :
db.collection.aggregate([ { $project: {_id :0, k12: "$k1.k12" } } ])
Test : mongoplayground
By using aggregation $project which is way more powerful than projection in .find() you can assign a field's value to a field. In the above query we're assigning value at k1.k12 to a field k12 using $ (Which helps to get value of referenced field).

Mongodb shell aggregate query comparing dates not returning results

I cant spot what the issue here is. This returns nothing
db.mycoll.aggregate([
{$project:{CreatedAt:"$CreatedAt",Now:"$$NOW",DateFloor:{$add:["$$NOW",-24*60*60000]}}},
{$match:{CreatedAt:{$gte:"$DateFloor"}}}
])
But this returns results - substituting DateFloor with actual value
db.mycoll.aggregate([
{$project:{CreatedAt:"$CreatedAt",Now:"$$NOW",DateFloor:{$add:["$$NOW",-24*60*60000]}}},
{$match:{CreatedAt:{$gte: ISODate("2020-04-28T23:17:56.547Z")}}}
])
Issue with your query is when you're doing :
{$match:{CreatedAt:{$gte:"$DateFloor"}}}
You're actually checking for documents where CreatedAt field's value to be greater than input string value "$DateFloor", So $match is not considering "$DateFloor" as another field in the same document rather it's considering it as a string value. So to compare two fields from same document you need to use $gte with $expr (which will let you use aggregation expressions within the query language).
{
$match: {
{
$expr: {
$gte: ["$CreatedAt", "$DateFloor"];
}
}
}
}
So you might get confused when I say aggregation expressions & why $gte needs to be wrapped inside $expr - In your actual query $gte refers to comparison operator in MongoDB but in this above query $gte refers to aggregation pipeline operator where both technically does the same but which is what needed to compare two fields from same document.

Aggregate not behaving in Meteor as in Mongo

This is the query that I'm trying to run. If I run it on the Mongo console I get
meteor:PRIMARY> db.keywords_o2m.aggregate({$match:{keyword:{$in:['sql']}}},{$unwind:'$synonym'},{$group:{_id:0,kw:{$addToSet:'$synonym'}}});
{ "_id" : 0, "kw" : [ "database" ] }
However, if I copypaste it and try to run it on Meteor calling Meteor.call('getAllKeywordSynonyms',kw,function(err,data){...}); with this code
if(Meteor.isServer){
Meteor.methods({
'getAllKeywordSynonyms':function(keyword){
console.log("keywordO2M aggregate");
console.log(keywordO2M.aggregate({$match:{keyword:{$in:['sql']}}},{$unwind:'$synonym'},{$group:{_id:0,kw:{$addToSet:'$synonym'}}}));
}
)};
}
I get
I20151220-12:49:38.197(-8)? keywordO2M aggregate
I20151220-12:49:38.197(-8)? [ { _id: 5676fe5a17aeddb799dc4ef8,
I20151220-12:49:38.197(-8)? keyword: 'sql',
I20151220-12:49:38.197(-8)? synonym: 'database' } ]
It looks like it ran the $match and ignored the $unwind and $group.
I've tried using meteorhacks:aggregate and monbro:mongod-mapreduce-aggregation, but no difference.
What am I doing wrong?
Meteor is not as forgiving with notation.
With the aggregation function, the pipeline stages need to all be passed as a single parameter in an array, so the correct syntax would be
console.log(keywordO2M.aggregate([{$match:{keyword:{$in:['sql']}}},{$unwind:'$synonym'},{$group:{_id:0,kw:{$addToSet:'$synonym'}}}]));
Note the square brackets so that only one parameter gets passed to aggregate.

In MongoDB, can I project whether a value has a substring to a new field?

In MongoDB, I want to group my documents based on whether a certain field has a certain substring. I was trying to project each document to a boolean that says whether the field has that substring/matches that pattern, and then group by that field.
How can I do this?
Edit:
1) I have tried the aggregation pipeline as
db.aggregate([
{
'$match': {
'$regex': '.*world'
}
}
])
But I get an error:
pymongo.errors.OperationFailure: command SON([('aggregate', u'test'), ('pipeline', [{'$match': {'$regex': '.*world'}}])]) failed: exception: bad query: BadValue unknown top level operator: $regex
2) I am NOT trying to select only the words that match the pattern. I would like to select every document. I would like to make a field called 'matched' which says whether the pattern was matched for each element.
The $regex operator needs to appear after the field that you are matching on:
db.aggregate([
{
"$match": {
"fieldName": {"$regex": ".*world"}
}
}
])
The "unknown top level operator" message means that the operator has to appear after the field to which it applies.