MongoDB: concatinate multiple number values to string - mongodb

I have a document (inside aggregation, after $group stage) which have an object (but I could form array, if I needed it to) with number values.
MongoPlayground example with data and my aggregate query available here.
And I want to make a new _id field during next $project stage, consisted of this three number values, like:
item_id | unix time | pointer
_id: 453435-41464556645#1829
The problem is, that when I am trying to use $concat, the query returns me an error like:
$concat only supports strings, not int
So here is my question: is it possible to achieve such results? I have seen the relevant question MongoDB concatenate strings from two fields into a third field, but it didn't cover my case.

The $concat only concatenate strings, these fields $_id.item_id contains int value and $_id.last_modified double value,
The $toString converts a value to a string,
_id: {
$concat: [
{
$toString: "$_id.item_id"
},
" - ",
{
$toString: "$_id.last_modified"
}
]
}
Playground: https://mongoplayground.net/p/SSlXW4gIs_X

Related

How do I calculate the average of multiple fields in a mongoDB document?

I have a collection with the following fields:
_id:612ff22c17286411a17252cf
WELL:"199-H4-15A"
TYPE:"E"
SYSTEM:"HX"
ID:"199-H4-15A_E_HX"
Jan-14:-168.8
Feb-14:-151
Mar-14:-164.1
Apr-14:-168.7
May-14:-172.6
Jun-14:-177.3
Jul-14:-177.6
Aug-14:-171.9
Sep-14:-138.9
Oct-14:-130.3
Nov-14:-163.8
Dec-14:-161.4
Jan-15:-168.7
Feb-15:-168.9
Mar-15:-168.6
Apr-15:-164.6
May-15:-141.7
Jun-15:-153.5
Jul-15:-163.7
Aug-15:-167.7
and I am trying to take the average of all of the month fields (e.g. all files like "Jan-14", "Feb-14" and so on). I was thinking of somehow pushing all of the month fields data into an array and then average the values but would like to avoid having to list all of the individual field names. Below is what I have so far.
[{$match: {
'WELL': '199-H4-15A'
}}, {$group: {
_id: null,
MonthAverageFlows: {
$push: {
$isNumber: ['$all']
}
}
}}, {$unwind: '$MonthAverageFlows'}, {$group: {
_id: null,
average: {
$avg: '$MonthAverageFlows.value'
}
}}]
All that comes out is ```null``. Any and all help would be appreciated. The raw data is in csv form:
WELL, TYPE, SYSTEM, ID, JAN-14, FEB-14, . . .
"199-H4-15A", "E", "HX", "199-H4-15A_E_HX", -168.8, -151, . . .
Using dynamic values as field name is generally considered as anti-pattern and you should avoid that. You are likely to introduce unnecessary difficulty to composing and maintaining your queries.
Nevertheless, you can do the followings in an aggregation pipeline:
$objectToArray to convert your raw document into k-v tuples array
$filter the array to get contain the monthly data only
$avg to calculate the sum
Here is the Mongo playground for your reference.

How to find in MongoDB by last 4 chars in ObjectID?

I don't want to expose the full object ID to the client, instead I want to show him only a short of the last 4 chars of the actual object ID of an entity in the collection.
For example: ObjectId("5fcca5d997239a74da0d67a9") will become just 67a9
So it will be much easier to "talk" with ids of documents instead of the full object it.
Then I need to find the document in the DB using only the 67a9.
Is this possible and how?
According to this issue in Jira the resolution is "Won't fix".
ObjectId is not a String, is another object, so $regex is no possible.
Check this example where $regex works ok when _id is an String but not an ObjectId.
So one possible option is duplicate every field _id in another field called id or whatever where the id is in String format.
Then, you can do this query:
db.collection.find({
"_id": {
"$regex": "67a9$"
}
})
Example here where I've added more _id fields that not match the pattern
As pointed out, regex won't work on an ObjectId. But there is an easy workaround. Just use aggregation to first convert your ObjectId into a string and then match it.
db.collection.aggregate([
{
$addFields: {
tempId: { $toString: '$_id' },
}
},
{
$match: {
tempId: { $regex: "67a9"}
}
}
])
Obviously not a great solution to use on very large collections.

Delete documents where value is not an array

It seems that mongodb allows the same syntax for deleting documents where the value is a single value, and whenever the value is present in a collection:
This
db.SomeCollection.deleteMany({UserId: 12345});
Can affect both {UserId: 12345} and {UserId: [12345, 67895, 87897]}.
I understand that this schema isn't ideal; we are using a singular UserId prop to denote both a single id int, and an array of ints. However, I've inherited a database that makes use of mongodb's dynamic nature, if you catch my drift.
What is the safest way to execute a deleteMany query, specifying I only want to delete documents where the value is a single integer?
You can use $type operator to check field type but it's not so obvious in this case. You can't check if UserId is of type double because MongoDB will indicate that array of doubles is a double as well (more here). So you need to invert your logic and check if documents you're trying to remove are not an array (type 4):
db.SomeCollection.deleteMany({ $and: [ { UserId: 12345 }, { UserId: { $not: { $type: 4 } } } ] })

MongoDB - Perform $divide on String fields

I am trying to perform an aggregation query on a collection. I want to divide 2 of the fields and check if this is less than a certain value.
The way this db was made, it has all the fields as strings, and this isn't something I can really change right now. I wondered if there was a way to cast the values as numbers, maybe using $let or something?
As it is I currently get the error
exception: $divide only supports numeric types, not String and String
This is an example of the sort of query I am trying to run:
db.myCollection.aggregate([{
"$project": {
"mins": {
"$divide": ["$ALOWOVRLTOTL", "$FEESCALLCOST"]}
}
},
"$match": {
"mins": {
"$lte": 40
}
}
}])
You won't be able to perform the divide operation on non-numerical values. A possible solution to this would be to run an update and parse the String values to a numerical value.
Have a look at this question:
how to convert string to numerical values in mongodb

Mongo: Query to get count of objects in a nested field

I needed some help to create a count query on nested objects in a field, across all documents. Each document json has a many fields. One particular field called "hotlinks" comprises of many internal dynamic object fields.
Doc1:
{
hotlinks : { 112222:{....} , 333333: {.....} , 545555: {.....} }
}
Doc2:
{
hotlinks : { 67756:{....} , 756767: {.....} , 1111111: {.....} }
}
Each document has a hotlinks fields. The hotlinks field comprises of varied inner hotlink objects. Each key is a java unique id and has objects that contain data (inner fields).
I needed a way to get the count of all the inner nested objects of the field – ‘hotlinks’.
For example the summation of inner objects of hotlinks in doc1 and doc2 would be 6.
Is there any way to do this via a single query to get the count across all documents.
Thanks a lot,
Karan
Quite possible if using MongoDB 3.6 and newer though the aggregation framework.
Use the $objectToArray operator within an aggregation pipeline to convert the document to an array. The return array contains an element for each field/value pair in the original document. Each element in the return array is a document that contains two fields k and v.
On getting the array, you can then leverage the use of the $size operator which returns the number of elements in the given array thus giving you the count per document.
Getting the count across all the documents requires a $group pipeline where you specify the _id key of null or a constant value which gives calculates accumulated values for all the input documents as a whole.
All this can be done in a single pipeline by nesting the expressions as follows:
db.collection.aggregate([
{ "$group": {
"_id": null,
"count": {
"$sum": {
"$size": { "$objectToArray": "$hotlinks" }
}
}
} }
])
Example Output
{
"_id" : null,
"count" : 6
}
this may not be the best approach, but you can define a javascript variable and sum up the counts. i.e;
var hotlinkTotal=0;
db.collection.find().forEach(function(x){hotlinkTotal+=x.hotlinks.length;});
print(hotlinkTotal);