How to correctly link collections in MongoDB/Mongoose - mongodb

I am currently developing a Questionbank and I would like to link test results to user accounts. I am new to the noSQL database structure and I just wanted an opinion from the experts as to the best way to link the results to the user.
Should I have the userSchema to have a reference to a user specific test score collection? Or just link to keys in a global collection of test scores with "ref:"?

You should think data which your need as a JSON and other entities as a properties and property array.
I hope this code will help you:
Question:
{
"title" : "It's a title of question",
"otherProperty" : Boolean,
"created_by: {
"name": "User Who Created Question",
"otherProperty":0
},
"answers": [
{"text":"Answer content",
"created_by": {
"name":"User Who Answered Question
},
"otherProperty":False
},
{"text":"Answer content",
"created_by": {
"name":"User Who Answered Question
},
"otherProperty":False
},
{"text":"Answer content",
"created_by": {
"name":"User Who Answered Question
},
"otherProperty":False
}
]
}
In side of code you should read mongoose docs but you will use Ref key usually. Like this:
var Schema = mongoose.Schema;
var QuestionSchema = new Schema({
title: {
type: String,
required: true
},
answers: [{
type: Schema.Types.ObjectId,
ref: 'Answer'
}]
})
var Answer = new Schema({
text: {
type: String,
required: true
},
created_by: {
type: Schema.Types.ObjectId,
ref: 'User'
}
})
I hope it will help you.
UPDATE: I just updated sample code for first example.

Related

Mongoose find subarray in array of object

Firstly, sorry for my english.
I hade such mongoose schema:
const option = new Schema<IAnswer>({
option: {
type: String,
require: true,
},
});
const question = new Schema<IQuestion>({
question: {
type: String,
require: true,
},
answers: [option],
answer: {
type: Number,
required: true,
},
});
const quiz = new Schema<IQuiz>({
name: {
type: String,
require: true,
unique: true,
},
questions: [question],
});
const Quiz = model<IQuiz>("Quiz", quiz);
export { Quiz };
And I need such result when I route to GET localhost:3000/quizes/quizId/questions/questionId/answers:
"answers": [
{"option": "Some text"},
...
]
And I need to get one answer when I route to GET localhost:3000/quizes/quizId/questions/questionId/answers/answerId
{"option": "Some text"}
So, maybe somebody can give me some recommendation how to do it? And also recommendation how to correct make all others CRUD(create, update, delete) methods will be very usefully.
Thanks!

How can I model my MongoDB database to allow me to easily add in new products & user interactions?

I am looking for the best way to model this scenario:
There is a ProductA model. Users can "like" or "dislike" ProductA documents. The documents are then added to an array in the User model called "likes" & "dislikes."
var UserSchema = new mongoose.Schema({
...,
likes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ProductA' }],
dislikes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ProductA' }],
...,
});
Now, I have realized that I want to add in a new Product: "ProductB." How do I restructure this database to keep this scalable and allow me to add new products? I am not sure what the best way to do this would be in MongoDB.
I believe my ideal scenario is the following psuedo-model:
var InteractionSchema= new mongoose.Schema({
product: // anonymous reference to an object
productType: //enum of which product it is
user: // user who made the interaction
interactionType: // like or dislike enum
});
I could not find any reference to how to handle anonymous references in MongoDB however. I would appreciate some advice
If I understand your requirement correctly, you can have three collections at a time:
products (contains all the products)
users (contains user information)
user_product_likes (contains user's like/dislike)
Respective schema can be,
UserInformationSchema :
{
name : {
type: String,
required: false
..
},
..
}
ProductSchema :
{
product_type : {
type: Integer,
},
...
}
InteractionSchema :
{
product_id : {
type: Integer
required: true
},
user_id : {
type: Integer
required: true
},
like : {
type: Boolean
required: false,
default:false
},
dislike : {
type: Booelan,
required: false,
default: false
}
}

Mongoose Update Single Value of an object in array

i'm currently trying to update an object's value(status) of the id "12345" from "Plan to Watch" to "Watch" in the array(movielist ) of a single user by using the data below
{
_id: "5d4813c0a14fcd44f83feda8",
userName : "Dave",
movielist :
[
{
_id:"5d29c4922ce984356cea8b48",
movie:
{
_id:"12345"
title:"The Avengers",
movieLength:"143min",
}
status:"Plan to watch"
},
{
_id:"5d276dd65f27682c26c6041b",
movie:
{
_id:"5d28ca94e2e19d6cbaecdef9"
title:"The Avengers",
movieLength:"143min",
}
status:"Completed"
}
]
}
I have tried using the mongoose below to change the value:
userModel.findByIdAndUpdate(5d4813c0a14fcd44f83feda8,{movielist :{id:12345}},{$set:{movielist :{status:"Completed"}}},callback)
But what i got is that the Attribute status that i was trying to update has been removed for object with id "12345" which is the result below :
{
_id: "5d4813c0a14fcd44f83feda8",
userName : "Dave",
movielist :
[
{
_id:"5d29c4922ce984356cea8b48",
movie:
{
_id:"12345"
title:"The Avengers",
movieLength:"143min",
}
},
{
_id:"5d276dd65f27682c26c6041b",
movie:
{
_id:"5d28ca94e2e19d6cbaecdef9"
title:"The Avengers",
movieLength:"143min",
}
status:"Completed"
}
]
}
I have tried using the mongoose below to change the value:
own below is the schema i have used for this object:
userSchema = schema({
userName: String,
movielist : [{id :{type: mongoose.Schema.Types.ObjectId,
ref: 'movies', required : true}, status : String}],
});
May i know where kind of horrible mistake did i commit?
Please update your query to this :
userModel.findOneAndUpdate({_id: ObjectId('5d4813c0a14fcd44f83feda8'), 'movielist.movie._id': '12345'}, {$set:{'movielist.$.status' :'Completed'}}, {returnNewDocument : true},callback)
can you correct your schema, you wrote stat!
userSchema = schema({
userName: String,
movielist : [{id :{type: mongoose.Schema.Types.ObjectId,
ref: 'movies', required : true}, status: String}],
});
So after a bit of research as well as looking through some other stackoverflow questions, i modified the answers you guys gave me and managed to change that status to "Completed" via the mongoose operator $elemMatch in the filter section of FindOneAndUpdate After the UserId, just want to thanks to all those who have guided me, and really sorry about my typo and being quite bad at this

How to set property of a model from a property of a `population` field in mongoose?

I have a Post and its author property is set to User by using ref. Suppose there is a country field in User, how to make Post schema to have a country property as well which value is from User.country after population.
const Post = new Schema({
text: String,
author: {
type: ObjectId,
ref: 'User'
},
// How to set the virtual property country which is from User.country?
});
I know there is a Populate Virtuals in Mongoose, but it seems it just another way of populating, it won't pick up one of the properties, but the whole referenced record. Or maybe I get it wrong?
I know I can refer the country from Post.author.country, but I want a Post.country as well.
How to solve this?
Could I do this at the schema level?
If you use Populate Virtuals you can do the following :
var schemaOptions = {
toObject: {
virtuals: true
},
toJSON: {
virtuals: true
}
};
var PostSchema = new Schema({
text: String,
id: false,
author: {
type: Schema.Types.ObjectId,
ref: 'User'
}
}, schemaOptions);
PostSchema.virtual('country').get(function() {
return this.author.country;
});
Using :
Post.find({}).populate('author').exec(function(error, data) {
console.log(JSON.stringify(data));
});
it gives :
[{
"_id": "59d924346be5702d16322a67",
"text": "some text",
"author": {
"_id": "59d91f1a06ecf429c8aae221",
"country": "France",
"__v": 0
},
"__v": 0,
"country": "France"
}]
Check this gist for a full example

MongoDB: How to define a schema?

So I have an application that uses MongoDB as a database. The application makes use of a few collections.
When and how should I go about defining the "schema" of the database which includes setting up all the collections as well as indexes needed?
AFAIK, you are unable to define empty collections in MongoDB (correct me if I am wrong, if I can do this it will basically answer this question). Should I insert a dummy value for each collection and use that to setup all my indexes?
What is the best practice for this?
You don't create collections in MongoDB.
You just start using them immediately whether they “exist” or not.
Now to defining the “schema”. As I said, you just start using a collection, so, if you need to ensure an index, just go ahead and do this. No collection creation. Any collection will effectively be created when you first modify it (creating an index counts).
> db.no_such_collection.getIndices()
[ ]
> db.no_such_collection.ensureIndex({whatever: 1})
> db.no_such_collection.getIndices()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "test.no_such_collection",
"name" : "_id_"
},
{
"v" : 1,
"key" : {
"whatever" : 1
},
"ns" : "test.no_such_collection",
"name" : "whatever_1"
}
]
Create empty collection
This is how you could create empty collection in MongoDB using build in interactive terminal:
db.createCollection('someName'); // create empty collection
Just you don't really have to, because as someone pointed before, they will get created in real time once you start to interact with the database.
MongoDB is schema-less end of story, but ...
You could create your own class that interacts with mongo Database. In that class you could define rules that have to fulfilled before it can insert data to mongo collection, otherwise throw custom exception.
Or if you using node.js server-side you could install mongoose node package which allows you to interact with database in OOP style (Why bother to reinvent the wheel, right?).
Mongoose provides a straight-forward, schema-based solution to model
your application data. It includes built-in type casting, validation,
query building, business logic hooks and more, out of the box.
docs: mongoose NPM installation and basic usage
https://www.npmjs.com/package/mongoose
mongoose full documentation http://mongoosejs.com
Mongoose use example (defining schema and inserting data)
var personSchema = new Schema({
name: { type: String, default: 'anonymous' },
age: { type: Number, min: 18, index: true },
bio: { type: String, match: /[a-zA-Z ]/ },
date: { type: Date, default: Date.now },
});
var personModel = mongoose.model('Person', personSchema);
var comment1 = new personModel({
name: 'Witkor',
age: '29',
bio: 'Description',
});
comment1.save(function (err, comment) {
if (err) console.log(err);
else console.log('fallowing comment was saved:', comment);
});
Wrapping up ...
Being able to set schema along with restriction in our code doesn't change the fact that MongoDB itself is schema-less which in some scenarios is actually an advantage. This way if you ever decide to make changes to schema, but you don't bother about backward compatibility, just edit schema in your script, and you are done. This is the basic idea behind the MongoDB to be able to store different sets of data in each document with in the same collection. However, some restriction in code base logic are always desirable.
As of version 3.2, mongodb now provides schema validation at the collection level:
https://docs.mongodb.com/manual/core/schema-validation/
Example for create a schema :
db.createCollection("students", {
validator: {
$jsonSchema: {
bsonType: "object",
required: [ "name", "year", "major", "address" ],
properties: {
name: {
bsonType: "string",
description: "must be a string and is required"
},
year: {
bsonType: "int",
minimum: 2017,
maximum: 3017,
description: "must be an integer in [ 2017, 3017 ] and is required"
},
major: {
enum: [ "Math", "English", "Computer Science", "History", null ],
description: "can only be one of the enum values and is required"
},
gpa: {
bsonType: [ "double" ],
description: "must be a double if the field exists"
},
address: {
bsonType: "object",
required: [ "city" ],
properties: {
street: {
bsonType: "string",
description: "must be a string if the field exists"
},
city: {
bsonType: "string",
description: "must be a string and is required"
}
}
}
}
}
}
})
const mongoose = require("mongoose");
const RegisterSchema = mongoose.Schema({
username: {
type: String,
unique: true,
requied: true,
},
email: {
type: String,
unique: true,
requied: true,
},
password: {
type: String,
requied: true,
},
date: {
type: Date,
default: Date.now,
},
});
exports.module = Register = mongoose.model("Register", RegisterSchema);
I watched this tutorial.
You have already been taught that MongoDB is schemaless. However, in practice, we have a kind of "schema", and that is the object space of the object, whose relations a MongoDB database represents. With the ceveat that Ruby is my go-to language, and that I make no claims about exhaustiveness of this answer, I recommend to try two pieces of software:
1. ActiveRecord (part of Rails)
2. Mongoid (standalone MongoDB "schema", or rather, object persistence system in Ruby)
Expect a learning curve, though. I hope that others will point you to solutions in other great languages outside my expertise, such as Python.
1.Install mongoose:
npm install mongoose
2. Set-up connection string and call-backs
// getting-started.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
//call-backs
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
// we're connected!
});
3. Write your schema
var kittySchema = new mongoose.Schema({
name: String
});
4. Model the schema
var Kitten = mongoose.model('Kitten', kittySchema);
5. Create a document
var silence = new Kitten({ name: 'Tom' });
console.log(silence.name); // Prints 'Tom' to console
// NOTE: methods must be added to the schema before compiling it with mongoose.model()
kittySchema.methods.speak = function () {
var greeting = this.name
? "Meow name is " + this.name
: "I don't have a name";
console.log(greeting);
}
enter code here
var Kitten = mongoose.model('Kitten', kittySchema);
Functions added to the methods property of a schema get compiled into the Model prototype and exposed on each document instance:
var fluffy = new Kitten({ name: 'fluffy' });
fluffy.speak(); // "Meow name is fluffy"