Home > Software engineering >  Aggregating the result of the MongoDB model query
Aggregating the result of the MongoDB model query

Time:09-11

I have a model Book with a field "tags" which is of type array of String / GraphQLString.

Currently, I'm able to query the tags for each book.

{
    books {
        id
        tags
    }
}

and I get the result:

{
  "data": {
    "books": [
      {
        "id": "631664448cb20310bc25c89d",
        "tags": [
          "database",
          "middle-layer"
        ]
      },
      {
        "id": "6316945f8995f05ac71d3b22",
        "tags": [
          "relational",
          "database"
        ]
      },
    ]
  }
}

I want to write a RootQuery where I can fetch all unique tags across all books. This is how far I am (which is not too much):

  tags: {
    type: new GraphQLList(GraphQLString),
    resolve(parent, args) {
        Book.find({}) // CAN'T FIGURE OUT WHAT TO DO HERE
        return [];
    }
  }

Basically, I'm trying to fetch all books and then potentially merge all tags fields on each book.

I expect that if I query:

{
    tags
}

I would get

["relational", "database", "middle-layer"]

I am just starting with Mongoose, MongoDB, as well as GraphQL, so not 100% sure what keywords to exactly look fo or even what the title of this question should be.

Appreciate the help.

CodePudding user response:

tags = Book.aggregate([
  {
    $project: {
      tags: 1,
      _id: 0,
    }
  },
])

This returns an array of objects that contain only the tags value. $project is staging this item in the aggregation pipeline by selecting keys to include, denoted by 1 or 0. _id is added by default so it needs to be explicitly excluded.

Then take the tags array that looks like this:

[
  {
    "tags": [
      "database",
      "middle-layer"
    ]
  },
  {
    "tags": [
      "relational",
      "database"
    ]
  }
]

And reduce it to be one unified array, then make it into a javascript Set, which will exclude duplicates by default. I convert it back to an Array at the end, if you need to perform array methods on it, or write back to the DB.

let allTags = tags.reduce((total, curr) => [...total, ...curr.tags], [])
allTags = Array.from(new Set(allTags))

const tags = [
  {
    "tags": [
      "database",
      "middle-layer"
    ]
  },
  {
    "tags": [
      "relational",
      "database"
    ]
  }
]

let allTags = tags.reduce((total, curr) => [...total, ...curr.tags], [])
allTags = Array.from(new Set(allTags))

console.log(allTags)

CodePudding user response:

You want to $unwind the arrays so they're flat, at that point we can just use $group to get unique values. like so:

db.collection.aggregate([
  {
    "$unwind": "$data.books"
  },
  {
    "$unwind": "$data.books.tags"
  },
  {
    $group: {
      _id: "$data.books.tags"
    }
  }
])

Mongo Playground

  • Related