Remove https/https from documents of mongodb data

Viewed 130

How to remove http:// or https:// from the beginning and '/' from the end of the tags.Domain in MongoDB aggregation?

Sample document:

{
    "_id" : ObjectId("5d9f074f5833c8cd1f685e05"),
    "tags" : [
        {
            "Domain" : "http://www.google.com",
            "rank" : 1
        },
        {
            "Domain" : "https://www.stackoverflow.com/",
            "rank" : 2
        }
    ]
}
2 Answers

The solution ended up being longer than I expected it to be (I hope someone can find a more concise solution), but here you go:

db.test.aggregate([
{$unwind:"$tags"}, //unwind tags so that we can separately deal with http and https
{
    $facet: { 
        "https": [{ // the first stage will...
            $match: { // only contain documents...
                "tags.Domain": /^https.*/ // that are allowed by the match the regex /^https.*/
            }
        }, {
            $addFields: { // for all matching documents...
                "tags.Domain": {"$substr": ["$tags.Domain",8,-1]} // we change the tags.Domain field to required substring (skip 8 characters and go on till the last character)
            }
        }],
        "http": [{ // similar as above except we're doing the inverse filter using $not
            $match: {
                "tags.Domain": { $not: /^https.*/ }
            }
        }, {
            $addFields: { // for all matching documents...
                "tags.Domain": {"$substr": ["$tags.Domain",7,-1]} // we change the tags.Domain field to required substring (skip 7 characters and go on till the last character)
            }
        }
        ]
    }
},

{ $project: { all: { $concatArrays: [ "$https", "$http" ] } } }, //we have two arrays at this point, so we just concatenate them both to have one array called "all"

//unwind and group the array by _id to get the document back in the original format

{$unwind: "$all"}, 

{$group: {
        _id: "$all._id",
        tags: {$push: "$all.tags"}
}}
])

For removing the / from the end, you can have another facet with a regex that matches the url (something like /.*\/$/ should work), and use that facet in the concat as well.

With help from: https://stackoverflow.com/a/49660098/5530229 and https://stackoverflow.com/a/44729563/5530229

As dnickless said in the first answered referred above, as always with the aggregation framework, it may help to remove individual stages from the end of the pipeline and run the partial query in order to get an understanding of what each individual stage does.

Taking an assumption that the Domain field in tags would contain valid URLs with valid appends and prepends of (https, http, //, /, com/, org/, /in)

  • The $trim operator is used to remove https://, http://, and / from tags.Domain

NOTE: This would not work for a URL that is already formatted and doesn't contain those characters at the beginning/end. Example: 'hello.com' would become 'ello.com', 'xyz.ins' would become 'xyz.in' etc.

Aggregation Query:

db.collection.aggregate([
  {
    $addFields:{
      "tags":{
        $map:{
          "input":"$tags",
          "as":"tag",
          "in":{
            $mergeObjects:[
              "$$tag",
              {
                "Domain":{ 
                  $trim: { 
                    "input": "$$tag.Domain", 
                    "chars": "https://" 
                  } 
                }
              }
            ]
          }
        }
      }
    }
  }    
]).pretty()

Output:(demo)

{
        "_id" : 2, //ObjectId
        "tags" : [
                {
                        "rank" : 1,
                        "Domain" : "www.google.com"
                },
                {
                        "rank" : 2,
                        "Domain" : "www.stackoverflow.com"
                }
        ]
}
Related