I am building an app of Todo Lists, and each list will have an x amount of tasks. The problem is I thought i could do something like this in the taskschema to reference the list the task belongs to:
list: {
type: mongoose.Schema.Types.ObjectId, ref: 'list'
}
Here´s the full code of my models:
const mongoose = require('mongoose')
const listSchema = new mongoose.Schema({
title: {
type: String,
unique: false,
required: [true, 'debes escribir un titulo']
},
createdAt: {
type: Date,
default: Date.now()
}
})
const mongoose = require('mongoose')
const taskSchema = new mongoose.Schema({
name: {
type: String,
unique: false,
required: true
},
doned: {
type: Boolean,
default: false
},
updatedAt: {
type: Date,
default: Date.now()
},
// a task belongs to a list:
list: {
type: mongoose.Schema.Types.ObjectId, ref: 'list'
}
})
But it seems there´s no much help in the web in how to make that work using that approach.
So I took a look at mongoDb documentation and they say they recommend using manual references
So i changed like this:
const mongoose = require('mongoose')
const taskSchema = require('./task')
const listSchema = new mongoose.Schema({
title: {
type: String,
unique: false,
required: [true, 'debes escribir un titulo']
},
createdAt: {
type: Date,
default: Date.now()
},
tasks: [
taskSchema
]
})
But now I´m clueless in how each time I create a task (a POST request to task is made) how I am at the same time will be related to a certain list.
Here´s my api routes for tasks:
routerTasks.post('/task', (req,res, next) => {
Task.create(req.body)
.then(task => res.send(task))
.catch(next)
})
routerTasks.put('/task/:idTask', (req, res) => {
Task.findByIdAndUpdate(req.params.idTask, req.body)
.then(task => res.send({nueva_informacion: task}))
.catch()
})
and for my lists:
routerLists.post('/list', (req, res, next) => {
List.create(req.body).then((list) => {
res.send(list)
}).catch(next)
})
routerLists.put('/list/:id', (req, res) => {
List.findByIdAndUpdate(req.params.id, req.body)
.then(list => res.send({nueva_informacion: list}))
})
My question is... maybe I should create the tasks in a put request of the newly created list? In that case, then the POST request of tasks are useless?
here´s the error that gives me when i try to add a Task and at the same time referencing it to a list:
routerTasks.post('/task/:listId', (req,res, next) => {
List.findOne({_id: req.params.listId}).then((record) => {
record.tasks.push(req.body);
record.save()
})
})
// TypeError: Invalid schema configuration: `model` is not a valid type within the array `tasks`
Related
I am a nodejs newbie. I have two simple models, User and Story. Here is what I want to do:
I want to retrieve all stories that have {status:"public"} and store it in an array called retrievedStories.
Then for each story I want to use its "user" field (which contains the object id of the user) to lookup the name of the user from User
Then add a new key in each element of retrievedStories called authorName with the name of the user.
Here are the models:
const UserSchema = new mongoose.Schema({
googleId: {
type: String,
required: true
},
displayName: {
type: String,
required: true
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
image: {
type: String,
},
createdAt: {
type:Date,
default: Date.now()
}
})
const StorySchema = new mongoose.Schema({
title: {
type: String,
required: true,
trim: true
},
body: {
type: String,
required: true
},
status: {
type: String,
default: 'public',
enum: ['public', 'private']
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
createdAt: {
type:Date,
default: Date.now()
}
})
And here is what I tried, but doesn't work. The stories are retrieved but the authorName is not added. Any help (possibly a better way to do this?) will be highly appreciated!
router.get('/',async (req,res)=>{
try {
const retrievedStories = await Story.find(
{status: "public"}
)
await Promise.all(retrievedStories.map(async (story) =>{
const author = await User.findById(story.user)
story.authorName = author.displayName
}))
return res.json(retrievedStories)
} catch (error) {
console.log(error)
}
})
You can simplify your query by using populate to retrieve User's data:
router.get('/', async (req, res) => {
try {
const retrievedStories = await Story.find({ status: 'public' })
.populate('user')
.exec();
return res.json(retrievedStories);
} catch (error) {
console.log(error);
}
});
You can then access User's displayName data on each Story by accessing story.user.displayName.
For more information on query population see the official docs.
Technology: MongoDB, ExpressJS
I have 3 schema
userSchema:
userSchema = {
name: {type: String},
password: {type: String},
email: {type: String},
friends: {type: [mongoose.Types.ObjectId]}
}
textPostSchema =
textPostSchema = {
text: {type: String},
postType: {type: String, default: "textPost"},
userId: {type: mongoose.Types.ObjectId}
}
articalPostSchema:
articalPostSchema = {
title: {type: String},
content: {type: String}
postType: {type: String, default: "articalPost"},
userId: {type: mongoose.Types.ObjectId}
}
now I have one social media application in which I have to show these two post when user's friend post's a post, and include infinite scroll. Both textPost and articalPost should be send if to frontend and only total 10 post should be sent at a time. How should I write a API for timeline?
output should look like:
{
post: [
{
title: "artical Post title",
content: "artical post content",
postType: "articalPost",
userId: "60b9c9801a2a2547de643ccd"
},
{
text: "text post ",
postType: "textPost",
userId: "60b9c9801a2a2547de643ccd"
},
... 8 more
]
}
UPDATE:
I got the solution:-
I created on more schema:
timelineSchema = {
postId: {
type: mongoose.Types.ObjectId,
required: true,
ref: function () {
switch (this.postCategoryType) {
case 'articleposts':
return 'ArticlePost';
case 'textposts':
return 'TextPost';
}
},
},
postCategoryType: {
type: String,
required: true,
},
userId: {
type: mongoose.Types.ObjectId,
required: true,
ref: 'User',
},
},
and then I created one function to get only friends post:
exports.getTimelinePosts = async (req, res) => {
try {
const timelinePosts = await TimelineModel.find({
userId: { $in: [...req.user.friends, req.params.id] },
})
.skip((req.params.page - 1) * 10)
.limit(10)
.sort({ createdAt: -1 })
.populate('postId');
return res.status(200).json({ status: 'success', data: timelinePosts });
} catch (error) {
return res.status(500).json(error);
}
};
To implement the pagination with Mongoose, You can do something like that.
const getPosts = async (userId, pageNumber) => {
let result = await Post.find({ userId })
.skip((pageNumber - 1) * 10)
.limit(10);
return result;
};
pageNumber is a counter that you need to pass from the frontend and will be incremented by 1 whenever a user hits the scroll limit.
If you want to query and merge data from multiple collections you need to update your schema to use populate. Just include ref where you are referring to other collections.
This may help.
https://mongoosejs.com/docs/populate.html
Assuming you are using express and mongoose. The code to fetch both,
// first bring all those schema from your mongoose models
const Article = require('./models/ArticleSchema');
const Text = require('./models/TextSchema');
const fetchArticleAndTextPost = async (req, res)=>{
//find all data
const articles = await Article.find();
const texts = await Text.find();
//join them together
const post = articles.concat(texts);
return res.status(200).json({
status: 200,
data: post,
})
}
I have below schema.
const mongoose = require('mongoose');
const JobSchema = new mongoose.Schema({
posterId: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
location: {
type: String,
required: true,
},
}, { timestamp: true });
module.exports = mongoose.model('Job', JobSchema);
And I use below code to save new data to this collection.
const Job = require('../../models/Job');
new Job({
posterId,
title,
location,
})
.save()
.then(job => {
console.log('JOB: ', job) // consoles the newly added job to collection.
if (job) {
response.json({
message: 'Job is saved successfully.',
});
} else {
response.json({
message: 'Job can not be saved at this time. Please try again later.',
});
}
})
.catch(error => {
response.json(error);
});
The above code successfully saves new information to job collection but when I check that in mongoDB using mongodb CLI. I do not see any job collection there so it is not creating the collection. What am I doing wrong and how can I fix this?
Hi everyone I am making a route to get the items that are created by the logged-in user but when I use the .filter function I get an error. Not sure why I am getting this error. I have made other apps before doing the same thing and never got an error
Item.filter is not a function
The my-items route
const requireAuth = require("../middleware/requireAuth");
const express = require("express");
const mongoose = require("mongoose");
const Item = mongoose.model("Item");
router.get("/my-items", requireAuth, async (req, res) => {
try {
const items = Item.filter((item) => item.userId === req.user.userId);
res.send(items);
} catch (err) {
console.log(err);
}
});
Item Schema
const mongoose = require("mongoose");
const itemSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
phone: {
type: mongoose.Schema.Types.String,
ref: "User",
},
email: {
type: mongoose.Schema.Types.String,
ref: "User",
},
seller: {
type: mongoose.Schema.Types.String,
ref: "User",
},
title: {
type: String,
required: true,
},
category: {
type: String,
required: true,
},
detail: {
type: String,
requiredL: true,
},
condition: {
type: String,
required: true,
},
price: {
type: Number,
required: true,
},
});
mongoose.model("Item", itemSchema);
const items = await Item.find(({userId:req.user.userId}).lean();
it should return exact items from db that you want you can use more query if you need.
Item is a model but not the documents in the database, you need to do a query first in order to get the items.
router.get("/my-items", requireAuth, async (req, res) => {
try {
const query = Item.find()
query.exec().then(items => {
const filteredItems = items.filter((item) => item.userId === req.user.userId);
res.send(items);
})
} catch (err) {
console.log(err);
}
});
This error can occur when you are trying to use the array methods on other data structures.
This piece of code returns an error .filter is not a function:
const myList = await getList().filter(item => item.myKey > 10);
Solution:
const data = await getList();
const myList = data.filter(item => item.myKey > 10);
Summary
In Mongoose have a post findOneAndUpdate hook and inside of this hook I need to query the database. I'm trying to do a .find() on another model, however each time I do, I get the following error:
Error
TypeError: Users.find is not a function
at model.Query.<anonymous> (/Users/benjilightstone/Coding/eSports-node-RESTful-API/api/models/fortniteUsers.js:29:32)
I have tried running the exact same .find() in one of my express routes and it works perfectly. I have checked the require() for Users and it's imported correctly. I have no idea why I'm getting this error and would love some help!
Code
fortniteUsers.js (Model with post hook)
const mongoose = require("mongoose");
const Users = require("./users");
const uniqueValidator = require("mongoose-unique-validator");
const statsFieldsSchema = require("./statsFields");
const fnUserPlatformSchema = {
//this is so that have a unique entry for a username &platform combo
fnUser: { type: String, require: true },
platform: { type: String, enum: ["pc", "xb1", "psn"], require: true }
};
//TODO took out updated at because the record shouldn't get updated?
//TODO think about usecase of Fortnite user changing their username
const fortniteUserSchema = mongoose.Schema({
// pass a javascript object that defines the schema
fnUserPlatform: { type: fnUserPlatformSchema, unique: true, require: true },
createdAt: { type: Date, require: true },
hasAccount: { type: Boolean, require: true, default: false },
updatedAt: { type: Date, require: false },
lifeTimeStats: { type: statsFieldsSchema, require: true }
});
fortniteUserSchema.post("findOneAndUpdate", async function(fortniteUser, next) {
try {
console.log("inside fortniteUser post hook", typeof fortniteUser._id);
const fnUserId = String(fortniteUser._id);
console.log("fnUserId", fnUserId);
// const userId = await User.find({ fnUser: fnUserId });
const userId = await Users.find({ fnUser: "5ccb08198f52f40117e950b3" });
console.log("userId", userId);
} catch (err) {
console.log("error in post hook", err);
}
});
fortniteUserSchema.plugin(uniqueValidator);
module.exports = mongoose.model("FortniteUser", fortniteUserSchema);
users.js (User model imported in fortniteUsers.js)
const mongoose = require("mongoose");
const FortniteUsers = require("./fortniteUsers");
require("mongoose-type-email");
const userSchema = mongoose.Schema({
// pass a javascript object that defines the schema
fnUser: {
type: mongoose.Schema.Types.ObjectId,
ref: "FortniteUser",
require: true
},
eSportsUsername: { type: String, require: true },
password: { type: String, require: true, minlength: 5 },
email: { type: mongoose.SchemaTypes.Email, require: true, unique: true },
ageConfirmed: { type: Boolean, require: true },
createdAt: { type: Date, require: true },
updatedAt: { type: Date, require: false }
});
userSchema.pre("save", function() {
console.log("inside pre save users.js");
console.log("docToSave", this); // QUESTION: how would I pass in req.body.fnUser
FortniteUsers.findOne({
fnUserPlatform: { username: req.body.fnUser, platform: "psn" }
})
.then(result => {
console.log("result in pre", result);
})
.catch(err => console.log("error i expect", err));
});
userSchema.post("save", function(user) {
//TODO where is user gettting passed from, I dont really understand this
console.log("we're in post save ... ", user);
FortniteUsers.findOneAndUpdate(
{ _id: user.fnUser },
{ $set: { hasAccount: true } },
function(err, doc) {
if (err) {
console.log("error in mongoose userSchema.post", err);
}
}
);
});
module.exports = mongoose.model("User", userSchema);
Look like the problem is because you require fortniteUsers and users in each other so one of them is not yet exported when call inside the other.
To verify this, you can console.log(FortniteUsers) and console.log(Users) after require them. You can see that FortniteUsers is Model {FortniteUsers} but Users is just {}.
You can solve it by moving this line const FortniteUsers = require("./fortniteUsers"); to be after the exports in users.js (not sure this is the good way but it works).