selecting data from several collections after finding the proper id in documents - node.js

I have an almost working solution, but I believe my solution is bad programmed and I don't know how to make it better. Maybe it should be done with mongoose population, but I can't figure out how it works and how to adjust my code.
I have 2 collections: author and books. They are imported from MySQL with data - so I can't change the structure.
author:
{ "_id" : ObjectId("59492addd80eb0f9c1b42fd9"), "id_a" : 1, "name" : "Agatha Christie", "gender" : "female", "born" : 1890, "birthplace" : "England", "genre" : "crime"
}
{ "_id" : ObjectId("594935e1d80eb0f9c1b42fdb"), "id_a" : 2, "name" : "Stephen King", "gender" : "male", "born" : 1947, "birthplace" : "U.S.", "genre" : "horror" }
books:
{ "_id" : ObjectId("59492cd1d80eb0f9c1b42fda"), "id_b" : 1, "title" : "Murder on the Orient Express", "id_a" : 1, "pub_date" : 1934, "publisher" : "Collins Crime Club",
"pages" : 256, "description" : "Hercule Poirot, the internationally famous detective, boards the Orient Express (Simplon-Orient-Express) in Istanbul. The train is unus
ually crowded for the time of year. Poirot secures a berth only with the help of his friend Monsieur Bouc, a director of the Compagnie Internationale des Wagons-Lits. W
hen a Mr. Harris fails to show up, Poirot takes his place. On the second night, Poirot gets a compartment to himself..." }
{ "_id" : ObjectId("59493779d80eb0f9c1b42fdc"), "id_b" : 2, "title" : "The A.B.C. Murders", "id_a" : 1, "pub_date" : 1936, "publisher" : "Collins Crime Club", "pages" :
256, "description" : "The novel follows the eponymous murders and their investigation as seen by Arthur Hastings, Poirot's old friend. Poirot receives typed letters si
gned by A.B.C. In each is given the date and location of the next murder. A.B.C. moves alphabetically: Alice Ascher is a tobacco shop owner killed in her shop in Andove
r, Betty Barnard is a flirty waitress killed in Bexhill, and Sir Carmichael Clarke is a wealthy man killed at his home in Churston..." }
{ "_id" : ObjectId("59493858d80eb0f9c1b42fdd"), "id_b" : 3, "title" : "The Shining", "id_a" : 2, "pub_date" : 1977, "publisher" : "Doubleday", "pages" : 447, "descripti
on" : "The Shining mainly takes place in the fictional Overlook Hotel, an isolated, haunted resort located in the Colorado Rockies. The history of the hotel, which is d
escribed in backstory by several characters, includes the deaths of some of its guests and of former winter caretaker Delbert Grady, who succumbed to cabin fever and ki
lled his family and himself..." }
I want to find with author's name his id in the collection author and use his id to find all his books in the collection books. But the json-result should be a combination of selected field from both collections. For example I search for Agatha Christie and want get following selected fields as one json-object (name and genger from author + title and description from books as one object) Desired Api result in postman:
[ {
"name": "Agatha Christie",
"gender": "femail",
"title" : "Murder on the Orient Express",
"description" : "Hercule Poirot, the internationally famous detective, boards the Orient Express (Simplon-Orient-Express) in Istanbul...."
},
{
"name": "Agatha Christie",
"gender": "femail",
"title" : "The A.B.C. Murders",
"description" : "The novel follows the eponymous murders and their investigation as seen by Arthur Hastings, Poirot's old friend..."
}]
here is my code:
api.js
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
mongoose.connect('mongodb://localhost/books');
var db = mongoose.connection;
db.on('connected', function() {
console.log('MongoDB connection successful');
});
Author = require('./models/books');
Book = require('./models/books');
app.post('/api/Books', function(req, res){
Author.getAuthor({name : req.body.name}, 10, function(err, data){
if (err){
throw err;
}
var tmp = data[0].id_a;
Book.getBook({id_a : tmp}, 10, function(err, data2){
if (err){
throw err;
}
var result = [data, data2];
console.log(result);
res.json(result);
});
});
});
app.listen(3000);
console.log('server started and waits on port 3000');
books.js
var mongoose = require('mongoose');
var authorSchema = mongoose.Schema({
id_a:{
type: Number,
required: true
},
name:{
type: String,
required: true
},
gender:{
type: String,
required: true
},
born:{
type: Number,
required: true
},
birthplace:{
type: String,
required: true
},
genre:{
type: String,
required: true
}},
{ collection: 'author'}
);
var booksSchema = mongoose.Schema({
id_b:{
type: Number,
required: true
},
title:{
type: String,
required: true
},
id_a:{
type: Number,
required: true
},
pub_date:{
type: Number,
required: true
},
publisher:{
type: String,
required: true
},
pages:{
type: Number,
required: true
},
description:{
type: String,
required: true
}},
{ collection: 'books'}
);
var Author = module.exports = mongoose.model('author', authorSchema);
var Book = module.exports = mongoose.model('books', booksSchema);
module.exports.getAuthor = function(query, limit, callback){
Author.find(query, {'_id': 0}).select('id_a').limit(limit).exec(callback);
}
module.exports.getBook = function(query, limit, callback){
Book.find(query).select('-_id id_a title').limit(limit).exec(callback);
}
With my app I can find the proper books to particular author, but my result is without author's name and gender - I don't know how to do it. Also I make a request with nested functions - there might be much better solution for it. My solution feels very dirty. How can I improve my code and get data from both collections? A working adjusted example would be realy great!

First you have to add those changes:
model:
var booksSchema = mongoose.Schema({
...
},
{ collection: 'books', toJSON: { virtuals: true } })
// Foreign keys definitions
// http://mongoosejs.com/docs/populate.html#populate-virtuals
booksSchema.virtual('author', {
ref: 'author',
localField: 'id_a',
foreignField: 'id_a',
justOne: true // for many-to-1 relationships
});
module.exports.getAuthor = function (query) {
return Author.findOne(query).exec();
}
// this help you get books with author
module.exports.getBook = function (query) {
return Book.find(query)
.populate('author')
.exec();
}
app code:
app.post('/api/Books', function (req, res) {
Author.getAuthor({ name: req.body.name }, 10)
.then(author => {
return Book.getBook({ id_a: author.id_a });
})
.then(result => {
console.log(result);
res.json(result);
})
.catch(error => {
// TODO:
});
});
result should be:
[{...book info,...author info }]
I hope this help you

Related

Mongoose : update a document in a pre save hook

i'm quite new to nodeJS and have some problem i cannot solve...
i would like to update a related document in a pre save hook in mongoose :
No errors, but as a result i always get:
updated:{"n":1,"nModified":0,"ok":1}
so document is never updated.... how can i solve this ? i know i can make all controls before, but pre-hook seems the good place for that.
thanks for your help...
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var suivilotSchema = new Schema({
code_lot: { type: String,Required: 'Code Lot cannot be left blank.' },
site_IMS: { type: String,Required: 'Site IMS cannot be left blank'},
date_plantws: { type: String,Required: 'tws plan prod cannot be left blank'},
nb_server: { type: Number,Required: 'nb Server cannot be left blank.',default: 1},
nb_server_done: { type: Number,Required: 'nb Server done cannot be left blank.',default: 0},
date_created: { type: Date,default: Date.now },
date_updated: { type: Date },
retry: { type: Number },
status: { type: Number}
});
suivilotSchema.pre('save', function (next) {
console.log('***** PRE HOOK suivilot *******');
var self = this;
this.constructor.findOne({'code_lot' : self.code_lot,'date_plantws' : self.date_plantws, 'retry' :
self.retry },function (err,existinglot) {
if (!existinglot){
console.log('SUIVILOTS : pas de resultat');
next();
}
else{
console.log('SUIVILOTS : lot exists: ',existinglot._id);
existinglot.updateOne({ '_id' : existinglot._id }, { $inc:{'nb_server' : 1}
},function(err,updated){
if(err)
console.log("Error during increment server :"+err);
console.log('updated:'+JSON.stringify(updated));
console.log('updated:'+JSON.stringify(existinglot));
});
}
});
});

Response output not showing entire information present in mongoose schema nodejs

I have designed a Mongoose schema like this :
const metricsSchema = mongoose.Schema({
_id : mongoose.Schema.Types.ObjectId,
level : String,
details: {
demo: String,
full: String
}
});
Also, I have handled the response as such :
router.post('/',(req, res, next)=>{
const metrics = new Metrics({
_id : new mongoose.Types.ObjectId(),
level : req.body.level,
details:{
demo: req.body.demo,
full: req.body.full
}
});
res.status(201).json({
metrics: metrics
})
});
However, when I use Postman to post JSON data like this :
{
"level" :"schema" ,
"details":{
"demo" : "2465",
"full" : "1211234"
}
}
I get output like this :
{
"metrics": {
"_id": "5e09c156b0ce8a4a54a3ecca",
"level": "schema"
}
}
I do not get the rest of the output : demo and full in the response json. I wish to get the output like this :
{
"metrics": {
"_id": "5e09c156b0ce8a4a54a3ecca",
"level": "schema"
"details": {
"demo": "2465",
"full": "1211234"
}
}
}
Update: I found one solution in which the Mongoose schema was divided into two parts :
const detailsSchema = mongoose.Schema({
_id : mongoose.Schema.Types.ObjectId,
demo: String,
full: String
});
mongoose.model('Details',detailsSchema );
const metricsSchema = mongoose.Schema({
_id : mongoose.Schema.Types.ObjectId,
level : String,
details: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Details'
}
});
However, this did not work as well.
You have to change the code as below:
router.post('/',(req, res, next)=>{
const metrics = new Metrics({
_id : new mongoose.Types.ObjectId(),
level : req.body.level,
details:{
demo: req.body.details.demo, <----- see the change here
full: req.body.details.full
}
});
res.status(201).json({
metrics: metrics
})
});

How to update array in mongodb using mongoose

when i try to update it dose not throw back any error it goes OK but when i check my datebase nothing i their updated nothing is modified pls help
this is my db
{
"_id" : ObjectId("56651f0e4905bd041cad0413"),
"creator" : ObjectId("566299dd17990464160ae27a"),
"content" : "this is my joke 2",
"created" : ISODate("2015-12-07T05:54:22.858Z"),
"__v" : 15,
"comments" : [
{
"posteruserId" : "5665e6867185d87c1e71dbdc",
"postedBy" : "lawrence nwoko",
"postterscomment" : "good joke",
"_id" : ObjectId("56660745f644c2501116acce")
},
{
"posteruserId" : "5665e6867185d87c1e71dbdc",
"postedBy" : "lawrence nwoko",
"postterscomment" : "good joke",
"_id" : ObjectId("56660b6d33c245c012104fdc")
}
]
}
this is my schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var JokesSchema = new Schema({
creator: {type: Schema.Types.ObjectId, ref: 'User'},
content: String,
created:{type:Date, default: Date.now},
comments: [{
text: String,
postedBy: String,
posteruserId :String,
date: String,
postterscomment:String
}]
})
module.exports = mongoose.model('Jokes_db', JokesSchema)
here i my post funtion
api.post('/update', function(req, res) {
// Joke.findById("56651f0e4905bd041cad0413", function (err, meeting) {
Joke.update({_id: "5665e6867185d87c1e71dbdc", 'comments._id' : "56660745f644c2501116acce"},
{'$set': {
'comments.$.postterscomment': "working"
}},
function(err, numAffected) {
if(err){
console.log(err)
}else{
res.json(numAffected)
}
}
);
});
It has been three days of trying to fix this problem but by his grace I have done it without help the problem user that I was not using the right id to make the query thanks for your help guys I hope this helps another user
api.post('/editecomments', function(req, res) {
Joke.update({_id: "56651f0e4905bd041cad0413", 'comments._id' : "56660745f644c2501116acce"},
{'$set': {'comments.$.postterscomment': 'working'}},
function(err, numAffected) {
if(err){
console.log(err)
}else{
res.json(numAffected)
}
}
);
});

Mongoose & float values

My lat & lng numbers are being converted to strings. My section integers are still the correct data type of Number. How do I set up model so that I can get my lat & lng back out as Float rather than String?
I'm storing latLng data in my db. Right now I have my data type set to Number for lat & lng. When I check out my db I see this:
{
"_id" : ObjectId("563bd98a105249f325bb8a7e"),
"lat" : 41.8126189999999980,
"lng" : -87.8187850000000054,
"created" : ISODate("2015-11-05T22:34:50.511Z"),
"__v" : 0,
"section" : 0,
}
But when I get my data back out using express I get this:
{
"_id": "563bd98a105249f325bb8a7e",
"lat" : "41.8126189999999980",
"lng" : "-87.8187850000000054",
"__v": 0,
"section" : 0,
"created" : "2015-11-05T22:34:50.511Z",
}
My model:
var WaypointSchema = new Schema({
lat: {
type: Number
},
lng: {
type: Number
},
section: {
type: Number
}
created: {
type: Date,
default: Date.now
}
});
mongoose.model('Waypoint', WaypointSchema);
Express controller:
exports.list = function(req, res) {
Waypoint.find().sort('-created').populate('user', 'displayName').exec(function(err, waypoints) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(waypoints);
}
});
};
While the mongoDB fully supports float type, the mongoose supports only type of Number which is integer. If you try to save to mongoDB float number using mongooses type of Number it will be converted to string.
To sort this out, you will need to load some plugin for mongoose which will extend its value types. There are some plugins which work best with currencies or dates, but in your case I would use https://www.npmjs.com/package/mongoose-double.
Your model after changes would look something like this:
var mongoose = require('mongoose')
require('mongoose-double')(mongoose);
var SchemaTypes = mongoose.Schema.Types;
var WaypointSchema = new Schema({
lat: {
type: SchemaTypes.Double
},
lng: {
type: SchemaTypes.Double
},
section: {
type: Number
}
created: {
type: Date,
default: Date.now
}
});
mongoose.model('Waypoint', WaypointSchema);
Hope it helps.
As of the current version of mongoose (v5.12.6), It supports Decimal128 which can be used for this.
var mongoose = require('mongoose');<br>
var Schema = mongoose.Schema;<br>
var Waypoint = new Schema({<br>
lat: {<br>
type: SchemaTypes.Double<br>
},<br>
lng: {<br>
type: SchemaTypes.Double<br>
},<br>
section: {<br>
type: Number<br>
}<br>
point: {<br>
type: [Number],<br>
index: '2d'<br>
},<br>
}, {<br>
timestamps: true<br>
})<br>
event.index({<br>
Point: '2dsphere'<br>
});<br>
module.exports = mongoose.model('Waypoint', Waypoint);<br>
waypoint.save(point: [parseFloat(values.latitude), parseFloat(values.longitude)],)

Append/Add Objects without creating a new Parent in Mongoose

My Schema be like as follows
var DeptSchema = new Schema({
name : {type : String, default: ''},
sku : {type : String, default: ''}, // (SKU = stock keeping unit)
Product : {
name : {type : String, default: '', unique:true},
sku : {type : String, default: '', unique:true}, // (SKU = stock keeping unit)
description : {type : String, default: '100gm'},
price : {type : String, default: ''},
quantity : {type : Number, default: '0'},
isFav : {type : Boolean, default: 'false'}
}
});
Via Mongoose I've Created an API, but PROBLEM starts when I want to add Products to a specific Dept(Department), A whole new Instance of Department is created instead of the new Product getting appended to the existing Department.
My POST/PUT stated below is
.put(function(req, res) {
// use our Dept model to find the Dept we want
Dept.findById(req.params.Dept_id, function(err, Dept) {
if (err)
res.send(err);
Dept.name = req.body.name; // update the Dept info
Dept.sku = req.body.sku;
Dept.Product.name = req.body.ProductName;
Dept.Product.sku = req.body.ProductSKU;
Dept.Product.description = req.body.ProductDescription;
Dept.Product.price = req.body.ProductPrice;
Dept.Product.quantity = req.body.ProductQuantity;
Dept.Product.isFav = req.body.ProductisFav;
// save the Dept
Dept.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Department updated!' });
});
});
})
.post(function(req, res) {
var dept = new Dept(); // create a new instance of the Dept model
dept.name = req.body.name; // set the Dept name (comes from the request)
dept.sku = req.body.sku;
dept.Product.name = req.body.ProductName;
dept.Product.sku = req.body.ProductSKU;
dept.Product.description = req.body.ProductDescription;
dept.Product.price = req.body.ProductPrice;
dept.Product.quality = req.body.ProductQuality;
dept.Product.isFav = req.body.ProductisFav;
// save the Dept and check for errors
dept.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Department created!' });
});
})
e.g. We can easily see from the output that Different Fruits instead of appending to the same Fruits Dept. are creating a whole another instance. Also why does ProductSchema not have auto generated Object Id?
[
{
"__v": 0,
"_id": "5528027cd4eb13d80cf81f87",
"Product":
{
"isFav": true,
"quantity": 34,
"price": "128",
"description": "1kg",
"sku": "APL",
"name": "Apple"
},
"sku": "FRT",
"name": "Fruits"
},
{
"_id": "552824abd67bf9d81391ad92",
"__v": 0,
"Product":
{
"isFav": true,
"quantity": 0,
"price": "40",
"description": "1kg",
"sku": "ORG",
"name": "Orange"
},
"sku": "FRT",
"name": "Fruits"
}
]
Thank You for being Patient.
You have declared Product to be an object and not an array.
Product: {...} --> Product: [{...}]
Also you would need to update your put method to push a new item onto the Dept.Product array rather than updating the properties of Dept. You can read how to properly use subdocs in the documentation.

Resources