Embedded Documents in MongoDB - mongodb

I'm having trouble understanding exactly how Embedded Documents are enforced in a soft schema such as MongoDB. if given a data-model like so:
User{
UserId:
Email:
Phone:
Reviews{
Rating:
Comment:
}//End reviews
}
Does this mean every User Document has only one review or that this is the format in which all reviews will be for a User?
Perhaps a more relational model of this would be the folloiwng:
User{
UserId:
Phone:
Email:
}
Reviews{
User:<UserID>
Rating:
Comment:
}
I know using references means a slower query but I don't understand how Embedded Documents would allow for multiple reviews.

In the case of having multiple document for the same user, MongoDB adds an unique _Id to the document every time you add a new document, so this wokrks good when you are adding multiple documents for the same user and as #wdberkeley stated, MongoDB does not enforce a schema,
"User" {
"UserId": "johndoe"
"Email": "johndoe#yahoo.com"
"Phone": "555-44444"
"Review"{
"Rating": "5"
"Comment": "this is my first comment"
}
}
"User" {
"UserId": "johndoe"
"Email": "johndoe#yahoo.com"
"Phone": "555-44444"
"Review"{
"Rating": "5"
"Comment": "this is my second comment"
}
}
"User" {
"UserId": "johndoe"
"Email": "johndoe#yahoo.com"
"Phone": "555-44444"
"Review"{
"Rating": "5"
"Comment": "this is my third comment"
}
}
In the case of one document for one user with multiple reviews, this can work fine too, can always get the document and add append reviews to it and call db.update() on the document,
"User" {
"UserId": "johndoe"
"Email": "johndoe#yahoo.com"
"Phone": "555-44444"
"Reviews"{[
{
"Rating": "5"
"Comment": "this is my first comment"
},
{
"Rating": "5"
"Comment": "this is my second comment"
},
{
"Rating": "5"
"Comment": "this is my third comment"
}
]}
}

Are you talking about how this is enforced in MongoDB or in an ODM like Mongoose? For MongoDB, the answer it simple: MongoDB doesn't enforce a schema. You can have all three of the following documents in a MongoDB collection:
{
"reviews" : {
"rating" : 9001,
"comment" : "nice scenery"
}
}
{
"reviews" : [
{
"rating" : [2, 4, 5],
"comment" : 19
},
{
"rating" : -845,
"comment" : "Powerful stuff"
}
]
}
{
"reviews" : "no"
}
In an ODM that enforces a schema application side, your notation is closest to something that would mean each document should have one reviews subdocument.

Related

Update a nested field with an unknown index and without affecting other entries

I have a collection with a layout that looks something like this:
student1 = {
"First_Name": "John",
"Last_Name": "Doe",
"Courses": [
{
"Course_Id": 123,
"Course_Name": "Computer Science",
"Has_Chosen_Modules": false
},
{
"Course_Id": 284,
"Course_Name": "Mathematics",
"Has_Chosen_Modules": false
}
]
};
I also have the following update query:
db.Collection_Student.update(
{
$and: [
{First_Name: "John"},
{Last_Name: "Doe"}
]
},
{
$set : { "Courses.0.Has_Chosen_Modules" : true }
}
);
This code will currently update the Computer Science Has_Chosen_Modules value to true since the index is hardcoded. However, what if I wanted to update the value of Has_Chosen_Modules via the Course_Id instead (as the course might not necessarily be at the same index every time)? How would I achieve this without it affecting the other courses that a given student is taking?
You can select any item in the sub array of your document by targeting any property in the sub array of your document by using dot .
You can easily achieve this by the following query.
db.Collection_Student.update(
{
First_Name: "John",
Last_Name: "Doe",
'Courses.Course_Id': 123
},
{
$set : { "Courses.$.Has_Chosen_Modules" : true }
}
);
Conditions in search filter are by default treated as $and operator, so you don't need to specifically write $and for this simple query.

mongo db how to store multi relation like graph

I have to store some users and their group relations like below
So I am planning to create a collection like below
UserGroupRelation Collections
{
"user":String,
"Group":String"
}
example of collections for Super admin users
{
"user":"Adminuser-1",
"Group":"Group1"
}
{
"user":"Adminuser-1",
"Group":"Group2"
}
{
"user":"Adminuser-1",
"Group":"Group3"
}
where user & Group column is indexed and I will run below kind of query
1.Whenever I want to check whether given user has access to the given group
db.UserGroupRelation.find( { user: "Adminuser-1", Group: "Group2" })
2.Also I want to delete all the association whenever we delete group
db.UserGroupRelation.deleteMany({ Group: "Group2" })
3.Also find all the users of a group
db.UserGroupRelation.find( { Group: "Group2" })
4.Find Hierarchy?, with my Approach I am not able to find
But with this approach I am duplicating lot of data also in real time I may have 10000 groups and 1 million user so there would be performance issue. And with this I am not able to maintain a hierarchy like SuperAdmin->SubAdmin->user of same group
I checked with mongo tree but it is not fitting to this requirement. is there a better way to handle this requirement in mongodb .?
This is the structure your graphic requirements show. It does still lead to repetition though so you will need to change it. Read up on one-many relationships.
{
"superAdmin_ID": "001",
"groups": [
{
"_id": "0",
"groupNumber": "1",
"users": [
{
"_userKey": "1023"
"userName": "Fred"
},
{
"_userKey": "1024"
"userName": "Steve"
}
],
"subAdmin": {
"_adminKey": "55230"
"adminName": "Maverick"
},
},
{
"_id": "1",
"groupNumber": "2",
"users": [
{
"_userKey": "1023"
"userName": "Fred"
},
{
"_userKey": "4026"
"userName": "Ella"
}
],
"subAdmin": {
"_adminKey": "55230"
"adminName": "Maverick"
},
},
{
"_id": "2",
"groupNumber": "3",
"users": [
{
"_userKey": "7026"
"userName": "James"
}
],
"subAdmin": {
"_adminKey": "77780"
"adminName": "Chloe"
},
},
]
}
You can also make subAdmin an array if you need more than one subAdmin within a group.

MongoDB Project - return data only if $elemMatch Exist

Hello Good Developers,
I am facing a situation in MongoDB where I've JSON Data like this
[{
"id": "GLOBAL_EDUCATION",
"general_name": "GLOBAL_EDUCATION",
"display_name": "GLOBAL_EDUCATION",
"profile_section_id": 0,
"translated": [
{
"con_lang": "US-EN",
"country_code": "US",
"language_code": "EN",
"text": "What is the highest level of education you have completed?",
"hint": null
},
{
"con_lang": "US-ES",
"country_code": "US",
"language_code": "ES",
"text": "\u00bfCu\u00e1l es su nivel de educaci\u00f3n?",
"hint": null
}...
{
....
}
]
I am projecting result using the following query :
db.collection.find({
},
{
_id: 0,
id: 1,
general_name: 1,
translated: {
$elemMatch: {
con_lang: "US-EN"
}
}
})
here's a fiddle for the same: https://mongoplayground.net/p/I99ZXBfXIut
I want those records who don't match $elemMatch don't get returned at all.
In the fiddle output, you can see that the second item doesn't have translated attribute, In this case, I don't want the second Item at all to be returned.
I am using Laravel as Backend Tech, I can filter out those records using PHP, but there are lots of records returned, and I think filtering using PHP is not the best option.
You need to use $elemMatch in the first parameter
db.collection.find({
translated: {
$elemMatch: {
con_lang: "IT-EN"
}
}
})
MongoPlayground

How to mass update in MongoDB after corruption

I upgraded Wekan from 0.48 to 0.95. It looks like what happened in Mongo is that it took the checklist collection from one containing a nested list of items and split it out into a new checklistItems collection. It appears to have copied the data correctly- except that instead of copying each item's title, it copied the checklist title to each list.
I started with this in wekan.checklists:
{
"_id": "z329QEDfjsuQcxz7E",
"cardId": "TBgz6gMGCcn9XNPSW",
"title": "A list",
"sort": 0,
"createdAt": {
"$date": "2018-05-09T22:20:50.537Z"
},
"items": [
{
"_id": "z329QEDfjsuQcxz7E0",
"title": "Do some stuff",
"isFinished": false,
"sort": 0
},
{
"_id": "z329QEDfjsuQcxz7E1",
"title": "Do some other stuff",
"isFinished": false,
"sort": 1
}
],
"userId": "YndMrPQ5XhZTTKD2S"
}
and wound up with the following in wekan.checklistItems:
{
"_id": "RADPEu4nhr9PgwPHH",
"title": "A list",
"sort": 0,
"isFinished": false,
"checklistId": "z329QEDfjsuQcxz7E",
"cardId": "TBgz6gMGCcn9XNPSW"
}
{
"_id": "Guy3aaJL4WLJQjzRX",
"title": "A list",
"sort": 1,
"isFinished": false,
"checklistId": "z329QEDfjsuQcxz7E",
"cardId": "TBgz6gMGCcn9XNPSW"
}
and this in wekan.checklists:
{ "_id" : "z329QEDfjsuQcxz7E", "cardId" : "TBgz6gMGCcn9XNPSW", "title" : "MVP", "sort" : 0, "createdAt" : ISODate("2018-05-09T22:20:50.537Z"), "userId" : "YndMrPQ5XhZTTKD2S" }
Is there a quick query to go back through my original wekan.checklists and update the titles in wekan.checklistItems? I note that the checklistIDs stayed the same but the card id's are different- I can of course load the old wekan.checklists collection into my current (upgraded) db to query against.
Fix: load your old db.checklists into db.checklistsOld (I used mongoimport -d wekan -c checklistsOld ~/checklistsOld.bson, where checklistsOld.bson held my backup from before the upgrade. Use the following script in Robo3T:
db.checklistsOld.find({}, {"_id": 1, "items.title":1, "items.sort": 1 }).forEach( (list, i, lists) => { 
  var checklistId = list._id;
  list.items.forEach( (item, j, items) => {
      var sort = item.sort,
          title = item.title;
      db.checklistItems.update({"checklistId": checklistId, "sort":sort}, {$set: {"title": title}} );
  });
});
Depending on how many items you have, you may need to adjust "shellTimeoutSec" in Robo3T (https://github.com/Studio3T/robomongo/wiki/Robomongo-Config-File-Guide)

MongoDB: How to update a compound item of an array ensuring no duplicates

Here below is a hypothetical Users collection where more than one address is allowed:
{
"firstName": "Joe",
"lastName": "Grey",
...
"addresses":
[
{
"name": "Default",
"street": "..."
...
},
{
"name": "Home",
"street": "..."
...
},
{
"name": "Office",
"street": "..."
...
}
]
}
Every address has a name... which should be unique – e.g. there couldn't be two addresses named Default. If I want to update let's say the address at index 1 (Home), how do I ensure the update data does not contain names Default and Office?
I guess a two-steps approach (i.e. find and then update) wouldn't be very correct since data might be updated between the find and the subsequent update operation, isn't?
var renamed = 'Office'; // from user input
var users = getUserMongoCollection();
users.update({_id:userId, 'addresses.name': { $ne : renamed } },
{ $set : { 'addresses.1.name' : renamed } }, function(err){
//all done!
});
Find the record by ID, and only update it if the array doesn't contain the new name.