Collection schema for a quiz app in Meteor - mongodb

I am creating a quiz app in Meteor.
What can a schema for a question look like? I am thinking of doing something like
const QuestionSchema = new SimpleSchema({
text: {
type: String,
},
correctAnswers: {
type: [Object],
},
'correctAnswers.$.text': {
type: String,
},
wrongAnswers: {
type: [Object],
},
'wrongAnswers.$.text': {
type: String,
},
});
but is it really smart? How can I save which answer some user chose?

As #gnerkus suggests it's simpler to have a correct boolean for each answer but each answer also needs an _id so that you reference it, for example which answers did the user select?
const AnswerSchema = new SimpleSchema({
_id: { type: String },
text: { type: String },
correct: { type: Boolean }
});
const QuestionSchema = new SimpleSchema({
text: { type: String },
answers: { type: [AnswerSchema] }
});

You'll need to define a separate schema for the answers.
var QuestionSchema = new SimpleSchema({
text: {
type: String
},
answers: [{
type: SimpleSchema.Types.ObjectId,
ref: 'AnswerSchema'
}]
});
var AnswerSchema = new SimpleSchema({
text: {
type: String
},
correct: {
type: Boolean
},
question: {
type: SimpleSchema.Types.ObjectId,
ref: 'QuestionSchema'
}
});
If you need to track the answers a user picked, you'll have to store them in the user instance:
var UserSchema = new SimpleSchema({
// Define other attributes here.
answers: [{
type: SimpleSchema.Types.ObjectId,
ref: 'AnswerSchema'
}]
});

Related

Create ref to sub document's array for each property of subdocument

Model A looks like
{ a : {
step1: [{
type: { type: String },
category: { type: String },
}],
step2: [{
type: { type: String },
category: { type: String },
}]
} }
Model B which I wanted to create should contain a prop which will ref to Model A.step1 or A.step2 , trying to acheive this by following
{ progress : [
{
step: {
type: Schema.Types.ObjectId,
ref: "A.a.{What should be here}",
required: true,
},
details: { type: Schema.Types.Mixed },
}
]
}
I think you need to create Schemas for all of them :)
I would just separate the three completely - not sure if this is viable to you as the whole idea behind this is a bit mysterious, but would something like this work for you?
const stepSchema = new Schema({
type: String,
category: String,
});
const Step = mongoose.model("steps", stepSchema);
const aSchema = new Schema({
step1: [stepSchema],
step2: [stepSchema],
});
const A = mongoose.model("as", aSchema);
const progressSchema = new Schema({
a: { type: aSchema, required: true, ref: "as"},
progress: [{ type: stepSchema, required: true, ref: "steps" }],
details: Schema.Types.Mixed,
});
const Progress = mongoose.model("progresses", aSchema);

Doubts on Mongodb query

I was designing a classifieds web app with the MERN stack. The MongoSchema is as shown below
const UserSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
books: [{
title: { type: String },
author: { type: String },
desc: { type: String },
price: { type: String },
image: { data: Buffer, contentType: String }
}],
date: {
type: Date,
default: Date.now
}
});
So all the other info except the books[] will be available after the initial sign-up, but what I want to do is to update the books array every time the user wishes to post a book for selling.
I'm planning to find the user by id, but I'm not quite sure how to add/append the info to the books array.
There are some answers to your question already in Stackoverflow.
For example:
Using Mongoose / MongoDB $addToSet functionality on array of objects
You can do something like this:
UserModel.js
const mongoose = require("mongoose");
const UserSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
books: [{
title: { type: String },
author: { type: String },
desc: { type: String },
price: { type: String },
image: { data: Buffer, contentType: String }
}],
date: {
type: Date,
default: Date.now
}
});
module.exports = User = mongoose.model("user", userSchema);
After that, in your router file you can try something like this for the books array:
const res = await User.updateOne({ email: 'gintama#bandainamco.com' }, {'$addToSet':{
'books':{
title: "Gintama: The Final",
author: "Sorachi",
desc: "Final Arc",
price: "44.99",
image: "src"
}}); //addToSet if you don't want any duplicates in your array.
OR
const res = await User.updateOne({ email: 'gintama#bandainamco.com' }, {'$push':{
'books':{
title: "Gintama: The Final",
author: "Sorachi",
desc: "Final Arc",
price: "44.99",
image: "src"
}}); //push if duplicates are okay in your array

mongodb doesnt save 2 objects inside the main object

I want to save an object that has and object and array inside it. But when I end up saving the data in the mongo, it doesnt save a few properties.
like "entityMap": {}, data: {}
body =
{ entityMap: {},
blocks:
[ { key: '637gr',
text: 'Books selected for the you ',
type: 'header-four',
depth: 0,
inlineStyleRanges: [Array],
entityRanges: [],
data: {} } ] }
Heres how my mongo schema structured.
const mongoose = require('mongoose');
const { Schema } = mongoose;
const bookSchema = new Schema({
body: {
type: {},
required: false
},
templateName: {
type: String,
required: true
},
subject: {
type: String,
required: true
},
googleId: {
type: String,
required:true
},
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now }
});
mongoose.model('books', bookSchema);
When declaring the property with type {}, mongoose uses the Schema.Types.Mixed type. This way the property may contain anything, but mongoose won't detect changes made to it. You have to manually tell mongoose that the property was modified:
book.body = { foo: { bar: { quux: 'foo' } } }
book.markModified('body');
book.save()
Mongoose SchemaTypes

Ordering two reference arrays together

Suppose I have the following schemas:
var QuizSchema = new mongoose.Schema({
name: { type: String, required: true },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }],
questionGroups: [{ type: mongoose.Schema.Types.ObjectId, ref: 'QuestionGroup' }]
});
var QuestionSchema = new mongoose.Schema({
number: { type: String, required: true }, // e.g. 1, a, i, anything
question: { type: String, required: true },
type: { type: String, enum: ['multiple choice', 'multiple select', 'short answer'] },
choices: [String],
answers: [String]
});
var QuestionGroupSchema = new mongoose.Schema({
number: { type: String, required: true }, // e.g. 1, a, i, anything
prompt: { type: String },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }]
});
I am trying to design a way that will allow me to order questions and question groups together.
I was thinking maybe of adding a new field order
var QuizSchema = new mongoose.Schema({
// ...
order: [
{
type: { type: String, enum: ['Question', 'QuestionGroup'] },
id: mongoose.Schema.Types.ObjectId // reference
}
]
});
such that in the database, the field would contain something such as
[
{ type: 'Question', id: ObjectId('57867a34567g67790') },
{ type: 'Question', id: ObjectId('57867a34567g67765') },
{ type: 'QuestionGroup', id: ObjectId('69864b64765y45645') },
{ type: 'Question', id: ObjectId('57867a34567g67770') },
{ type: 'QuestionGroup', id: ObjectId('69864b64767y45647') }
]
This may mean that I would need to "populate" the ordered list of questions and question groups as
quiz.populate('questions questionGroups').exec(function (err, quiz) {
// sort questions and groups by the order
quiz.order = quiz.order.map(function (o) {
if (o.type === 'QuestionGroup') {
return quiz.questionGroups.id(o.id);
}
return quiz.questions.id(o.id);
});
});
So my question: is there a better way to design this?
Virtuals can come in handy here; without persisting order field in db and doing calculations on client each time:
var QuizSchema = new mongoose.Schema({
name: { type: String, required: true },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }],
questionGroups: [{ type: mongoose.Schema.Types.ObjectId, ref: 'QuestionGroup' }]
},
{
toObject: {
virtuals: true
},
toJSON: {
virtuals: true
}
}
);
QuizSchema
.virtual('order')
.get(function() {
return this.questions.concat(this.questionGroups); //questions followed by questionGroups
});
Sort on createdAt is of course optional, but for that you need to have this field in Question and QuestionGroup:
Quiz.find({}, function (err, quiz) {
//...
})
.populate({path : 'questions', options: {sort: { 'createdAt': 1 }}})
.populate({path : 'questionGroups', options: {sort: { 'createdAt': 1 }}});

Meteor Simple-Schema Collection2 help needed for build nested schema

I am trying to build a collection schema with Meteor Collection2.
Possible schema for my collection is :
{
items: {
id: Meteor.ObjectID
title: String,
Desc: String,
createdAt: new Date(),
creator: String,
invitedUsers: [String],
items2: [{
id: String,
pos: Number,
dur: Number,
startTime: Number,
endTime: Number,
duration: Number
items3: [{
id: String,
itemtype: String,
style: String,
items4: [{
id: String,
position: Number,
dome: String
}]
}]
}]
}
}
So how can I best build the Collection2 collection with the above nested schema and the best way to perform insert, update and remove queries on it.
Update:
So now as suggested by Andrei Karpushonak this is what I have got:
Item4 = new SimpleSchema({
position: {
type: Number
},
dome: {
type: String
}
});
Item3 = new SimpleSchema({
itemtype: {
type: String
},
style: {
type: String
},
items4: {
type: [Item4]
}
});
Item2 = new SimpleSchema({
pos: {
type: Number
},
dur: {
type: Number
},
startTime: {
type: Number
},
endTime: {
type: Number
},
duration: {
type: Number
},
items3 : {
type: [Item3]
}
});
Items = new Meteor.Collection2('items', {
schema : new SimpleSchema({
title: {
type: String
},
Desc: {
type: String
},
createdAt: {
type: new Date()
},
creator: {
type: String
},
invitedUsers: {
type: [String]
},
items2: {
type: [Item2]
}
})
});
So now I am trying to figure out how can I do the insert, update, remove operations on such a schema?
Do I do for individual schemas for as whole? An example will be very helpful.
Any help will be highly appreciated.
Thanks in Advance,
Praney
You have two options:
Create sub-schema:
item2 = new SimpleSchema({
id: String,
pos: Number
})
item1 = new SimpleSchema({
id: Meteor.ObjectID,
title: String,
items2: [item2]
});
Use dot notation:
item1 = new SimpleSchema({
id: String,
pos: Number,
"item2.id": String,
"item2.pos": String
});
I think first approach fits your model better, as you have array of objects as value of items2