Mongoose Insert many to one - node.js

I need to help!
I'm creating a website with nodejs and mongo for learning.
I have a problem that I know the best way to do it.
I have two collections codes and tag into table codes I have the tags field is array of tags.
CodeModel:
var CodeSchema = new Schema({
title: { type: 'String', required: true },
text: { type: 'String', required: true },
url: { type: 'String', required: true },
uri: String,
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now },
owner: {
type: Schema.ObjectId,
ref: 'User'
},
tags: [
{
type: Schema.ObjectId,
ref: 'Tag'
}
]
});
CodeSchema.pre("save", function (next) {
// if create for first time
if (!this.created_at) {
this.created_at = Date.now();
}
next();
});
module.exports = mongoose.model('Code', CodeSchema);
And My Tag Model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TagSchema = new Schema({
name: 'string'
});
module.exports = mongoose.model('Tag', TagSchema);
when I get the result in my rest I got it:
[
{
"_id": "5540f557bda6c4c5559ef638",
"owner": {
"_id": "5540bf62ebe5874a1b223166",
"token": "7db8a4e1ba11d8dc04b199faddde6a250eb8a104a651823e7e4cc296a3768be6"
},
"uri": "test-save",
"url": "http://www.google.com.br/",
"text": " hello ",
"title": "testing...",
"__v": 0,
"tags": [
{
"_id": "55411700423d29c70c30a8f8",
"name": "GO"
},
{
"_id": "55411723fe083218869a82d1",
"name": "JAVA"
}
],
"updatedAt": "2015-04-29T15:14:31.579Z",
"createdAt": "2015-04-29T15:14:31.579Z"
}
]
This I populate into database, I don't know how I insert it, is there any way automatic with mongoose that to do it or I need to create by myself?
I am testing with this json:
{
"url": "http://www.google.com.br/",
"title": "Test inset",
"text": "insert code",
"tags": [
"ANGULAR",
{
"_id": "55411700423d29c70c30a8f8",
"name": "GO"
}
]
}
I need to do a insert of tags, if I have id or not. Do I need to create it or has way to do it automatically?
and how can I do it?
Sorry my english =x

Generally speaking to create and save a document in a mongo database using mongooseJS is fairly straightforward (assuming you are connected to a database):
var localDocObj = SomeSchemaModel(OPTIONAL_OBJ); // localDocObj is a mongoose document
localDocObj.save(CALLBACK); // save the local mongoose document to mongo
If you have an object that is of the same form as the schema, you can pass that to the constructor function to seed the mongoose document object with the properties of the object. If the object is not valid you will get an invalidation error passed to the callback function on validate or save.
Given your test object and schemas:
var testObj = {
"url": "http://www.google.com.br/",
"title": "Test inset",
"text": "insert code",
"tags": [
"ANGULAR",
{
"_id": "55411700423d29c70c30a8f8",
"name": "GO"
}
]
};
var codeDoc = Code(testObj);
codeDoc.save(function (err, doc) {
console.log(err); // will show the invalidation error for the tag 'Angular'
});
Since you are storing Tag as a separate collection you will need to fetch/create any tags that are string values before inserting the new Code document. Then you can use the new Tag documents in place of the string values for the Code document. This creates an async flow that you could use Promises (available in newer node releases) to manage.
// Create a promise for all items in the tags array to iterate over
// and resolve for creating a new Code document
var promise = Promise.all(testObj.tags.map(function(tag) {
if (typeof tag === 'object') {
// Assuming it exists in mongo already
return tag;
}
// See if a tag already exists
return Tag.findOne({
name: tag
}).exec().then(function(doc) {
if (doc) { return doc; }
// if no tag exists, create one
return (Tag({
name: tag
})).save(); // returns a promise
});
})).then(function(tags) {
// All tags were checked and fetched/created if not an object
// Update tags array
testObj.tags = tags;
// Finally add Code document
var code = Code(testObj);
return code.save();
}).then(function(code) {
// code is the returned mongo document
console.log(code);
}).catch(function(err) {
// error in one of the promises
console.log(err);
});

You can do it like
var checkNewTagAndSave = function(data, doc, next){ // data = req.body (your input json), doc = mongoose document to be saved, next is the callback
var updateNow = function(toSave, newTags){
// save your mongoose doc and call the callback.
doc.set(toSave);
doc.save(next);
};
var data = req.body;
var tagsToCreate = [];
var tagids = [];
data.tags.forEach(function(tag, index){
if(typeof(tag) == 'string') {
tagsToCreate.push({ name: tag });
} else tagids.push(tag._id);
});
data.tags = tagids;
if(tagsToCreate.length === 0) updateNow(data);
else {
mongoose.model('tag').create(tagsToCreate, function(err, models){
if(err || !models) return next(err);
else {
models.forEach(function(model){
data.tags.push(model._id);
});
updateNow(data, models);
}
});
}
};
Hope code is reflecting its logic itself
usage :
after you have found your Code document say aCode
just call
checkNewTagAndSave(req.body, aCode, function(err, doc){
//end your response as per logic
});

Related

Mongoose and Postman: test a model with nested objects

I created a model like this in nodeJS, using Mongoose:
'use strict';
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var plantSchema = new Schema({
plantData: [
{
family: { type: String, default: 'Liliaceae' },
genusObj: {
genus: { type: String, required: 'Please enter the genus plant name' },
tulipGroup: { type: String }, // e.g. Single Early
tulipGroupNumber: { type: Number } // e.g. 1
},
species: { type: String, required: 'Please enter the species plant name' },
commonName: { type: String },
description: { type: String },
mainImage: {},
otherImages: {},
images: {},
}
],
detailsData: [ .... ]
});
module.exports = mongoose.model('plants', plantSchema);
And this is my controller:
var mongoose = require('mongoose'),
Plant = mongoose.model('plants');
// READ ALL
exports.list_all_plants = function(req, res) {
Plant.find({}, function(err, plants) {
if (err) {
res.send(err);
}
res.json(plants);
});
};
// CREATE
exports.create_new_plant = function(req, res) {
var new_plant = new Plant(req.body);
new_plant.save(function(err, plant_inserted) {
if (err) {
res.send(err);
}
res.json(plant_inserted);
});
};
// READ (probably plantId comes from an _id previously retrieved)
exports.read_a_plant = function(req, res) {
Plant.findById(req.params.plantId, function(err, plant_searched) {
if (err) {
res.send(err);
}
res.json(plant_searched);
});
};
// UPDATE
exports.update_a_plant = function(req, res) {
Plant.findOneAndUpdate(
{
_id: req.params.plantId
},
req.body,
{new: true},
function(err, plant_to_update) {
if (err) {
res.send(err);
}
res.json(plant_to_update);
}
);
};
// DELETE
exports.delete_a_plant = function(req, res) {
Task.remove(
{
_id: req.params.plantId
},
function(err, task) {
if (err) {
res.send(err);
}
res.json({ message: 'Plant successfully deleted' });
}
);
};
And finally, i have this router:
'use strict';
module.exports = function(app) {
var plantList = require('../controllers/plantController');
// plant routes
app.route('/plants')
.get(plantList.list_all_plants)
.post(plantList.create_new_plant);
app.route('/plants/:plantId')
.get(plantList.read_a_plant)
.put(plantList.update_a_plant)
.delete(plantList.delete_a_plant);
What I'd like to do is testing all this with Postman.
If I try with the GET method, using simply
http://localhost:3000/plants
everything works fine: I mean, it returns an empty array (mongodb is up and running, and everything is set).
Now I wanted to try to insert a new element with Postman: I selected POST and x-www-form-urlencoded under body. Required properties are plantData{genusObj{genus}} and plantData{species} : since I'm quite new with both postman and mongodb, how can I enter a sub-element in postman, to create a new Plant ?
there are only KEY and VALUE options, and i don't know how to write a sub-key like plantData->genusObj->genus.
P.S.: Suggestions on data model are welcome, I tried to build a generic plant database but oriented on tulips (so usually i can enter tulips, but if i need to enter something else, i can).
Well, it seems that this answer fits to me: in fact, on Postman i selected under "body" the "raw" option, then I selected JSON instead of TEXT from the dropdown menu, and finally I used this object (meanwhile I slightly changed the
model) - don't forget the " symbols everywhere, like I did - ' is not accepted:
{
"plantData": [
{
"family": "Liliaceae",
"genusObj": {
"genus": "Tulipa",
"tulipGroup": "Single Late",
"tulipGroupNumber": 5
},
"species": "TEST",
"sellName": "Queen of night",
"description": "black tulip",
"mainImage": "",
"otherImages": "",
"images": ""
}
],
"sellingData": [
{
"price": 0.50,
"availableQuantity": 100
}
],
"detailsData": [
{
"heightInCm": "60-65",
"floweringTime": "late spring",
"plantDepthCm": "20",
"plantSpacingCm": "10",
"bulbSizeInCm": "12",
"flowerColor": "Black",
"lightRequirements": "full sun"
}
]
}

Can't push items in mongo array

I can't push items into MongoDB array every time that i try to push a new element it creates an empty object and i cant figure out why,
I already used the
Collection.Array.push({element})&
Collection.save()
but i cant figure out a solution
This is My Schema
const Schema = mongoose.Schema;
var ParticipantSchema = new Schema({
nom:{Type:String},
prenom:{Type:String},
email:{Type:String}
})
var CompetitionSchema = new Schema({
nom:String,
date:Date,
place:String,
participant :[ParticipantSchema]
})
module.exports = mongoose.model("Competition",CompetitionSchema);
This is my funtion
exports.addParticipant=function(req,res){
var newParticipant={
"nom":req.body.nom,
"prenom":req.body.prenom,
"email":req.body.email
}
Competition.updateOne(
{ _id:req.body.id},
{ $push: { participant: newParticipant } },
(err,done)=>{
return res.json(done)
}
);
}
the result is always an empty object like below
{
"_id": "5ded0eeb85daa100dc5e57bf",
"nom": "Final",
"date": "2019-01-01T23:00:00.000Z",
"place": "Sousse",
"participant": [
{
"_id": "5ded0eeb85daa100dc5e57c0"
},
{
"_id": "5dee3c1b08474e27ac70672e"
}
],
"__v": 0
}
There is no problem in your code, the only problem is that in schema definition you have Type, but it must be type.
If you update your ParticipantSchema like this, it will work:
var ParticipantSchema = new Schema({
nom: { type: String },
prenom: { type: String },
email: { type: String }
});
You are using another Schema in the Array. This results in so-called subdocuments (https://mongoosejs.com/docs/subdocs.html). Mongoose does not populate subdocuments by default. So all you see is just the _id. You can use the populate method to see all subdocuments in detail. ( https://mongoosejs.com/docs/populate.html ) .
Example :
Competition.
find({}).
populate('participant').
exec(function (err, comps) {
//
});
You can either use populate on the Model or on the Document. For populating a document, take a look at https://mongoosejs.com/docs/api.html#document_Document-populate . There is also a auto-populate plugin available via npm but in most cases it's not necessary : https://www.npmjs.com/package/mongoose-autopopulate .

Check if user id is in mongoose document - if yes, change property value to true?

I have an expressjs router that looks in MongoDB collection using the mongoose findById method. It returns an object where inside there exist an userHasSelected array with users id. I dont want to return users id, but just check if current users (the one who made the request) exist in the array. If he is then return true instead of returning the user id.
The verifytoken middleware in the router adds a user id property to the request.That user id is available in the get router message - can i somehow pass that to the Mongoose schema ???
//My router
router.get('/challenge/:challengeId', verifyToken ,function (req, res){
//+ Return one challenge and its options
//- Check if userId is set to options and winner properties
let userId = req.userId;
console.log(userId);
let challengeId = req.params.challengeId;
Challenge.findById(challengeId, (err, suc)=>{
if(err){
res.status(304).send(err);
}
Challenge.
res.status(200).send(suc);
});
})
// And the mongoose Schema
const mongoose = require('mongoose');
var Schema = mongoose.Schema;
//Optionsschema is use in the ChallengeSchema
var OptionSchema = new Schema({
name: { type: String},
isCorrect : { type: Boolean },
description: { type: String },
image : { type : String },
userHasSelected : { type : Object, get : returnUserChallengeStatus}
})
OptionSchema.set('toObject', { getters: true });
OptionSchema.set('toJSON', { getters: true });
var ChallengeSchema = new Schema({
shortEventId : String,
organization: String,
name: String,
winner: String,
options : [OptionSchema]
});
ChallengeSchema.set('toObject', { getters: true });
ChallengeSchema.set('toJSON', { getters: true });
ChallengeSchema.virtual('born').get(function(value) {
return this.name + "plus andet"
});
module.exports = mongoose.model('challenge', ChallengeSchema);
So again - I dont want to return the user id from the userHasSelected array - just check if he is there and if yes, use a getter or a method to set value to true.
Updated explanation
The findById returns this object / document
{
"_id":"5b86dc5bfb6fc03893e55001",
"shortEventId": "d2d3",
"organization": "Braedstrup",
"name": "1. december",
"winner": "5b864cbaa9ce291b148ddd6d",
"options": [
{
"name": "Matas",
"isCorrect": "true",
"description": "Matas er byens førende indenfor pleje og Matas er byens førende indenfor pleje og omsorg Matas er byens førende indenfor pleje og omsorg",
"image": "https://cityxpstorage.blob.core.windows.net/images/Matas.png",
"userHasSelected": [
{
"userId": "5b864cbaa9ce291b148ddd6d"
}
]
},
{
"name": "Føtex",
"isCorrect": "false",
"description": "Føtex er en dejlig butik",
"image": "https://cityxpstorage.blob.core.windows.net/images/Føtex.png"
},
{
"name": "Kvickly",
"isCorrect": "false",
"description": "Kvickly er en dejlig butik",
"image": "https://cityxpstorage.blob.core.windows.net/images/Matas.png"
},
{
"name": "MC Jørgensen",
"isCorrect": "false",
"description": "MC Jørgensen er en dejlig butik",
"image": "https://cityxpstorage.blob.core.windows.net/images/Matas.png"
}
],
"startDate": "2018-10-06T00:00:00.000Z",
"endDate": "2018-10-06T23:59:00.000Z"
}
So the nested array 'userHasSelected' contains information about the users id. I do not want to send that - instead I would like to a {userId : true}.
I have read that getters a able to handle outgoing data.
Posible Solution
I could make the check inside the router get method before returning the object to the client like this
// If user is in array set user to true. I would like to move this responsibility to the schema / document.
suc.options.forEach(option => {
if(Array.isArray(option.userHasSelected))
option.userHasSelected = {userId : true}
});
But I would really like schema to be responsible for that - Is that possible?
I had similar issue and found a workaround. Simply create an optional field on your responsible modal schema, let call it "status". On your controller, check if your array includes requested user's id and write to that field. For example;
on your schema;
caseStatus: {
type: Boolean,
required: false
},
voters: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
}],
then on your controller;
let theCase = await Case.find({speciality: res.locals.user_speciality }).exec();
let status = theCase.voters.includes(res.locals.user_id);
caseItem.caseStatus = status;

How two select two column value as key value pair in mongoose using expressjs

i have the schema is like below
Resource.js
var mongoose = require("mongoose"),
Schema = mongoose.Schema,
objectId = mongoose.Schema.ObjectId;
var lableShema = new Schema({
labelName: { type: String },
language: { type: String, },
resourceKey: { type: String, },
resourceValue: { type: String, }
}, {
versionKey: false
});
var lableShema = mongoose.model('LabelKeyResource', lableShema);
module.exports = lableShema;
in db i have the data like this,
{
"_id": "59b1270b4bb15e1358e47cbd",
"labelName": "submit",
"__v": 0,
"resourceKey": "submit_btn",
"resourceValue": "Submit",
"language": "engilish"
}
i'm using the select function is
lableResource.find({ language: req.params.ln}, function (err, data) {
if (err) {
res.send(err);
return;
}
res.send(data);
but i want this format how to that...
{"submit_btn":"Submit","select_lbl":"Please Select"}
You can format the data after getting the data from Mongo.
This is how you can do it:
var obj = {
[data.resourceKey]: data.resourceValue,
select_label: "Please Select"
};
This will give you the object: {"submit_btn":"Submit","select_lbl":"Please Select"}

create a new object Id in mongoDB using node js

I am using the below code to insert data to mongodb
router.post('/NewStory', function (req, res) {
var currentObject = { user: userId , story : story , _id:new ObjectID().toHexString() };
req.db.get('clnTemple').findAndModify({
query: { _id: req.body.postId },
update: { $addToSet: { Stories: currentObject } },
upsert: true
});
});
This code is working fine if i remove the _id:new ObjectID().toHexString()
What i want to achieve here is that for every new story i want a unique _id object to be attached to it
What am i doing wrong?
{
"_id": {
"$oid": "55ae24016fb73f6ac7c2d640"
},
"Name": "some name",
...... some other details
"Stories": [
{
"userId": "105304831528398207103",
"story": "some story"
},
{
"userId": "105304831528398207103",
"story": "some story"
}
]
}
This is the document model, the _id that i am trying to create is for the stories
You should not be calling .toHexString() on this as you would be getting a "string" and not an ObjectID. A string takes more space than the bytes of an ObjectId.
var async = require('async'),
mongo = require('mongodb'),
db = require('monk')('localhost/test'),
ObjectID = mongo.ObjectID;
var coll = db.get('junk');
var obj = { "_id": new ObjectID(), "name": "Bill" };
coll.findAndModify(
{ "_id": new ObjectID() },
{ "$addToSet": { "stories": obj } },
{
"upsert": true,
"new": true
},
function(err,doc) {
if (err) throw err;
console.log(doc);
}
)
So that works perfectly for me. Noting the "new" option there as well so the modified document is returned, rather than the original form of the document which is the default.
{ _id: 55c04b5b52d0ec940694f819,
stories: [ { _id: 55c04b5b52d0ec940694f818, name: 'Bill' } ] }
There is however a catch here, and that is that if you are using $addToSet and generating a new ObjectId for every item, then that new ObjectId makes everything "unique". So you would keep adding things into the "set". This may as well be $push if that is what you want to do.
So if userId and story in combination already make this "unique", then do this way instead:
coll.findAndModify(
{
"_id": docId,
"stories": {
"$not": { "$elemMatch": { "userId": userId, "story": story } }
}
},
{ "$push": {
"stories": {
"userId": userId, "story": story, "_id": new ObjectID()
}
}},
{
"new": true
},
function(err,doc) {
if (err) throw err;
console.log(doc);
}
)
So test for the presence of the unique elements in the array, and where they do not exist then append them to the array. Also noting there that you cannot do an "inequality match" on the array element while mixing with "upserts". Your test to "upsert" the document should be on the primary "_id" value only. Managing array entries and document "upserts" need to be in separate update operations. Do not try an mix the two, otherwise you will end up creating new documents when you did not intend to.
By the way, you can generate an ObjectID just using monk.
var db = monk(credentials.database);
var ObjectID = db.helper.id.ObjectID
console.log(ObjectID()) // generates an ObjectID

Resources