proper way to reference documents with Mongoose - node.js

I see in all examples the suffix "_id" on a field referencing to another document.
Example:
record: {
_id : ObjectId("57f2fb5d1c6c3b0de45b170e",
artist_id: "prince" )
}
artist: {
_id: "prince"
}
Being that my artist mongo Schema has the "unique" attribute on the name field.
Is it Ok to things like below ?
record: {
_id : ObjectId("57f2fb5d1c6c3b0de45b170e",
artist: "prince" )
}
artist: {
_id : ObjectId(6eygdqzd5d1c6c3b0de45b1s0r",
name: "prince"
}
Or should you always reference directly the Id like in the first example?

if you visualize your problem in RDBMS world, there too to establish a foreign key constraint the field should be primary key in the referenced table and the same rule applies here.
now in your artist document though each document is going to contain a unique artist name but the name field itself is not key (primary key) but the ID is.
hence you have to establish the reference using the _id field.
what you can do is for ease if you want rather than relying on the mongodb generated ID field you can probably use name as the _id.

Related

MongoDb Insert many not working with ordered false option in transaction

I am creating a collection blog with the following unique index:
{
"title" : 1,
"author" : 1
}
unique:true
This collection consists the following document
{
"title":"Java",
"author":"ABC",
code:123
}
Now I want to insert the following document into it
[{
"title":"JAVA",
"author":"ABC",
code:234
}]
I am using insertMany query to add this to the collection. but the collection has a unique index. so when I try to insert the above document in the collection it will give an error like the title is not unique. but I want check other field error also here so I am using insertMany query with ordered option like:
db.insertMany("blog",[{
"title":"JAVA",
"author":"ABC",
code:234
}]
, {ordered: false})
the above query error gives me all two field names. but when I am trying to use this with Mongodb transaction it will gives only one field error I am using Transaction like:
let session = await db.startSession({});
db.insertMany("blog",[{
"title":"JAVA",
"author":"ABC",
code:234
}]
, {ordered: false, session})
SO how I get all fields name in result whose no unique using insertMany, ordered, and session? Kindly help me here

Pushing JSON with internal relational data to MongoDB?

I have a JSON I would like to put into my mongo db database. My JSON holds some relational data. This data is internal to that JSON, I don't need to store this anywhere else. Example:
{
title: "John film list"
films [
{
title: "Once upon a time in Hollywood"
director: '1a' //referencing the director using an ID of type string
},
{
title: "Some film with empty director field",
director: ''
}
],
directors: [
{
id: '1a', //find the director here
name: 'Tarantino'
}
]
}
I do not need to store anything centrally (I don't need a big list of directors somewhere), but in this very document I need to be able to look up the director (1a) and get back Tarantino.
I managed to push this JSON format to MongoDB. However, it gives my schemas new ids (_id-field) and I am confused now as to how to relate the two properly in mongo?
The default unique primary key in the MongoDB document is _id. When you insert a new document, it returns the unique id of the record that inserted (created).
The value inside this id is ObjectId who creates based on time. you can read about it here.
If you want to use your own value for the _id, have to pass it when you call the insert, like this:
db.directors.insertOne({_id: '1a', name: 'Tarantino'})

Modelling reference to embedding document using Mongoose

I am modelling two types of events (events and subevents) in a MongoDB like this:
var EventSchema = mongoose.Schema({
'name' : String,
'subEvent' : [ SubeventSchema ]
});
var SubeventSchema = mongoose.Schema({
'name' : String
});
Now when I query a subevent I want to be able to also retrieve data about its corresponding superevent, so that some example data retrieved using Mongoose population feature could look like this:
EventModel.findOne({
name : 'Festival'
})
.populate('subEvent')
.execute(function (err, evt) { return evt; });
{
name : 'Festival',
subEvent: [
{ name : 'First Concert' },
{ name : 'Second Concert' }
]
}
EventModel.findOne({
'subEvent.name' : 'FirstConcert'
}, {
'subEvent.$' : 1
})
.populate('superEvent') // This will not work, this is the actual problem of my question
.execute(function (err, subevt) { return subevt; });
{
name: 'First Concert',
superEvent: {
name: 'Festival'
}
}
A solution I can think of is not to embed but to reference like this:
var EventSchema = mongoose.Schema({
'name' : String,
'subEvent' : [ {
'type' : mongoose.Schema.Types.ObjectId,
'ref' : 'SubeventSchema'
} ]
});
var SubeventSchema = mongoose.Schema({
'name' : String,
'superEvent' : {
'type' : mongoose.Schema.Types.ObjectId,
'ref' : 'EventSchema'
}
});
I am looking for a solution based on the first example using embedded subevents, though. Can this be achieved and in case yes, how?
I think your mental model of document embedding isn't correct. The major misunderstanding (and this is very common) is that you "query a subevent" (query an embedded document). According to your current Event schema, a Subevent is just a document embedded in an Event document. The embedded SubEvent is not a top-level document; it's not a member of any collection in MongoDB. Therefore, you don't query for it. You query for Events (which are the actual collection-level documents in your schema) whose subEvents have certain properties. E.g. one way people translate the query
db.events.find({ "subEvent" : { "name" : "First Concert" } })
into plain English is as "find all the subevents with the name "First Concert". This is wrong. The right translation is "find all events that have at least one subevent whose name is "First Concert" (the "at least one" part depends on knowledge that subEvent is an array).
Coming back to the specific question, you can hopefully see now that trying to do a populate of a "superevent" on a subevent makes no sense. Your queries return events. The optimal schema, be it subevents embedded in events, one- or two-way references between events and subevents documents in separate collections, or events denormalized into the constituent subevent documents, cannot be determined from the information in the question because the use case is not specified.
Perhaps this is a situation where you need to modify your thinking rather than the schema itself. Mongoose .populate() supports the basic ideas of MongoDB "projection", or more commonly referred to as "field selection". So rather than try to model around this, just select the fields you want to populate.
So your second schema form is perfectly valid, just change how you populate:
EventModel.find({}).populate("subEvent", "name").execute(function(err,docs) {
// "subevent" array items only contain "name" now
});
This is actually covered in the Mongoose documentation under the "populate" section.

MongoDB find documents based on array of values

I have one collection, called "games" whose documents store the ids of the owners of games.
{
"owner" : "88878b6c25c167d"
}
{
"owner" : "88878b6c25c167d"
}
{
"owner" : "af565f77f73469b"
}
Then I have another collection called "users".
{
"id" : "af565f77f73469b"
}
{
"id" : "a881335e1d4cf17"
}
{
"id" : "aa3ce3f7767c46b"
}
{
"id" : "d19e52c0bd78bcb"
}
{
"id" : "88878b6c25c167d"
}
So the first query I do retrieves the owners of all the games and stores those values in an array.['88878b6c25c167d', '88878b6c25c167d', 'af565f77f73469b']
The second query I want to perform should retrieve the documents of the users with the corresponding IDs. Previously I had used this query:
db.users.find({'id':
{'$in': [
'88878b6c25c167d',
'88878b6c25c167d',
'af565f77f73469b'
]}})
Here's the problem with this: It does not return duplicate documents if a user is listed twice, as above. Instead I just get one. This breaks my application. How can I make sure that each owner returns a document?
MongoDB works perfectly fine --- it finds all user, whose id-s are contained in the array.
Do not know the broader context of your needs (maybe tell us what you want to achieve -- not what is wrong?), but if you want to have an association between games and users something like that may be suitable:
after retrieving collection of games; just create an auxiliary hash map (normal JS object) that for given owner id will return the array of its games.
retrieve info about users who are owners.
if you want to know, which games belong to particular user just pass her id to data structure from 1. and get the arrays with games.
Is it what you were looking for? Do you need help with 1.?

Using 'generic' virtual for mongoose schema

I want to convert _id variable as id.
So i want to add virtual 'id' field to all the schema i am going to create, that will return the value of '_id' whenever i access 'id' field of the model.
from the documentation http://mongoosejs.com/docs/2.7.x/docs/virtuals.html
i found that, first i have to create schema then apply the virtual 'id' field individually.
I want to simply add virtual field to the base mongoose.Schema and then whenever i will create a new schema, that all will have virtual field without any manual effort on each of the individual schema.
EDIT :
i am also using Backbone.Model and i have created an 'id' field for each model. If i get the simply use id in the front end codes, i get the error that id field not exists. BUT when i set idAttribute as '_id' to each model then everything goes OK. That means backbone model want to have _id, and the reason behind mongoose schema have _id not id. So can i interpret that, mongoose does not automatically add virtual id mapped to _id? Correct me if i am wrong.
For backbone, "id" is it's default idAttribute name, so just don't add any code there and everything will work as intended.
For mongoose, the answer boils down to "it's complicated", but the main points are:
By default mongoose will give each schema a virtual called "id" which will be the document's _id as a string
However, by default .toJSON doesn't include virtuals, so when you send a mongoose document to backbone in the browser, it gets just "_id" and not "id"
to quickly get a JSON representation including the virtuals, use myModelInstance.toJSON({virtuals: true}). You'll get both "_id" and "id"
You need to read up on the transform options for toObject and toJSON to get a full picture of what you can do and how, but the gist is (based on examples straight from the docs)
// specify the transform schema option
if (!schema.options.toJSON) schema.options.toJSON = {};
schema.options.toJSON.transform = function (doc, ret, options) {
// remove the _id of every document before returning the result
delete ret._id;
}
schema.options.toJSON.virtuals = true;

Resources