Home > Enterprise >  Perform search with facets unknown upfront Atlas MongoDB
Perform search with facets unknown upfront Atlas MongoDB

Time:01-11

I have the following document structure in MongoDB:

{
  // other keys,
  tags: [
    tagA: "red",
    tagB: "green"
  ]
},
{
  // other keys,
  tags: [
    tagA: "orange",
    tagB: "green",
    tagC: "car"
  ]
}

I want to perform a $facets search that gives me the following output (name of each tag values that occur on that tag count of these value):

{
  [
    tagA: {
      red: 1,
      orange: 1
    },
    tagB: {
      green: 2
    },
    tagC: {
      car: 1
    }
  ]   
}

The tricky part is that the facets are unknown upfront (they can vary), and every tutorial I found only works for a predefined set of facets.

Is it possible?

P.S.: how to get the output of this to come alongside with a given query? So that the return is something like:

{
  queryResults: [all the results, as in a normal query],
  facets: [result showed in accepted answer]
}

CodePudding user response:

If you consider having this as input (i've added bracket around object in your array) :

[
  {
    tags: [
      {
        tagA: "red"
      },
      {
        tagB: "green"
      }
    ]
  },
  {
    tags: [
      {
        tagA: "orange"
      },
      {
        tagB: "green"
      },
      {
        tagC: "car"
      }
    ]
  }
]

You could then do an aggregation pipeline as follow :

db.collection.aggregate([
  {
    "$unwind": "$tags"
  },
  {
    "$addFields": {
      "kv": {
        "$objectToArray": "$tags"
      }
    }
  },
  {
    "$unwind": "$kv"
  },
  {
    "$group": {
      "_id": {
        key: "$kv.k",
        value: "$kv.v"
      },
      "count": {
        "$sum": 1
      }
    }
  },
  {
    "$group": {
      "_id": "$_id.key",
      "value": {
        "$push": {
          "k": "$_id.value",
          "v": "$count"
        }
      }
    }
  },
  {
    $project: {
      val: [
        {
          k: "$_id",
          v: {
            "$arrayToObject": "$value"
          }
        }
      ]
    }
  },
  {
    $project: {
      res: {
        "$arrayToObject": "$val"
      }
    }
  },
  {
    $replaceRoot: {
      newRoot: "$res"
    }
  }
])

It would give you this result :

[
  {
    "tagA": {
      "orange": 1,
      "red": 1
    }
  },
  {
    "tagB": {
      "green": 2
    }
  },
  {
    "tagC": {
      "car": 1
    }
  }
]

You can see this on mongoplayground : https://mongoplayground.net/p/FZbM-BGJRBm Hope this answer your question.

  • Related