I'm trying to match documents without a substring in an array field, in an aggregation pipeline.
I believe I need to add a field and then do a $match on -1, but I'm stuck on $indexOfBytes to find substrings in an array field.
db.collectionName.aggregate([{$addFields:{indexOfA:{$indexOfBytes:['$arrayName.fieldName1.fieldName2','A']}}}])
I get the error:
MongoServerError: PlanExecutor error during aggregation :: caused by :: $indexOfBytes requires a string as the first argument, found: array
collection example:
[
{ arrayName: [ fieldName1: { fieldName2: 'A' } ] },
{ arrayName: [ fieldName1: { fieldName2: 'B' } ] },
{ arrayName: [ fieldName1: { fieldName2: 'C' } ] },
{ arrayName: [ ] }
]
desired result (:
[
{ arrayName: [ fieldName1: { fieldName2: 'B' } ] },
{ arrayName: [ fieldName1: { fieldName2: 'C' } ] },
{ arrayName: [ ] }
]
Maybe something like this:
db.collection.aggregate([
{
$addFields: {
arrayName: {
"$filter": {
"input": "$arrayName",
"as": "a",
"cond": {
$eq: [
{
$indexOfBytes: [
"$$a.fieldName1.fieldName2",
"A"
]
},
-1
]
}
}
}
}
},
{
$match: {
$expr: {
$ne: [
{
$size: "$arrayName"
},
0
]
}
}
}
])
Explained:
$filter arrayName elements not having A in fieldName2
$match only the non-empty arrayName arrays
playground
Related
I want to update many documents based on the condition in MongoDB.
MODEL is the collection which has the document with below information.
"info": [
{
"field1": "String1",
"field2": "String2"
},
{
"field1": "String1",
"field2": "String_2"
}
],
"var": "x"
I need to update all the "String1" value of field1 with "STRING_NEW". I used the below query to update but not working as expected.
db.model.updateMany(
{ "info.field1": { $exists: true } },
[
{ "$set": {
"info": {
"$map": {
"input": "$info.field1",
"in": {
"$cond": [
{ "$eq": ["$$this.field1", "String1"] },
"STRING_NEW",
$$this.field1
]
}
}
}
} }
]
)
Please have a look and suggest if anything is to be modified in the above query.
Solution 1
With the update with aggregation pipeline, you should iterate the object in info array and update the iterated object by merging the current object with new field1 field via $mergeObjects.
db.model.updateMany({
"info.field1": "String1"
},
[
{
"$set": {
"info": {
"$map": {
"input": "$info",
"in": {
"$cond": [
{
"$eq": [
"$$this.field1",
"String1"
]
},
{
$mergeObjects: [
"$$this",
{
"field1": "STRING_NEW"
}
]
},
"$$this"
]
}
}
}
}
}
])
Demo Solution 1 # Mongo Playground
Solution 2
Can also work with $[<identifier>] positional filtered operator and arrayFilters.
db.model.updateMany({
"info.field1": "String1"
},
{
"$set": {
"info.$[info].field1": "STRING_NEW"
}
},
{
arrayFilters: [
{
"info.field1": "String1"
}
]
})
Demo Solution 2 # Mongo Playground
I have the following data:
{
_id: "1"
transitions: [
{
"_id" : "11"
"name" : "Tr1"
"checkLists" : [
{ _id: "111", name: "N1"},
{ _id: "112", name: "N2"}
]
}
]
}
I used the following code to get the name N2 by query of _id:112
db.collection.findOne({ 'transitions.checkLists._id: new ObjectId("112") } }}, { 'transitions.checkLists.$': 1 })
but the result returns back both of them:
{ _id: ObjectId("1"),
transitions:
[ { checkLists:
[ { name: 'N1', _id: ObjectId("111") },
{ name: 'N2', _id: ObjectId("112") } ] } ] }
I would like to find and get only the name N2 by query of _id:112
Expected Result:
{ _id: ObjectId("1"),
transitions:
[ { checkLists:
[ { name: 'N2', _id: ObjectId("112") } ] } ] }
You can do it via the aggregation framework as follow:
db.collection.aggregate([
{
$match: {
"transitions.checkLists._id": "111"
}
},
{
"$addFields": {
"transitions": {
"$map": {
"input": "$transitions",
"as": "t",
"in": {
"$mergeObjects": [
"$$t",
{
"checkLists": {
"$filter": {
"input": "$$t.checkLists",
"as": "c",
"cond": {
$eq: [
"$$c._id",
"111"
]
}
}
}
}
]
}
}
}
}
},
{
"$addFields": {
transitions: {
$filter: {
input: "$transitions",
as: "elem",
cond: {
"$ne": [
"$$elem.checkLists",
[]
]
}
}
}
}
}
])
Explained:
Match the transitions.checkLists._id element in first stage.
Map/mergeobjects with filtered checklists to filter only the needed object from the transitions.checkLists array.
Remove the transitions elements where no checkList exists.
( this need to be done to remove elements for same document where there is no matching _id's )
Playground
The output of the db.name.aggregate() function gives output:
[{}, {"abc": "zyx"}, {}, "opk": "tyr"]
Actual output desired :
[{"abc": "zyx"}, "opk": "tyr"]
Firstly, your output is not a valid array. It should be like this:
[{}, {"abc": "zyx"}, {}, {"opk": "tyr"}]
Now, to obtain your desired output, you can add the following $match stage, to
your pipeline:
db.collection.aggregate([
{
"$match": {
$expr: {
"$gt": [
{
"$size": {
"$objectToArray": "$$ROOT"
}
},
0
]
}
}
}
])
Here, we are converting the document to an array using $objectToArray, and then we check whether the size of that array is greater than 0. Only those documents are kept in the output.
Playground link.
What if you data looks like this.
[
{
"arr": [
{},
{
"abc": "zyx"
},
{},
{
"opk": "tyr"
}
]
}
]
The aggregation be like this to remove empty objects
db.collection.aggregate([
{
"$unwind": {
"path": "$arr",
}
},
{
"$match": {
arr: {
"$ne": {}
}
}
},
{
"$group": {
_id: "$_id",
arr: {
$push: "$arr"
}
}
}
])
Outputs
[
{
"_id": ObjectId("5a934e000102030405000000"),
"arr": [
{
"abc": "zyx"
},
{
"opk": "tyr"
}
]
}
]
Demo#mongoplayground
https://mongoplayground.net/p/BjGxzlrlj6s
I have an object schema that looks like this:
{
"_id":"ObjectId(""30t00594537da2r7awe083va"")",
"balances":{
"intraday":[
[
1630939075734,
1899.09
],
[
1630939435939,
1899.32
],
[
1632306730756,
0
],
[
1632306759376,
0
],
[
1632272012916,
1372.22
]
]
}
}
I want to remove all arrays within "balances.intraday" array whose the second element is equal to 0.
so my desired array will looks like this:
{
"_id":"ObjectId(""30t00594537da2r7awe083va"")",
"balances":{
"intraday":[
[
1630939075734,
1899.09
],
[
1630939435939,
1899.32
],
[
1632272012916,
1372.22
]
]
}
}
I tried to use the $pull command but it only removes the index and not the whole array.
======================= Edit =======================
Thanks to Tom Slabbaert the solution is as follows :
db._get_collection().update_many(
{},
[
{
"$set": {
"balances.intraday": {
"$filter": {
"input": "$balances.intraday",
"cond": {
"$ne": [
{
"$arrayElemAt": [
"$$this",
1
]
},
0
]
}
}
}
}
}
])
You should use pipelined updates for this, like so:
db.collection.updateMany(
{},
[
{
$set: {
"balances.intraday": {
$filter: {
input: "$balances.intraday",
cond: {
$ne: [
{
$arrayElemAt: [
"$$this",
1
]
},
0
]
}
}
}
}
}
])
Mongo Playground
I am having the following collection:
{
"price" : [
55800000,
62800000
],
"surface" : [
81.05,
97.4
],
}
I would want to calculate the price/m2. I have tried the following but got the following error: "$divide only supports numeric types, not array and array".
db.entries.aggregate(
[
{ $project: { url: 1, pricePerSquareMeter: { $divide: [ "$price", "$surfaces" ] } } }
]
)
Would you know how to solve this? Ultimately would want to have an array like this:
{
"price" : [
55800000,
62800000
],
"surface" : [
81.05,
97.4
],
"pricePerSquareMeter" : [
688463.91,
644763.86
]
}
Important: The price and surface should also be ordered so that the calculation is valid.
You can use below aggregation
db.collection.aggregate([
{ "$addFields": {
"pricePerSquareMeter": {
"$map": {
"input": { "$range": [0, { "$size": "$price" }] },
"in": {
"$divide": [
{ "$arrayElemAt": ["$price", "$$this"] },
{ "$arrayElemAt": ["$surface", "$$this"] }
]
}
}
}
}}
])
MongoPlayground
You can use below aggregation
db.entries.aggregate([
{ $addFields: {
pricePerSquareMeter: {
$map: {
input: '$price',
as: 'item',
in: {
$divide: [
"$$item",
{ $arrayElemAt: [
"$surface",
{ "$indexOfArray": ["$price", "$$item"] }
]}
]
}
}
},
}}])