How to programmatically add to an array in database from frontend React - reactjs

I basically have this route
app.post("/order/:orderId/:productId", async (req, res) => {
const result = await Order.findByIdAndUpdate(
req.params.orderId,
{
$push: {
products: req.params.productId
}
},
{ new: true }
);
res.send(result);
});
I have two collections, namely Product and Orders. The goal is to get, for instance, a particular Order with id(5ddfc649e1e9e31220ce6a16) , and post a product with id(5de02c4a0ed3160368b9a550) inside an Array field inside this Orders collection. In Postman, I can do that manually by just adding the ObjectIds in the URL like so:
Http://localhost:3000/orders/5ddfc649e1e9e31220ce6a16/5de02c4a0ed3160368b9a550.
and I get this Response :
{
"products": [
"5ddfb388b14c5b41e0607a5e",
"5de02c4a0ed3160368b9a550" // newly added Product
],
"_id": "5ddfc649e1e9e31220ce6a16",
"issuedBy": "issuedBy",
"collectedBy": "collectedBy",
"quantity": 123,
"createdAt": "2019-11-28T11:48:40.500Z",
"updatedAt": "2019-11-28T11:59:51.659Z",
"__v": 0
}
My challenge is, how do I do this programmatically from the UI(Reactjs) side?
// Product Schema
const mongoose = require("mongoose");
const ProductSchema = new mongoose.Schema(
{
name: String,
description: String,
price: Number,
quantity: Number,
supplier: String
},
{ timestamps: true }
);
module.exports = mongoose.model("Product", ProductSchema);
// Orders Schema
const mongoose = require("mongoose");
const OrderSchema = new mongoose.Schema(
{
issuedBy: String,
collectedBy: String,
quantity: Number,
products: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Product",
required: true
}
]
},
{ timestamps: true }
);
module.exports = mongoose.model("Order", OrderSchema);
I would really appreciate any suggestion or a sample code snippet

I think what you are looking for is, you need to hit this API from your front end i.e. React app.
http://localhost:3000/orders/<order-id-here>/<product-id-here>
And then supply the dynamic value for your order id and product id.
You can hit api using fetch. More info
You can simply take some variables in your front end app. As I don't know how your order id and product id are being generated so you need to check on that and then something like,
let orderId = someValue
let productId = someOtherValue
let url = "http://localhost:3000/orders/"+orderId+"/"+productId
But I think the concern would be GET and POST method. Right now you have app.post but the params are in URL so instead you should go for GET method i.e. app.get

Related

Join 2 collections in mongo with a where clause [duplicate]

I have a collection named Releases that holds a subdocument named product.
My collection looks like this:
{
"_id": ObjectId("5b1010e4ef2afa6e5edea0c2"),
"version": "abc",
"description": "<p>abc</p>\n",
"product": {
"_id": ObjectId("5b100c9949f43c6b6f10a93f"),
"name": "Product 1",
"description": "<p>abc</p>\r\n",
"image": "Product 1.png",
"__v": 0
},
"releasedate": ISODate("2018-05-30T00:00:00Z"),
"__v": 0
}
I am trying to find all releases associated to a specific product.
var id = req.body.productId
var query = {'product._id' : id};
Release.find(query)
.select('_id version description product releasedate')
.sort({releasedate: 1 })
.exec()
.then(releases => {
console.log(releases);
res.status(200).json(releases);
})
But it gives me an empty array if i console.log(releases)
I've been stuck with this for a while and asking u guys for help. What am i doing wrong. I read the documentation on https://docs.mongodb.com/manual/tutorial/query-embedded-documents/ and tried to apply that to my code but i cant get it to work.
My Schema looks like this:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Product = new Schema({
_id: Schema.Types.ObjectId,
name: String,
description: String,
image: String,
});
module.exports = mongoose.model('Product', Product);
You need to change your id from string to mongoose objectId
var id = req.body.productId
var query = {'product._id' : mongoose.Types.ObjectId(req.body.productId)};
Release.find(query)
.select('_id version description product releasedate')
.sort({releasedate: 1 })
.exec()
.then(releases => {
console.log(releases);
res.status(200).json(releases);
})

Normalizing my JSON response using normalizr with redux

I'm trying to reshape my Redux store so I can query by and filter my data easily.
I have an API endpoint that returns back an order.
The order looks like this at a high level:
Order
+ references
+ item_details
- category_id
- product_id
- product
So an order has many references, and the references have many item_details.
The item details has a category and product.
const data = {
id: 3939393,
discount: 0,
references: [
{
id: 123,
order_id: 3939393,
name: "order 1",
item_details: [
{
id: 100,
order_id: 3939393,
product_id: 443,
sort_order: 1,
category_id: 400,
product: {
id: 443,
name: "hello world",
price: 199
}
},
{
id: 200,
order_id: 3939393,
product_id: 8080,
sort_order: 2,
category_id: 500,
product: {
id: 8080,
name: "hello json",
price: 299
}
}
]
}
]
};
export default data;
So far my schema definitions look like this:
export const productSchema = new schema.Entity("products");
export const itemDetailSchema = new schema.Entity("itemDetails", {
product: productSchema
});
export const references = new schema.Entity("references", {
item_details: new schema.Array(itemDetailSchema)
});
export const orderSchema = new schema.Entity("orders");
const result = normalize(data, orderSchema);
console.log("result is: " + JSON.stringify(result));
How can I get the products in its own section in the normalized JSON? Currently the products are still embedded inside the JSON.
Would I use normalizr to create state "index" type looks like this:
productsInReferences: {
123: [400, 8080]
}
If not, how exactly to I generate these types of JSON lookups?
I created a codesandbox with my code so far.
https://codesandbox.io/s/xpl4n9w31q
I usually think Normalization schemes from the deepest nested structure all the way to the one containing the data for those cases. Remember that you can do explicit array definition through [someSchema], also every level should be contained on a nested schema, in this case you forgot the references: [referenceSchema] on the orderSchema.
The correct normalization would be:
// Product Schema
const productSchema = new schema.Entity("products");
// Item detail schema containing a product schema OBJECT within the property product
const itemDetailSchema = new schema.Entity("itemDetails", {
product: productSchema
});
// Reference schema containing an ARRAY of itemDetailSchemes within the property item_details
const referenceSchema = new schema.Entity("references", {
item_details: [itemDetailSchema]
});
// Order schema containing an ARRAY of reference schemes within the property references
const orderSchema = new schema.Entity("orders", {
references: [referenceSchema]
});
const result = normalize(data, orderSchema);
console.dir(result);
This would be the result after normalizing.
Object
entities:
itemDetails: {100: {…}, 200: {…}}
orders: {3939393: {…}}
products: {443: {…}, 8080: {…}}
references: {123: {…}}
result: 3939393

mongoose + nodejs push array to database

I'm new to NodeJS + Mongoose and having trouble pushing an array to my database via mongoose.
I have the following schema:
const StudentSchema = mongoose.Schema({
name: {
type: String
},
quizzes: [{
quiz: String,
answers: []
}]
});
What I'm trying to do is have an array of quiz objects in which the quiz number is shown and the array of answers for that particular quiz.
When I create a new user, the student object looks like this in mongoose:
{
"_id" : ObjectId(id here),
"name" : "John",
"quizzes" : [ ],
"__v" : 0
}
The function I'm using to update the quiz array:
module.exports.addQuiz = function(student_id, answers, callback){
Student.findByIdAndUpdate(
student_id,
{$push: {"quizzes": {answers: answers}}},
{safe: true, upsert: true},
function(err, model) {
console.log(err);
}
);
}
And this is my route which calls the function whenever the endpoint is hit with a student_id, which will then be used to find the student and push to the array
router.post('/quiz/:student_id', (req, res, next) => {
var student_id = req.params.id;
var answers = req.body.answers;
Student.addQuiz(student_id, answers, (err, answers) => {
//error handling
})
});
I'm trying to test this by sending a post request to /quiz/:student_id with an id of a student in my database with the following JSON sent in the body:
[{
"quiz": "Quiz 1",
"answers": ["Answer 1", "Answer 2"]
}]
Although when I try this it ends up getting hung somewhere and the request never completes - I also get a "null" in the console.
Can anyone help me out? Thank you.

Mongo schema, array of string with unique values

I'm creating the schema for a mongo document and I can do everything except prevent duplicates in a non-object array.
I'm aware of the addToSet, but I'm referring to Mongo Schema.
I don't want to check on Update using $addToSet, rather I want this to be part of my schema validation.
Example below.
let sampleSchema = {
name: { type: 'String', unique: true },
tags: [{ type: 'String', unique: true }]
}
The above snippet prevents name from having duplicate values. It allows tags to be stored as a string array.
But.. I cannot limit the array to be unique strings.
{ name: 'fail scenario', tags: ['bad', 'bad', 'array']}
I'm able to insert this record which should be a fail scenario.
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const _ = require('underscore');
let sampleSchema = new mongoose.Schema({
name: {
type: 'String',
unique: true
},
tags: [{
type: 'String'
}]
})
sampleSchema.pre('save', function (next) {
this.tags = _.uniq(this.tags);
next();
});
const Sample = mongoose.model('sample', sampleSchema, 'samples');
router.post('/sample', function (req, res, next) {
const sample = new Sample(req.body);
sample.save()
.then((sample) => {
return res.send(sample);
})
.catch(err => {
return res.status(500).send(err.message);
})
});
I've come to the conclusion that this is impossible to do via Mongoose Schema.
JSON schema is done like so.
let schema = {
name: { type: 'string' }
tags: {
type: 'array',
items: { type: 'string', uniqueItems: true }
}
}
I'll validate with JSON schema before creating Mongo Document.
This method builds on Med's answer, handles references, and done completely in scheme validation.
let sampleSchema = new mongoose.Schema({
strings: [{type: 'String'}],
references: [{type: mongoose.Schema.Types.ObjectId, ref: 'Reference'],
});
sampleSchema.pre('save', function (next) {
let sample = this;
sample.strings = _.uniq(sample.strings, function(i) {return (i._id) ? i._id.toString() : i;});
sample.references = _.uniq(sample.references, function(i) {return (i._id) ? i._id.toString() : i;});
return next();
});
I'm a little late, but maybe this will help someone in the future.
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
},
reference: {
type: [mongoose.Schema.Types.ObjectId],
ref: 'SomeOtherSchema',
// Add a custom validator.
validate: {
// The actual validator function goes here.
// "arr" will be the value that's being validated (so an array of
// mongoose new ObjectId statements, in this case).
validator: arr => {
// Convert all of the items in the array "arr", to their string
// representations.
// Then, use those strings to create a Set (which only stores unique
// values).
const s = new Set(arr.map(String));
// Compare the Set and Array's sizes, to see if there were any
// duplicates. If they're not equal, there was a duplicate, and
// validation will fail.
return s.size === arr.length;
},
// Provide a more meaningful error message.
message: p => `The values provided for '${ p.path }', ` +
`[${ p.value }], contains duplicates.`,
}
},
});
The above commented code should be pretty self explanatory.
With the newer version(s) of MongoDB, you can use $addToSet to append to an array if and only if the new value is unique compared to the items of the array.
Here's the reference: https://www.mongodb.com/docs/manual/reference/operator/update/addToSet/
Here's an example:
const SampleSchema = new mongoose.Schema({
tags: [String]
});
const Sample = mongoose.model('Sample', SampleSchema);
// append to array only if value is unique
Sample.findByIdAndUpdate({_id: 1, {$addToSet: {tags: "New Tag"}}});
This will effectively update the tags if the "New Tag" is not already present in the tags array. Otherwise, no operation is done.

Mongoose doesn't create subdocument from JSON array

I'm trying to write a JSON object that contains both first-level data along with arrays into MongoDB.
What happens instead is all first-level data is stored, but anything contained in an array isn't. When logging the data the server receives, I see the entire object, which leads me to believe there's something wrong with my Mongoose code.
So for example if I send something like this:
issueId: "test1",
issueTitle: "testtest",
rows: [
{order:1,data: [object]},
{order:2,data: [object]},
]
Only the following gets stored:
issueId: "test1",
issueTitle: "testtest",
lastUpdated: Date,
I have the following model for Mongo:
//model.js
var mongoose = require('mongoose');
var model = mongoose.Schema({
issueId : String,
issueTitle : String,
lastUpdated : {type: Date, default : Date.now},
rows : [{
order : Number,
data : [
{
title : String,
text : String,
link : String,
}
]
}]
});
module.exports = mongoose.model('Model', model);
And the routing code, where I believe the problem likely is:
//routes.js
const mongoose = require('mongoose');
const Model = require('./model.js');
...
app.post('/api/data/update', function(req, res) {
let theData = req.body.dataToInsert;
console.log(JSON.stringify(theData,null,4));
Model.findOneAndUpdate(
{issueId : theData.issueId},
{theData},
{upsert: true},
function(err,doc){
if(err) throw err;
console.log(doc);
});
});
As well, here's the part of the Angular controller storing the data. I don't think there's any problem here.
pushToServer = function() {
$http.post('/api/data/update',{
dataToInsert : $scope.dataObject,
}).then(function successCallback(res){
console.log("all good", JSON.stringify(res,null,3));
}, function errorCallback(res){
console.log("arg" + res);
});
}
Look at the first question in the mongoose FAQ:
http://mongoosejs.com/docs/faq.html
Mongoose doesn't create getters/setters for array indexes; without them mongoose never gets notified of the change and so doesn't know to persist the new value. The work-around is to use MongooseArray#set available in Mongoose >= 3.2.0.
// query the document you want to update
// set the individual indexes you want to update
// save the document
doc.array.set(3, 'changed');
doc.save();
EDIT
I think this would work to update all of the rows. I'd be interested to know if it does work.
let rowQueries = [];
theData.rows.forEach(row => {
let query = Model.findOneAndUpdate({
issueId: theData.issueId,
'row._id': row._id
}, {
$set: {
'row.$': row
}
});
rowQueries.push(query.exec());
});
Promise.all(rowQueries).then(updatedDocs => {
// updated
});

Resources