I am trying to update a sequelize database, where the fields that need to be updated is optional. The problem is that I have 3 fields that need to be updated which are all optional. I do not want to check each field one by one calling update method. Cause that will mean multiple call to the api. Sample raw body input in JSON
{
"authorIds": [1, 5],
"tags": ["tech", "health"],
"text": "Some very short blog post text here."
}
Any of these fields can be optional. This is what I have so far
const { authorIds, tags, text } = req.body;
// case where all fields came in
if (authorIds && tags && text) {
try {
const ids = authorIds.join(',');
const tagValue = tags.join(',');
await Post.update(
{ authorIds: ids, tags: tagValue, text: text },
{ where: { id: postId } }
);
} catch (error) {
res.json({ error: 'Please check your body format' });
}
}
Note I am using SQLite, so I can not store arrays, that why am making the inputs. into string
Thanks
You can easily construct an object that you need to pass as the first argument to update dynamically:
if (authorIds || tags || text) {
try {
const fieldsToUpdate = {}
if (authorIds && authorIds.length) {
const ids = authorIds.join(',');
fieldsToUpdate.authorIds = ids;
}
if (tags && tags.length) {
const tagValue = tags.join(',');
fieldsToUpdate.tags = tagValue;
}
if (text) {
fieldsToUpdate.text = text;
}
await Post.update(
fieldsToUpdate,
{ where: { id: postId } }
);
} catch (error) {
res.json({ error: 'Please check your body format' });
}
}
Also you can try to use object deconstruction along with ternary operators to combine all fields right in the update call.
...(authorIds && authorIds.length ? { authorIds: authorIds.join(',') } : {}).
Related
Bootcamp student here. I seem to be having trouble passing in the result of function renderBadge(license) to the generateREADME function. I am using inquirer to grab inputs, and generate a readme. The functions renderBadge() and licenseLink() is solely pertaining to license portion of the inquirer. However, I can't seem to pass this info along and display it to the generating function. Is there a way to do this? What am I doing wrong? Thanks in advance.
Upon function execution, the ${badge} seems to be undefined.
const inquirer = require("inquirer");
const fs = require("fs");
const generateREADME = ({ title, description, installation, usage, contributions, tests, license, github, email, badge,}) =>
`# ${title}
${badge}
## Description
${description}
(Rest of Readme Generation here)
`
inquirer
.prompt([
{
(other prompts here)
},
{
type: "list",
name: "license",
message: "What license is your project?",
choices: [
"Apache 2.0",
"Boost",
"GNU AGPL v3",
"MIT",
"Perl",
"other",
],
validate: (licenseInput) => {
if (licenseInput) {
return true;
} else {
console.log(`Please enter your project's license!`);
return false;
}
},
}
])
.then((answers) => {
const readmePageContent = generateREADME(answers);
renderBadge(answers)
fs.writeFile('README.md', readmePageContent, (err) => {
err ? console.log(err) : console.log('Successfully generated README!')
})
})
function renderBadge(license) {
let badge = ''
if (license === 'Apache 2.0') {
badge = `![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)]`
} else if (license === 'Boost') {
badge = `![License](https://img.shields.io/badge/License-Boost_1.0-lightblue.svg)]`
} else if (license === 'GNU APGL v3') {
badge = `![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)]`
} else if (license === 'MIT') {
badge = `![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)]`
} else if (license === 'Perl') {
badge = `![License: Artistic-2.0](https://img.shields.io/badge/License-Perl-0298c3.svg)]`
} else {
badge = ''
}
return badge;
generateREADME(badge)
}
The main issue here is the way you pass/accept arguments.
answers is an object containing the key/value-pairs an example could be:
const answers = {
title: "Hello World",
description: "Hello World, this is a test description",
// ...
license: "GNU AGPL v3",
};
You then pass the answers object to renderBadge.
renderBadge(answers)
However in renderBadge you expect license as the sole argument.
function renderBadge(license) {
// ...
}
Since you passed the whole answers object, that is what you will receive. Meaning that the licence parameter will contain the answers object.
To fix this you should pass just the license to renderBadge, not all the answers. So use renderBadge(answers.license) instead.
Alternatively you could also use object destructuring like you did in generateREADME, and define renderBadge as:
function renderBadge({ license }) {
// ...
}
If you choose to use object destructuring, you should still pass the full answers object to renderBadge, so renderBadge(answers).
The second, non-essential mistake is:
return badge;
generateREADME(badge) // never executed
The line after the return is never executed. This doesn't really break anything, since you didn't need that line anyways, so it can just be removed.
Lastly, and probably most importantly the order of the following lines are incorrect.
const readmePageContent = generateREADME(answers);
renderBadge(answers.license) // after the first fix
The renderBadge() call should be made before you render the readme file, the resulting contents should then be passed as argument to generateREADME().
const badge = renderBadge(answers.license);
const readmePageContent = generateREADME({ ...answers, badge });
This uses the spread syntax in object literals combined with the property definition shorthand to pass a single object, containing al the required arguments.
So the final result might look like this (with minimum changes):
const inquirer = require("inquirer");
const fs = require("fs");
const generateREADME = ({title, description, installation, usage, contributions, tests, license, github, email, badge,}) => (
`# ${title}
${badge}
## Description
${description}
(Rest of Readme Generation here)
`
);
inquirer.prompt([
{
(other prompts here)
},
{
type: "list",
name: "license",
message: "What license is your project?",
choices: [
"Apache 2.0",
"Boost",
"GNU AGPL v3",
"MIT",
"Perl",
"other",
],
validate: (licenseInput) => {
if (licenseInput) {
return true;
} else {
console.log(`Please enter your project's license!`);
return false;
}
},
}
]).then((answers) => {
const badge = renderBadge(answers.license); // pass only the license, not all the anwers
const readmePageContent = generateREADME({ ...answers, badge }); // pass the answers combined with the badge
fs.writeFile('README.md', readmePageContent, (err) => {
err ? console.log(err) : console.log('Successfully generated README!')
})
});
function renderBadge(license) {
let badge = ''
if (license === 'Apache 2.0') {
badge = `![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)]`
} else if (license === 'Boost') {
badge = `![License](https://img.shields.io/badge/License-Boost_1.0-lightblue.svg)]`
} else if (license === 'GNU APGL v3') {
badge = `![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)]`
} else if (license === 'MIT') {
badge = `![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)]`
} else if (license === 'Perl') {
badge = `![License: Artistic-2.0](https://img.shields.io/badge/License-Perl-0298c3.svg)]`
} else {
badge = ''
}
return badge; // removal of generateREADME
}
When you put a return statement in a function, all the code below it doesn't execute.
In your renderBadge function, you invoke generateREADME(badge) after the return statement. So it never runs:
function renderBadge(license) {
let badge = ''
...
} else {
badge = ''
}
return badge;
generateREADME(badge) // this line doesn't execute
}
To call generateREADME() with the renderBadge function output, you need to remove the generateREADME(badge) statement:
function renderBadge(license) {
let badge = ''
...
} else {
badge = ''
}
return badge;
}
After that, call the function in the relevant place and store the output in the variable like so:
...
.then((answers) => {
const readmePageContent = generateREADME(answers);
input = renderBadge(answers)
generateREADME(input)
Or you can do it succinctly like this:
...
generateREADME(renderBadge(answers))
...
I'm using Azure search on my project, and I want to do an autocomplete text field, it works as expected. here's the code :
const suggestItems = async (req, res) => {
try {
// Reading inputs from HTTP Request
const q = (req.query.q || (req.body && req.body.q));
const top = (req.query.top || (req.body && req.body.top));
const suggester = (req.query.suggester || (req.body && req.body.suggester));
// Let's get the top 5 suggestions for that search term
const suggestions = await client.suggest(q, suggester, { top: parseInt(top) });
//const suggestions = await client.autocomplete(q, suggester, {top: parseInt(top)});
console.log(suggestions.results)
return res.status(status.OK)
.json({ suggestions: suggestions.results})
} catch (error) {
handleError(res, error)
}
}
her's the result :
[
{ text: 'Alpha Aromatics (MA)', document: { id: '4' } },
{ text: 'Alpha Aromatics (USA)', document: { id: '5' } },
{ text: 'Art Land - Winter Palace', document: { id: '6' } },
{ text: 'Alpha Aromatics (USA)', document: { id: '3' } }
]
here's the quesry passed by postman :
{
"q":"ar","top":5,"suggester":"sg"
}
but the problem is , on the result I have just the text and the id of the document , I'm looking for other fields like status for example, how can get that please ?
I am guessing "Status" is one of your index fields, from the question. You need to make sure you mark the fields you need to be returned in the results as retrievable in your index definition. It looks you only have text and id fields as retrievable. For more information: https://learn.microsoft.com/en-us/azure/search/search-what-is-an-index.
Example:
Let's say we query the server with this request, we only want to get the following user's Email, My current implementation requests the whole User object from the MongoDB, which I can imagine is extremely inefficient.
GQL
{
user(id:"34567345637456") {
email
}
}
How would you go about creating a MongoDB filter that would only return those Specified Fields? E.g,
JS object
{
"email": 1
}
My current server is running Node.js, Fastify and Mercurius
which I can imagine is extremely inefficient.
Doing this task is an advanced feature with many pitfalls. I would suggest starting building a simple extraction that read all the fields. This solution works and does not return any additional field to the client.
The pitfalls are:
nested queries
complex object composition
aliasing
multiple queries into one request
Here an example that does what you are looking for.
It manages aliasing and multiple queries.
const Fastify = require('fastify')
const mercurius = require('mercurius')
const app = Fastify({ logger: true })
const schema = `
type Query {
select: Foo
}
type Foo {
a: String
b: String
}
`
const resolvers = {
Query: {
select: async (parent, args, context, info) => {
const currentQueryName = info.path.key
// search the input query AST node
const selection = info.operation.selectionSet.selections.find(
(selection) => {
return (
selection.name.value === currentQueryName ||
selection.alias.value === currentQueryName
)
}
)
// grab the fields requested by the user
const project = selection.selectionSet.selections.map((selection) => {
return selection.name.value
})
// do the query using the projection
const result = {}
project.forEach((fieldName) => {
result[fieldName] = fieldName
})
return result
},
},
}
app.register(mercurius, {
schema,
resolvers,
graphiql: true,
})
app.listen(3000)
Call it using:
query {
one: select {
a
}
two: select {
a
aliasMe:b
}
}
Returns
{
"data": {
"one": {
"a": "a"
},
"two": {
"a": "a",
"aliasMe": "b"
}
}
}
Expanding from #Manuel Spigolon original answer, where he stated that one of the pitfalls of his implementation is that it doesn't work on nested queries and 'multiple queries into one request' which this implementation seeks to fix.
function formFilter(context:any) {
let filter:any = {};
let getValues = (selection:any, parentObj?:string[]) => {
//selection = labelSelection(selection);
selection.map((selection:any) => {
// Check if the parentObj is defined
if(parentObj)
// Merge the two objects
_.merge(filter, [...parentObj, null].reduceRight((obj, next) => {
if(next === null) return ({[selection.name?.value]: 1});
return ({[next]: obj});
}, {}));
// Check for a nested selection set
if(selection.selectionSet?.selections !== undefined){
// If the selection has a selection set, then we need to recurse
if(!parentObj) getValues(selection.selectionSet?.selections, [selection.name.value]);
// If the selection is nested
else getValues(selection.selectionSet?.selections, [...parentObj, selection.name.value]);
}
});
}
// Start the recursive function
getValues(context.operation.selectionSet.selections);
return filter;
}
Input
{
role(id: "61f1ccc79623d445bd2f677f") {
name
users {
user_name
_id
permissions {
roles
}
}
permissions
}
}
Output (JSON.stringify)
{
"role":{
"name":1,
"users":{
"user_name":1,
"_id":1,
"permissions":{
"roles":1
}
},
"permissions":1
}
}
I'm trying to set an unlimited query parameter in express js.But I couldn't figure out how should I implement that in my code. I'm using MongoDB aggeration
I want to build unlimited facets searched with multiple $match stage
Which works like this:
'http://localhost:4000/search?text=mango'
'http://localhost:4000/search?text=mango&key=brand&value=rasna' //unlimited facets.
'http://localhost:4000/search?text=mango&key=brand&value=rasna&key=color&value=yellow' //unlimited facet parameters
Here's my code to do this:
app.get("/search", async(request, response) => {
try {
const textsearch = request.query.text;
var keystore = request.query.key; //storing `key` in 'keystore'
var valuestore = request.query.value; //storing `value` in `valuestore`
if (keystore, valuestore) {
facetjson = [
{
'$match': {
[keystore]: `${valuestore}` //Storing key and value in $match
}
}
]
const Pipeline = [{
'$search': {
'text': {
'query': `${textsearch}`,
'path': 'title',
}
}
},
{
'$limit': 5
}
]
//Pushing 'facetjson' array into Pipeline array to make a filtered search possible.
const newitem = insert(Pipeline, Pipeline.length - 1, facetjson)
let result = collection.aggregate(newitem).toArray();
response.send(result);
} else {
const Pipeline = [{
'$search': {
'text': {
'query': `${textsearch}`,
'path': 'title',
}
}
},
{
'$limit': 5
}
]
let result = collection.aggregate(Pipeline).toArray();
response.send(result);
};
} catch (error) {
response.status(500).send({ message: error.message });
}
})
(JSFIDDLE code Example)[https://jsfiddle.net/divyanshuking/z0vo589e/]
==> I know that I've to pass $match in the Pipeline array each time for single Key , Value Pair. Doing many google searches I've figured out that I've to use the Rest Parameter (...keystore,...valuestore). But I didn't know how to implement this. Have you guys any better idea to do solve this problem? Pls help me:
Why don’t you use forEach or something
function endPoint (req, res) {
const queriesFound ={}
req.query.forEach(query=>{
queriesFound[query]=query;
}
QueriesFound will be an object
{ “Name”:”Name”, “AnotherParam”:”AnotherParam” }
}
//QueriesFound will be an object
{
“Name”:”Name”,
“AnotherParam”:”AnotherParam”
}
Your request URL has a wrong structure for query parameters. If you want to pass multiple kay/value pairs in URL, the correct structure is like this:
'http://localhost:4000/search?text=mango&brand=rasana&color=yellow
This code should work with this URL structure:
app.get("/search", async(request, response) => {
try {
//We need "search pipeline stage" in all conditions. whether we got a key/value pair in query or not.
//so we use "search stage" when declare pipeline array;
let pipeline = [{
'$search': {
'text': {
'query': `${request.query.text}`,
'path': 'title',
}
}
}];
//If there are keys/values pairs in the query parameters, we add match stage to our pipeline array;
if(request.query) {
let match = {}, hasMatchSatge = false;
for(let item in request.query){
if(item !=== 'text'){
match[item] = request.query[item];
hasMatchStage = true;
}
}
if(hasMatchStage) pipeline.push({'$match': match});
}
//Finally, we add our "limit stage" to the pipeline array;
pipeline.push({'$limit' : 5});
let result = collection.aggregate(pipeline).toArray();
response.status(200).send(result);
} catch (error) {
response.status(500).send({ message: error.message });
}
})
This is my schema:
detail: [{
quantity: Number,
product:{
name: String,
code: Number,
price: Number
},
subtotal: Number
]}
This is my validations method
const validations = values => {
const errors = {
product: {}
}
if(!values.detail ||
!values.detail.length){
errors.detail = {_error: 'at
least one item must be
required'}
}
else{
const detailArrayErrors = []
values.detail.forEach(
(item,itemIndex) =>{
const detailErrors = {}
if(!item || !item.quantity){
detailErrors.quantity
='Required'
detailArrayErrors[itemIndex]=
detailErrors
}
if(!item || !item.subtotal){
detailErrors.subtotal
= 'required'
detailArrayErrors[itemIndex]
= detailErrors
}
//How can I access product
// in validations method
})
if(detailArrayErrors.length)
errors.detail =
detailArrayErrors
}
return errors;
}
export default validations;
product is my nested json object inside detail. Detail is an array. I want to validate product. How can I access a nested json object inside an array for validate it?
I've tried using for... of, but it makes not results.
I was searching on web but I couldn't find nothing.
How can I do that?
Anyone who knows?
values.detail.forEach(d=> console.log(d.product));
To get array of invalid e.g.:
let invalidItems = values.detail.filter(d => !d.product || !d.quantity ||
!d.product.name);
To do something on each item in array:
this.values.detail.forEach(i =>
{
let detailErrors = {'quantity': null, product: null};
if (!i.quantity)
{
detailErrors.quantity= 'Required'
}
if (!i.product)
{
detailErrors.product = 'Required'
}
if (i.product && !i.product.price)
{
detailErrors.product = {'price' :'Required'}
}
});