pdad04
pdad04

Reputation: 75

Returning random key and word from array in mongoDB

I have a collection with one document. It looks like this:

 words: {
   wordOne: ["Cat", "Dog","Fish"],
   wordTwo: ["Red", "Blue", "Green"],
   wordThree: ["Square","Cirecle","Triangle"]
 }

I would like to query the DB to select a random key (wordOne, wordTwo or wordThree) and then return a random word from the array on that key.

So far I've written a query that returns the entire document and then I use JS to select a random key and then a random word. That does work of course but I've read that if you can have the DB do the work, it's better to do it that way. Plus I'm really curious how it would be accomplished.

I'm using Node and mongoose

Upvotes: 2

Views: 551

Answers (2)

Valijon
Valijon

Reputation: 13103

Alternative solution:

db.collection.aggregate([
  {
    $project: {
      words: {
        $reduce: {
          input: {
            $objectToArray: "$words"
          },
          initialValue: [],
          in: {
            $concatArrays: [
              "$$value",
              {
                $map: {
                  input: "$$this.v",
                  as: "w",
                  in: {
                    key: "$$this.k",
                    word: "$$w"
                  }
                }
              }
            ]
          }
        }
      }
    }
  },
  {
    $unwind: "$words"
  },
  {
    $replaceWith: "$words" // For MongoDB v3.4 $replaceRoot:{newRoot:"$words"}
  },
  {
    $sample: {
      size: 1
    }
  }
])

MongoPlayground

Upvotes: 1

mickl
mickl

Reputation: 49955

You can transform all the arrays contatined within words object by using $reduce, $objectToArray and $concatArrays. This will give you one long array of all strings which can be then passed as a parameter to $unwind to get single word per document. Lastly you can run $sample to get random document:

db.collection.aggregate([
    {
        $project:{
            all: {
                $reduce: {
                    input: { $objectToArray: "$words" },
                    initialValue: [],
                    in: { $concatArrays: [ "$$value", "$$this.v" ] }
                }
            }
        }
    },
    {
        $unwind: "$all"
    },
    {
        $project: {
            _id: 0,
            sampleWord: "$all"
        }
    },
    {
        $sample: { size: 1 }
    }
])

Mongo Playground

Upvotes: 1

Related Questions