How to include external array inside MongoDB aggregation? - mongodb

In aggregation project array element from index x to y where x is defined inside collection and y is an array element corresponding to the index of the array element which is matched. Please see below example then it will become quite easier to understand what i am trying to say.
coupons collection
{"coupon_id": "coupon01", "codes": ["FLAT30", "FLAT50", "FLAT70", "FLAT90"], "curr_index": 0}
For example see below example code here i am trying to get coupon codes starting from curr_index to (curr_index + n) where n is the number in coupon_ctrs corresponding to the index of coupons_ids example- for id "584559bd1f65d363bd5d25fd" n is 1, for id "58455a5c1f65d363bd5d2600" n is 2 and for the id "584878eb0005202c64b0cc5d" n is 3.
coupons_ctrs = [1, 2, 3];
coupons_ids = ["584559bd1f65d363bd5d25fd", "58455a5c1f65d363bd5d2600", "584878eb0005202c64b0cc5d"];
int n = 2;
couponmodel.aggregate(
{ $match : { '_id': { $in : coupons_ids }} },
{ $project: {_id:0, codes : /* How to use slice here so that codes array will be returned between cur_index and (curr_index + coupons_ctr corresponding to the index coupon id is found, example- for _id "584559bd1f65d363bd5d25fd" it should be 1 and so on) */} },
function(err, docs) {
if (err) {
} else {
}
});
UPDATE
As suggested by Styvane i could use $zip and it would work perfect but as i am using mongoDB 3.2.11 so i can't use it, so what can be the solution for using functionality of $zip in mongodb 3.2.11 ??
Can anyone please tell me how can i include this coupon_ctrs array and use it inside aggregation pipeline.

You need to apply a $slice expression to each element in the "coupons_ctrs" array using the $map operator which means that we use the literal "coupons_ctrs" array as "input" to $map.
let coupons_ids = [
"584559bd1f65d363bd5d25fd",
"58455a5c1f65d363bd5d2600",
"584878eb0005202c64b0cc5d"
];
let coupons_ctrs = [1, 2, 3];
db.couponmodel.aggregate(
[
{ "$match" : { "_id": { "$in" : coupons_ids } } },
{ "$project": {
"codes": {
"$map": {
"input": coupons_ctrs,
"as": "n",
"in": {
"$slice": [
"$codes",
"$curr_index",
{ "$add": [ "$curr_index", "$$n" ] }
]
}
}
}
}}
]
)
Which yields:
{
"codes" : [
[ "FLAT30" ],
[ "FLAT30", "FLAT50" ],
[ "FLAT30", "FLAT50", "FLAT70" ]
]
}
In MongoDB 3.4 we can use the $zip operator to do this:
db.couponmodel.aggregate(
[
{ "$project": {
"codes": {
"$map": {
"input": {
"$zip": {
"inputs": [ coupons_ids, coupons_ctrs ]
}
},
"as": "item",
"in": {
"coupon_id": { "$arrayElemAt": [ "$$item", 0 ] },
"value": {
"$slice": [
"$codes",
"$curr_index",
{ "$add": [
"$curr_index",
{ "$arrayElemAt": [ "$$item", 1 ] }
] }
]
}
}
}
}
}}
]
)
which return something like this:
{
"codes" : [
{
"coupon_id" : "584559bd1f65d363bd5d25fd",
"value" : [ "FLAT30" ]
},
{
"coupon_id" : "58455a5c1f65d363bd5d2600",
"value" : [ "FLAT30", "FLAT50" ],
},
{
"coupon_id" : "584878eb0005202c64b0cc5d",
"value" : [ "FLAT30", "FLAT50", "FLAT70" ]
}
]
}

Related

How to flatten nested array (parent->child) tree (of any depth) in MongoDB aggregation?

I have a nested tree level structure of item->items that looks something like this
{ "id":"1",
"type":"panel",
"items": [
{ "id":"2", "type":"input", },
{ "id":"4", "type":"group", "items": [
{ "id":"5", "type":"input" },
{ "id":"6", "type":"panel", "items":[...] },
]
}
]}
I'm looking to flatten the tree and get a single array list of all items like this:
[ { "id":"1", "type":"panel", },
{ "id":"2", "type":"input", },
{ "id":"4", "type":"panel", },
{ "id":"5", "type":"input", },
...
]
Is there a generic way to flatten the tree (that would work for any depth level)?
All answers I found here just manually $unwind each child level (I can't predict the number of levels) nor do I have reference to parent to use traverse with $graphLookup.
Or something like {'$*.items'}?
MQL doesn't have functions, so we can't recur, if we find a array.
Maybe there is a way to do it with MQL and 1 query.
But there is way to do it fast with more than 1 query.
The bellow example is 1 level/query.
With small change it can do 10 level/query or 100 level/query etc
so only 1 query will be needed, but we will do some redadent attempts to flatten arrays even if they are empty.
First 1 small modification.
Add 1 field on all documents "all-items": [{"id": "$id","type": "$type"}]
and removed the top level "id" and "type". Like bellow
aggregate(
[ {
"$project" : {
"all-items" : [ {
"id" : "$id",
"type" : "$type"
} ],
"items" : 1
}
} ]
)
Modified data
[
{
"all-items": [
{
"id": "1",
"type": "panel"
}
],
"items": [...like it was...]
}
]
And now we can do it with multiple queries 1 per/level
First call, code example
Second call, code example, with the result of first call
Third call we dont need, while will be false.
In each call we do $out, and we aggregate on the result of previous call.
while(there_is_1_document_with_not_empty_items[]) (send 1 find query)
db.collection.aggregate([
{
"$addFields": {
"level-nlevel": {
"$reduce": {
"input": "$items",
"initialValue": [
[],
[]
],
"in": {
"$let": {
"vars": {
"info": "$$value",
"i": "$$this"
},
"in": {
"$let": {
"vars": {
"level": {
"$arrayElemAt": [
"$$info",
0
]
},
"nlevel": {
"$arrayElemAt": [
"$$info",
1
]
}
},
"in": [
{
"$concatArrays": [
"$$level",
[
{
"id": "$$i.id",
"type": "$$i.type"
}
]
]
},
{
"$cond": [
{
"$isArray": [
"$$i.items"
]
},
{
"$concatArrays": [
"$$nlevel",
"$$i.items"
]
},
"$$nlevel"
]
}
]
}
}
}
}
}
}
}
},
{
"$project": {
"all-items": {
"$concatArrays": [
"$all-items",
{
"$arrayElemAt": [
"$level-nlevel",
0
]
}
]
},
"items": {
"$arrayElemAt": [
"$level-nlevel",
1
]
}
}
}
])
This flattens per document(no $unwind is used), if you want to flatten all collection, $unwind one time after the while ends the $all-items.
There is not a mongodb query language aggregation sage that supports flatting to an unknown depth, but $function would allow you to execute a method against the document
here is a javascript example:
var fn = function(items) {
var ret = [];
var toCheck = [...items];
while (toCheck.length) {
var nxtToCheck = [];
for (var item of toCheck) {
ret.push({ id: item.id, type: item.type });
nxtToCheck.push(...(item.items || []));
}
toCheck = nxtToCheck;
}
return ret;
}
db.myCol.aggregate([
{ $match: {} },
{ $addFields: { allItems: { $function: { body: fn, args: ["$items"], lang: "js" } } } }
]);

MongoDB - Performing an upsert on an array if arrayFilters are not satisfied

I have the following document stored in mongo:
{
"_id" : ObjectId("5d1a08d2329a3c1374f176df"),
"associateID" : "1234567",
"associatePreferences" : [
{
"type" : "NOTIFICATION",
"serviceCode" : "service-code",
"eventCode" : "test-template",
"preferences" : [
"TEXT",
"EMAIL"
]
},
{
"type" : "URGENT_NOTIFICATION",
"serviceCode" : "service-code",
"eventCode" : "test-template",
"preferences" : [
"TEXT"
]
}
]
}
I am basically trying to query one of the elements of the associatePreferences array based off of its type, serviceCode, and eventCode and add a new value to the preferences array. However, if that combination of type, serviceCode, and eventCode is not present, I would like to add a new element to the associatePreferences array with those values. This is my current query:
db.user_communication_preferences.update(
{'associateID':'testassociate'},
{$addToSet:{'associatePreferences.$[element].preferences':"NEW_VALUE"}},
{arrayFilters:[{'element.serviceCode':'service-code-not-present', 'element.eventCode':'event-code-not-present','element.type':'URGENT_NOTIFICATION'}]}
)
This query works if all of the arrayFilters are present in the an element of associatePreferences, but it does not add a new element if it is not present. What am I missing?
You can use aggregation pipeline to check the existence of the element, then append the element to associatePreferences array conditionally. Finally, using the aggregation result to update back your document.
db.user_communication_preferences.aggregate([
{
"$match": {
"associateID": "testassociate"
}
},
{
"$addFields": {
"filteredArray": {
"$filter": {
"input": "$associatePreferences",
"as": "pref",
"cond": {
$and: [
{
$eq: [
"$$pref.type",
"URGENT_NOTIFICATION"
]
},
{
$eq: [
"$$pref.eventCode",
"event-code-not-exists"
]
},
{
$eq: [
"$$pref.serviceCode",
"service-code-not-exists"
]
}
]
}
}
}
}
},
{
$addFields: {
"needAddElement": {
$eq: [
{
"$size": "$filteredArray"
},
0
]
}
}
},
{
"$addFields": {
"associatePreferences": {
"$concatArrays": [
"$associatePreferences",
{
"$cond": {
"if": {
$eq: [
"$needAddElement",
true
]
},
"then": [
{
"type": "URGENT_NOTIFICATION",
"serviceCode": "service-code-not-exists",
"eventCode": "event-code-not-exists",
"preferences": [
"TEXT"
]
}
],
"else": []
}
}
]
}
}
}
]).forEach(result){
db.user_communication_preferences.update({
_id : result._id
}, {
$set: {
"associatePreferences" : result.associatePreferences
}
})
}

I want to extract a substring from mongo db field and check if it's divisible by 4

I want to extract a substring from mongo db field and check if it's divisible by 4.
I have some a document like below:
{
"form" : "form-1002",
"requestType" : "POST"
},
{
"form" : "form-1003",
"requestType" : "POST"
}
I want to extract the documents on the basis of form field like form-1002, the 1002 is divisible by 2. and then update the request type based on query results. How can i achieve this?
Using MongoDB 4.0 and newer:
Construct an aggregate pipeline that makes use of the $substrCP to return the substring of the form field with the numerical part.
Once you get the substring, convert it to a numerical value using $toInt or $convert.
Use the $mod operator to get the remainder when dividing the result from the previous operation by 2.
Following is an example that shows the full pipeline with different fields that show how each of the above operator operates:
const pipeline = [
{ "$addFields": {
"formValue": {
"$toInt": {
"$substrCP": [
"$form",
5,
99999999
]
}
},
"remainder": {
"$mod": [
{
"$toInt": {
"$substrCP": [
"$form",
5,
99999999
]
}
},
2
]
},
"computedRequestType": {
"$cond": [
{
"$eq": [
{
"$mod": [
{
"$toInt": {
"$substrCP": [
"$form",
5,
99999999
]
}
},
2
]
},
0
]
},
"POST",
"GET"
]
}
} }
]
db.collection.aggregate(pipeline)
And for those two documents the above yields:
/* 1 */
{
"_id" : ObjectId("5b979ad11f21b55f5a1399c6"),
"form" : "form-1002",
"requestType" : "POST",
"formValue" : 1002,
"remainder" : 0,
"computedRequestType" : "POST"
}
/* 2 */
{
"_id" : ObjectId("5b979ad11f21b55f5a1399c7"),
"form" : "form-1003",
"requestType" : "POST",
"formValue" : 1003,
"remainder" : 1,
"computedRequestType" : "GET"
}
To update the collection you may need to add another pipeline which uses the $out operator on the same collection but the pipeline would be as:
const pipeline = [
{ "$addFields": {
"requestType": {
"$cond": [
{ "$eq": [
{ "$mod": [
{ "$toInt": {
"$substrCP": [
"$form",
5,
99999999
]
} },
2
] },
0
] },
"POST",
"GET"
]
}
} },
{ "$out": "collection" }
]
db.collection.aggregate(pipeline)

MongoDB insert document "or" increment field if exists in array

What I try to do is fairly simple, I have an array inside a document ;
"tags": [
{
"t" : "architecture",
"n" : 12
},
{
"t" : "contemporary",
"n" : 2
},
{
"t" : "creative",
"n" : 1
},
{
"t" : "concrete",
"n" : 3
}
]
I want to push an array of items to array like
["architecture","blabladontexist"]
If item exists, I want to increment object's n value (in this case its architecture),
and if don't, add it as a new Item (with value of n=0) { "t": "blabladontexist", "n":0}
I have tried $addToSet, $set, $inc, $upsert: true with so many combinations and couldn't do it.
How can we do this in MongoDB?
With MongoDB 4.2 and newer, the update method can now take a document or an aggregate pipeline where the following stages can be used:
$addFields and its alias $set
$project and its alias $unset
$replaceRoot and its alias $replaceWith.
Armed with the above, your update operation with the aggregate pipeline will be to override the tags field by concatenating a filtered tags array and a mapped array of the input list with some data lookup in the map:
To start with, the aggregate expression that filters the tags array uses the $filter and it follows:
const myTags = ["architecture", "blabladontexist"];
{
"$filter": {
"input": "$tags",
"cond": {
"$not": [
{ "$in": ["$$this.t", myTags] }
]
}
}
}
which produces the filtered array of documents
[
{ "t" : "contemporary", "n" : 2 },
{ "t" : "creative", "n" : 1 },
{ "t" : "concrete", "n" : 3 }
]
Now the second part will be to derive the other array that will be concatenated to the above. This array requires a $map over the myTags input array as
{
"$map": {
"input": myTags,
"in": {
"$cond": {
"if": { "$in": ["$$this", "$tags.t"] },
"then": {
"t": "$$this",
"n": {
"$sum": [
{
"$arrayElemAt": [
"$tags.n",
{ "$indexOfArray": [ "$tags.t", "$$this" ] }
]
},
1
]
}
},
"else": { "t": "$$this", "n": 0 }
}
}
}
}
The above $map essentially loops over the input array and checks with each element whether it's in the tags array comparing the t property, if it exists then the value of the n field of the subdocument becomes its current n value
expressed with
{
"$arrayElemAt": [
"$tags.n",
{ "$indexOfArray": [ "$tags.t", "$$this" ] }
]
}
else add the default document with an n value of 0.
Overall, your update operation will be as follows
Your final update operation becomes:
const myTags = ["architecture", "blabladontexist"];
db.getCollection('coll').update(
{ "_id": "1234" },
[
{ "$set": {
"tags": {
"$concatArrays": [
{ "$filter": {
"input": "$tags",
"cond": { "$not": [ { "$in": ["$$this.t", myTags] } ] }
} },
{ "$map": {
"input": myTags,
"in": {
"$cond": [
{ "$in": ["$$this", "$tags.t"] },
{ "t": "$$this", "n": {
"$sum": [
{ "$arrayElemAt": [
"$tags.n",
{ "$indexOfArray": [ "$tags.t", "$$this" ] }
] },
1
]
} },
{ "t": "$$this", "n": 0 }
]
}
} }
]
}
} }
],
{ "upsert": true }
);
I don't believe this is possible to do in a single command.
MongoDB doesn't allow a $set (or $setOnInsert) and $inc to affect the same field in a single command.
You'll have to do one update command to attempt to $inc the field, and if that doesn't change any documents (n = 0), do the update to $set the field to it's default value.

how to convert string to numerical values in mongodb

I am trying to convert a string that contains a numerical value to its value in an aggregate query in MongoDB.
Example of document
{
"_id": ObjectId("5522XXXXXXXXXXXX"),
"Date": "2015-04-05",
"PartnerID": "123456",
"moop": "1234"
}
Example of the aggregate query I use
{
aggregate: 'my_collection',
pipeline: [
{$match: {
Date :
{$gt:'2015-04-01',
$lt: '2015-04-05'
}}
},
{$group:
{_id: "$PartnerID",
total:{$sum:'$moop'}
}}]}
where the results are
{
"result": [
{
"_id": "123456",
"total": NumberInt(0)
}
}
How can you convert the string to its numerical value?
MongoDB aggregation not allowed to change existing data type of given fields. In this case you should create some programming code to convert string to int. Check below code
db.collectionName.find().forEach(function(data) {
db.collectionName.update({
"_id": data._id,
"moop": data.moop
}, {
"$set": {
"PartnerID": parseInt(data.PartnerID)
}
});
})
If your collections size more then above script will slow down the performance, for perfomace mongo provide mongo bulk operations, using mongo bulk operations also updated data type
var bulk = db.collectionName.initializeOrderedBulkOp();
var counter = 0;
db.collectionName.find().forEach(function(data) {
var updoc = {
"$set": {}
};
var myKey = "PartnerID";
updoc["$set"][myKey] = parseInt(data.PartnerID);
// queue the update
bulk.find({
"_id": data._id
}).update(updoc);
counter++;
// Drain and re-initialize every 1000 update statements
if (counter % 1000 == 0) {
bulk.execute();
bulk = db.collectionName.initializeOrderedBulkOp();
}
})
// Add the rest in the queue
if (counter % 1000 != 0) bulk.execute();
This basically reduces the amount of operations statements sent to the sever to only sending once every 1000 queued operations.
Using MongoDB 4.0 and newer
You have two options i.e. $toInt or $convert. Using $toInt, follow the example below:
filterDateStage = {
'$match': {
'Date': {
'$gt': '2015-04-01',
'$lt': '2015-04-05'
}
}
};
groupStage = {
'$group': {
'_id': '$PartnerID',
'total': { '$sum': { '$toInt': '$moop' } }
}
};
db.getCollection('my_collection').aggregate([
filterDateStage,
groupStage
])
If the conversion operation encounters an error, the aggregation operation stops and throws an error. To override this behavior, use $convert instead.
Using $convert
groupStage = {
'$group': {
'_id': '$PartnerID',
'total': {
'$sum': {
'$convert': { 'input': '$moop', 'to': 'int' }
}
}
}
};
Using Map/Reduce
With map/reduce you can use javascript functions like parseInt() to do the conversion. As an example, you could define the map function to process each input document:
In the function, this refers to the document that the map-reduce operation is processing. The function maps the converted moop string value to the PartnerID for each document and emits the PartnerID and converted moop pair. This is where the javascript native function parseInt() can be applied:
var mapper = function () {
var x = parseInt(this.moop);
emit(this.PartnerID, x);
};
Next, define the corresponding reduce function with two arguments keyCustId and valuesMoop. valuesMoop is an array whose elements are the integer moop values emitted by the map function and grouped by keyPartnerID.
The function reduces the valuesMoop array to the sum of its elements.
var reducer = function(keyPartnerID, valuesMoop) {
return Array.sum(valuesMoop);
};
db.collection.mapReduce(
mapper,
reducer,
{
out : "example_results",
query: {
Date: {
$gt: "2015-04-01",
$lt: "2015-04-05"
}
}
}
);
db.example_results.find(function (err, docs) {
if(err) console.log(err);
console.log(JSON.stringify(docs));
});
For example, with the following sample collection of documents:
/* 0 */
{
"_id" : ObjectId("550c00f81bcc15211016699b"),
"Date" : "2015-04-04",
"PartnerID" : "123456",
"moop" : "1234"
}
/* 1 */
{
"_id" : ObjectId("550c00f81bcc15211016699c"),
"Date" : "2015-04-03",
"PartnerID" : "123456",
"moop" : "24"
}
/* 2 */
{
"_id" : ObjectId("550c00f81bcc15211016699d"),
"Date" : "2015-04-02",
"PartnerID" : "123457",
"moop" : "21"
}
/* 3 */
{
"_id" : ObjectId("550c00f81bcc15211016699e"),
"Date" : "2015-04-02",
"PartnerID" : "123457",
"moop" : "8"
}
The above Map/Reduce operation will save the results to the example_results collection and the shell command db.example_results.find() will give:
/* 0 */
{
"_id" : "123456",
"value" : 1258
}
/* 1 */
{
"_id" : "123457",
"value" : 29
}
You can easily convert the string data type to numerical data type.
Don't forget to change collectionName & FieldName.
for ex : CollectionNmae : Users & FieldName : Contactno.
Try this query..
db.collectionName.find().forEach( function (x) {
x.FieldName = parseInt(x.FieldName);
db.collectionName.save(x);
});
Eventually I used
db.my_collection.find({moop: {$exists: true}}).forEach(function(obj) {
obj.moop = new NumberInt(obj.moop);
db.my_collection.save(obj);
});
to turn moop from string to integer in my_collection following the example in Simone's answer MongoDB: How to change the type of a field?.
String can be converted to numbers in MongoDB v4.0 using $toInt operator. In this case
db.col.aggregate([
{
$project: {
_id: 0,
moopNumber: { $toInt: "$moop" }
}
}
])
outputs:
{ "moopNumber" : 1234 }
Here is a pure MongoDB based solution for this problem which I just wrote for fun. It's effectively a server-side string-to-number parser which supports positive and negative numbers as well as decimals:
db.collection.aggregate({
$addFields: {
"moop": {
$reduce: {
"input": {
$map: { // split string into char array so we can loop over individual characters
"input": {
$range: [ 0, { $strLenCP: "$moop" } ] // using an array of all numbers from 0 to the length of the string
},
"in":{
$substrCP: [ "$moop", "$$this", 1 ] // return the nth character as the mapped value for the current index
}
}
},
"initialValue": { // initialize the parser with a 0 value
"n": 0, // the current number
"sign": 1, // used for positive/negative numbers
"div": null, // used for shifting on the right side of the decimal separator "."
"mult": 10 // used for shifting on the left side of the decimal separator "."
}, // start with a zero
"in": {
$let: {
"vars": {
"n": {
$switch: { // char-to-number mapping
branches: [
{ "case": { $eq: [ "$$this", "1" ] }, "then": 1 },
{ "case": { $eq: [ "$$this", "2" ] }, "then": 2 },
{ "case": { $eq: [ "$$this", "3" ] }, "then": 3 },
{ "case": { $eq: [ "$$this", "4" ] }, "then": 4 },
{ "case": { $eq: [ "$$this", "5" ] }, "then": 5 },
{ "case": { $eq: [ "$$this", "6" ] }, "then": 6 },
{ "case": { $eq: [ "$$this", "7" ] }, "then": 7 },
{ "case": { $eq: [ "$$this", "8" ] }, "then": 8 },
{ "case": { $eq: [ "$$this", "9" ] }, "then": 9 },
{ "case": { $eq: [ "$$this", "0" ] }, "then": 0 },
{ "case": { $and: [ { $eq: [ "$$this", "-" ] }, { $eq: [ "$$value.n", 0 ] } ] }, "then": "-" }, // we allow a minus sign at the start
{ "case": { $eq: [ "$$this", "." ] }, "then": "." }
],
default: null // marker to skip the current character
}
}
},
"in": {
$switch: {
"branches": [
{
"case": { $eq: [ "$$n", "-" ] },
"then": { // handle negative numbers
"sign": -1, // set sign to -1, the rest stays untouched
"n": "$$value.n",
"div": "$$value.div",
"mult": "$$value.mult",
},
},
{
"case": { $eq: [ "$$n", null ] }, // null is the "ignore this character" marker
"then": "$$value" // no change to current value
},
{
"case": { $eq: [ "$$n", "." ] },
"then": { // handle decimals
"n": "$$value.n",
"sign": "$$value.sign",
"div": 10, // from the decimal separator "." onwards, we start dividing new numbers by some divisor which starts at 10 initially
"mult": 1, // and we stop multiplying the current value by ten
},
},
],
"default": {
"n": {
$add: [
{ $multiply: [ "$$value.n", "$$value.mult" ] }, // multiply the already parsed number by 10 because we're moving one step to the right or by one once we're hitting the decimals section
{ $divide: [ "$$n", { $ifNull: [ "$$value.div", 1 ] } ] } // add the respective numerical value of what we look at currently, potentially divided by a divisor
]
},
"sign": "$$value.sign",
"div": { $multiply: [ "$$value.div" , 10 ] },
"mult": "$$value.mult"
}
}
}
}
}
}
}
}
}, {
$addFields: { // fix sign
"moop": { $multiply: [ "$moop.n", "$moop.sign" ] }
}
})
I am certainly not advertising this as the bee's knees or anything and it might have severe performance implications for larger datasets over a client based solutions but there might be cases where it comes in handy...
The above pipeline will transform the following documents:
{ "moop": "12345" } --> { "moop": 12345 }
and
{ "moop": "123.45" } --> { "moop": 123.45 }
and
{ "moop": "-123.45" } --> { "moop": -123.45 }
and
{ "moop": "2018-01-03" } --> { "moop": 20180103.0 }
Three things need to care for:
parseInt() will store double data type in mongodb. Please use new NumberInt(string).
in Mongo shell command for bulk usage, yield won't work. Please DO NOT add 'yield'.
If you already change string to double by parseInt(). It looks like you have no way to change the type to int directly. The solution is a little bit wired: change double to string first and then change back to int by new NumberInt().
If you can edit all documents in aggregate :
"TimeStamp": {$toDecimal: {$toDate: "$Your Date"}}
And for the client, you set the query :
Date.parse("Your date".toISOString())
That's what makes you whole work with ISODate.
Try:
"TimeStamp":{$toDecimal: { $toDate:"$Datum"}}
Though $toInt is really useful, it was added on mongoDB 4.0, I've run into this same situation in a database running 3.2 which upgrading to use $toInt was not an option due to some other application incompatibilities, so i had to come up with something else, and actually was surprisingly simple.
If you $project and $add zero to your string, it will turn into a number
{
$project : {
'convertedField' : { $add : ["$stringField",0] },
//more fields here...
}
}
It should be saved. It should be like this :
db. my_collection.find({}).forEach(function(theCollection) {
theCollection.moop = parseInt(theCollection.moop);
db.my_collection.save(theCollection);
});
Collation is what you need:
db.collectionName.find().sort({PartnerID: 1}).collation({locale: "en_US", numericOrdering: true})
db.user.find().toArray().filter(a=>a.age>40)