I'm curious what the best way to structure my mongoose Schema is. I'm making a movie site with the User schema set like this:
let UserSchema = new mongoose.Schema({
username: {
type: String,
index: true
},
password: {
type: String
},
email: {
type: String
},
name: {
type: String
},
watchlist: [mongoose.Schema.Types.Mixed],
seen: {
like : {
type: [mongoose.Schema.Types.Mixed]
},
dislike: {
type: [mongoose.Schema.Types.Mixed]
}
},
});
When a user clicks either a thumbs up or thumbs down icon, an object of
{movieID: movieID, title: movieTitle, poster: moviePoster}
will be sent to the like or dislike array in the seen property. I'm going to use this info in my EJS template to determine if a movie has already been seen and if seen and liked then gray out the dislike button and if seen and disliked gray out the like button. I also want to be able to remove the item from seen if the use clicks a 'clear' button. In order to do that it seems like I'm going to have to loop over both arrays in order to determine if the movie is included in one and then remove it. Seems like there has to be a better way to do it. I was thinking of structuring the 'seen' object using the movieID as a key, like this:
seen: {
'123': {
movieID: '123',
title: 'movieA',
poster: 'movieA-poster.jpg',
like: true
},
'456' : {
movieID: '456',
title: 'movieB',
poster: 'movieB-poster.jpg',
like: false
}
}
But when I try to send the data to seen using .findOne and this function:
const movieObj = {
id: req.body.movieID,
title: req.body.movieTitle,
poster_path: req.body.poster_path,
};
User.findOne({_id: req.user._id}, function(err, user) {
if (err) {
console.log(err);
} else {
user.seen[req.body.movieID] = movieObj;
user.save(function(){});
}
}
I still just get an empty object returned to me. I changed the schema seen object to:
seen: { type: mongoose.Schema.Types.Mixed, default: {} }
and have set {minimize: false}, because I read that mongoose by default doesn't allow empty objects. Appreciate any guidance on what I'm doing wrong or if you have a better way to efficiently structure the schema to allow seen movies to easily be added or removed from the db.
I think mongoose populate will help you here.
Just read about it, you will get a good idea about it.
Here is a good link with an example:
https://hashnode.com/post/how-do-i-successfully-populate-a-mongoose-schema-cj339r6kt004g5mk83ycujilq
Please look this Models and queries that can help you do build your schemas.
1.User Schema
let UserSchema = new mongoose.Schema({
username: {
type: String,
index: true
},
password: {
type: String
},
email: {
type: String
},
name: {
type: String
},
watchlist: [mongoose.Schema.Types.Mixed],
like : [mongoose.Schema.Types.ObjectId], //remove
deslike : [mongoose.Schema.Types.ObjectId], //remove
seen : [{
movieId : { type: : mongoose.Schema.Types.ObjectId, ref: 'Movie' },
isStatus : {type : string} //Enum [like,dislike,'seen']
}]
})
2.Movie Schema
let movieSchema = new mongoose.Schema({
tile: {
type: String
},
description: { type: String }
})
3.Data store in Both Table
/user/
{
"_id" : ObjectId("5b5acaf0589ff6bfb5dd091f"),
"seen" : [
{
"movieId" : ObjectId("5b9e2544953b5f69683059d4"),
"isStatud" : "like"
},
{
"movieId" : ObjectId("5b9e2544953b5f69683059d6"),
"isStatud" : "dislike"
},
{
"movieId" : ObjectId("5b9e256d953b5f69683059ee"),
"isStatud" : "seen"
}
]
"like" : [
ObjectId("5b9e2544953b5f69683059d4"),
ObjectId("5b9e256d953b5f69683059ee")
],
"deslike" : [
ObjectId("5b9e2544953b5f69683059d6")
]
}
/movie/
{
"_id" : ObjectId("5b9e2544953b5f69683059d4"),
"title" : "movie1",
"description" : "desc1"
}
{
"_id" : ObjectId("5b9e2544953b5f69683059d6"),
"title" : "movie2",
"description" : "desc2"
}
{
"_id" : ObjectId("5b9e256d953b5f69683059ee"),
"title" : "movie3",
"description" : "desc3"
}
Query to get users by movie
/This is working fine and tested/.
db.getCollection('user').aggregate([{
$match : { _id : ObjectId("5b5acaf0589ff6bfb5dd091f") }
},
{$lookup : {
from : 'movie',
localField : 'like',
foreignField : '_id',
as : "likes"
}},
{$lookup : {
from : 'movie',
localField : 'deslike',
foreignField : '_id',
as : "deslikes"
}}
])
/Query with group/
db.getCollection('user').aggregate([{
$match : { _id : ObjectId("5b5acaf0589ff6bfb5dd091f") }
},
{$unwind : '$seen'},
{$lookup : {
from : 'movie',
localField : 'seen.movieId',
foreignField : '_id',
as : "seen.movieData"
}},
{$unwind : '$seen.movieData'},
{ $group: { "_id": "$_id",
"name" : { "$first": "$name" }, //use same all other field of user
"seen" : {"$push" : "$seen"}
} ,
}
])
Please check and let me know any help.
Related
I got this user schema
const UserSchema = new Schema({
email: {
type: String,
required: true,
unique: true,
},
groups: [
{
groupName: {
type: String,
required: true,
},
groupMembers: [{ type: Schema.Types.ObjectId, ref: "GroupMember" }],
},
],
});
And I want to delete a group from the ‘groups’ array based on given ‘userId’ and ‘groupId’, my attempt (with express and mongoose):
router.delete(
"/:userId/:groupId",
catchAsync(async (req, res) => {
const { userId, groupId } = req.params;
const updatedUser = await User.findByIdAndUpdate(
userId,
{ $pull: { "groups.$._id": groupId } },
{ new: true }
);
res.send({ updatedUser });
})
);
The response of the request: I get an error: “The positional operator did not find the match needed from the query.”
Edit:
After I delete a group I need to remove all the group members in the groupMembers array.
User collection structure example:
{
"_id" : "111",
"email" : "michael#gmail.com",
"username" : "michael098",
"groups" : [
{
"_id" : "222"
"groupName" : "family",
"groupMembers" : [
{
"_id" : "333"
"name" : "Liam"
},
{
"_id" : "444"
"name" : "Noah"
}
]
},
{
"_id" : "555"
"groupName" : "friends",
"groupMembers" : [
{
"_id" : "666"
"name" : "Oliver"
}
]
}
]
}
Inside every group there is group members and I have a collection for the group members that I ref in the UserSchema : groupMembers: [{ type: Schema.Types.ObjectId, ref: "GroupMember" }]
GroupMember collection structure example:
{
{
"_id" : "333"
"name" : "Liam"
},
{
"_id" : "444"
"name" : "Noah"
},
{
"_id" : "666"
"name" : "Oliver"
}
}
For example when I get the params of userId="111" and groupId="222" I will delete the whole 'family' group and the whole group members in the groupMembers array (Liam and Noah) from the GroupMember collection.
GroupMember collection after deleting the group with _id="222":
{
{
"_id" : "666"
"name" : "Oliver"
}
}
Assuming an actual doc might look like this (using strings instead of ObjectId to keep the example tighter):
{_id:1, groups: [ { type: "ID1", ref: "XX" }, { type: "ID2", ref: "G1" }, { type: \
"ID3", ref: "G2" } ] }
then this update will remove the subdoc in the groups array where _id = 1 and type = ID2:
db.foo.update({_id:1},
{ $pull: { groups: { type: "ID2" } }}
);
This question already has answers here:
How to push an array of objects into an array in mongoose with one call?
(4 answers)
Closed 3 years ago.
I'm using Angular 7 with node.js, express and mongoose
I have a problem when adding objects to my mongodb via mongoose, it saves only "_id": ObjectId('randomID') instead of the values i want.
I'm trying to add values to an existing document in my collection with nested arrays.
To be more precise: there are customers with a customer number, customer name and domains.
Domains shall be an array with a domain.
A domain has a name value and two more arrays (called "in" & "out")
both arrays contain a graph array with nodes and links.
nodes have 3 values (id, label, description) and links have also 3 values (source, target, label)
First Problem I have, is that I cant add new domain to my domains array
I don't understand what am I am doing wrong??
Or what is best practice to insert and/or update nested arrays inside nested arrays?
edit: I try to understand, why am I saving only the ObjectId() with no values, linked question doesn't work for me.
here is an Example of what i want in my db:
{
"_id" : ObjectId("5c76a093aac6fa3f140a5672"),
"KdNr" : "10004",
"Kundenname" : "Customer GmbH",
"__v" : 0,
"domains" : [ {
"domain" : "testB.de",
"in" : [ {
"content" : "New Graph B",
"graph" : { ... } } ],
"out" : [ {
"content" : "Another new Graph B",
"graph" : { ... } } ]
}, [ {
"domain" : "testA.de",
"in" : [ {
"content" : "New Graph A",
"graph" : { ... } } ],
"out" : [ {
"content" : "Another new Graph A",
"graph" : { ... } } ]
} ]
}
here is an Example of what i get (not what I want):
{
"_id" : ObjectId("5c76a093aac6fa3f140a5672"),
"KdNr" : "10004",
"Kundenname" : "Customer GmbH",
"__v" : 0,
"domains" : [ {
{ "_id" : ObjectId("5c7f86ad42d63141fc921d04") },
{ "_id" : ObjectId("5c655c828be0b2b295aa126f") }
] }
here is my Schema:
const mongoose = require('mongoose');
const graphSchema = mongoose.Schema({
graph: {
nodes: [{
id: { type: String, required: true, unique: true },
label: { type: String, required: true },
description: { type: String }
}],
links: [{
source: { type: String, required: true },
target: { type: String, required: true },
label: { type: String }
}]
}
});
const domainSchema = mongoose.Schema({
domain: {
name: { type: String, unique: true, required: true },
in: {
content: { type: String },
graphSchema
},
out: {
content: { type: String },
graphSchema
}
}
});
const diagramSchema = mongoose.Schema({
KdNr: { type: String, required: true, index: true, unique: true },
Kundenname: { type: String, required: true },
domains: [{
domainSchema
}]
});
module.exports = mongoose.model('Diagram', diagramSchema);
here is my domains-routes.js:
// core Modules
const express = require('express');
// own modules
const Diagram = require('../models/diagrams');
const router = express.Router();
// add Domain to Diagram
router.put('/:KdNr', (req, res, next) => {
console.log(req.body.domain);
const data = {
domains : [{
domain: req.body.domain,
in: [{
content : "New Graph",
graph : {}
}],
out: [{
content : "New Graph",
graph : {}
}]
}]
}
Diagram.updateOne(
{ KdNr: req.params.KdNr },
{ $push: data }
).then(result => {
if(result) {
console.log('Diagram found in database:');
console.log(result);
res.status(200).json({ message: 'Diagram saved' });
} else {
res.status(404).json({ message: 'Diagram for customer: '
+ req.params.KdNr + 'not found!'})
}
})
})
module.exports = router;
Try this in
Diagram.updateOne
{ $push:{"domains": data }}
Given the following document in my DB:
{
"_id" : ObjectId("5c03b36d13032cc192d33f84"),
"type" : "Feature",
"properties" : {
"name" : "point 1",
"amenity" : "test",
"popupContent" : "test"
},
"geometry" : {
"type" : "Point",
"coordinates" : [
[ -3.68814468383789,
40.5248912033234]
]
}
}
And given the following mongoose query:
function findVertex(req,res){
Vertex2.find({"_id":"5c03b36d13032cc192d33f84"}, function(err,obj) {
if(err){
res.status(500).send(error);
}else{
var geoJson=obj[0];
console.log(geoJson)
res.status(200).send(geoJson);
}
})
}
What I receive is:
{ properties: { name: 'point 1', amenity: 'test', popupContent: 'test' },
_id: 5c03b36d13032cc192d33f84,
type: 'Feature' }
Why geometry is not in the response? If I do the same query in robomongo I have the object with the coordinates.
Base of mongoDb GeoJSON object documnet your point geometry structure is incorrect, coordinates must be an array with 2 element, not an array that contain another array
At the end I have fixed it changing the model schema.
Initially I had this:
_id:String,
type:String,
properties:{
name:String,
amenity:String,
popupContent:String
},
geometry:{
type:String,
coordinates:[]
}
Now I have this:
type:String,
properties:{
name:String,
amenity:String,
popupContent:String
},
geometry:{
type: {type: String, default: 'MultiPoint'},
coordinates: {type: []}
}
And I have the desired object in return:
{"properties":{"name":"Coors Field","amenity":"Baseball Stadium","popupContent":"This is where the Rockies play!"},"geometry":{"type":"MultiPoint","coordinates":[[-3.68814468383789,40.5248912033234],[-3.70814468383789,40.5248912033234],[-3.68230819702148,40.5074040815359]]},"_id":"5c03a3da82822133f0406667","type":"Feature","__v":0}
I want to create a subdocument in a subobject field, not to update.
My Schema:
var DemandeSchema = new Schema({
titre: {
type: String,
required: true
},
description: {
type: String,
required: true
},
type: {
type: String,
required: true
},
answer: {}
});
My code:
demande.update(
{ name: 'answer' },
{ $push: req.body.answer },
{ upsert: true },
function(error, user) {
if (error) return next(error);
else {
return true;
}
}
)
req.body.answer = {
"id": "57f512f4360d8818a4e5ea3d",
"answer": {
"122547eee99" : {
"review" : "1.3",
"login" : "new"
}
}
}
But this code doesn't create a new field in my DB, it just updates the field answer when I just want to create a new object field in the answer field.
Actual Result:
{
"_id" : ObjectId("57f512f4360d8818a4e5ea3d"),
"titre" : "TEST",
"description" : "ee",
"type" : "ee",
"__v" : 0,
"answer" : {
"122547eee98" : {
"review" : "8.8",
"login" : "x"
}
}
}
Expected Result:
{
"_id" : ObjectId("57f512f4360d8818a4e5ea3d"),
"titre" : "TEST",
"description" : "ee",
"type" : "ee",
"__v" : 0,
"answer" : {
"122547eee98" : {
"review" : "8.8",
"login" : "x"
},
"122547eee99" : {
"review" : "1.3",
"login" : "new"
}
}
}
var DemandeSchema = new Schema({
titre: {
type: String,
required: true
},
description: {
type: String,
required: true
},
type: {
type: String,
required: true
},
answer: []
});
Answer field curly braces would convert to square brackets for pushing all new answers.
Conclusion: It creates an array.
Instead of the $push operator which works on arrays, use the $set operator together with the dot notation to set the subdocument in the embedded answer document.
You would need to preprocess the document to use in your update so that it will have the dot notation. The following mongo shell example demonstrates this:
var obj = {
"id": "57f512f4360d8818a4e5ea3d",
"answer": {
"122547eee99" : {
"review" : "1.3",
"login" : "new"
}
}
},
update = {};
var key = Object.keys(obj.answer)[0]; // get the dynamic key "122547eee99"
update["answer."+key] = obj.answer[key]; // create the update object with dot notation
/*
update = {
"answer.122547eee99": {
"review" : "1.3",
"login" : "new"
}
}
*/
db.demandes.update(
{ "_id" : ObjectId(obj.id)},
{ "$set": update },
{ "upsert": true }
)
Using the same concept as above, you can create the documents to use in your update as follows:
var update = {},
key = Object.keys(req.body.answer.answer)[0]; // get the dynamic key "122547eee99"
// create the update object with dot notation
update["answer."+key] = req.body.answer.answer[key];
demande.update(
{ "_id": req.body.answer.id },
{ $set: update },
{ upsert: true },
function(error, user) {
if (error) return next(error);
else {
return true;
}
}
);
Try this, and in schema answer: [],
demande.findOne( { name: 'answer' }, function(err, result){
result.answer.push({ans:req.body.answer})
var dem = new Demande(result); // Demande is ur mongoose schema model,
dem.save(function(err, result){
console.log(result);
});
})
This question already has an answer here:
How to add data to array in Mongoose Schema
(1 answer)
Closed 6 years ago.
I have just started using node with express framework and mongo as a database.
I created the schema like this :
var JsonSchema = new Schema({
type: String,
properties: {
OBJECTID: Number,
AREA: Number,
PERIMETER: Number,
ESYE_CODE: Number,
Descriptio: String
},
geometry: {
type: String,
coordinates: [Number, Number]
}
});
and then I query :
router.get('/mapjson/:OBJECTID', function(req, res) {
if(req.params.OBJECTID) {
Json.findOne({OBJECTID: req.params.OBJECTID }, {}, function(err, docs){
res.json(docs);
} else {
console.log("THERE WAS AN ERROR HERE!!!");
}
});
But the results I get are missing the geometry field.
Sample of results I get :
{
"_id":"57e43ec60534d33ccc13099b",
"type":"Feature",
"properties":{
"OBJECTID":212428,
"AREA":131.001991421,
"PERIMETER":49.9141344212,
"ESYE_CODE":147,
"Descriptio":"Ελληνικά"
}
}
and what I get when I query the collection from mongo shell :
db.points.findOne({'properties.OBJECTID': 212428})
{
"_id" : ObjectId("57e43ec60534d33ccc13099b"),
"type" : "Feature",
"properties" : {
"OBJECTID" : 212428,
"AREA" : 131.001991421,
"PERIMETER" : 49.9141344212,
"ESYE_CODE" : 147,
"Descriptio" : "Ελληνικά"
},
"geometry" : {
"type" : "Point",
"coordinates" : [
23.812561006040106,
38.093951650544334
]
}
}
In coordinates you can set type as Array:
geometry: {
type: String,
coordinates: Array
}