Saving document and its object id for referencing in other documents - javascript

In a Node.js App with Mongodb/Mongoose. i have two collections. Users and Books. My question is: when a user saves a book i have to save it in the Books collection and its object id inside Users collection for referencing. Two save operation for one user input.
Is this correct way? is this Mongodb standard? in a relation based database system its a wrong architecture but in the lack of Join in Mongodb what should i do? if i have a large database should i save current books id inside each of related collections?
I know that i can has books embedded into each user document but it has own problems.
Im confused. what should i do?

You can create a MySQL "join" in MongoDB with Mongoose. It is not the MongoDB standard but it makes developing in MongoDB sometimes a lot easier. Below are two simple example schema's.
var BookSchema = new Schema({
title: {
type String
},
author: {
type: String
}
});
module.exports = mongoose.model('Book', BookSchema);
var UserSchema = new Schema({
username: {
type: String,
required: true
},
books: [{
type: String,
ref: 'Book'
}]
});
module.exports = mongoose.model('User', UserSchema);
In the last schema there is a "books" property with a reference to books. You can store Strings of Object id's inside the array (not as an object). If you want, you can also store Object id's instead of Strings. Below is an example schema for a user:
{
username: "Fcoder",
books: ["550adf3899fbe92a168d3051", "550adf3899fbe92a168d3052"]
}
When querying your MongoDB database, you can populate the data. Your query will look like this:
User.find({}).populate('books').exec(function(err, data) {
// callback
});
Inside data, you will find something like this:
{
username: "Fcoder",
books: [{
_id: ObjectId("550adf3899fbe92a168d3051"),
title 'Some title 1',
author: 'Some author 1'
}, {
_id: ObjectId("550adf3899fbe92a168d3052"),
title 'Some title 2',
author: 'Some author 2'
}
}

Related

How to implement many to many relationship in mongoDb?

I am working on a photography blog, in which I need to store images according to the tags provided by the user. I tried googling and came up with implementing a many to many relationship in mongoDb , but I am still confused as how to take multiple inputs from the user and store them separately . I tried something like this but I don't know how to proceed further.
This is my picture model:-
const mongoose = require("mongoose");
const snapSchema = new mongoose.Schema({
Caption: {
type: String,
required: "Caption cannot be blank."
},
image: String,
imageId: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
tags: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Tag"
}
]
});
module.exports = mongoose.model("Snap", snapSchema);
This is my tag model:-
const mongoose = require("mongoose");
const tagSchema = new mongoose.Schema({
text: String,
snaps: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Snap"
}
]
});
module.exports = mongoose.model("Tag", tagSchema);
If you think about it, usually an Image can have multiple Tags, not the other way around. I wouldn't associate Images with Tags directly. If you want to present images tag-wise, you simply filter your images by tags associated with them. So, your Snap model is perfect, but your Tag does not need association with any Image (it can live without an image, right?).
For presentation purposes, you do a simple filter:
Order.find( { tags: your_tag_id } )
Here's a great example of how to query by array content: https://docs.mongodb.com/manual/tutorial/query-arrays/
As for your update scenario, I imagine that user can input multiple tags for a single image, right? In that case, you simply insert relevant tag ids to tags image field.

Mongoose: Populate path using field other than _id

By default mongoose/mongo will populate a path using the _id field, and by it seems like there is no way to change the _id to something else.
Here are my two models which are connected with one-to-many relationship:
const playlistSchema = new mongoose.Schema({
externalId: String,
title: String,
videos: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Video',
}],
});
const videoSchema = new mongoose.Schema({
externalId: String,
title: String,
});
Normally, when querying a playlist you would populate videos just with .populate('videos'), but in my case I would like to use the externalId field instead of the default _id. Is that possible?
As far as I know, the way to achieve this with mongoose presently is by using virtuals. When populating virtuals, you can specify the localField and foreignField to whatever you want, so you are no longer bound to the default _id as foreignField. More details about this here.
For the scenario described in your question, you would need to add a virtual to the playerlistSchema, something like this:
playlistSchema.virtual('videoList', {
ref: 'Video', // The model to use
localField: 'videos', // The field in playerListSchema
foreignField: 'externalId', // The field on videoSchema. This can be whatever you want.
});
Now, whenever you query for player lists, you can populate the videoList virtual to get the referenced video documents.
PlaylistModel
.findOne({
// ... whatever your find query needs to be
})
.populate('videoList')
.exec(function (error, playList) {
/* if a playList document is returned */
playList.videoList; // The would be the populated array of videos
})

Preventing Duplicate ID Creation in MongoDB

In my Node/MongoDB backend I have a model that references a payers collection, like so:
clients: [{ id: { type: mongoose.Schema.Types.ObjectId, ref: 'clients' } }],
This is working, in that an id that's a reference to the correct "client" gets inserted. However, what's also happening is that mongo is auto-inserting a mongo ID. So in the document in question I end up with this:
clients: [{
id: 6b8702ad021ba27d4a3b26h9, // my correct ref object ID
_id: 4n8702bv036ba12g6a3b28f4 // an additional object ID auto inserted by mongo
}]
How do I prevent the auto insertion of the mongo ID in a situation like this? And, relatedly, if I were to use an _ in my original ref, like so:
clients: [{ _id: { type: mongoose.Schema.Types.ObjectId, ref: 'clients' } }],
...would this prevent this from happening to begin with, since there would already be a value for "_id"? In other words, would Mongo then NOT auto insert another ID? If so, that's the route I will take.
Yes, overwriting _id will work. Just be aware that _id is your database's primary key, so it needs to be unique or Mongo will throw an error.

Setting up an array of collections in MongoDB using Node.js

I am creating a game where, as part of a collection named Game, I am trying to label one of the elements of it. The element in question is supposed to be an array of userNames from another collection. I can't seem to figure out how to access that. Here is what I have in the games collection:
var mongoose = require('mongoose');
var schema = mongoose.Schema;
var ObjectId = schema.ObjectId;
module.exports.Game = mongoose.model('Game', new schema({
id: ObjectId,
gameRoomName: { type: String, required: '{PATH} is required.' },
players: { }
}));
The users collection:
var mongoose = require('mongoose');
var schema = mongoose.Schema;
module.exports.users = mongoose.model('Users', new schema({
userName: {type: String, required: '{PATH} is required.'}
}));
Basically, the usernames for a game will be saved in the Users schema. Then, I'd like to access that and insert it into the Game schema in the players space. I'm imagining it to be something like {type: collection.users}, however, that doesn't seem to be doing the trick.
You can store players as the array of references to Users model
module.exports.Game = mongoose.model('Game', new schema({
.
.
players: [{type: Schema.Types.ObjectId, ref: 'Users'}],
)}
Access later by:
Game.find()
// filter 'players' field
.select('players')
// populate players with only 'username' field
.populate('players', 'username')
.exec(function(err, username) {
// anything with players
});
Long story. You will be good to go after finishing the article
There's a few ways to solve the situation here...but ultimately I think it depends on what data you have readily available at the time you want to add the users into the game object and also how you want to retrieve the data when you need it.
If you have all the usernames cached, whether as objects or just the username itself, it would be more efficient to just add them into the game object.
Example:
var usernamesExample = ["Mike", "Ike", "Clara", "Joe"];
Game.findById(gameIdExample, function(error, foundGame){
// handle errors/checks/etc.
foundGame.players = usernamesExample;
foundGame.save();
})
I personally think this approach is best performance wise. Then again it might not work for your situation, in which case I would need further clarification into how you obtain the username data for the game.

Mongoose best practices for CRUD with a Schema reference

I'd like to know the best practices for adding CRUD endpoints for a Schema which references another Schema. I have a Company Schema and an Address Schema, where a Company will have an Array of Address IDs defined in its Model like this:
CountrySchema = new Schema({
addresses: [{
type: Schema.Types.ObjectId,
ref: 'Address'
}]
});
When creating a new Company, my first thoughts are to loop through the Addresses and create them all in the database before proceeding to save the Company. One very annoying issue with this could be that if 3 Addresses save, and 1 does not, I will need to go back and remove those 3 Addresses.
Creating the Address before creating the Company could be something like this:
function CreateModel1WithStuff(data, cb) {
if (data.child) { // Save child model first
data.child = Model2(data.child);
data.child.save(function(err) {
cb(err, err ? null : Model1(data));
});
} else { // Proceed without dealing with child
cb(null, Model1(data));
}
}
CreateModel1WithStuff({
foo: 'abc',
child: {
bar: 'cba'
}
}, function(err, doc) {
doc.save();
});
Another issue is with updating a Company's Address, is it better to update the Address separately using an Address endpoint? Or is it better to send the new Address to the Company endpoint and let that endpoint check if the Address should be updated. This would be kind of similar to the previous point.

Categories

Resources