Sblu
Sblu

Reputation: 53

How do you convert a hexadecimal string into a number in mongodb?

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

Answers (2)

Sblu
Sblu

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

Wernfried Domscheit
Wernfried Domscheit

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

Related Questions