Insert data into array of schema with NodeJS and mongoose - javascript

I have the next Schema:
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = Schema({
id: String,
name: [{
name: String,
surname1: String,
surname2: String,
}]
});
module.exports = mongoose.model('User', userSchema);
And I created the next function:
module.exports.signUp = function(req,res){
console.log('POST signUp');
let user = new User();
for(var key in req.body) {
console.log(key);
switch (key) {
case 'name[name]':
user.name.push({name:"name"});
break;
case 'name[surname1]':
user.name.push({surname1:"surname1"});
break;
case 'name[surname2]':
user.name.push({surname2:"surname2"});
break;
}
}
res.status(202).send({message: user});
}
I need the next results:
{
"message": {
"_id": "5b61e242c4874a045dd4185a",
"name": [
{
"name": "name",
"surname1": "surname1",
"surname2": "surname2"
}
]
}
}
And I get:
{
"message": {
"_id": "5b61e242c4874a045dd4185a",
"name": [
{
"_id": "5b61e242c4874a045dd4185b",
"name": "name"
},
{
"_id": "5b61e242c4874a045dd4185c",
"surname1": "surname1"
},
{
"_id": "5b61e242c4874a045dd4185d",
"surname2": "surname2"
}
]
}
}
It generates multiples _id and different JSON format.
I tried with user.name[0]="name", user.name.name="name" but It doesn't work...
When I tried user.name = {"name":"name"} works, but after that I put
user.name = {"surname1":"surname1"} and the name doesn't exists.
Can you help me please?
Thank you!

You're looping through req.body and pushing a key each time. You need to be doing it all at once. Something like
const name={};
if ('name[name]' in req.body){name.name=req.body['name[name]'];}
if ('name[surname1]' in req.body){name.surname1=req.body['name[surname1]'];}
if ('name[surname2]' in req.body){name.surname2=req.body['name[surname2]'];}
user.name.push(name);
Each entry in the name array will have a separate id though because they're declared as a subdocument in Mongo. You'll still be able to access the user by its id, but you'll also have access to each name object individually.
If each user just needs one name though, you can simplify the schema to
const userSchema = Schema({
id: String,
name: {
name: String,
surname1: String,
surname2: String,
}
});
and then when you have made the name object do
user.name=name;

Related

how to make an inner join with mongodb and mongoose and nestjs?

I have a system with mongodb genericized by mongoose in nest js how do I bring customer and product data in the same request
in the database the data is like this:
"_id": "621d2137abb45e60cf33a2d4",
"product_id": [
"621cedbf79d68fb4689ef0cb",
"621cedbf79d68fb4689ef0cb",
"621cedbf79d68fb4689ef0cb"
],
"client_id": "621d19f890ec693f1d553eff",
"price": 597,
"__v": 0
my serviçe:
findAll() {
return this.sealsModel.find().exec();
}
This way didn't work:
findAll() {
var a = this.sealsModel.find().exec()
return this.sealsModel.aggregate([{
$lookup: {
from: 'seals',
localField: 'client_id',
foreignField: '_id',
as: 'client'
}
}])
.exec()
}
but return this:
"_id": "621d3e0a1c3bcac85f79f7cc",
"product_id": [
"621cedbf79d68fb4689ef0cb",
"621cedbf79d68fb4689ef0cb",
"621cedbf79d68fb4689ef0cb"
],
"client_id": "621d19f890ec693f1d553eff",
"price": 597,
"__v": 0,
"client": []
An alternative to Mongo's $lookup operator is Mongoose's populate function. In order to use it properly, you first have to update your model.
You should have a client model that looks like this:
// /models/Client.js
const mongoose = require("mongoose");
const clientSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
... // the rest of your fields
});
module.exports = mongoose.model('Client', clientSchema);
The key thing to notice here is the last line. The model is referenced as "Client".
The change you have to make is mainly in your Seal model :
// /models/Seal.js
const mongoose = require("mongoose");
const sealSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
client_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Client" // We use the same name we gave to our client model in the last file
}
});
module.exports = mongoose.model('Seal', sealSchema);
Then, in your code, you should be able to use:
const Seal = require("YOUR_PATH/models/Seal.js")
Seal.find().populate("client_id")
One important side note: your model shows the mongo ID as a string
"_id": "621d3e0a1c3bcac85f79f7cc",
"client_id": "621d19f890ec693f1d553eff", <---
Make sure that the field client_id is of type ObjectId or it won't work:
"client_id": ObjectId("621d19f890ec693f1d553eff")

Array of {key: value} in mongoose schema

I have a comments field in my interface and schema as follows
export interface Blog extends Document {
...
comments: { user: ObjectId; comment: string }[]
...
}
const blogSchema = new Schema<Blog>({
...
comments: [
{
user: {
type: ObjectId
},
comment: {
type: String
}
}
],
...
})
However, schema throws this error 'ObjectId' only refers to a type but is being used as a value here.. I know that schema is written kind of in a weird way, but I'm confused with how to improve.
What I want in database is something like
[
{
user: "Vivid",
comment: "Vivid's comment"
},
{
user: "David",
comment: "David's comment"
}
]
I believe you need to change ObjectId to
mongoose.Schema.Types.ObjectId
Either that, or you could do something like:
const mongoose = require('mongoose');
// ... other imports
const { ObjectId } = mongoose.Schema.Types;
// ... now use in code as `ObjectId`

MongoDB Aggregate is not matching specific field

I'm new to Aggregation in MongoDB and I'm trying to understand the concepts of it by making examples.
I'm trying to paginate my subdocuments using aggregation but the returned document is always the overall values of all document's specific field.
I want to paginate my following field which contains an array of Object IDs.
I have this User Schema:
const UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true,
required: true
},
firstname: String,
lastname: String,
following: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}],
...
}, { timestamps: true, toJSON: { virtuals: true }, toObject: { getters: true, virtuals: true } });
Without aggregation, I am able to paginate following,
I have this route which gets the user's post by their username
router.get(
'/v1/:username/following',
isAuthenticated,
async (req, res, next) => {
try {
const { username } = req.params;
const { offset: off } = req.query;
let offset = 0;
if (typeof off !== undefined && !isNaN(off)) offset = parseInt(off);
const limit = 2;
const skip = offset * limit;
const user = await User
.findOne({ username })
.populate({
path: 'following',
select: 'profilePicture username fullname',
options: {
skip,
limit,
}
})
res.status(200).send(user.following);
} catch (e) {
console.log(e);
res.status(500).send(e)
}
}
);
And my pagination version using aggregate:
const following = await User.aggregate([
{
$match: { username }
},
{
$lookup: {
'from': User.collection.name,
'let': { 'following': '$following' },
'pipeline': [
{
$project: {
'fullname': 1,
'username': 1,
'profilePicture': 1
}
}
],
'as': 'following'
},
}, {
$project: {
'_id': 0,
'following': {
$slice: ['$following', skip, limit]
}
}
}
]);
Suppose I have this documents:
[
{
_id: '5fdgffdgfdgdsfsdfsf',
username: 'gagi',
following: []
},
{
_id: '5fgjhkljvlkdsjfsldkf',
username: 'kuku',
following: []
},
{
_id: '76jghkdfhasjhfsdkf',
username: 'john',
following: ['5fdgffdgfdgdsfsdfsf', '5fgjhkljvlkdsjfsldkf']
},
]
And when I test my route for user john: /john/following, everything is fine but when I test for different user which doesn't have any following: /gagi/following, the returned result is the same as john's following which aggregate doesn't seem to match user by username.
/john/following | following: 2
/kuku/following | following: 0
Aggregate result:
[
{
_id: '5fdgffdgfdgdsfsdfsf',
username: 'kuku',
...
},
{
_id: '5fgjhkljvlkdsjfsldkf',
username: 'gagi',
...
}
]
I expect /kuku/following to return an empty array [] but the result is same as john's. Actually, all username I test return the same result.
I'm thinking that there must be wrong with my implementation since I've only started exploring aggregation.
Mongoose uses a DBRef to be able to populate the field after it has been retrieved.
DBRefs are only handled on the client side, MongoDB aggregation does not have any operators for handling those.
The reason that aggregation pipeline is returning all of the users is the lookup's pipeline does not have a match stage, so all of the documents in the collection are selected and included in the lookup.
The sample document there is showing an array of strings instead of DBRefs, which wouldn't work with populate.
Essentially, you must decide whether you want to use aggregation or populate to handle the join.
For populate, use the ref as shown in that sample schema.
For aggregate, store an array of ObjectId so you can use lookup to link with the _id field.

Mongoose updateMany documents containing specific Array element

I've got a Mongoose schema set up as follows:
const mongoose = require('mongoose');
const TodoSchema = mongoose.Schema({
id: {
type: String,
required: true,
},
todos: {
type: Array,
required: true,
},
date: {
type: Date,
default: Date.now(),
},
});
module.exports = mongoose.model('todo', TodoSchema, 'todos');
Each of the elements in the todos Array is an Object and has the following format (example):
{
id: 1,
todo: "Do the dishes."
category: "Kitchen"
}
There are multiple documents in my Todo collection and they all contain the same list of Todos. If I wanted to update a specific Todo across ALL documents, I figure I need to use updateMany. I'm using the following in my Todo Update route to update all instances of a Todo:
const { todo } = req.body; // todo.todo contains "Clean the dishes." as an update
Todo.updateMany(
{
todos: { $elemMatch: { id: todo.id } },
},
{ $set: { todo: todo } }
);
I'm assigning the result of the above route code to a variable and console logging the result which comes back with:
{ ok: 0, n: 0, nModified: 0 }
What am I doing wrong? The passed todo id matches the id of a Todo in each of the Todos arrays.
First of all, for your object array, is recommendable create a schema too:
const subSchema = new mongoose.Schema({
id: Number,
todo: String,
category: String
})
const MongooseModel = new mongoose.Schema({
id: String,
todos: [subSchema],
date: Date
})
So now, your array object is defined.
And, the query question is something like that:
db.collection.update({
"todos.id": todo.id
},
{
"$set": {
"todos.$": {newTodo}
}
},
{
"multi": true
})
First, you look for all elements that match your criteria; that is: todos.id = todo.id, then you use $ operator to set all element that match the criteria with your object.
The last line multi is to updated all element that match.
Example playground here
Using moongoose, multi attribute is not neccessary because is set true by default using updateMany().
So moongose query should be something like that.
var update = await model.updateMany(
{
"todos.id": 1
},
{
"$set": {
"todos.$": {
"id": 20,
"todo": "newTodo",
"category": "newCategory"
}
}
})
And for this example data the result is
{ n: 3, nModified: 3, ok: 1 }

How to update array of key/value pairs into a mongoose?

Background: I have an array of objects sent from Vue Axios. These objects are key value pairs which is stored in the req.body.
Req.body
keyValue: [{key:"some key", value:"some value"}, {key:"some key2", value:"some value2"}]
Note: I can expect to receive a req.body with a numerous amount objects withing the array. I can not access the "key" and "value" in the objects without adding a [ ] or req.body.keyValue[0].
How can I dynamically add each object's "key" and "value" into mongoose without having to explicitly call a specific object?
I am trying to do something like this:(failed attempt)
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Schema = new Schema({
Pair: [{
Key: String,
Value: Number
}]
});
router.post("/", (req, res) => {
User.update({},
{$push:
{Pair:{
Key: req.body.keyValue.key,
Value: req.body.keyValue.value
}
}
},
(err,result)=>{
if(err){
console.log(err);
res.status(400).send('Error')
}else{
res.status(200).send(result);
}
})
}
I hope I was able to explain well enough. Let me know if there is any confusions. Thanks!
User Schema
userId: {
type: String
},
password: {
type: String
},
friends: [{
userId: String,
followSent: Boolean,
followAccepted: Boolean,
followbackSent: Boolean,
followbackAccepted: Boolean,
inchats: Boolean
}]
Update Code
userModel.updateOne({ userId: "MyId" , "friends.userId": "frndId"},{
$set: {
'friends.$.followSent': true}},(err, docs) => {
})
The point here is that when your call req.body.keyValue.key and req.body.keyValue.value, they are in a javascript array req.body.keyValue[].
Presuming that the req.body.keyValue will be always a valid array with the { key : '...', value : '...' } you can use the MongoDB $each operator to update your document.
As:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Schema = new Schema({
Pair: [{
key: String,
value: Number
}]
});
router.post("/", (req, res) => {
User.update(
{},
{
$push: {
Pair:{
$each : req.body.keyValue
}
}
},
(err,result)=>{
if(err){
console.log(err);
res.status(400).send('Error')
}else{
res.status(200).send(result);
}
}
);
}
Now just be careful that req.body.keyValue has the right capitalization on each element, so you don't have *K*ey and/or *k*ey, that will not match your schema. =]
Edit
Just to explain how the $each operator will work, see the following example:
req.body = {
keyValue : [
{ key : "key1", value : 1 },
{ key : "key2", value : 2 }
]
};
Document in User collection before the update:
{
_id : ObjectId(),
Pair : [
{ key : "key_A", value : 99 }
]
}
After the .update() with the $each operator, the expected updated document:
{
_id : ObjectId(),
Pair : [
{ key : "key_A", value : 99 },
{ key : "key1", value : 1 },
{ key : "key2", value : 2 }
]
}

Categories

Resources