DEV Community

Cover image for Mastering The Database - Accessing Nested Records - Series #10
Functional Javascript
Functional Javascript

Posted on

2

Mastering The Database - Accessing Nested Records - Series #10

Intro

In the last series we retrieved a random sample of docs.
It was very simple. Essentially a oneliner.

But supposes we wanted to select a random sample of items that existed as subdocs of the outer level docs in our app?

Actually, it's not too hard.
And we don't use any loops.
Instead we "unwind", or your could think of it as "flattening" our arr of docs.

If an artist has an arr of albums, then each artist doc has an arr of subdocs.

If an artist's arr of albums each contain an arr of song, then we have a doc that has an arr of subdocs whose subdocs also have an arr of subdocs.

Here is what the outer doc's data shape look like:

/*
data shape:
 {
    "artist": "Julian Lennon",
    "albums": [
      {
        "albumTitle": "Valotte (1984)",
        "albumSongs": [
          {
            "song": "Valotte"
          },
          {
            "song": "O.K. for You"
          },
          {
            "song": "On the Phone"
//....
*/
Enter fullscreen mode Exit fullscreen mode

Here is the query to select a random set of song (in this case 5 of them)

  mgArr(dbEnum.nlpdb, collEnum.songsColl,
    unwindArr("albums"),
    unwindArr("albums.albumSongs"),
    randomSample(5),
    projectIncludeNoId("artist", "albums.albumSongs.song"),
  )
Enter fullscreen mode Exit fullscreen mode

Here are five random songs.
We've included the artist so we can see who the song is performed by:

/*
output:

[
  {
    "artist": "Miley Cyrus",
    "albums": {
      "albumSongs": {
        "song": "Girls Just Wanna Have Fun"
      }
    }
  },
  {
    "artist": "Creedence Clearwater Revival",
    "albums": {
      "albumSongs": {
        "song": "Penthouse Pauper"
      }
    }
  },
  {
    "artist": "Judas Priest",
    "albums": {
      "albumSongs": {
        "song": "Out In The Cold"
      }
    }
  },
  {
    "artist": "Akon",
    "albums": {
      "albumSongs": {
        "song": "Throw Dat"
      }
    }
  },
  {
    "artist": "Nazareth",
    "albums": {
      "albumSongs": {
        "song": "Hit The Fan"
      }
    }
  }
]
*/

Enter fullscreen mode Exit fullscreen mode

Notes

1.
The unwindArr is a wrapper func around the $unwind stage operator.
The wrappers make the code look cleaner, and they're usually oneliners:

export const unwindArr = k => ({ $unwind: "$" + k });
Enter fullscreen mode Exit fullscreen mode

2.
We have to unwind the outer arr, then we can next unwind the inner arr. We could keep on going if there were more arrs. For example, each song could have an arr of songwriters.

3.
Once all the songs are unwound, we can grab a random sample of them using the $sample stage operator.
The RandomSample wrapper is a simple oneliner:

export const randomSample = lim => ({ $sample: { size: lim } });
Enter fullscreen mode Exit fullscreen mode

4.
Notice the dot notation in the unwind func in order to express the path to the nest arrs.

5.
Finally, for ease of display reason, I "project" out only two fields. My wrapper func, the "NoId" variant, excludes the Primary Key. I use this variant just for testing
The raw $project syntax would look like this (0 means exclude, 1 means include):

{ $project: { _id: 0, artist: 1, "albums.albumSongs.song": 1 } },
Enter fullscreen mode Exit fullscreen mode

6.
Take note also, that conveniently, we can swap out and use either the wrapper funcs, or the raw MongoDB stage syntax with our call to the MongoDB aggregate func. This is because the wrapper funcs simply just returns the raw syntax anyhow, of each stage in the pipeline.

Resources

https://docs.mongodb.com/manual/reference/operator/aggregation/unwind

https://docs.mongodb.com/manual/reference/operator/aggregation/sample

$150K MiniMax AI Agent Challenge — Build Smarter, Remix Bolder, Win Bigger!

Join the MiniMax AI Agent Challenge — Build your first AI Agent 🤖

Developers, innovators, and AI tinkerers, build your AI Agent and win $150,000 in cash. 💰

Read more →

Top comments (0)

Heroku

Tired of jumping between terminals, dashboards, and code?

Check out this demo showcasing how tools like Cursor can connect to Heroku through the MCP, letting you trigger actions like deployments, scaling, or provisioning—all without leaving your editor.

Learn More

👋 Kindness is contagious

Explore this practical breakdown on DEV’s open platform, where developers from every background come together to push boundaries. No matter your experience, your viewpoint enriches the conversation.

Dropping a simple “thank you” or question in the comments goes a long way in supporting authors—your feedback helps ideas evolve.

At DEV, shared discovery drives progress and builds lasting bonds. If this post resonated, a quick nod of appreciation can make all the difference.

Okay