2

Being new to NoSQL / MongoDB I wonder how I can get a specific object from a potentially big document.

A document inside of my project collection looks like this:

{
    "_id" : ObjectId("5935a41f12f3fac949a5f925"),
    "project_id" : 13,
    "updated_at" : ISODate("2017-06-28T01:43:50.994Z"),
    "created_at" : ISODate("2017-06-05T18:34:07.150Z"),
    "owner" : ObjectId("591eea4439e1ce33b47e73c3"),
    "name" : "My demo project 1",
    "visibility" : 0,
    "uploaded_files" : [ 
        {
            "fieldname" : "sourceStrings",
            "originalname" : "Log_20-6-2017_19-03-24-626.txt",
            "encoding" : "7bit",
            "mimetype" : "text/plain",
            "_id" : ObjectId("5952deb44fb371d8bc00dd43")
        }]
}

Instead of just one "uploaded file object" there may be hundreds. I need to get the project information (like owner, visibility, name and so on) and then I want to get the uploaded_file object by it's _id. Obviously I could iterate through all objects inside of uploaded_files but I assume that this is performance wise horrible. What I've got so far is:

var projectId = req.params.projectId
var fileId = req.body.fileId
Project.findOne({ project_id: projectId }).populate('owner').then(project => {
        if (!project)
            return res.send(404, { error: "Couldn't find a project with this id"

        // Here I want to get the uploaded_file object

})

My question:

  1. So how can I efficiently fetch a single object from the uploaded_files array by it's _id field?
  2. I need that behaviour for two cases, first is that I need to delete/modify the uploaded file or I want to get all it's information.

Is there a better database/document design for what I am trying to achieve?

kentor
  • 16,553
  • 20
  • 86
  • 144

1 Answers1

3

you can try to project the needed object using "$elemMatch" as following:

Project.findOne({ project_id: projectId } , 
{uploaded_files: {$elemMatch: {_id:  fileId}}})

That will return the array with the needed file object only .

and related to how to update it you can try the following:

Project.update({ project_id: projectId , 'uploaded_files._id' : 
fileId} , 
{$set : {"uploaded_files.$.originalname" : "new data"} }) 
//for example updating its "original name" filed and so on.

and notice that "$" references the first item match the query and it should be only one due to depending on "_id" property in the query.

finally to delete specific element form the array, you can try the following:

Project.update({ project_id: projectId }, 
{$pull : {"uploaded_files" : {_id:fileId}} })
Ninja
  • 486
  • 4
  • 12
  • Unfortunately this will ONLY return the file I am looking for but I also need the project data (like owner, see original question). What do you recommend? – kentor Jun 30 '17 at 18:37
  • 1
    Sorry for this delay, i wrote an example to explain the idea, but using the projection you can return all what you need as in the following projection object: {uploaded_files: {$elemMatch: {_id: fileId}} , owner:1 , visibility:1 , name:1} – Ninja Jun 30 '17 at 20:57
  • 1
    To deliver the whole concept, the _id is returned always by default but you can exclude it by adding "_id:0" to the projection object. for more info. you can check the mongo document: https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results/ – Ninja Jun 30 '17 at 20:59
  • actually it doesn't work yet for some reason. I've tried to run `db.getCollection('projects').findOne({project_id: 13}, {uploaded_files: {$elemMatch: {_id: '595309d6f085b9e938bdb810'}}})` and all it returned was the _id of the project: http://i.imgur.com/JPgQJKp.png. I can confirm that fileId is correct – kentor Jul 01 '17 at 00:25
  • Edit: I had to add ObjectId() for the _id values – kentor Jul 01 '17 at 00:27