JSON variable block - json

I am having json template for elasticsearch query:
var getTemplate = function (agg, filter_term) {
var template = {
"size": 0,
track_total_hits: true,
query: {
bool: {
must: [
{
"query_string": {
"query": filter_term
}
},
aggs: {
"nested" : { "value_count" : { "field" : agg } },
agg: {
terms: {
field: agg,
size: 10,
order: {
"_count": "desc"
}
}
}
}
};
return template;
Sometimes, I want to skip the block with query_string so I want to pass in in the function call. So instead of this:
const callTerminated = agg_filter.getTemplate( 'bbbbb', 'aaa');
Do that:
const callTerminated = agg_filter.getTemplate( 'bbbbb', {"query_string": {"query": aaa }});
Or:
const callTerminated = agg_filter.getTemplate( 'bbbbb', "");
But how to change the template query? It wants comma after variable but when I skip query_string, I don't need comma.
New json template:
var getTemplate = function (agg, filter_term) {
var template = {
"size": 0,
track_total_hits: true,
query: {
bool: {
must: [
filter_term //here it wants comma
aggs: {
"nested" : { "value_count" : { "field" : agg } },
agg: {
terms: {
field: agg,
size: 10,
order: {
"_count": "desc"
}
}
}
}
};
return template;
So how to do it?

Related

How to keep empty cells as empty in JSON output instead of ignore the cell (Vue-XLSX)

I'm reading a excel file and converting it to JSON object using vue-xlsx in a Nuxt project. But there can be empty cells. Vue-xlsx ignores them.
Returned JSON object is like this:
[
{ "Header1": "a", "Header3": "c" },
{ "Header1": "d", "Header2": "e", "Header3": "f" },
{ "Header1": "g", "Header2": "h", "Header3": "i" }
]
I don't need to print this using 'xlsx-table' component because I need JSON object.
Tried to pass defval property as options on this way. But not success.
<section>
<input type="file" #change="onChange" />
<xlsx-read :options="options" :file="file">
<xlsx-json :sheet="selectedSheet" >
<template #default="{collection}">
<!-- <xlsx-table :sheet="selectedSheet" /> -->
<div>
{{ json = collection }}
</div>
</template>
</xlsx-json>
</xlsx-read>
</section>
<script>
export default {
data() {
return {
file: null,
options: {
defval: "",
},
json: null,
selectedSheet: 0,
};
},
}
</script>
I need some help to generate this kind of JSON object:
How Can I do this with Vue-XLSX.
This can be done with VanillaJS, no need to rely on vue-xlxs on this one.
If we do start from
[
{ "Header1": "a", "Header3": "c" },
{ "Header1": "d", "Header2": "e", "Header3": "f" },
{ "Header1": "g", "Header2": "h", "Header3": "i" }
]
We can have the following
<script>
export default {
data() {
return {
array: [
{ Header1: 'a', Header3: 'c' },
{ Header1: 'd', Header2: 'e', Header3: 'f' },
{ Header1: 'g', Header2: 'h', Header3: 'i' },
],
}
},
mounted() {
const neededHeaders = ['Header1', 'Header2', 'Header3']
const arrayWithFallback = this.array.map((row) => {
for (const headerKey of neededHeaders) {
if (!(headerKey in row)) {
// if you're missing one of the header, let's create a default value for it
row[headerKey] = 'your-fallback-here' // can also be an empty string of course
}
}
return row
})
console.log('arrayWithFallback', arrayWithFallback)
},
}
</script>
And it'll create this at the end
[
{ "Header1": "a", "Header2": "your-fallback-here", "Header3": "c" },
{ "Header1": "d", "Header2": "e", "Header3": "f" },
{ "Header1": "g", "Header2": "h", "Header3": "i" }
]
I used "parsed" event that was mentioned in the documentation for "xlsx-json" component. Here is the full Nuxt component for me:
If you have the "header info":
<template>
<div>
<h3>Import XLSX</h3>
<input type="file" #change="onChange" />
<xlsx-read :file="file">
<xlsx-json #parsed = "parsFunc">
<template #default="{collection}">
<div class="divShow">
{{ collection }}
</div>
</template>
</xlsx-json>
</xlsx-read>
</div>
</template>
<script>
/* put the address that matches for your app */
import { XlsxRead, XlsxJson } from "~/node_modules/vue-xlsx/dist/vue-xlsx.es.js";
export default {
components: {
XlsxRead,
XlsxJson
},
data() {
return {
file: null,
/* this array contains the exact words that you write on the first line of xlsx file */
headerData: ["Header1", "Header2", "Header3"],
};
},
computed: {
lengData: function() {
return this.headerData.length;
}
},
methods: {
onChange(event) {
this.file = event.target.files ? event.target.files[0] : null;
},
sortOutput(elemEach) {
/* this function sorts the new object */
let arrObj = Object.entries(elemEach);
arrObj.sort((a,b) => a[0].localeCompare(b[0]));
let arraySorted = Object.fromEntries(arrObj);
return arraySorted;
},
parsFunc(event) {
/* this function loops throught elements in "xlsx" file and replaces empty cells with "" */
let counter = 0;
event.forEach(element => {
for (let it = 0; it < this.lengData; it++) {
if( element[this.headerData[it]] == undefined ) {
element[this.headerData[it]] = "";
}
}
let sortedElem = this.sortOutput(element);
event[counter] = sortedElem;
counter++;
}); // end of forEach
}
}
};
</script>
<style scoped>
/* this is the styles that I used */
.divShow {
background-color: #865611;
padding: 50px;
color: white;
}
</style>
If you want to dynamically generate the "header info":
<template>
<div>
<h3>Import XLSX</h3>
<input type="file" #change="onChange" />
<xlsx-read :file="file">
<xlsx-json #parsed = "parsFunc">
<template #default="{collection}">
<div class="divShow">
{{ collection }}
</div>
</template>
</xlsx-json>
</xlsx-read>
</div>
</template>
<script>
/* put the address that matches for your app */
import { XlsxRead, XlsxJson } from "~/node_modules/vue-xlsx/dist/vue-xlsx.es.js";
export default {
components: {
XlsxRead,
XlsxJson
},
data() {
return {
file: null,
headerData: [],
};
},
computed: {
lengData: function() {
return this.headerData.length;
}
},
methods: {
onChange(event) {
this.file = event.target.files ? event.target.files[0] : null;
},
findHeader(elem, newArr) {
/* this function finds the headers of the "xlsx" file */
this.headerData = [];
let convertArr = Object.entries(elem);
convertArr.forEach(element2 => {
newArr.push( Object.entries(element2[1]).length );
});
let indexOfMaxValue = newArr.reduce((iMax, x, i, arr) => x > arr[iMax] ? i : iMax, 0);
let arrayExtract = Object.entries( convertArr[indexOfMaxValue][1] );
arrayExtract.forEach(element3 => {
this.headerData.push(element3[0]);
});
this.replaceEmpty(elem);
},
sortOutput(elemEach) {
/* this function sorts the new object */
let arrObj = Object.entries(elemEach);
arrObj.sort((a,b) => a[0].localeCompare(b[0]));
let arraySorted = Object.fromEntries(arrObj);
return arraySorted;
},
parsFunc(event) {
/* this function acts after submitting file and call another function to find "headers" */
let emptyArr = [];
this.findHeader(event, emptyArr);
},
replaceEmpty: function(eventPass) {
/* this function loops throught elements in "xlsx" file and replaces empty cells with "" */
let counter = 0;
eventPass.forEach(element => {
for (let it = 0; it < this.lengData; it++) {
if( element[this.headerData[it]] == undefined ) {
element[this.headerData[it]] = "";
}
}
let sortedElem = this.sortOutput(element);
eventPass[counter] = sortedElem;
counter++;
}); // end of forEach
} // end of replaceEmpty
}
};
</script>
<style scoped>
/* this is the styles that I used */
.divShow {
background-color: #865611;
padding: 50px;
color: white;
}
</style>
But for working on this version, you must have at least one full row in your xlsx file. For example this code does not work for this kind of xlsx file:

How to output object of an array in the root of the document in MongoDB using aggregation?

I have this document :
{"_id":"1", "elem":"ok",
"arrayOfObjects":[
{"type":"k","fieldx":"lol"},
{"type":"SndObject","fieldy":"foo"},
{"type":"Object1","fieldx":"bob"}
]
}
what is the aggregation to have this output:
{"_id":"1", "elem":"ok",
"Object1":[
{"type":"Object1","fieldx":"lol"},
{"type":"Object1","fieldx":"bob"}
],
"SndObject":[{"type":"SndObject","fieldy":"foo"}]
}
I found a way out, but it need me to know all the type i have:
{
"$addFields" : {
"Object1" : {
"$filter": {
"input": "$arrayOfObjects",
"as": "types",
"cond": {
"$and": [{ "$eq": [ "$$types.type", "Object1" ] }]
}
}
}
}
}
It would be best if i can loop over my arrayOfObjects and get the same result without pre knowledge of the type.
Might be there would be more easy option than this,
$unwind deconstruct arrayOfObjects array
$group by _id, type and elem, construct array of arrayOfObjects
$arrayToObject convert k and v from array to object
$group by _id and merge objects in root
db.collection.aggregate([
{ $unwind: "$arrayOfObjects" },
{
$group: {
_id: {
type: "$arrayOfObjects.type",
_id: "$_id"
},
elem: { $first: "$elem" },
arrayOfObjects: { $push: "$arrayOfObjects" }
}
},
{
$group: {
_id: "$_id._id",
elem: { $first: "$elem" },
arrayOfObjects: {
$mergeObjects: {
$arrayToObject: [[
{
k: "$_id.type",
v: "$arrayOfObjects"
}
]]
}
}
}
}
])
Playground

Printing Ajax results in HTML

I have this J Query code:
$(document).ready(function(){
var $outData = $('#data');
var ajaxUrl = 'url.json';
$.ajax(
{
type: 'GET',
url:ajaxUrl,
success: function (result) {
console.log(result.passwords);
}
})
}
);
JSON file looks like this:
{
"passwords":[
{
"value":"tom",
"count":"432517"
},
{
"value":"anaconda",
"count":"454658"
},
{
"value":"111111",
"count":"148079"
},
What I need to do, is to print out each of these objects to be printed out in an ordered list (for instance it should look like this:
tom 432517
anaconda 454658
111111 148079
So far, nothing I have tried works. Althoug, I can console.log the entire object. Any suggestions?
Example rendering:
var $outData = $('#data');
var ajaxUrl = 'url.json';
$.ajax(
{
type: 'GET',
url:ajaxUrl,
success: function (result) {
result.passwords.forEach(pwd => $outData.append(`<div>${pwd.value} ${pwd.count}</div>`));
}
})
}
);
you can create elements and append to dom when you are done,
const data = {
"passwords": [{
"value": "tom",
"count": "432517"
},
{
"value": "anaconda",
"count": "454658"
},
{
"value": "111111",
"count": "148079"
}
]
}
const ol = document.createElement("ol");
for (const {
value,
count
} of data.passwords) {
const li = document.createElement("li")
li.innerText = `${value} -> ${count}`
ol.appendChild(li)
}
document.querySelector("#root").appendChild(ol)
<div id="root"></div>

ExpressJS set the Depth of JSON Parsing

I want to set the depth of JSON parsing in Express middleware express.json().
For example, if I would set the option to parse the depth=1, then
'{ "email": { "$ne": "user#example.com" } }'
will be parsed to
{ email: "[object Object]" }
-- or --
When I set depth=2, then
'{ "email": { "$ne": "user#example.com" } }'
will be parsed to
{ email: { '$ne': 'user#example.com' } }
And so on,
In this case, there will be no issue of default depth, as the developer will be aware of how many nesting they will allow while development.
PS: It will prevent the application from being vulnerable to NoSQL Injection.
Just write you own middleware:
const get_depth = (obj) => {
let depth = 0
for(const key in obj) {
if( obj[key] instanceof Object ) {
depth = Math.max(get_depth(obj[key]), depth)
}
}
return depth+1
}
const depth_limit = 2
const limit_depth = function(req, res, next) {
if( get_depth(req.body) > depth_limit ) throw new Error("Possible NoSQL Injection")
next()
}
app.use(limit_depth)
Or, if you prefer "[object Object]":
let limit_depth = (obj, current_depth, limit) => {
for(const key in obj) {
if( obj[key] instanceof Object ) {
if( current_depth+1 === limit ) {
obj[key] = "[object Object]" // or something similar
}
else limit_depth(obj[key], current_depth+1, limit)
}
}
}
app.use(function(req, res, next) { limit_depth(req.body, 0, depth_limit); next() })
I write down the query, Maximum 6-8 depth goes. when use lookup inside the lookup.
const [result] = await Collection.aggregate([
{ $match:statusObj },
{
$project:{
_id:1,
name:1
}
},
{
$lookup:{
from:"articles",
let: { "cat_id":"$_id"},
pipeline:[
{
$match:{
$expr:{
$and: [
{ $eq: ["$category_id", "$$cat_id"] },
{ $eq: ["$isDeleted", false] },
{ $eq: ["$type", type] }
]
}
}
},
{
$lookup:{
from:"view_articles",
let: { "article_id":"$_id"},
pipeline:[
{
$match:{
$expr:{
$and: [
{ $eq: ["$article_id", "$$article_id"] },
{ $eq: ["$isDeleted", false] }
]
}
}
}
],
as:"viewCount"
}
},
{
$addFields:{
noOfViewCount : { $size:"$viewCount"}
}
} ],
as:"articleCategoryData"
}
},
{
$addFields: {
postCount: {$size:"$articleCategoryData" },
tempsArray: { $map:
{
input: "$articleCategoryData",
as: "tempData",
in: { $add: "$$tempData.noOfViewCount" }
}
},
},
},
{
$addFields: {
viewCount:{ $sum:"$tempsArray" }
},
},
{
$project:{
_id: 1,
name: 1,
postCount: 1,
viewCount: 1
}
},
{
$facet: {
count: [
{
$count: "total"
}
],
result: [{ $match: {} }, { $skip: skipRecord }, { $limit: limit }]
}
}
]);
you can set depth to 10. If you feel JSON is coming wrong then increase it :)
In case anyone who doesn't want to change the value of req.body, can use this function from here
function serializer(payload: any, cdepth: number, options: Options): void {
const main: any = {}
const maxDepth = typeof options.maxNestingLevel == 'number' ? (options.maxNestingLevel == 0 ? 1 : options.maxNestingLevel) : 1
for (const key in payload) {
// check for object
if (payload[key] instanceof Object) {
// check if depth is limited, replace if needed
if (cdepth === maxDepth) {
main[key] = options.replaceWith
} else {
// serialize the nested
main[key] = serializer(payload[key], cdepth + 1, options)
}
} else {
// add to main object if not to be checked
main[key] = payload[key]
}
}
return main
}

JSON Transformation to required format

We are working on a Middleware platform where we are required to respond to consumer with a JSON data in a particular format.
The Data we get from south bound API is a key value pair and this needs to be mapped to an understandable format for the consumer
We tried json-path, ObjectMapper but none of them is giving us the expected result for transforming
Respnse from backend API
{
"details": [
{
"name": "x.y.z.name","value": "TR-54695"
},
{
"name": "a.b.c.standards","value": "DOCSIS"
},
{
"name": "x.x.x.hversion","value": "10"
},
{
"name": "x.x.x.sversion","value": "9.1.116V"
},
{
"name": "x.x.x.uptime","value": "8000"
},
{
"name": "x.x.x.accessallowed","value": "true"
},
]
}
To be transformed to
{
"myData": {
"myInfo": {
"productClass": "TR-54695",
"supportedStandards": "DOCSIS",
"hardwareVersion": "10",
"softwareVersion": "9.1.116V",
"modemMacAddress": "",
"upTime": "8000",
"modemNetworkAccessAllowed": true
}
}
}
Do not like manual work, so here generated demo using 2 functions.
Mind ticking accept button under voting in case you like some answer.
function translate(src, mapping) {
var dst = { "myData": { "myInfo": { "modemMacAddress": "" } } }
//in case order matters:
dst = { "myData": { "myInfo": { "productClass": "", "supportedStandards": "", "hardwareVersion": "", "softwareVersion": "", "modemMacAddress": "", "upTime": "", "modemNetworkAccessAllowed": undefined } } }
var trueFalse = { "false": false, "true": true };
src = src.details;
for (var i = 0; i < src.length; i++) {
dst.myData.myInfo[mapping[src[i].name]] = trueFalse[src[i].value] || src[i].value;
}
return dst;
}
function generateMapping(src, dst) {
src = src.details;
var backLinks = {}, rename2 = {};
for (var i = 0; i < src.length; i++) {
backLinks[src[i].value] = src[i].name;
}
dst = dst.myData.myInfo;
for (var i in dst) {
rename2[backLinks[dst[i]]] = i;
}
return rename2;
}
var src = {
"details": [
{ "name": "x.y.z.name", "value": "TR-54695" },
{ "name": "a.b.c.standards", "value": "DOCSIS" },
{ "name": "x.x.x.hversion", "value": "10" },
{ "name": "x.x.x.sversion", "value": "9.1.116V" },
{ "name": "x.x.x.uptime", "value": "8000" },
{ "name": "x.x.x.accessallowed", "value": "true" },
]
}
var dst = {
"myData": {
"myInfo": {
"productClass": "TR-54695",
"supportedStandards": "DOCSIS",
"hardwareVersion": "10",
"softwareVersion": "9.1.116V",
"modemMacAddress": "",
"upTime": "8000",
"modemNetworkAccessAllowed": true
}
}
}
var mapping = generateMapping(src, dst);
// var mapping = {
// "x.y.z.name": "productClass",
// "a.b.c.standards": "supportedStandards",
// "x.x.x.hversion": "hardwareVersion",
// "x.x.x.sversion": "softwareVersion",
// "undefined": "modemMacAddress",
// "x.x.x.uptime": "upTime",
// "x.x.x.accessallowed": "modemNetworkAccessAllowed"
// }
var result = translate(src, mapping);
console.log(JSON.stringify(result, null, 2));
console.log(JSON.stringify(mapping, null, 2));
You can use below code and use codesandbox link (check console output ) for exact response and this link for key:value pair.
let response = {
details: [
{
name: "x.y.z.name",
value: "TR-54695"
},
{
name: "a.b.c.standards",
value: "DOCSIS"
},
{
name: "x.x.x.hversion",
value: "10"
},
{
name: "x.x.x.sversion",
value: "9.1.116V"
},
{
name: "x.x.x.uptime",
value: "8000"
},
{
name: "x.x.x.accessallowed",
value: "true"
}
]
};
// convert function for key value pair
function convertResponse(responseData) {
let output = { myData: { myInfo: {} } };
let outputRef = output.myData.myInfo;
responseData.forEach(element => {
outputRef[element.name] = element.value
});
return output;
}
// OR convert Function for getting exact same output
function convertResponse(responseData) {
let output = { myData: { myInfo: {} } };
let outputRef = output.myData.myInfo;
responseData.forEach(element => {
if (element.name === "x.y.z.name") {
outputRef.productClass = element.value;
} else if (element.name === "a.b.c.standards") {
outputRef.supportedStandards = element.value;
} else if (element.name === "x.x.x.hversion") {
outputRef.hardwareVersion = element.value;
} else if (element.name === "x.x.x.sversion") {
outputRef.softwareVersion = element.value;
} else if (element.name === "x.x.x.uptime") {
outputRef.upTime = element.value;
} else if (element.name === "x.x.x.accessallowed") {
outputRef.modemNetworkAccessAllowed = element.value;
}
});
return output;
}
//Function Call
console.log(convertResponse(response.details));