MongoDB Aggregate vs. Query Example


Uncategorized

Updated May 4th, 2022

Task: Need to leverage a given “username” to look up the “_id” of a user in a “users” collection. Then use that “_id” to find all documents in a “cars” collection where the “_id” from the “users” collection matches the “authorId”

Code with Aggregate:

const usersCollection = client.db().collection("users")
      let cars = await usersCollection
        .aggregate([
          // look up id from users given the username
          { $match: { username: username } },
          { $lookup: { from: "cars", localField: "_id", foreignField: "authorId", as: "carDocument" } },
          { $unwind: "$carDocument" },
          {
            $project: {
              _id: 1,
              username: 1,
              useful_miles: 1,
              monthly_miles: 1,
              // kind of merge the two together
              carId: "$carDocument._id",
              authorId: "$carDocument.authorId",
              description: "$carDocument.description",
              price: "$carDocument.price",
              miles: "$carDocument.miles",
              link: "$carDocument.link",
              createdDate: "$carDocument.createdDate"
            }
          }
        ])
        .toArray()

Output:

{
    username: 'pepper',
    useful_miles: 150000,
    monthly_miles: 1250,
    cars: {
      authorId: new ObjectId("62728d8c6f59729aaf25f53d"),
      description: '2017 Honda HR-V EX AWD',
      price: 17250,
      miles: 73516,
      link: 'https://www.whatEvs.com/blippidty-bloppity-boop',
      createdDate: 2022-05-04T14:28:28.420Z
    }
  }

Some notes on the aggregate:

Is there a way to run the “toString” method on the “authorId” property to prevent getting the new ObjectId? (Cannot just add “$carDocument._id.toString()” or the propoerty will not be output at all. See docs for a possible solution).

The “$unwind” property is used to only re-spell out as a nested object. You can alternatively just have all on one level.

Code with Query

const usersCollection = client.db().collection("users")

      const sessionDoc = await usersCollection.findOne({ username: username })
      let sessionId
      if (!sessionDoc) {
        throw "no session doc"
      }
      sessionId = sessionDoc._id.toString()

      const carsCollection = client.db().collection("cars")

      const cars = await carsCollection.find({ authorId: new ObjectId(sessionId) }).toArray()

Output of Query Code

[
  {
    _id: new ObjectId("627299c16f59729aaf25f53e"),
    authorId: new ObjectId("627267346f59729aaf25f539"),
    description: '2017 Honda HR-V AWD',
    price: 17250,
    miles: 73516,
    link: 'https://www.cargurus.com/Cars/inventorylisting/viewDetailsFilterViewInventoryListing.action?sourceContext=carGurusHomePageModel&entitySelectingHelper.selectedEntity=&zip=34685#listing=313275506/NONE',
    createdDate: 2022-05-04T15:20:32.130Z
  },
  {
    _id: new ObjectId("627299dd6f59729aaf25f53f"),
    authorId: new ObjectId("627267346f59729aaf25f539"),
    description: '2017 Chevy Equinox LT FWD',
    price: 15792,
    miles: 81860,
    link: 'https://www.cargurus.com/Cars/inventorylisting/viewDetailsFilterViewInventoryListing.action?sourceContext=carGurusHomePageModel&entitySelectingHelper.selectedEntity=&zip=34685#listing=324379551/NONE',
    createdDate: 2022-05-04T15:21:00.914Z
  }
]

Some notes on the query

Need two steps to get the data, 1.) findOne on users collection and 2.) use find on the cars collection.

Need to return two things into one object manually.

Overall

Run one with aggregate but I don’t want the user data on every document, although I do need it.

How to Measure Which is Better?

Is there a metric, (Big O)?