Mongoose find subarray in array of object - mongodb

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!

Related

Look up and create or update object inside array

I am currently trying to setup a schema for custom Discord guild commands:
const GuildCommandsSchema = new mongoose.Schema({
_id: String,
commands: [
{
name: {
type: String,
unique: true,
required: true,
},
action: {
type: String,
required: true,
},
author: {
type: String,
required: true,
},
},
],
});
Is this ok, performancewise, or could I improve it?
I feel like Mongo would need to look through all commands, since it can't index any commands inside 'commands' even though 'name' is unique.
If that's fine, how can I access the values inside commands?
I would need to find the right command via 'name' if it exists, otherwise create it and add/update 'action' + 'author'.
I tried something like this:
const updatedCommand = await GuildCommands.findOneAndUpdate(
{ _id },
{
$set: {
[`commands.$[outer].name`]: name,
[`commands.$[outer].action`]: action,
[`commands.$[outer].author`]: author,
},
},
{
arrayFilters: [{ 'outer.name': name }],
}
);
Unfortunately that does not create commands if they don't exist.
Thanks for your help
aggregate
db.collection.update({},
{
$set: {
"commands.$[c].name": "1",
"commands.$[c].author": "1",
"commands.$[c].action": "1"
}
},
{
arrayFilters: [
{
"c.author": "34"
}
],
multi: true
})
mongoplayground
To answer my own question:
I changed my Schema to use Maps instead of Arrays for performance improvments and also better model management.
const GuildCommandsSchema = new mongoose.Schema(
{
_id: String,
commands: {
type: Map,
of: {
_id: false,
name: {
type: String,
required: true,
},
action: {
type: String,
required: true,
},
active: {
type: Boolean,
required: true,
default: true,
},
author: {
type: String,
required: true,
},
},
},
},
{ versionKey: false }
);
The new query to find and update/create a command is also better imo:
const findCommand = await GuildCommands.findOne({ _id });
if (!action) {
const getCommand = findCommand.commands.get(name);
if (getCommand) {
message.reply(getCommand.action);
} else {
message.reply(`Cannot find ${name}`);
}
} else {
findCommand.commands.set(name, {
name,
action,
author,
});
findCommand.save();
}

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);

Mongoose populate depending on conditions

My service uses MongoDB and Mongoose. I have two DBs: Users and Posts. In Posts schema I have parameters:
"author", that contains userId from Users DB
"anonymous", a boolean-parameter that shows if the post is anonymous or not.
I can't solve the problem: when I request data from Posts DB I want to populate author in the "author" parameter only for non-anonymous posts, for anonymous ones I'd like to return null or not to return this parameter at all.
I've tried to use "match", but it doesn't work.
How can I solve this problem?
Thank you.
Code example:
const postSchema = mongoose.Schema(
{
author: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
anonymous: {
type: Boolean,
required: true,
default: false,
},
content: {
type: String,
required: true,
},
date: {
type: Date,
required: true,
default: Date.now,
},
},
{
timestamps: true,
}
);
For population I use pre:
postSchema.pre(/^find/, function (next) {
this.populate({
path: 'author',
select: '_id login',
});
next();
});

add a youtube video link in mongoose schema

I am creating a video sharing platform where anyone can add youtube videos and their preferred image But for the backend I am using mongodb for that I am creating mongoose schema I need to add youtube video link to the schema which also validates whether it is a youtube video link or not so how can I achieve his task
const mongoose = require("mongoose");
const validate = require("validator");
const config = require("../config/config");
const videoSchema = mongoose.Schema(
{
title :{
type: String,
required: true,
trim: true,
},
genre:{
type: String,
required: true,
trim: true,
},
viewCount :{
type: Number,
default: config.default_view_count,
},
releaseDate :{
type : Date,
required: true,
default: Date.now,
},
votes :{
type: [Object],
upvotes: {
type: Number,
},
downvotes:{
type: Number,
}
},
contentrating: {
type: Number,
required: true,
trim: true,
},
previewimage:{
type: 'image/png',
required: true,
}
},
//create createdAt and updateAt field automatically
{
timestamps: true,
}
)
const Video = mongoose.model('Video',videoSchema)
module.exports = {Video};
If your link is just a simple youtube link and you're looking to check it's validity, you can use the youtube API and request information about the video pointed to by that link.
There's an example that requests information about a specific video :
https://developers.google.com/youtube/v3/getting-started#Sample_Partial_Requests
If no information or an error is returned, then you know it's an invalid link.
I guess an easy way is to just validate if the video link starts with https://www.youtube.com/. You can add a new field to your Schema Model and add validation to that field. If validation fails, document will not be stored and an error will be thrown.
You can add a new youtube_video_link field in your Schema Model like this:
const mongoose = require("mongoose");
const validate = require("validator");
const config = require("../config/config");
const videoSchema = mongoose.Schema(
{
title :{
type: String,
required: true,
trim: true,
},
genre:{
type: String,
required: true,
trim: true,
},
viewCount :{
type: Number,
default: config.default_view_count,
},
releaseDate :{
type : Date,
required: true,
default: Date.now,
},
votes :{
type: [Object],
upvotes: {
type: Number,
},
downvotes:{
type: Number,
}
},
contentrating: {
type: Number,
required: true,
trim: true,
},
previewimage:{
type: 'image/png',
required: true,
},
youtube_video_link: {
type: String,
validate: {
validator: function(v) {
return /^https:\/\/www.youtube.com\/.*$/.test(v);
},
message: props => `${props.value} is not a valid youtube link.`
},
required: [true, 'Youtube video link is required.']
}
},
//create createdAt and updateAt field automatically
{
timestamps: true,
}
)
const Video = mongoose.model('Video',videoSchema)
module.exports = {Video};

MongoDB: Set and get Sub Document Schema

I'm using mongoose and I have users collection shown below, but I now want to allow the user to save a number of articles, an article has a title, subtitle, and body, One user can have many articles.
How can I restructure the users collection to allow the articles to be added
const userSchema: Schema = new Schema(
{
email: { type: String, required: true, unique: true },
fullName: { type: String, required: true },
password: { type: String, required: true },
},
{
timestamps: true,
}
);
I'm using the below to set new data to the user's collection, how do I adapt it to allow me to set and get the new articles detailed above?
const confirmed = await userModel
.findOneAndUpdate(
{ email },
{
$set: { password },
}
)
.exec();
You can set the option strict: false and add(save) new fields to your schema.
const userSchema: Schema = new Schema(
{
email: { type: String, required: true, unique: true },
fullName: { type: String, required: true },
password: { type: String, required: true },
},
{
strict: false,
timestamps: true,
}
);
Here is the docs