Mastering The Database - Accessing Nested Records - Series #10
Tags: database, mongodb, javascript, express
Published: 2021-02-11 17:13: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:
```js
/*
data shape:
 {
    "artist": "Julian Lennon",
    "albums": [
      {
        "albumTitle": "Valotte (1984)",
        "albumSongs": [
          {
            "song": "Valotte"
          },
          {
            "song": "O.K. for You"
          },
          {
            "song": "On the Phone"
//....
*/
```


Here is the query to select a random set of song (in this case 5 of them)
```js
  mgArr(dbEnum.nlpdb, collEnum.songsColl,
    unwindArr("albums"),
    unwindArr("albums.albumSongs"),
    randomSample(5),
    projectIncludeNoId("artist", "albums.albumSongs.song"),
  )
```

Here are five random songs.
We've included the artist so we can see who the song is performed by:
```js
/*
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"
      }
    }
  }
]
*/

```


## 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:
```js
export const unwindArr = k => ({ $unwind: "$" + k });
```

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:
```js
export const randomSample = lim => ({ $sample: { size: lim } });
```
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):
```js
{ $project: { _id: 0, artist: 1, "albums.albumSongs.song": 1 } },
```

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