Body parameter in ArangoDB - arangodb

I wrote the following router in Foxx Microservices:
router.post('/PersonInformation', function (req, res) {
const keys = db._query(aql`
FOR p IN Personal
FILTER (p.stateId IN ${req.queryParams.statePar} OR IS_NULL(${req.queryParams.statePar}))
AND p.empDate >= TO_NUMBER(${req.queryParams.fromDatePar}) AND p.empDate <= TO_NUMBER(${req.queryParams.toDatePar})
RETURN p
`);
res.send(keys);
})
.queryParam('statePar', joi.array().default(null), 'State Parameter')
.queryParam ('fromDatePar', joi.string().required(), 'FromDate Parameter')
.queryParam('toDatePar', joi.string().required(), 'ToDate Parameter')
.response(joi.array().items(
joi.string().required()
).required(), 'List of entry keys.')
.summary('List entry keys')
.description('Assembles a list of keys of entries in the collection.');
How i can convert queryParam to body parameter. I used .body instead of .queryParam, but it does not answer. I also wrote the table as follows, but it still does not work:
router.post('/PersonInformation', function (req, res) {
const distributorIdPar = req.body;
const cityPar = req.body;
const productIdPar = req.body;
const fromDatePar = req.body;
const toDatePar = req.body;
const keys = db._query(aql`
const keys = db._query(aql`
FOR p IN Personal
FILTER (p.stateId IN ${req.body.statePar} OR IS_NULL(${req.body.statePar}))
AND p.empDate >= TO_NUMBER(${req.body.fromDatePar}) AND p.empDate <= TO_NUMBER(${req.body.toDatePar})
RETURN p
`);
res.send(keys);
})
.response(joi.array().items(
joi.string().required()
).required(), 'List of entry keys.')
.summary('List entry keys')
.description('Assembles a list of keys of entries in the collection.');

You have a duplicate line const keys = db._query(aql` that breaks the syntax. Furthermore, you are not setting .body(joi.object()) or .body(['application/json']), which leads to req.body being a Buffer:
If no body has been defined for the current route, the value will be identical to rawBody – docs
You also assign req.body to various variables but don't actually use them.
I would assign the body parameters to local variables using destructuring for the guaranteed values, and use a regular assignment for statePar so that it can default to null if it is omitted from the body. Furthermore, I would enforce numeric values for fromDatePar and toDatePar (note that this requires strict() in joi or it will happily accept number strings) and remove TO_NUMBER() from the query. Below code accepts either an array of strings for statePar or expects the attribute to not be present. It disallows "statePar":null, "statePar":[], and "statePar":[null], but you could change that if desired.
'use strict';
const joi = require('joi');
const { db, aql } = require('#arangodb');
const createRouter = require('#arangodb/foxx/router');
const router = createRouter();
router.post('/PersonInformation', function (req, res) {
const statePar = req.body.statePar || null;
const { fromDatePar, toDatePar } = req.body;
const keys = db._query(aql`
FOR p IN Personal
FILTER (p.stateId IN ${statePar} OR IS_NULL(${statePar}))
AND p.empDate >= ${fromDatePar} AND p.empDate <= ${toDatePar}
RETURN p
`);
res.send(keys);
})
.body(joi.object({
statePar: joi.array().items(
joi.string().required()
),
fromDatePar: joi.number().required(),
toDatePar: joi.number().required(),
}).required().strict(), 'Search parameters')
.response(joi.array().items(
joi.string().required()
).required(), 'List of entry keys.')
.summary('List entry keys')
.description('Assembles a list of keys of entries in the collection.');
module.context.use(router);

Related

Mongoose Trouble Adding Subdocs to Array

I am looking to push to an array on my users schema in mongoose. I am following mongoose's Adding Subdocs to Arrays documentation but keep getting an error on user.notes.push(newNotebook)
TypeError: Cannot read properties of undefined (reading 'push')
Here is my setup:
const User = require('../models/userModel')
const addNotebook = async (req, res) => {
...
const user = User.findOne({ userId })
const newNotebook = { userId: userId, notebookId: notebookId, title: title, numNotes: 0 }
user.notebooks.push(newNotebook)
}
Any help would be much appreciated.
Update--
Here is the solution:
const User = require('../models/userModel')
const addNotebook = async (req, res) => {
...
const user = User.findOne({ userId })
if (!user) return
const newNotebook = { userId: userId, notebookId: notebookId, title: title, numNotes: 0 }
user.notebooks = user.notebooks || []; // returns user.notebooks if defined else empty array
user.notebooks.push(newNotebook)
await user.save()
}
Big thanks to Mandeep for showing me the !user condition and .save() method. Special thanks to Sardar for bringing the user.notebooks = user.notebooks || [] solution to light.
Disclaimer: upon watching this video on MongoDB anti patterns, I learned nesting boundless data under a single document can be problematic since each MongoDB document is limited to 16mb. It is also supposedly inefficient relative to saving your data in respective collections. Use caution when adding subdocs to arrays.
It would be like this:
const addNotebook = async(req, res) => {
const user = await User.find({
userId
}).limit(1).exec()
if (!user) return
const newNotebook = {
userId: userId,
notebookId: notebookId,
title: title,
numNotes: 0
}
user.notebooks.push(newNotebook)
await user.save()
}
This is probably caused because "notebooks" is undefined by default for your userModel/schema
You can resolve this by two methods
const User = require('../models/userModel')
const addNotebook = async(req, res) => {
...
const user = User.find({
userId
})
const newNotebook = {
userId: userId,
notebookId: notebookId,
title: title,
numNotes: 0
}
user.notebooks = user.notebooks || []; // returns user.notebooks if defined else empty array
user.notebooks.push(newNotebook)
}
RECOMMENDED
Or you can set default value for notebooks in your userModel like this (might not update your existing user documents)
How to set default value in schema mongoose?

object is not update within map function and await too

Actually am getting details from cart table it display an array of objects , that object contains product id and quantity , after that i want to include price and name for my local display so i want to update the object for my reference, but name and price contains another table . here my code
exports.cartList = async (req, res) => {
const CartId = req.profile.CartId;
await Cart.findById(CartId).then(data =>
{
const products = data.products;
const exprd = products;
const length = products.length;
exprd.map((item, index) =>
{
const prodID = item.productId;
const quantity = item.quantity;
Product.findById(prodID).then(prdDet => {
const price = prdDet.price;
const inven = prdDet.inventory;
const name = prdDet.name;
console.log(name);
console.log('CHECKERSP');
if(quantity < inven)
{
({ ...exprd, stock: 'stock', })
}
else
{
({ ...exprd, stock: 'Out of Stock', })
}
({ ...exprd, name: name, price: price })
});
console.log('ex2',exprd);
})
console.log('ex1',exprd);
res.json(exprd)
});
}```
but object is not updated , i don't know what i did wrong because am very new to these things . Help me to overcome this problem, Thanks in advance.
First I think you have missed await before "Product.findById(prodID)"
Further,
In if and else block you have to assign the value after declaration.
Example:
let obj ={name:'XYZ'};
Either it should be:
let obj1 = {...obj, name:'ABC'};
Or
obj = {...obj, name:'ABC'};
Furthermore, If you are doing await inside a loop,
traditional loops are better. Map, filter and all can be tricky and await might not act as desired.
Kindly let me know if there is anything missing or wrong...
Just working things according to the funny experiences I acquired.
Hope this helps.

Joi validate if the number is a multiple of x

Current validation:
const myPacket = Joi.object({
cost: Joi.number().precision(2).required()
})
Here, how do I check if my cost field is a multiple of 0.25 or not? So, for example, valid values for cost would be 1.25, 2.5, 2.25, etc.
You can use custom method and write your own logic there https://github.com/sideway/joi/blob/master/API.md#anycustommethod-description
example
const Joi = require('joi')
const myCustomValidation = (value, helpers) => {
if (typeof value !== 'number') return helpers.error('any.invalid')
if (parseFloat(value*4) % 1 === 0) {
// your logic above
return value
} else return helpers.error('any.invalid')
}
const schema = Joi.object({
customField: Joi.number().custom(myCustomValidation, 'my custom validation description')
})
schema.validate({customField: 1.25})
schema.validate({customField: 1.251})

Why my mongoose schema add an array on top of my current array?

I'm querying a mongo document to retrieve the array info. The thing is my array is inside another array. I don't know how this happened. I'm force to map twice every time to get inside. Anyone knows why?
Here's the mongoose schema:
const mongoose = require("mongoose");
const mongo = require("mongodb");
const insertedLaw = require("./civilcode_section2_21.json");
const Law = mongoose.model(
"Laws",
new mongoose.Schema({
law_name: String,
section_name: String,
section_text: [String],
}),
);
// Law.insertMany(insertedLaw); (commented out because using Nodemon)
exports.Law = Law;
Here's my node route:
const express = require("express");
const {
Law,
} = require("../models/law");
const router = express.Router();
router.get("/", async (req, res) => {
const laws = await Law.find();
res.send(laws);
});
module.exports = router;
Here's my Jsx:
const urlLaw = "http://localhost:3000/api/laws";
class Dashboard extends Component {
state = {
laws: {},
sectionText: [],
};
async componentDidMount() {
await this.getLaws();
}
getLaws = async () => {
try {
const { data: laws } = await http.get(urlLaw);
const sectionText = laws.map((law) => law.section_text);
this.setState({ laws, sectionText });
console.log("You data has been received!");
} catch (ex) {
if (ex.response && ex.response === 404) alert("error receiving data");
}
};
Here's the result in console:
console.log
Here's my Mongo Document:
mongodoc
I can fetch the data but I feel it will become very clunky as I add more query. Thanks
I'm assuming your console.log image shows the result of await http.get(urlLaw).
You get nested arrays because in MongoDB and mongoose, "find" returns an array of documents. If you had more than one Law in your Laws collection, it would be clearer since you would see that this second document is returned too.
To sum up, the "outer" array is the array of the documents found by MongoDB and the "inner" array is the section_text field you want.
Use findOne if you want a document to be returned instead of an array of documents.

Is it possible to add an additional piece of logic to a "#hapi/joi" schema object?

const Joi = require('#hapi/joi');
function validateInput(input)({
const schema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
numApples: Joi.number().min(1).max(5).required(),
numOranges: Joi.number().min(1).max(5).required()
});
return schema.validate(input);
}
What I'm trying to do is throw an error if the total apples and oranges doesn't equal 3 or more.
Is it possible to add custom logic this way using #hapi/joi?
You could define a new value numApplesAndOranges and set it equal to numApples + numOranges before including it in your schema.
You can now test like so: numApplesAndOranges: Joi.number().min(3).required().
Full code would look like:
const Joi = require('#hapi/joi');
function validateInput(input)({
const numApplesAndOranges = input.numApples + input.numOranges;
const schema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
numApples: Joi.number().min(1).max(5).required(),
numOranges: Joi.number().min(1).max(5).required(),
numApplesAndOranges: Joi.number().min(3).required()
});
return schema.validate({...input, numApplesAndOranges});
}
You can do something similar to below:
const Joi = require('#hapi/joi');
function validateInput(input) {
// custom validation function by hapi/joi
const sum = function(fields = [], maxValue) {
return function(value, helpers) {
const sum = 0
fields.forEach(f => {
sum = sum + +input[f];
});
if (sum > maxValue) {
return helpers.error('any.invalid');
}
}
}
const schema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
numApples: Joi.number().min(1).max(5).required(),
numOranges: Joi.number().min(1).max(5).required().custom(sum(['numApples', 'numOranges'], 3))
});
return schema.validate(input);
You can learn more about custom validation in Joi here.
Hope this helps!

Resources