1

I am using a collection in Mongo with a price field with multiple money type :

{
  price: '15 gp' // 15 gold pieces
}

or

{
  price: '4 sp' // 0.4 gold pieces
}

I'm looking for a way to modify this field before querying the collection.

For example doing string modifications to remove the gp/sp and doing math operations to have a correct price in "GP" (1 GP = 10 SP)

This would help ordering the collection since mongo can't understand that 10 sp < 2 gp.

Is there a way to use Aggregation and regex to do it ?

2
  • You can try using the $replaceOne aggregate operator (MongoDB v5.x). Commented Nov 2, 2021 at 11:33
  • $replaceOne would replace the whole document with an other. I'm looking for a way to "format" the price field to remove the "gp", "sp" and use the same money. Commented Nov 2, 2021 at 11:45

2 Answers 2

1

first add new field as rate and check the gp or sp and put rate of each price for example sp have rate of 10 and gp have rate of 1 then add universalprice field with multiply of rate of price and value of price after that you could compare price

db.collection.aggregate([
  {
    "$addFields": {
      "newField": {
        "$split": [
          "$price",
          ","
        ]
      }
    }
  },
  {
    "$project": {
      price: {
        $reduce: {
          input: "$newField",
          initialValue: "",
          in: {
            $concat: [
              "$$value",
              "$$this"
            ]
          }
        }
      }
    }
  },
  {
    "$addFields": {
      "rate": {
        "$switch": {
          "branches": [
            {
              "case": {
                "$regexMatch": {
                  "input": "$price",
                  "regex": ".*gp*."
                }
              },
              "then": "1"
            },
            {
              "case": {
                "$regexMatch": {
                  "input": "$price",
                  "regex": ".*sp*."
                }
              },
              "then": "10"
            }
          ],
          default: 1
        }
      }
    }
  },
  {
    "$project": {
      price: 1,
      universalPrice: {
        "$multiply": [
          {
            "$toInt": "$rate"
          },
          {
            "$toInt": {
              $first: {
                "$split": [
                  "$price",
                  " "
                ]
              }
            }
          }
        ]
      }
    }
  }
])

https://mongoplayground.net/p/S5EIUdWRp5W

Sign up to request clarification or add additional context in comments.

7 Comments

This solution is working for basic integers like 5 gp, thought I'm having issues with numbers with a thousand separator like 4,500 gp. Is there a filter to remove the , ?
ok let me update my asnwer
my answer has been updated
Thanks for your help, is there a way to keep all fields from the initial collection ? (not only price) ?
Could you explain with example?
|
0

You can use this aggregation stage to convert the price field value:

db.collection.aggregate([
  { 
      $addFields: { 
          price: {
              $function: {
                  body: function(inPrice) {
                               let outPrice;
                               if (/gp/.test(inPrice)) {
                                   outPrice = parseInt(inPrice.replace("gp", "").trim()) * 10;
                               }
                               else if (/sp/.test(inPrice)) {
                                   outPrice = parseInt(inPrice.replace("sp", "").trim());
                               }
                               else {
                                   outPrice = inPrice; // whatever needs here...
                               }
                               return outPrice;
                  },
                  args: [ "$price" ],
                  lang: "js"
              }
          }
      }
  }
])

For example, price: "5 gp" will convert to price: 50 and price: "12 sp" will convert to price: 12. Note the values are converted to number type fields for comparison and also calculations.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.