0

I have a structure which looks like below:

course: { // course 
   id,
   coursename,
   sections: { // has an array of sections
    section: {
      id,
      sectionname
      cards: {  // each section has an array of cards
         card: {
            id  
            cardname 
         }
      }
    }
  }
}

Now, I am given the card ID. Suppose cardID is 301. I don't have details about the course or the section in which the card is stored. I only have the cardID. I want to update the cardname from "Test 1" to "Test 2".

Please provide some information about how to update the name of card given only the cardId. Also please provide information about how I can access a single card using only cardId.

If i run the following query:

 db.course.find( {"sections.cards.id" : ObjectId("5859517447c4286d0f0639ee")},
{"sections.cards.$":1,_id:0})

The whole section object is returned instead of returning a single card. Please provide some infromation about how to access a single card using only cardId.

An example of document that i have:

{ 
   "_id" : ObjectId("58595041cfdc46100f92a985"), 
   "modelType" : "Course",
   "name" : "c1",
   "sections" : [ 
        { 
           "id" : ObjectId("5859504dcfdc46100f92a986"),
           "name" : "s1", 
           "cards" : [ 
               { 
                  "id" : ObjectId("58595057cfdc46100f92a987"),
                  "name" : "card1", 
               }, 
               { 
                  "id" : ObjectId("5859506ccfdc46100f92a988"),
                  "name" : "card2"
               }
         ]
      }
   ] 
}

Please provide information about how to update a card using only card Id.

Suppose I want to change the card's name. How do I achieve it?

5
  • What do you want to do? can you post a document as an example? It's not clear which property is your array and which is your object Commented Dec 21, 2016 at 8:09
  • @DennyHiu, I have added an example document. I want to get a specific card using only card id. Commented Dec 21, 2016 at 8:18
  • @MaulikSoneji did you try $elemMatch ? Commented Dec 21, 2016 at 8:40
  • @Yogesh, I have written the query that i tried above. I don't know about $elemMatch. Can you please provide some more explanation or write the query to do so? Commented Dec 21, 2016 at 8:49
  • @MaulikSoneji I have just write an answer, hope it is what you need. Feel free to ask :) Commented Dec 21, 2016 at 8:54

1 Answer 1

3

I hope this answer is enough for your needs:

db.collection.aggregate([
   {$unwind: "$sections"}, // unwind the array
   {$unwind: "$sections.cards"}, // unwind the array
   {$match: {"sections.cards.id": ObjectId("5859506ccfdc46100f92a988")}}, // filter
   {$project: {_id: 0, id: "$sections.cards.id", name: "$sections.cards.name"}} // cut and clear any property that you don't need
])

which result in(I tested it using your sample):

{
   "name" : "card2",
   "id" : ObjectId("5859506ccfdc46100f92a988")
}

Explanation:

  1. First off, you need aggregate(find any library like mongojs or mongoose and how to do this within their respective manual),
  2. then unwind sections and cards inside it.
  3. After both steps, you need to filter documents resulted from unwind operation according to your requirement. In this case: which card match your ID
  4. Clear any properties that you don't need
  5. Return the result

To Update properties inside a document:

It's impossible to update an element inside of an array without knowledge of its index, so first, you must find out its index/position inside. The simplest way I could think of is by using for (since yours is double tier-ed array, I think mapReduce method will be much more complicated):

// first, find the document, where xxx is the target card ID
db.collection.find({'sections.cards.id': ObjectId("xxx")}, function(err, reply){
     // iterates through the documents
     for(var z = 0; z < reply.length; z++){
         // iterates through the `sections`
         for(var y = 0; y < reply[z].sections.length; y++){
             // iterates through the `cards`
             for(var x = 0; x < reply[z].sections[y].cards.length; x++){
                  if(reply[z].sections[y].cards[x].id.toString() === "xxx")                   
                  {
                       // do what you want to do the card here
                       reply[z].sections[y].cards[x].name = "updated name";
                  }
             }
         }

         // save the updated doc one-by-one,
         db.collection.update({_id: reply._id}, reply);
     }


});

The best solution

The best solution is: Don't have this kind of document in the first place. schema-free database approach DOESN'T mean that you can put everything inside a document. You had to consider the functionality and accessibility of data inside next time you design the document schema.

You can rationalize the documents according to your needs. In this case, you should split the course document, since it clear that card property should be an independent collection from course. If you worry that you'll need to join both collection, worry not. Since 3.2 mongodb introduced $lookup for this kind of application.

Not only it'll make your life easier, it'll make mongo queries run faster too.

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

2 Comments

Also, please provide information about how to update the card as well.
I'm afraid that you'llneed separate functions to update this document. The simplest way to do this is by using update method. I will update my answer soon

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.