I am trying to create a parent-child relationship between two collections in MongoDB.
The first way:
// users document
{
"_id": "63e422adcb1ee8e6ffa4f4c7",
"name": "Joe"
}
// address documents
{
"_id": "77775554441ee8e6ffa4f4c7",
parent_id: "63e422adcb1ee8e6ffa4f4c7", // reference to users document
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
},
{
"_id": "55545adcb1ee8e6ffa4f4c7a",
parent_id: "63e422adcb1ee8e6ffa4f4c7", // reference to users document
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
}
Here have the users collection will have a unique "_id" objectid, and the address collection will have a "_id" objectid and a "parent_id" field that references the parent "_id".
The second way
// users document
{
"_id": "63e422adcb1ee8e6ffa4f4c7",
"name": "Joe",
[
"77775554441ee8e6ffa4f4c7", // reference to address document
"55545adcb1ee8e6ffa4f4c7a" // reference to address document
]
}
// address documents
{
"_id": "77775554441ee8e6ffa4f4c7",
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
},
{
"_id": "55545adcb1ee8e6ffa4f4c7a",
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
}
Here, the first method mirrored the users collection the _id of the address collection
Which is better for performance, This is not a discussion, but I really have a problem that the child collection contains more than 10 million and I was thinking of distributing the data to the parent.
Assuming that the parent_id field and the unnamed child array are indexed, the performance of reads will be about the same for both.
The difference in writes would be while addresses the first way permits just inserting the new addresses, while the second way would also require updating the user document.
Related
Let say we have a document in mongodb
{
id : 101,
addresses : [
{
id: 1,
suite: "flat 201",
street: "north street"
city: "london",
country: "uk"
}
]
}
i want to update multiple properties of the object at once without replacing whole object.
For example :
const newAddress = {
id:1,
suite: "flat 301",
street: "south street"
}
userCollection.updateOne(
{id : 101},
{ $set : {"addresses.$[address]" : newAddress } },
{arrayFilters: [{address.id : newAddress.id}]}
)
This operation sets the object with new fields instead of updating only the given fields.
When you update an object's properties, it must be required to specify each one by one, and if the properties are dynamic then you have to prepare a payload for an update on client side,
I assume you are using javascript,
const newAddress = {
id:1,
suite: "flat 301",
street: "south street"
};
let set = {};
for (let n in newAddress) {
set["addresses.$[address]."+n] = newAddress[n];
}
console.log(set);
// prepared update object
{
"addresses.$[address].id": 1,
"addresses.$[address].suite": "flat 301",
"addresses.$[address].street": "south street"
}
You can pass set to update part in query,
userCollection.updateOne(
{ id : 101 },
{ $set : set },
{ arrayFilters: [{ "address.id" : newAddress.id }] }
)
You can do this directly in an update query but you have to use an update with an aggregation pipeline and I don't recommend this because it requires multiple expression operators and it is expensive.
I was looking at the example here:
_id: "joe",
name: "Joe Bookreader"
}
// address documents
{
patron_id: "joe", // reference to patron document
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
}
{
patron_id: "joe",
street: "1 Some Other Street",
city: "Boston",
state: "MA",
zip: "12345"
}
Where they embed the address document inside the person document. The example makes sense if you have a single item in your person document, but how would you scale this if you had a person document with 100 items and addresses for each of them? Is there a way to create a schema and upload the data or do you have to manually embed the address documents for each person?
You should read about the Data Model Design section in their documentation. For this pattern the Relationships with Document References would match.
In short your User document would have an array containing the IDs of the referenced Address documents. But the Addresses are stored in a different collection. You would need a second query to select them.
Example from the documentation:
{
name: "O'Reilly Media",
founded: 1980,
location: "CA",
books: [123456789, 234567890, ...]
}
{
_id: 123456789,
title: "MongoDB: The Definitive Guide",
author: [ "Kristina Chodorow", "Mike Dirolf" ],
published_date: ISODate("2010-09-24"),
pages: 216,
language: "English"
}
{
_id: 234567890,
title: "50 Tips and Tricks for MongoDB Developer",
author: "Kristina Chodorow",
published_date: ISODate("2011-05-06"),
pages: 68,
language: "English"
}
My collection looks this:
{ _id: 1, author: "Jack London", bookName: "The Sea-Wolf" }
{ _id: 2, author: "Alex Kershaw", bookName: "Jack London" }
I want to be able to search by author or by bookName only, but MongoDB allows you to create only 1 text index. Thanks for help.
For Example:
I wanna search for author only :
db.collection.find({$text:{$search:"Jack London"}})//search in 'author' field
Or by bookName only :
db.collection.find({$text:{$search:"Jack London"}})//search in 'bookName' field
If I have compound index {author: "text", bookName: "text"} it will return me both entries, which is not what I want.
If I have a collection with multiple documents for one person like:
{ FullName: "Jane Doe", Camp: "may12", Week1: "0", Week2: "1", Week3: "0" }
{ FullName: "Jane Doe", Camp: "may13", Week1: "0", Week2: "0", Week3: "1" }
{ FullName: "Jane Doe", Camp: "may14", Week1: "0", Week2: "1", Week3: "0" }
and I need to update her name to "Jane Smith", I get her name from a Session:
var nameAddressFullName = Session.get('clientName');
Get all the documents with her name in put them into an array:
var documentNameAddressP = Programs.find({ FullName: nameAddressFullName}).fetch();
Then try to update all the names (I need to update based on _id values only), I'd need something like:
Programs.update({ _id: {$in: {documentNameAddressP._id}}, {$set: {FullName: nameChange}}); //????
We need to update all Jane Doe's documents to the full name Jane Smith keeping all the rest of the properties intact.
If you do this update via a method call to server then you can bulk update all the documents there in one go with {multi: true} in your update query.
From the client you can easily iterate over the cursor:
Programs.find({ FullName: nameAddressFullName}).forEach(function(doc){
Programs.update({ _id: doc._id },{ $set: { FullName: nameChange }});
});
You can select element to update in update query:
Programs.update({FullName: nameAddressFullName},{$set: {FullName: nameChange}})
https://docs.mongodb.org/manual/reference/operator/update/set/
Assume I have a Student collection:
{
name: "ABC",
age: 10,
address {
city: "CITY1",
state: "STATE",
}
}
{
name: "DEF",
age: 11,
address {
city: "CITY2",
state: "STATE",
}
}
{
name: "ABC",
age: 12,
address {
city: "CITY1",
state: "STATE",
}
}
Can I get the list of all unique City values from the list? For example, with the above 3 documents, I would like to get the list {"CITY1", "CITY2"}
I was just getting started with MongoDB from Relational Database, so this is a little confused for me, since I needed another Address table for it and I can just use SELECT DISTINCT to get what I want.
MongoDB has a similar db.collection.distinct() command.
To access elements in the address subdocument you need to use dot notation, so the complete query would be:
db.Student.distinct("address.city")
Some helpful documentation links to help you make the translation from SQL queries:
SQL to MongoDB Mapping Chart
SQL to Aggregation Mapping Chart
Just for notes, there is already distinct as mentioned, but for a more conventional response, use aggregate:
db.Student.aggregate([
{"$unwind": "$address" }},
{"$group": { "_id": "$address.city" }},
{"$project": { "_id": 0, "city" : "$_id" }}
])
Long winded compared to distinct, but it depends on what your eyes want.