0

I'm using the mongodb driver for node.js to query my MongoDB document store.

I have the following data in a document store named companies:

{
  companyName: "My Company",
  users: [
    {
      first: "Nick",
      last: "Kewney",
      email: [email protected],
      username: "this.username",
      company: "Test Company",
    }
  ],
  _id: ObjectId("54a0831fcad79dbf082d65e0")
}

I want to query the store and find any users with the username 'this.username' and return the first.

My attempt below returns the single result but as an array.

db.companies.findOne({ "users.username": "this.username" }, {_id: 0, 'users.$': 1}, next);

Returns...

{
  "users": [
    {
      "first": "Nick",
      "last": "Kewney",
      "email": "[email protected]",
      "username": "this.username",
      "company": "Test Company"
    }
  ]
}

My desired result is only the first item, e.g.

    {
      "first": "Nick",
      "last": "Kewney",
      "email": "[email protected]",
      "username": "this.username",
      "company": "Test Company"
    }
1
  • An obvious solution would be to reference users[0] from the findOne() result. That's certainly more efficient than server-side contortions to achieve the same outcome. Commented Dec 29, 2014 at 7:24

1 Answer 1

1

There are limitations to what can be done with the basic projection operations available to the .find() method of MongoDB. ,findOne() essentially just wraps .find() to return a single document and not a cursor. But these are basically the same.

If you want first manipulation in the server returned result, you need the .aggregate() method. This has a more detailed $project stage than can do further manipulation, along with other stage operators to get the result:

db.companies.aggregate(
    [
        // Query argument to match document
        { "$match": {
            "users.username": "this.username"
        }},

        // Flatten array out
        { "$unwind": "$users" },

        // Filter array
        { "$match": {
            "users.username": "this.username"
        }},

        // Project wanted fields
        { "$project": {
            "_id": 0,
            "first": "$users.first",
            "last": "$users.last",
            "username": "$users.username",
            "email": "$users.email",
            "company": "$users.company"
        }}
    ],
    function(err,result) {

    }
);

That will give you the result with a restructured document, and also return possible matches for multiple array items as separate results if you needed to.

But for my money, generally just live with what you get from the basic projection. Sure it's nested and also still an array with only 1 item in it, but this is easy to code to manipulate your response. Especially if you truly are using .findOne() in your intended code, as one result is not hard to manipulate there.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the response Neil. How would I project the companyName or _id properties in the root to my result?
@Nick In exactly the same way. The $project pipline supports the "dot notation" forms as specific property names, hence the $ prefixed variables for the document properties. So you can use $companyName to either another or same field name or just use "companyName": 1 as per standard projection, seeing you are not changing anything. But you must specify all properties you wish to appear. The only exception is _id which is always there unless turned off via "_id": 0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.