How to split a string field only if it exists in MongoDB? - mongodb

I was trying to do something like the following
db.collection.updateMany(
{"obj.field": {$exists: true}},
{$set: {"obj.field": {$split: ["$obj.field", ", "]}}}
);
However, it seems $split is not supported in update. How can I split a string field only if it exists?

$split is an aggregation operator, not an update operator.
In order to use split in an update, you will need to pass an aggregation pipeline as the second argument to updateMany.
Since $set is both an update operator and an aggregation pipeline (alias for $addFields), that might look like:
db.collection.updateMany(
{"obj.field": {$exists: true}},
[{$set: {"obj.field": {$split: ["$obj.field", ", "]}}}]
);
Note that this require MongoDB 4.2+

Related

What is the most efficient way to bulk toggle boolean value in mongodb?

I am trying to use aggregation pipeline and $not operator to bulk update fields of matched documents, but its not working.
I know but don't want to use js loop to modify them as it is inefficient way to do that.
let result = await User.updateMany(
{_id: {$in: ids}},
[{
$set: {isActive: {$not: ["$isActive"]}}
}]
)

Saving field result of aggregation unwind in collection

I need save the field idOriginAccount in collection banking_atendimento.
the result of aggregate operation returns
I'm using robo3t to make the query
see the query
db.getCollection('banking_atendimento').aggregate([{
$lookup:{
from:"banking_ted",
localField:"idtransacao",
foreignField:"id",
as:"transacao",
}
},
{$unwind: '$transacao'},
])
What operator i do use to insert the field in collection "banking_ted"
As Andrews suggested, you can use $out to write to a new collection.
Here's the complete query for you
db.getCollection('banking_atendimento').aggregate([{
$lookup:{
from:"banking_ted",
localField:"idtransacao",
foreignField:"id",
as:"transacao",
}
},
{$unwind: '$transacao'},
{$out: 'banking_ted'}
])

The $in operator inside $project, $match or find()

I have a find query that uses $in to check whether the specified array is contained within the collection string array:
db.Doc.find({ tags: { '$in': ['tag1','tag2'] } })
I am in the process of refactoring this query to use the aggregation framework, but I can't find the equivalent $in comparison operator at the $project or $match aggregation stages.
Is it possible to use the $in comparison operator at the $project or $match stages of an aggregation query.
To answer your question: yes, but not as you would expect. It is possible to use the $in operator at the $project or $match stages of an aggregation query, but the usage and the purpose aren't quite the same in each.
There are two extremely different types of the "same" $in operator (making a semantic confusion):
Non-aggregational $in: Usually narrows down the results, like a filter. It has no way to add information to the result set, if it doesn't match. Can be used both within find() collection method and inside the aggregational (quite confusing semantic ah?) $match.
Aggregational $in: Usually adds boolean information to the result set, can be used as a logic expression inside $cond, and might also remove some results when is used with $redact. Can be used in $project, $addFields, etc. (but cannot (!) be used within find() or $match). The structure is: { $in: [ <needle expression>, <array haystack expression> ] }, and all of this grey line becomes either true or false (I used PHP's documentation's in_array needle-heystack semantic to better explain). So, { $in [ 'foo', [ 'foo', 'bar', 'baz' ] ] } is true because foo is inside the array.
However, in the previous non-aggregational $in, the { maybeFooField: { $in: [ 'foo', 'bar', 'baz' ] } } structure query simply narrows down the result set, and it doesn't result in a boolean true or false.
Going back to your refactoring, the question is what are your intended results? Why did you switch to the aggregation framework from the beginning?
If you only want to narrow down or filter out the result set, and then use some other aggregation computations, use the simple non-aggregational $in operator.
db.Doc.aggregate([
{ $match: { tags: {$in: ['tag1','tag2'] } } } // non-aggregational $in
])
However, if you want to add information based on the existence or absence of certain tags, use the aggregational $in operator.
db.Doc.aggregate([
{ $project: { hasAnyTag: {$in: [$tags, ['tag1', 'tag2'] ] } } } // aggregational $in
])
Note, you have more aggregational operators to play with arrays, like: $setIntersection and $setIsSubset.
The query: db.Doc.find({ tags: { '$in': ['tag1','tag2'] } }) is equivalent to:
db.Doc.aggregate([
{$match:{tags: {$in: ['tag1','tag2'] }}}
])
And when u use $in at projection like below:
db.Doc.aggregate([
{$project:{tags: {$in: ['tag1','tag2'] }}}
])
Result will be tags:true or tags:false depending upon whether there's match or not.

$project multiple fields into one field

I'm working with MongoDB 3.0 (we won't be upgrading until next year.) I have a requirement to get a list of unique values across multiple fields in a collection. The fields have the same value most of the time. This can be accomplished in version 3.2 by something like this:
db.mydata.aggregate([
{'$project': {'combined_users': ['$user1', '$user2']}},
{'$unwind': '$combined_users'},
{'$group': {_id: 1, {$addToSet: '$combined_users'}}}
The issue is in version 3.0 we get "disallowed field type Array in..." at the combined_data.
How do I accomplish the same thing in Mongo 3.0?
You need to use the $setUnion operator
db.mydata.aggregate([
{'$project': { 'combined_users': { "$setUnion": ['$user1', '$user2'] }}}
])

Indexes with $in or $or

My document structure is something like this
{
_id: "id1",
field1: "val1",
field2: "val2",
outcome: "ABC"
}
I have created index on outcome field. I have to find all documents with {outcome:"ABC"} or {outcome:"XYZ"} only. There is no major difference in query execution time if i use $or or $in. e.g
db.coll.find({$or:[{outcome:"ABC"},{outcome:"XYZ"}]});
db.coll.find({outcome:{$in:["ABC","XYZ"]}});
Which operator should i use $or or $in in this case? And why? Any help would be appreciated.
MongoDB docs
When using $or with <expressions> that are equality checks for the value of the same field, use the $in operator instead of the $or operator.
For example, to select all documents in the inventory collection where the quantity field value equals either 20 or 50, use the $in operator:
db.inventory.find ( { quantity: { $in: [20, 50] } } )
So in your case it is better to use
db.coll.find({outcome:{$in:["ABC","XYZ"]}});