I'm currently trying to wrap my head around creating a many-to-many relationship in mongodb/mongoose.
I have two Schemas:
// User Schema
var UserSchema = mongoose.Schema({
email: {
type: String,
index:true
},
name: {
type: String
},
tasks:[{type:mongoose.Schema.ObjectId, ref: 'Tasks'}]
});
and
// Tasks Schema
var TaskSchema = mongoose.Schema({
name: {
type: String,
index: true
},
description: {
type: String
},
status:{
type: String
},
});
The idea here, is that each user can take on a task and the task would have it's own status for each user (e.g. notStarted, inProgress, Complete, Failed). This status would change as the user progresses through the task. Each user would see the same tasks(i.e. name + description), but would have a different status associated with it. Additionally, each task has it's own status (Available, Hidden), which would dictate if the users can see the task or not. This would not be unique for each user.
This is my thought process so far:
I'm thinking that I can nest the objectIds of every task in with the user along with the status. For example:
{
email: "Bob#bob.com"
name: "Bob",
tasks: {
{ _id: 001, status: "Active"},
{_id: 002, status: "inProgress"},
{ _id: 003, status: "Failed"}
}
},
{
email: "Mary#mary.com"
name: "Mary",
tasks: {
{ _id: 001, status: "Failed"},
{ _id: 002, status: "Active"},
{ _id: 003, status: "Active"}
}
}
However, this means that whenever I create a new task, I need to add it to the task list of all users, with the status set to a default (e.g. notStarted). Additionally whenever I add a new user, I need to get all tasks and add them to the user's list of tasks.
This seems sort of clunky to me, and I feel like there must be a better way. If this is the best way to do it, I'm not quite sure what I would use to write this. I was thinking maybe use addFields or would Push be more appropriate? Or would that create arrays that grow without bound? which might not be the best idea since it's a Mongo anti-pattern from what I've read?
I also found this post that's sort of related, but it's from ~6 years ago, and I think I need a little bit more depth or code examples.
Any help would be much appreciated!
If you want to add new task in user whenever new task will created then your query should be :
taskCtr.create = (req, res) => {
const {
name,
description,
status
} = req.body;
TaskSchema.create({
name,
description,
status
}).then((result) => {
if (result) {
UserSchema.update({}, {
$push: {
tasks: {
_id: result._id,
status: result.status
}
}
}, {
multi: true
}).then((userUpdated) => {
res.status(200).json({
message: 'A new Task created successfully!'
})
}).catch((err) => {
res.status(500).json({
error: 'Internal server error!'
});
});
}
}).catch((err) => {
res.status(500).json({
error: 'Internal server error!'
});
});
};
Related
I have a group-based document application, an error is happening in the sharing part. The application is composed of the following parts: Groups, viewers, users and documents.
A group can have many viewers and users, a user can have many documents. I'm working on the part where viewers can see all documents of users associated with the group the viewer is associated with
My controller
router.get("link", async (req, res) => {
const group = await Group.find({ viewer: req.session.userId }).select("id")
console.log(group); // This console.log returns the id in a array: [ { _id: new ObjectId("6323a88670c0dd9aaa5017d2") } ]
console.log(group.id); // This console.log returns undefined
const user = await User.find({ group: group.id });
console.log(user); // Nothing because the group.id is undefined
const documents = await Document.find({
user: user.id,
});
return res.render("page", {
documents,
});
Group schema
name: {
type: String,
required: true,
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
viewer: [{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
}],
createdAt: {
type: Date,
default: Date.now,
}
I'm not able to retrieve the id from Group.find; what could be happening?
Because you want to have one value. So you can use findOne. Due to using findOne, you can reach group._id.
const group = await Group.findOne({ viewer: req.session.userId }).select("id")
console.log(group); { _id: new ObjectId("6323a88670c0dd9aaa5017d2") }
If you try to take the value from your array, you should take 0. element of array. Because it is an array and has elements. You are trying to reach element's id value.
But which element's id ? You need to declare it. Therefore, need to use group[0]._id. But if you want to reach just one element, using findOne() is better.
const group = await Group.find({ viewer: req.session.userId }).select("id")
console.log(group[0]); { _id: new ObjectId("6323a88670c0dd9aaa5017d2") }
I hope, it is clear and helps you. Good luck
I have two new collections in MongoDB of data that I pulled from an old Firestore database that I'm moving to mongo. Since the total number between these two collections is roughly 20,000, I opted to paste the raw JSON into the insert document section in mongo, which worked like a charm and I didn't have to write a new insert route to do the same.
I then created a schema in Mongoose that matched the inserted documents, and tried to use the schema to pull back some data, and its always returning nothing.
An example of a ticket inserted via JSON:
{
"title": "How to add and manage users for your company in QuickBooks Online",
"priority": "1",
"type": "Video",
"course": "G205",
"transcriptId": "07dom27Zz98jakvB1oh5",
"status": "In Review",
"tags": "",
"url": "",
"revisionNumber": 0,
"directoryId": 19,
"checkedOut": false
},
And my schema I made to match. The collection name in mongo is also called oldTickets, the plural of my schema name here:
const mongoose = require('mongoose');
var Schema = mongoose.Schema
const schema = new Schema({
course: { type: String },
title: { type: String },
priority: { type: String },
type: { type: String },
course: { type: String },
transcriptId: { type: String },
status: { type: String },
tags: { type: String },
url: { type: String },
revisionNumber: { type: Number },
directoryId: { type: Number },
checkedOut: { type: Boolean },
});
module.exports = mongoose.model('oldTicket', schema);
And finally my model import and fetch call:
const OldTicket = require('./models/model_old_ticket');
/***************************************************************************
* Get Old Tickets - Returns all old tickets, 10 at a time
****************************************************************************/
app.get('/getOldTickets/:offset', (req, res) => {
checkConnection();
OldTicket.find().skip(parseInt(req.params.offset)).limit(10).exec((err, data) => {
if (err){ res.status(500).send({err: err}); }
//If we got data, count the tickets & return the tickets & count
if (data) {
OldTicket.find().countDocuments().then(count => {
return res.status(200).send({
tickets: data,
count: count
})
})
}
});
});
Why isn't this finding anything? Both the count and the tickets are 0. I've run into this issue before when manually creating a collection without a schema, and in those instances I would simply delete the collection, write a route to create a document, and then things would work fine. But with the large data size of these two collections, I'd rather not do that since everything should be working as is.
Edit: Example of document in Mongo
And the name of the collection I'm currently viewing:
And I just now realized that for some reason there are now two collection names, oldTickets, which has data, and oldtickets, which is empty. I'm assuming my query is searching through the empty one? How can I get it to go to the one that actually has data?
can you attach the screenshot of your data with the collection? might be it's different.in mongoose, every collection name is complete with 's'. please verify your collection is created manually by you then it has to same as mongoose schema and also completed with 's'.
example:
const mongoose = require("mongoose");
const schema = new mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
index: true
},
filmId: {
type: mongoose.Schema.Types.ObjectId,
index: true
},
filmType: {
type: String,
index: true
},
birthday: {
type: Date
},
age: {
type: Number
},
terms: {
type: Boolean
}
},
{
versionKey: false,
timestamps: true,
}
);
schema.index({ filmId: 1, user: 1 })
module.exports = mongoose.model("UserAgeVerification", schema);
see my database
Instead of having sub-documents, as these schemas/models are queried a lot and independently, I decided to use "Ref" feature of Mongoose. The use case is a Job holding multiple Requests, so I have a Job schema and a Request schema like below..
const jobSchema = new Schema({
_id: {
type: Schema.Types.UUID,
},
status: {
type: String,
enum: _.keys(jobStatus),
},
requestIds: [
{
type: Schema.Types.UUID,
ref: 'Request',
},
],
});
and
const request = new Schema({
_id: {
type: Schema.Types.UUID,
},
jobId: {
type: Schema.Types.UUID,
ref: 'Job',
},
status: {
type: String,
enum: _.keys(requestStatus),
},
});
The request status are updated independently, but the job status is dependent on the completion of the request status. So, if all the requests are completed, i need to mark the job status as completed. In order to update the job to Completed, i need to look at if all the requests Ids are having the status to completed.
How do I do in Mongoose query/ update, preferably, a single call to the database.
i have a model that it has some json type attribute and now I want to use find on this json attribute. may please help how can i do it?
model:
module.exports = {
attributes: {
ownerInfo: {
type: 'json',
description: 'owner info of Task',
example: {id: '', fullName: '', emailAddress: ''}
},
name: {
type: 'string',
required: true,
description: 'Name of Task',
example: 'Business App'
},
users: {
type: 'json',
columnType: 'array',
description: 'Users id of task',
defaultsTo: [],
example: [{id :'', emailAddress: '', fullName: '', status: ['admin', 'leader', 'user']}]
}
}
}
actually i need 2 different query, one for finding ownerInfo.id and another else is finding user[].id.
in second query attribute is Array.
I searched a lot in Internet and tried many time with the queries like below but have not got any result.
tasks = await Task.find({
where: {
ownerInfo: {
id: params.id
}
}
})
for Array i had no any Idea how to search in Array of Object. please share your Idea.
tasks = await Task.find({
where: {
users: {
// how to search status of user ??
}
}
})
and also in MongoDb better I save short user info in task collection or like relative DB only keep the id of them and with populate reach that data? which method is faster and better as performance?
advanced thanks
I'm building a app where you can vote on user submitted polls. Here's the schema for my polls:
const pollSchema = new Schema({
userID: String,
pollQuestion: String,
pollOptions: [
{
name: String,
quantity: Number
}
]
})
This is the express route that handles incrementing the quantity of a poll option:
app.put('/vote', (req, res) => {
Poll.update(
{
id: req.body.id,
'pollOptions.name': req.body.selection
},
{ $inc: { 'pollOptions.$.quantity': 1 } }
).then(updatedPoll => {
console.log('poll', updatedPoll)
res.send(updatedPoll)
})
})
here req.body.selection is the option that the user has chosen to vote for.
I basically want the quantity of the user's chosen option to increment by one. This way of updating the database I derived from the answer to this question
When I send a request to this route I get some very unexpected behaviour. For one thing, the database doesn't get updated or changed in any way. The second thing is the updatedPoll is a really bizarre object. Here's what i get from the console.log:
poll { n: 0,
nModified: 0,
opTime:
{ ts: Timestamp { _bsontype: 'Timestamp', low_: 1, high_: 1510783687 },
t: 1 },
electionId: 7fffffff0000000000000001,
ok: 1 }
The one thing I've tried is include { new: true } as a third parameter after the object with the $inc operator. I doesn't change anything. Can someone please help with this issue?
Thanks to Neil Lunn in the comments, changing my route to the following worked for me:
poll.put('/vote', (req, res) => {
Poll.findOneAndUpdate(
{
_id: req.body.id,
pollOptions: {
$elemMatch: { name: req.body.selection }
}
},
{ $inc: { 'pollOptions.$.quantity': 1 } },
{ new: true }
).then(poll => {
res.send(poll)
})
})