Reputation: 53
I have a collection that has strings such as this:
left_eye_val : "0x0", right_eye_val : "0x2"
I'm setting up some derivative fields as part of a aggregation pipeline which at its first stage must convert the hexadecimal strings "0x0", "0x2" into numbers.
The operator I tried:
{$toInt:"$left_eye_val"}
returns Illegal hexadecimal input in $convert with onError value:0x0
Is there a way to convert these strings into numbers using built-in mongodb operators? If not, what are some of the ways one might accomplish this?
Upvotes: 1
Views: 2201
Reputation: 53
@Domscheit's answer works if the hexadecimal string does not contain the '0x' in front of it; if it does, then modify the function posted by @Domscheit as follows:
function baseToDecimal(input, base) {
// works up to 72057594037927928 / FFFFFFFFFFFFF8
var field = input;
return {
$sum: {
$map: {
input: { $range: [0, { $strLenBytes: field }] },
in: {
$multiply: [
{ $pow: [base, { $subtract: [{ $strLenBytes: field }, { $add: ["$$this", 1] }] }] },
{ $indexOfBytes: ["0123456789ABCDEF", { $toUpper: { $substrBytes: [field, "$$this", 1] } }] }
]
}
}
}
};
}
and call it as follows, using the replaceOne
function to remove the x
db.collection.aggregate([{$set:{lHex: {$replaceOne: {input:"$hex", find:"x", replacement: "0"}}}}, {$set: {decimal: baseToDecimal("$lHex", 16)}}])
Upvotes: 0
Reputation: 59436
Such function does not exist yet, see Support base conversion in $convert
You can define functions like this:
function decimalToBase(input, base) {
// works up to 72057594037927928 / FFFFFFFFFFFFF8
var field = "$" + input;
return {
$let: {
vars: {
bits: {
$reduce: {
input: { $range: [0, 56] },
initialValue: [{ dec: field }],
in: {
$cond: {
if: { $gt: [{ $last: "$$value.dec" }, 0] },
then: {
$concatArrays: ["$$value",
[{
b: { $substrBytes: ["0123456789ABCDEF", { $mod: [{ $last: "$$value.dec" }, base] }, 1] },
dec: { $trunc: { $divide: [{ $last: "$$value.dec" }, base] } }
}]
]
},
else: "$$value"
}
}
}
}
},
in: {
$reduce: {
input: { $reverseArray: "$$bits.b" },
initialValue: "",
in: { $concat: ["$$value", "$$this"] }
}
}
}
}
};
function baseToDecimal(input, base) {
// works up to 72057594037927928 / FFFFFFFFFFFFF8
var field = "$" + input;
return {
$sum: {
$map: {
input: { $range: [0, { $strLenBytes: field }] },
in: {
$multiply: [
{ $pow: [base, { $subtract: [{ $strLenBytes: field }, { $add: ["$$this", 1] }] }] },
{ $indexOfBytes: ["0123456789ABCDEF", { $toUpper: { $substrBytes: [field, "$$this", 1] } }] }
]
}
}
}
};
}
and then use it, for example like this:
db.collection.insertOne({ hex: "A3F" })
db.collection.aggregate({ $set: { decimal: baseToDecimal("hex", 16)} })
Upvotes: 2