Displaying specific id from json file with reactjs - node.js

How could I display data from a json file using reactjs.
I would like to display a specific value on my website using a json file.
Using this example json file.
const data = [
{
items: {
item: [
{
id: "0001",
type: "donut",
name: "Cake",
ppu: 0.55,
batters: {
batter: [
{
id: "377",
type: "Regular",
},
{
id: "609",
type: "Chocolate",
},
{
id: "788",
type: "Blueberry",
},
{
id: "809",
type: "Devil's Food",
},
],
},
topping: [
{
id: "5001",
type: "None",
},
{
id: "5002",
type: "Glazed",
},
{
id: "5005",
type: "Sugar",
},
{
id: "5007",
type: "Powdered Sugar",
},
{
id: "5006",
type: "Chocolate with Sprinkles",
},
{
id: "5003",
type: "Chocolate",
},
{
id: "5004",
type: "Maple",
},
],
},
],
},
},
];
How could I access for example items.item.batters.batter for id 609 and display the type in this case "Chocolate" using reactjs without using something like this:
batters.batter[1].type
and instead displaying it for the specific id (609)?

If you need to find a specific object, that has a specific value in a list of objects, you can do it as such:
const [batter, setBatter] = useState([
{
id: "377",
type: "Regular"
},
{
id: "609",
type: "Chocolate"
},
{
id: "788",
type: "Blueberry"
},
{
id: "809",
type: "Devil's Food"
}
]);
const [results, setResults] = useState([]);
const searchInList = () => {
let _results = [];
let toSearch = 609;
for (var i = 0; i < batter.length; i++) {
for (key in batter[i]) {
if (batter[i][key].indexOf(toSearch) != -1) {
_results.push(batter[i]);
}
}
}
};
setResults(_results);
useEffect(() => {
searchInList();
}, [batter]);
You can read more about this here: JS search in object values

Related

Nested object iteration

Fighting with this one for a while now...
I'm trying to modify each image object by adding more keys-values and changing some values.
As the keys are not having the same names, I need to identify the images by .hasOwnProperty('formats')
The object can be even more nested not only with key-values pairs but with arrays as well (this is where it gets complicated)
const object = {
name: "help",
mainImage: {
name: "picture1",
formats: {
small: {
name: "small_picture1",
},
medium: {
name: "medium_picture1",
},
large: {
name: "large_picture1",
},
},
},
carousel: [
{
name: "picture2",
formats: {
small: {
name: "small_picture2",
},
medium: {
name: "medium_picture2",
},
large: {
name: "large_picture2",
},
},
},
{
name: "picture3",
formats: {
small: {
name: "small_picture3",
},
medium: {
name: "medium_picture3",
},
large: {
name: "large_picture3",
},
},
},
],
nestedPage: {
name: "moreHelp",
mainImage: {
name: "picture4",
formats: {
small: {
name: "small_picture4",
},
medium: {
name: "medium_picture4",
},
large: {
name: "large_picture4",
},
},
},
carousel: [
{
name: "picture5",
formats: {
small: {
name: "small_picture5",
},
medium: {
name: "medium_picture5",
},
large: {
name: "large_picture5",
},
},
},
{
name: "picture5",
formats: {
small: {
name: "small_picture5",
},
medium: {
name: "medium_picture5",
},
large: {
name: "large_picture5",
},
},
},
],
},
};
This is what I tried, but it only goes to a certain level.
const customSanitizer = (entity) => {
const isObject = (value) => {
return !!(value && typeof value === "object" && !Array.isArray(value));
};
if (isObject(entity)) {
Object.entries(entity).forEach(([key, value]) => {
if (isObject(entity[key])) {
if (value.hasOwnProperty("formats")) {
entity[key] = formatImageObject(value);
} else {
customSanitizer(entity[key]);
}
} else {
customSanitizer(entity[key]);
}
});
}
if (Array.isArray(entity)) {
entity.forEach((node) => {
customSanitizer(node);
});
}
}

Group nested fields with aggregation and return results with others fields mongo db

I need to change the structure of some field in my mongoDB document.
Here the sample:
[
{
_id: "ObjectId('997v2ha1cv9b0036fa648zx3')",
title: "Adidas Predator",
size: "8",
colors: [
{
hex: "005FFF",
name: "Blue"
},
{
hex: "FF003A",
name: "Red"
},
{
hex: "FFFE00",
name: "Yellow"
},
{
hex: "07FF00",
name: "Green"
},
],
extras: [
{
description: "laces",
type: "exterior"
},
{
description: "sole",
type: "interior"
},
{
description: "logo"
},
{
description: "stud",
type: "exterior"
}
],
media: {
images: [
{
url: "http://link.com",
type: "exterior"
},
{
url: "http://link3.com",
type: "interior"
},
{
url: "http://link2.com",
type: "interior"
},
{
url: "http://link4.com",
type: "exterior"
}
]
}
}
];
My goal is to group some fields:
colors need to be and array with just the colors,
extras need to be an array with 3 object each one for a "type" (interior, exterior, null)
the same for images that is inside media
Here what I expected:
{
_id: "ObjectId('997b5aa1cv9b0036fa648ab5')",
title: "Adidas Predator",
size: "8",
colors: ["Blue", "Red", "Yellow", "Green"],
extras: [
{type: exterior, description: ["laces", "stud"]},
{type: interior, description: ["sole"]},
{type: null, description: ["logo"]}
],
images: [
{type: exterior, url: ["http://link.com", "http://link4.com"]},
{type: interior, url: ["http://link2.com", "http://link3.com"]},
]
};
With my code I can achieve my goal but I don't understand how to show all the information together through the pipeline.
Here my code:
db.collection.aggregate([
{
$project: {
title: 1,
size: 1,
colors: "$colors.name",
extras: 1,
media: "$media.images"
},
},
{
$unwind: "$media"
},
{
$group: {
_id: {
type: "$media.type",
url: "$media.url",
},
},
},
{
$group: {
_id: "$_id.type",
url: {
$push: "$_id.url"
},
},
},
]);
The result is:
[
{
_id: "exterior",
url: [
"http://link.com",
"http://link4.com"
]
},
{
_id: "interior",
url: [
"http://link3.com",
"http://link2.com"
]
}
];
If I do the same thing with extras I get the same (correct) structure.
How can I show all the data together like in the expected structure?
Thanks in advice.
The strategy will be to maintain the require parent fields throughout the pipeline using $first to just grab the initial value, It ain't pretty but it works:
db.collection.aggregate([
{
"$addFields": {
colors: {
$map: {
input: "$colors",
as: "color",
in: "$$color.name"
}
}
}
},
{
$unwind: "$extras"
},
{
"$addFields": {
imageUrls: {
$map: {
input: {
$filter: {
input: "$media.images",
as: "image",
cond: {
$eq: [
"$$image.type",
"$extras.type"
]
}
}
},
as: "image",
in: "$$image.url"
}
}
}
},
{
$group: {
_id: {
_id: "$_id",
extraType: "$extras.type"
},
extraDescriptions: {
"$addToSet": "$extras.description"
},
imageUrls: {
"$first": "$imageUrls"
},
colors: {
$first: "$colors"
},
size: {
$first: "$size"
},
title: {
$first: "$title"
}
}
},
{
$group: {
_id: "$_id._id",
colors: {
$first: "$colors"
},
size: {
$first: "$size"
},
title: {
$first: "$title"
},
images: {
$push: {
type: {
"$ifNull": [
"$_id.extraType",
null
]
},
url: "$imageUrls"
}
},
extras: {
$push: {
type: {
"$ifNull": [
"$_id.extraType",
null
]
},
description: "$extraDescriptions"
}
}
}
}
])
Mongo Playground
You can try $function operator, to defines a custom aggregation function or expression in JavaScript.
$project to show required fields and get array of colors name
$function, write your JS logic if you needed you can sort this logic of group, it will return result with 2 fields (extras, images)
$project to show required fields and separate extras and images field from result
db.collection.aggregate([
{
$project: {
title: 1,
size: 1,
colors: "$colors.name",
result: {
$function: {
body: function(extras, images) {
function groupBy(objectArray, k, v) {
var results = [], res = objectArray.reduce((acc, obj) => {
if (!acc[obj[k]]) acc[obj[k]] = [];
acc[obj[k]].push(obj[v]);
return acc;
}, {});
for (var o in res) {
results.push({ [k]: o === 'undefined' ? null : o, [v]: res[o] })
}
return results;
}
return {
extras: groupBy(extras, 'type', 'description'),
images: groupBy(images, 'type', 'url')
}
},
args: ["$extras", "$media.images"],
lang: "js"
}
}
}
},
{
$project: {
title: 1,
size: 1,
colors: 1,
extras: "$result.extras",
images: "$result.images"
}
}
])
Playground
IMPORTANT:
Executing JavaScript inside an aggregation expression may decrease performance. Only use the $function operator if the provided pipeline operators cannot fulfill your application's needs.

mongodb insert data into the array of objects and update it

I need to make a vote, it looks like an array of objects, look like the user’s ID and the value that he set.
If the user has already voted, but changed his value, you need to change the value of the rate in the array of objects for this user.
I need to make an array of objects into which data will be inserted like this {rate: 3, user: "asdr2r24f2f42f24"} and if the user has already voted in this array, then you need to change the value rate of the given user
I already tried to do something, but it seems to me you can write something better, can you help?
JSON https://jsoneditoronline.org/?id=442f1dae0b2d4997ac69d44614e55aa6
router.post('/rating', (req, res) => {
console.log(req.body)
// { id: 'f58482b1-ae3a-4d8a-b53b-ede80fe1e225',
// rating: 5,
// user: '5e094d988ddbe02020e13879' }
Habalka.find({
_id: req.body.id
})
.then(habalka => {
// here I need to check whether the user has already voted or not, and from this whether to add an object with it or update the number
Habalka.updateOne(
{_id: req.body.id},
{$push: {rating: {rate: req.body.rating, user: req.body.user}}}
)
.then(e => {
console.log(e)
})
});
});
Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const HabalkaSchema = new Schema({
_id: {
type: String
},
bio: {
firstname: String,
lastname: String,
middlename: String,
company: String
},
rating: [
],
files: [
{
_id: {
type: String
},
destination: {
type: String
},
filename: {
type: String
},
path: {
type: String
},
folder: {
type: String
},
info: {
size: {
type: Number
},
mimetype: {
type: String
},
encoding: {
type: String
},
originalname: {
type: String
},
fieldname: {
type: String
},
},
date: {
type: Date,
default: Date.now
},
bio: {
type: Object
},
userId: String,
guessId: {},
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Habalka = mongoose.model('habalka', HabalkaSchema);
This is an aggregation query which inserts a new user or updates the rating of existing user in the rating array:
The req.body.id, req.body.user and req.body.rating are set as follows for the example code:
var ID = 1, INPUT_USER = "new user", INPUT_RATE = 5;
const matchStage = { $match: { _id: ID } };
const facetStage = {
$facet: {
new_user: [
{ $match: { "rating.user": { $not: { $eq: INPUT_USER } } } },
{ $addFields: { rating: { $concatArrays: [ "$rating", [ { user: "new user", rate: INPUT_RATE } ] ] } } },
],
user: [
{ $match: { "rating.user": INPUT_USER } },
{ $addFields: {
rating: {
$map: {
input: "$rating",
as: "r",
in: {
$cond: [ { $eq: [ "$$r.user", INPUT_USER ] },
{ user: "$$r.user", rate: { $add: [ "$$r.rate", INPUT_RATE ] } },
"$$r"
]
}
}
}
} }
]
}
};
const projectStage = {
$project: {
result: { $arrayElemAt: [ { $concatArrays: [ "$user", "$new_user" ] }, 0 ] }
}
};
const queryPipeline = [
matchStage,
facetStage,
projectStage
];
// Run the aggregation query and get the modified document
// after applying the user and rate data in the rating array.
// The result of the aggregation is used to update the collection.
col.aggregate(queryPipeline).toArray( ( err, docs ) => {
console.log("Aggregation output:");
console.log( JSON.stringify( docs[0] ) );
// Update the aggregate result to the collection.
col.updateOne( { _id: docs[0].result._id },
{ $set: { rating: docs[0].result.rating } },
( err, updateResult ) => {
console.log( 'Updated count: ', updateResult.matchedCount );
}
);
callback(docs);
} );
Example collection document:
{ "_id" : 1, "rating" : [ { "user" : "user1", "rate" : 2 } ] }
If the input is var ID = 1, INPUT_USER = "new user", INPUT_RATE = 5; the updated document will be:
{ "_id" : 1, "rating" : [ { "user" : "user1", "rate" : 2 }, { "user" : "new user", "rate" : 5 } ] }
If the input is var ID = 1, INPUT_USER = "user1", INPUT_RATE = 5; the updated document will be:
{ "_id" : 1, "rating" : [ { "user" : "user1", "rate" : 7 } ] }

how to pass param while getting billing data azure function

For getting the azure subscription billing data I am passing params it's not apply
const axios = require('axios');
let usage = [];
function getUsage(subscriptionId, accessToken) {
const url = `https://management.azure.com/subscriptions/${subscriptionId}/providers/Microsoft.Consumption/usageDetails?api-version=2019-01-01`
const options = {
headers: {
Authorization: `Bearer ${accessToken}`
}
}
// I am trying to pass data for filtering
const params ={
type: "Usage",
timeframe: "MonthToDate",
dataset: {
granularity: "None",
aggregation: {
totalCost: {
name: "PreTaxCost",
function: "Sum"
}
},
grouping: [
{
type: "Dimension",
name: "MeterCategory"
},
{
type: "Dimension",
name: "ResourceLocation"
},
{
type: "Dimension",
name: "ResourceGroup"
} ]
}
}
axios.get(url, options, params).then(response => {
console.log('response.data------', response.data);
}).catch(error => {
console.log(error);
});
}
//calling API here
await getUsage(
"subscriptionId",
"access-token"
);
Please change the format of your params from
params ={ type: "Usage", timeframe: "MonthToDate", dataset: { granularity: "None", aggregation: { totalCost: { name: "PreTaxCost", function: "Sum" } }, grouping: [ { type: "Dimension", name: "MeterCategory" }, { type: "Dimension", name: "ResourceLocation" }, { type: "Dimension", name: "ResourceGroup" } ] } }
to
params ={params:{ type: "Usage", timeframe: "MonthToDate", dataset: { granularity: "None", aggregation: { totalCost: { name: "PreTaxCost", function: "Sum" } }, grouping: [ { type: "Dimension", name: "MeterCategory" }, { type: "Dimension", name: "ResourceLocation" }, { type: "Dimension", name: "ResourceGroup" } ] } }}
Just add a "{params:" at the beginning and add a "}" at the end.
I think "params" should be a parameter of it. Please have a try.

How can I upsert multiple objects with MongoDB & Node.js?

Let's say I have an array of Movie genres like so:
[
{ id: 28, name: 'Action' },
{ id: 12, name: 'Adventure' },
{ id: 16, name: 'Animation' },
{ id: 35, name: 'Comedy' },
{ id: 80, name: 'Crime' },
{ id: 99, name: 'Documentary' },
{ id: 18, name: 'Drama' },
{ id: 10751, name: 'Family' },
{ id: 14, name: 'Fantasy' },
{ id: 10769, name: 'Foreign' },
{ id: 36, name: 'History' },
{ id: 27, name: 'Horror' },
{ id: 10402, name: 'Music' },
{ id: 9648, name: 'Mystery' },
{ id: 10749, name: 'Romance' },
{ id: 878, name: 'Science Fiction' },
{ id: 10770, name: 'TV Movie' },
{ id: 53, name: 'Thriller' },
{ id: 10752, name: 'War' },
{ id: 37, name: 'Western' }
]
and I have a connection to a MongoDB (v3.2) instance: db, and I'm using the standard mongodb Node.js driver (const mongodb = require('mongodb').MongoClient).
What I want to be able to do is one bulk upsert operation onto a collection, say genres, where the _id field maps to the id field of our genre objects.
Now, I know I could loop through each item in the array, and do a simple upsert:
for (let i = 0; i < genres.length; i++) {
await db.collection('genres').update(
{ _id: genres[i].id },
genres[i],
{ upsert: true }
);
}
But this feels wasteful and wrong.
Is there an easier way to do what should be a relatively simple task?
Thanks
Use the bulkWrite API to carry out the updates:
var bulkUpdateOps = genres.map(function(doc) {
return {
"updateOne": {
"filter": { "_id": doc.id },
"update": { "$set": { "name": doc.name } },
"upsert": true
}
};
});
db.collection('genres').bulkWrite(bulkUpdateOps, function(err, r) {
// do something with result
})
If you're dealing with larger arrays i.e. > 1000 then consider sending the writes to the server in batches of 500 which gives you a better performance as you are not sending every request to the server, just once in every 500 requests:
var bulkUpdateOps = [],
counter = 0;
genres.forEach(function(doc) {
bulkUpdateOps.push({
"updateOne": {
"filter": { "_id": doc.id },
"update": { "$set": { "name": doc.name } },
"upsert": true
}
});
counter++;
if (counter % 500 == 0) {
db.collection('genres').bulkWrite(bulkUpdateOps, function(err, r) {
// do something with result
});
bulkUpdateOps = [];
}
})
if (counter % 500 != 0) {
db.collection('genres').bulkWrite(bulkUpdateOps, function(err, r) {
// do something with the result
});
}
I would try:
db.collection('genres').update(genres, {upsert: true, multi: true});
Note: untested code...
UPDATE: to remap id field to _id:
var _genres = genres.map(function(genre) {
return { _id: genre.id, name: genre.name };
});
db.collection('genres').update(_genres, {upsert: true, multi: true});

Resources