One to one with populate mongoose not working - mongodb

I'm new to mongoose and mongodb.
I have two collection (cart and produk)
1 cart have 1 produk, and I get the cart and populate the product but is not show the data relations.
Here the code:
routing
router.route('/relations/:app_id')
.get(cartController.relation);
model (cartModel)
var mongoose = require('mongoose');
var cartSchema = mongoose.Schema({
app_id: {
type: String,
required: true
},
product_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Produk'
},
qty: Number
});
var collectionName = 'cart';
var Cart = module.exports = mongoose.model('Cart', cartSchema, collectionName);
module.exports.get = function (callback, limit) {
Cart.find(callback).limit(limit);
}
model (produkModel)
var mongoose = require('mongoose');
// Setup schema
var produkSchema = new Schema({
name: {
type: String,
required: true
},
stok: Number
});
// Export Cart model
var collectionName = 'produk';
var Produk = module.exports = mongoose.model('Produk', produkSchema, collectionName);
module.exports.get = function (callback, limit) {
Produk.find(callback).limit(limit);
}
controller (cartController)
Cart = require('../model/cartModel');
exports.relation = function (req, res) {
const showCart = async function() {
const carto = await Cart.find().select('app_id product_id qty').populate("produk");
return carto;
};
showCart()
.then(cs => {
return apiResponse.successResponseWithData(res, "Operation success", cs);
})
.catch(err => console.log(err));
};
// Result
{
"status": 1,
"message": "Operation success",
"data": [
{
"_id": "60af72022d57d542a41ffa5a",
"app_id": "CvR4dTTjC7qgr7gA2yoUnIJnjRXaYokD6uc2pkrp",
"qty": 1,
"product_id": "60112f3a25e6ba2369424ea3"
},
{
"_id": "60b020536ccea245b410fb38",
"app_id": "CvR4dTTjC7qgr7gA2yoUnIJnjRXaYokD6uc2pkrp",
"product_id": "603f5aff9437e12fe71e6d41",
"qty": 1
}
]
}
expecting result
{
"status": 1,
"message": "Operation success",
"data": [
{
"_id": "60af72022d57d542a41ffa5a",
"app_id": "CvR4dTTjC7qgr7gA2yoUnIJnjRXaYokD6uc2pkrp",
"qty": 1,
"product_id": {
"_id": "60112f3a25e6ba2369424ea3",
"name": "snack"
}
},
{
"_id": "60b020536ccea245b410fb38",
"app_id": "CvR4dTTjC7qgr7gA2yoUnIJnjRXaYokD6uc2pkrp",
"product_id": {
"_id": "603f5aff9437e12fe71e6d41",
"name": "snack"
}
"qty": 1
}
]
}
what I miss ???
Thanks for your help

You need to pass the path to populate or an object specifying parameters to .populate(). So in this case, Your code should be:
const carto = await Cart.find().select('app_id product_id qty').populate("product_id");

Related

How can I send updated object from subdocument array of objects using mongoose as response

I want to send updated object as response from subdocument.
From my carts model, I am querying the subdocument array which is cartItems. After querying and doing crud operation, I don't want to send the full cartItems array as response. I want to send the updated object as response.
exports.removeCartItem = async (req, res) => {
const { itemId } = req.params
const { email } = req.user
const { id } = req.body
const targetUser = await User.findOne({ email }).exec()
const cartItemRemoved = Cart.findOneAndUpdate(
{
user: targetUser._id,
},
{
$pull: { cartItems: { _id: itemId } },
},
{ new: true },
).exec((err, data) => {
if (err) {
console.log(er)
}
res.json(data)
})
}
This is the response I am getting right now:
{
"user": "621def0665c08eff01794f6e",
"cartItems": [
{
"product": "6228edb603d5e7ca773a2b04",
"quantity": 5,
"price": 200,
"_id": "622b04741bab0093c963ef18"
}
],
"_id": "622b04741bab0093c963ef17",
"__v": 0
}
I want send the updated object as response - something like below:
{
"product":"6228edb603d5e7ca773a2b04",
"quantity": 5,
"price": 200,
"_id": "622b04741bab0093c963ef18"
}

Problem setting up node js server to listen for webhook and post to database

Good morning everyone, I'm having a bit of a struggle setting up a server to listen for webhook data and post it to a database. I'm mostly front-end, so some of this is a bit new for me. So I have a deli website that i built on snipcart. I have a receipt printer that queries an api and prints out new orders. So what I'm wanting is a server to listen for the webhook and store the info in a database. I've got it where it listens for the webhook correctly, but it refuses to post to the database. Here's the code in the app.js file.
'use strict';
require('./config/db');
const express = require('express');
const bodyParser = require('body-parser');
const fetch = require('node-fetch');
const app = express();
var routes = require('./api/routes/apiRoutes');
routes(app);
let orderToken;
app.use(bodyParser.urlencoded({extended:true}));
app.use(bodyParser.json());
app.listen(process.env.PORT || 8080);
app.post('/hook', (req, res) => {
orderToken = req.body.content.token;
console.log(orderToken);
const secret = "snipcart api key";
const apiFetch = async function(){
};
let buffered = new Buffer.from(secret);
let base64data = buffered.toString('base64');
const start = async function(){
const request = await fetch('https://app.snipcart.com/api/orders/'+orderToken, {
headers: {
'Authorization': `Basic ${base64data}`,
'Accept': 'application/json'
}
});
const result = await request.json();
console.log(result);
};
start();
res.status(200).end();
});
app.get('/', (req, res) => {
res.send('hello world')
});
Here's the code in my apiController.js file
const mongoose = require('mongoose'),
Order = mongoose.model('apiModel');
// listAllOrders function - To list all orders
exports.listAllOrders = (req, res) => {
api.find({}, (err, api) => {
if (err) {
res.status(500).send(err);
}
res.status(200).json(api);
});
};
// createNewOrder function - To create new Order
exports.createNewOrder = (req, res) => {
let newApi = new api (req.body);
newApi.save((err, api) => {
if (err) {
res.status(500).send(err);
}
res.status(201).json(api);
});
};
// deleteOrder function - To delete order by id
exports.deleteOrder = async ( req, res) => {
await api.deleteOne({ _id:req.params.id }, (err) => {
if (err) {
return res.status(404).send(err);
}
res.status(200).json({ message:"Order successfully deleted"});
});
};
and my apiModel.js file
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ApiSchema = new Schema({
customerName: {
type:String,
required:true
},
customerPhone: {
type:String,
required:true
},
name: {
type:String,
required:true
},
orderNumber: {
type:String,
required:true
},
price: {
type:String,
required:true
},
customFields: {
type:Array,
required:false
},
});
module.exports = mongoose.model("apiModel", ApiSchema);
apiRoutes.js
module.exports = function(app){
var orderList = require('../controllers/apiController');
app
.route('/orders')
.get(orderList.listAllOrders)
.post(orderList.createNewOrder);
app
.route('/order/:id')
.delete(orderList.deleteOrder);
};
and my db.js
const mongoose = require("mongoose");
//Assign MongoDB connection string to Uri and declare options settings
var uri = "<mongodb atlas info>
retryWrites=true&w=majority";
// Declare a variable named option and assign optional settings
const options = {
useNewUrlParser: true,
useUnifiedTopology: true
};
// Connect MongoDB Atlas using mongoose connect method
mongoose.connect(uri, options).then(() => {
console.log("Database connection established!");
},
err => {
{
console.log("Error connecting Database instance due to:", err);
}
});
and here's a sample response that I need to place into the database
{
"token": "93c4604e-35ac-4db7-b3f1-2871476e9e6a",
"creationDate": "2013-10-22T20:54:40.377Z",
"modificationDate": "2013-10-22T20:55:45.617Z",
"status": "Processed",
"paymentMethod": "CreditCard",
"invoiceNumber": "SNIP-1427",
"email": "geeks#snipcart.com",
"cardHolderName": "Geeks Snipcart",
"creditCardLast4Digits": "4242",
"billingAddressName": "Geeks Snipcart",
"billingAddressCompanyName": "Snipcart",
"billingAddressAddress1": "4885 1ere Avenue",
"billingAddressAddress2": null,
"billingAddressCity": "Quebec",
"billingAddressCountry": "CA",
"billingAddressProvince": "QC",
"billingAddressPostalCode": "G1H2T5",
"billingAddressPhone": "1-877-301-4813",
"notes": null,
"shippingAddressName": "Geeks Snipcart",
"shippingAddressCompanyName": "Snipcart",
"shippingAddressAddress1": "4885 1ere Avenue",
"shippingAddressAddress2": null,
"shippingAddressCity": "Quebec",
"shippingAddressCountry": "CA",
"shippingAddressProvince": "QC",
"shippingAddressPostalCode": "G1H2T5",
"shippingAddressPhone": "1-877-301-4813",
"shippingAddressSameAsBilling": true,
"finalGrandTotal": 287.44,
"shippingFees": 10,
"shippingMethod": "Shipping",
"items": [
{
"uniqueId": "1aad3398-1260-419c-9af4-d18e6fe75fbf",
"id": "1",
"name": "Un poster",
"price": 300,
"quantity": 1,
"url": "http://snipcart.com",
"weight": 10,
"description": "Bacon",
"image": "",
"customFieldsJson": "[]",
"stackable": true,
"maxQuantity": null,
"totalPrice": 300,
"totalWeight": 10
},
...
],
"taxes": [
{
"taxName": "TPS",
"taxRate": 0.05,
"amount": 12.5,
"numberForInvoice": ""
},
{
"taxName": "TVQ",
"taxRate": 0.09975,
"amount": 24.94,
"numberForInvoice": ""
},
...
],
"rebateAmount": 0,
"subtotal": 310,
"itemsTotal": 300,
"grandTotal": 347.44,
"totalWeight": 10,
"hasPromocode": true,
"totalRebateRate": 20,
"promocodes": [
{
"code": "PROMO",
"name": "PROMO",
"type": "Rate",
"rate": 20,
},
...
],
"willBePaidLater": false,
"customFields": [
{
"name":"Slug",
"value": "An order"
},
...
],
"paymentTransactionId": null,
}
I dont need all the info placed in the database, just a few key items, like customer name, phone number and the order info. but if there's more than one item in the order, I need it to take that into account and add all the items in the order. here is the docs for the printer that i'm needing to integrate https://star-m.jp/products/s_print/CloudPRNTSDK/Documentation/en/index.html Would appreciate any help that you all can give me. Thanks!
Snipcart will send the webhook to you endpoint for different events. I would suggest you to first filter the event by eventName, because you want to listen for only the order.completed event. After that from the body of the request message, you can extract the items that will be in the req.body.content.items. You can take from the available info what you want and store only that in the database.
Try this:
app.post('/hook', (req, res) => {
if (req.body.eventName === 'order.completed') {
const customer_name = req.body.content.cardHolderName;
const customer_phone req.body.content.billingAddressPhone;
const order_number = req.body.content.invoiceNumber;
let items = [];
req.body.content.items.forEach((item) => {
items.push({
name: item.name,
price: item.price,
quantity: item.quantity,
id: item.uniqueId
});
})
// Now store in database
apiFetch.create({
customerName: customer_name,
customerPhone: customer_phone
name: customer_name,
orderNumber: order_number
customFields: items
}).then(()=>{
res.status(200).json({success:true});
}, (error)=>{
console.log('ERROR: ', error);
})
}
};

how to dynamically change whole object (inc. array) in mongodb?

I use following code to dynamically update whole object in mongoDB:
module.exports = function(req, res, next){
const Model = require('../models/' + req.body.where)
for(let i=0; i<req.body.array.length; i++){
Model.updateOne({
"_id": req.body.array[i]._id,
"user_ID": req.body.user_ID
},{
$set: req.body.array[i]
}).catch(next)
if(i+1 == req.body.array.length) res.send({})
}
}
but the code is not working, when model own array:
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
const exerciseSchema = new Schema({
title: {
type: String,
required: [true, 'required!']
}
}, { _id : false })
const workout_planSchema = new Schema({
title: {
type: String,
required: [true, 'required!']
},
description: {
type: String
},
user_ID: {
type: String,
required: [true, 'required!']
},
exercises: [exerciseSchema]
})
const workout_plan = mongoose.model('workout_plan', workout_planSchema)
module.exports = workout_plan
I would like to update whole object with totally new values, staying only with the same _id.
For example , I have following value in DB:
"_id": "604a16f6cf847c1810c8fd08",
"title": "1",
"user_ID": "Test",
"exercises": [{
"title": "123"
}],
and I am sending array which looks like this:
"_id": "604a16f6cf847c1810c8fd08",
"title": "2",
"user_ID": "Test",
"exercises": [{
"title": "234"
},{
"title": "235"
}],
and the result should be same as the array I am sending. How can I change my code to reach this?
PS: Basiclly I want to make object in DB = sent object
so the answer is to change the main function like this:
module.exports = function(req, res, next){
const Model = require('../models/' + req.body.where)
for(let i=0; i<req.body.array.length; i++){
Model.replaceOne({
"_id": req.body.array[i]._id,
"user_ID": req.body.user_ID
},
req.body.array[i]
).catch(next)
if(i+1 == req.body.array.length) res.send({})
}
}
Try this one:
for( let elem of req.body.array ) {
let workout_plan = await Model.findById(elem._id);
workout_plan.title = elem.title;
workout_plan.user_ID = elem.user_ID;
workout_plan.exercises = elem.exercises;
await workout_plan.save();
}

How to multiply NumberDecimal values in mongodb

I have the following structure:
{
"_id": "5d0118f0f57a282f89bc5f71",
"product": {
"_id": "5cfed37375a13067dd01ddb7",
"name": "My product",
"description": "My description",
"purchased_amount": 15,
"unit_price_mex": "45",
"unit_price_to_sell": "5",
"travel": "5cf58713d6f7f1657e2d8302",
"__v": 0,
"id": "5cfed37375a13067dd01ddb7"
},
"client": {
"_id": "5cf1778efffb651fad89d8b6",
"name": "Client name",
"description": "",
"__v": 0
},
"purchased_amount": 3,
"fch": "13/6/2019",
"__v": 0
},
{
"_id": "5d0151afda1a446008f1817b",
"product": {
"_id": "5cfed1995eaf2665c45efd82",
"name": "Camisa",
"description": "Camisas buenas",
"purchased_amount": 10,
"unit_price_mex": "100",
"unit_price_to_sell": "15",
"travel": "5cf56b04462a865264fabb9d",
"__v": 0,
"id": "5cfed1995eaf2665c45efd82"
},
"client": {
"_id": "5cf1778efffb651fad89d8b6",
"name": "Randy",
"description": "El que trabaja aqui",
"__v": 0
},
"purchased_amount": 34,
"fch": "12/6/2019",
"__v": 0
},
Where client and product are of type ObjectId. This is the Schema:
Client Model
var mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
var clientSchema = new mongoose.Schema({
name: String,
description: String
}).plugin(mongoosePaginate);
var Client = mongoose.model('Client', clientSchema);
module.exports = Client;
Product Model
var mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
var productSchema = new mongoose.Schema({
name: String,
description: String,
purchased_amount: Number,
unit_price_mex: mongoose.Schema.Types.Decimal128,
unit_price_to_sell: mongoose.Schema.Types.Decimal128,
travel: { type: mongoose.Schema.Types.ObjectId, ref: 'Travel' }
}).plugin(mongoosePaginate);
productSchema.set('toJSON', {
getters: true,
transform: (doc, ret) => {
if (ret.unit_price_mex) {
ret.unit_price_mex = ret.unit_price_mex.toString();
}
if ( ret.unit_price_to_sell ) {
ret.unit_price_to_sell = ret.unit_price_to_sell.toString();
}
}
})
var Product = mongoose.model('Product', productSchema);
module.exports = Product;
I need to get the multiplication sum of purchased_amount by product.unit_price_to_sell. My code is the following but always returns 0. Apparently, "$product.unit_price_to_sell" does not return the decimal value.
var aggregate = InvoiceModel.aggregate([
{ $match: { client: mongoose.Types.ObjectId( id ) } },
{ $group: { _id: null, total: { $sum: { $multiply: [ "$purchased_amount", "$product.unit_price_to_sell" ] } } } }
]);
InvoiceModel.aggregatePaginate(aggregate, {}, (error, aggs) => {
InvoiceModel.paginate({ client: id },{ page, limit, populate: 'client product' }, (err, value) => {
return res.status(200).send({
results: value.docs,
totalPages: value.totalPages,
totalDocs: value.totalDocs,
purchase_amount_total : aggs.docs[0].total
})
})
})
MongoDB cannot use string values in arithmetic expressions. You must either store the values using their numeric non-string representations, or you must use an aggregation operator like $toDecimal to convert the values to their numeric representations first.
Modifying your $group stage to something like the following should work:
{ $group: { _id: null, total: { $sum: { $multiply: [ "$purchased_amount", { $toDecimal: "$product.unit_price_to_sell" } ] } } }
Please note, however, that this will only work for MongoDB versions >= 4.0. If you're using an older version of MongoDB, you will either need to upgrade it to at least version 4.0 or begin converting your existing values from strings to numbers.

Populating array in mogo

I have created the following Schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Player = require('./player');
var gameSchema = new Schema({
created_at: Date,
nrOfCards: String,
players: [{
sticks: String,
player: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Player'
}
}],
});
var Game = mongoose.model('Game', gameSchema);
The saving part works fine and a saved object may look something like this:
"_id": "57dd11aca0c36114588fd250",
"nrOfCards": "3",
"__v": 0,
"players": [
{
"_id": "57d415e527c20f3ed2416e05",
"age": "33"
},
{
"_id": "57d417df2186d53f3d49c996",
"age": "73"
},
{
"_id": "57d41d85ec315d4234010c7d",
"age": "20"
}
]
},
After having saved an object I would like to have it returned with the player-field populated. Here is my attempt:
app.post('/api/games', function(req, res) {
Game.create({
players : req.body.activePlayers,
nrOfCards: req.body.nrOfCards,
}, function(err, game) {
if (err) {
res.send(err);
} else {
Game.findOne(game)
.populate('players.player')
.exec(function (err, newgame) {
if (err) return handleError(err);
console.log(newgame);
res.json(newgame);
});
}
});
});
Thinking that the .populate('players.player') should do the trick , but I'm receiving the unpopulated field containing the _id of player only.
Tips appreciated. Thanks!
Use
player: {
type: Schema.Types.ObjectId,
ref: 'Player'
}
into your schema.