I am trying to get my hands on javascript and elasticsearch and I was trying to create queries using the elastic-builder javascript lib.
I might be missing something which I am trying to figure out but unfortunately I am unable to.
Problem: I am trying to create multilevel aggregation like below,
"aggs": {
"1": {
"date_histogram": {
"field": "f1",
"calendar_interval": "1D"
},
"aggs": {
"2": {
"date_histogram": {
"field": "f2",
"calendar_interval": "1D"
},
"aggs": {
"3": {
"date_histogram": {
"field": "f3",
"calendar_interval": "1D"
}
}
}
}
}
}
But what I get instead is this:
"aggs": {
"1": {
"date_histogram": {
"field": "f1",
"calendar_interval": "1D"
},
"aggs": {
"2": {
"date_histogram": {
"field": "f2",
"calendar_interval": "1D"
}
},
"3": {
"date_histogram": {
"field": "f3",
"calendar_interval": "1D"
}
}
}
}
The current output I get has two aggregations nested in one. I am trying to build it using an array with aggregations defined in it.
The code I used is below:
let a = [
esb.dateHistogramAggregation('1', "d[key]['field']").calendarInterval('1D'),
esb.dateHistogramAggregation('2', "d[key]['field']").calendarInterval("1D"),
esb.dateHistogramAggregation('3', "d[key]['field']").calendarInterval("1D")
];
let m = null;
for(i=0;i<a.length;i++) {
if(i === 0) {
m = a[i]
} else {
m.agg(a[i])
}
}
//m = esb.dateHistogramAggregation('1', "d[key]['field']").calendarInterval('1D')
//m = m.agg(esb.dateHistogramAggregation('2', "d[key]['field']").calendarInterval("1D").agg(esb.dateHistogramAggregation('3', "d[key]['field']").calendarInterval("1D")))
esb.requestBodySearch()
.query(
esb.boolQuery()
.must(esb.matchQuery('message', 'this is a test'))
.filter(esb.termQuery('user', 'kimchy'))
.filter(esb.termQuery('user', 'herald'))
.should(esb.termQuery('user', 'johnny'))
.mustNot(esb.termQuery('user', 'cassie'))
)
.agg(esb.termsAggregation('user_terms', 'user').agg(esb.termsAggregation('user_terms', 'user').agg(esb.termsAggregation('user_terms', 'user'))))
.agg(m);
The lines commented in the code will output the result I'm expecting. What am I doing wrong?
You can turn the array into a group of sub-aggregations like so:
let a = [
esb.dateHistogramAggregation('1', "d[key]['field']").calendarInterval('1D'),
esb.dateHistogramAggregation('2', "d[key]['field']").calendarInterval("1D"),
esb.dateHistogramAggregation('3', "d[key]['field']").calendarInterval("1D")
];
const reqBody = esb.requestBodySearch()
.agg(
a[0].agg(
a[1].agg(
a[2]
)
)
);
I solved it like below. I am not sure this is right way. But someone can correct me if I am wrong.
let temp = null;
for (i = a.length - 1; i >= 0; i--) {
if (i === a.length - 1) {
temp = a[i];
} else {
temp = a[i].agg(temp)
}
}
Related
I get an input like this:
input 1:
{
"name": "Ben",
"description": "Ben",
"attributes": [
{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
input 2
{
"name": "Ice",
"description": "Ice",
"attributes": [
{
"type": "Background",
"value": "Green"
},
{
"type": "Hair-color",
"value": "White"
}
]
}
input 3
{
"name": "Itay",
"description": "Itay",
"attributes": [
{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
What I want to do is count the amount of each type of background and each type of hair-color appearing.
(These are sample examples and in reality there are more types and different values)
Let's say in these examples we have 2 objects that have a background as default then I want to have a count of that like so:
export interface TraitCount {
value: string,
count: number
}
export interface CountOfEachAttribute {
trait_type: string,
trait_count: traitCount[] | null,
total_variations: number
}
I want the most effective code because there are other aspects to the code, in addition it will run on 5-10k queries not just three, so needs
to run in good times too :D
(It's similar to my other question done with python but now I need it in js also)
Atm it's something like this:
(Apart of a much bigger code so keep that in mind)
setInitalCountOfAllAttribute( state, { payload }: PayloadAction<CountOfEachAttribute[] | null> ) {
if (payload === null) {
state.countOfAllAttribute = null;
} else {
state.countOfAllAttribute = payload;
}
},
setCountOfAllAttribute(state, { payload }: PayloadAction<Attribute>) {
if (state.countOfAllAttribute !== null) {
state.countOfAllAttribute.map(
(countOfEachAttribute: CountOfEachAttribute) => {
// Find the trait type
if (countOfEachAttribute.trait_type === payload.trait_type) {
// initiate the trait count array to store all the trait values and add first trait value
if (countOfEachAttribute.trait_count === null) {
const new_trait_count = { value: payload.value, count: 1 };
countOfEachAttribute.trait_count = [new_trait_count];
countOfEachAttribute.total_variations++;
}
// Trait array already existed.
else {
// Check if value already present or not
const checkValue = (obj: any) => obj.value === String(payload.value);
const isPresent = countOfEachAttribute.trait_count.some(checkValue)
const isPresent2 = countOfEachAttribute.trait_count.find((elem: any) => elem.value === String(payload.value))
// Value matched, increase its count by one
if (isPresent2) {
countOfEachAttribute.trait_count &&
countOfEachAttribute.trait_count.map((trait) => {
if (trait.value === payload.value) {
trait.count++;
}
});
}
// Value doesn't match, add a new entry and increase the count of variations by one
else {
const new_trait_count = { value: payload.value, count: 1 };
countOfEachAttribute.trait_count = [
...countOfEachAttribute.trait_count,
new_trait_count,
];
countOfEachAttribute.total_variations++;
}
}
}
}
);
}
},
You can merge all arrays and use Array.reduce.
const input1 = {
"name": "Ben",
"description": "Ben",
"attributes": [{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
const input2 = {
"name": "Ice",
"description": "Ice",
"attributes": [{
"type": "Background",
"value": "Green"
},
{
"type": "Hair-color",
"value": "White"
}
]
}
const input3 = {
"name": "Itay",
"description": "Itay",
"attributes": [{
"type": "Background",
"value": "Default"
},
{
"type": "Hair-color",
"value": "Brown"
}
]
}
const mergedInput = [input1, input2, input3];
const result = mergedInput.reduce((acc, item) => {
item.attributes.forEach(attrItem => {
const existType = acc.find(e => e.trait_type == attrItem.type);
if (existType) {
var existAttr = existType.trait_count.find(e => e.value == attrItem.value);
if (existAttr) {
existAttr.count++;
} else {
existType.trait_count.push({
value: attrItem.value,
count: 1
});
existType.total_variations++;
}
} else {
acc.push({
trait_type: attrItem.type,
trait_count: [{
value: attrItem.value,
count: 1
}],
total_variations: 1
})
}
});
return acc;
}, []);
console.log(result);
I suggest instead of creating an array for trait_count to make it an object so you don't have to iterate over it whenever you are adding a new attribute. In the snippet below I'm using the value of the attribute as a sort of hash that allows the access to the given property without having to call the Array.prototype.find function
const input1 = {"name":"Ben","description":"Ben","attributes":[{"type":"Background","value":"Default"},{"type":"Hair-color","value":"Brown"}]};
const input2 = {"name":"Ice","description":"Ice","attributes":[{"type":"Background","value":"Green"},{"type":"Hair-color","value":"White"}]};
const input3 = {"name":"Itay","description":"Itay","attributes":[{"type":"Background","value":"Default"},{"type":"Hair-color","value":"Brown"}]};
function countAtributes(input, totalCounts={}) {
input.attributes.forEach((attribute) => {
if (!totalCounts[attribute.type])
totalCounts[attribute.type] = {trait_type: attribute.type, trait_count: {}, total_variations: 0};
if (!totalCounts[attribute.type].trait_count[attribute.value]) {
totalCounts[attribute.type].trait_count[attribute.value] = {value: attribute.value, count: 1};
totalCounts[attribute.type].total_variations+=1;
}
else totalCounts[attribute.type].trait_count[attribute.value].count +=1;
})
}
const totalCounts = {};
countAtributes(input1, totalCounts);
countAtributes(input2, totalCounts);
countAtributes(input3, totalCounts);
console.log(totalCounts);
It could be turned into the array afterwards with Object.values if necessary
I believe it is a much better approach to what you had before as you don't have to iterate over the tables of trait_counts. In theory it should significantly reduce the time taken. Iterating over the array and checking a condition each time is much slower than key lookup in Javascript object
I'm writing a function that takes arguments and add them to form a line to look for data in a JSON file. I've defined a variable for the readFileSync and the add to it the arguments of the function to look for the data.
var jf = require('jsonfile'),
file = 'logins.json',
i = 1;
var jsonData = jf.readFileSync(file);
function getJSONData() {
var n = 1;
var com = '';
do {
if (arguments[n] !== undefined) {
com += `['${arguments[n]}']`;
}
n++;
} while (n < arguments.length);
return com;
}
var h = getJSONData(i, 'operator', 'id');
console.log(jsonData[i] + h);
This is my JSON:
[
{
"operator": {
"id": "avalle",
"pass": "Aa123456",
"something": "idk",
"account": [
{
"type": "asd",
"idk": "asd"
},
{
"type": "asd",
"idk": "asd"
}
]
}
},
{
"operator": {
"id": "oleal",
"pass": "Aa123456",
"something": "idk",
"account": [
{
"type": "asd",
"idk": "asd"
},
{
"type": "asd",
"idk": "asd"
}
]
}
}
]
I should get a line of jsonData[i]['param1']['param2'] that locates the data in the file.
Instead i get undefined or [object Object]['operador']['id']
If you want a property to be returned from the function you can make this change:
function getJSONData(jsonData) {
var n = 1;
var result = jsonData;
do {
if (result[arguments[n]]) {
result = result[arguments[n]]
} else {
console.error(`Property ${arguments[n]} does not exist on obj:`, result)
}
n++;
} while (n < arguments.length);
return result;
}
var h = getJSONData(jsonData[i], 'operator', 'id');
Otherwise you return a string from getJSONData that looks like "[prop1][prop2]" and it will not retrieve a property by trying to concat Object + string
I have following Json which i need to insert into a table.
I want to convert each student detail into a row.
Because if i loop through the rows as per the existing structure i am reading one column as a row.
var json {
"Students":[
{
"name":{
"value":"Allan"
},
"number":{
"value":"123"
}
},
{
"name":{
"value":"Frank"
},
"number":{
"value":"456"
}
}
]
}
Ideally i want to the above as
{ "name": "Allan", "number": 123};
{ "name": "Frank", "number": 456};
I am looping through the Json as below
var objectKeys = Object.keys(json);
for (var key in objectKeys)
{
var student = json.Students;
for (var i = 0; i < student .length; i++) {
for (var column in json.Students[i]) {
window.print(column);
window.print(json.Students[i][column].value);
}
}
}
NOTE: No JQuery, want to achieve the above through normal Javascript.
If you want to transform the data, you can use Array.map
var json = {"Students":[{"name":{"value":"Allan"},"number":{"value":"123"}},{"name":{"value":"Frank"},"number":{"value":"456"}}]};
let result = json.Students.map(o => ({
name: o.name.value,
number: o.number.value
}));
console.log(result);
If you want to access the data, you can use Array.forEach
var json = {"Students":[{"name":{"value":"Allan"},"number":{"value":"123"}},{"name":{"value":"Frank"},"number":{"value":"456"}}]};
json.Students.forEach(o => console.log({name: o.name.value, number: o.number.value}));
var json = {
"Students":[
{
"name":{
"value":"Allan"
},
"number":{
"value":"123"
}
},
{
"name":{
"value":"Frank"
},
"number":{
"value":"456"
}
}
]
}
var studentData = JSON.stringify(json.Students);
var convertedData = JSON.parse(studentData.replace(/\{\"value\"\:/g,"").replace(/\}\,\"number/g,',"number').replace(/\"\}\}/g,'"}'));
Try this :)
No map or reduce. Just classic Javascript.
var json = {
"Students": [{
"name": {
"value": "Allan"
},
"number": {
"value": "123"
}
},
{
"name": {
"value": "Frank"
},
"number": {
"value": "456"
}
}
]
};
for (var student of json["Students"]) {
console.log(student); //your logic goes here.
}
Got an object containing a user id for each user and prices, would like to create a new object/array for each user (no duplicates) and be able to calculate the total sum of price for each user. Tried using Object.values() with map and filter but can't get it to work properly
{
"data": {
"item1": {
"price": "20",
"user": "user1"
},
"item2": {
"price": "10",
"user": "user2"
},
"item3": {
"price": "50",
"user": "user1"
}
}
}
Output something like this:
{
"users": {
"user1": {
"totalSum": "70",
},
"user2": {
"totalSum": "10",
}
}
}
I'm thinking about using map to present the "users"-data, maybe an array would be better?
Using function reduce.
Important: The attribute price is a String, this approach uses object Number to convert that value to a numeric one.
var obj = { "data": { "item1": { "price": "20", "user": "user1" }, "item2": { "price": "10", "user": "user2" }, "item3": { "price": "50", "user": "user1" } }};
var result = Object.keys(obj.data).reduce((a, k) => {
if (a.users[obj.data[k].user]) {
a.users[obj.data[k].user].totalSum += Number(obj.data[k].price);
} else {
a.users[obj.data[k].user] = {
"totalSum": Number(obj.data[k].price)
}
}
return a;
}, {
'users': {}
});
console.log(result);
.as-console-wrapper {
max-height: 100% !important; top: 0;
}
You could leverage ```reduce, more information here
code (haven't tried this)
var data = JSON.parse(mainObj).data;
var usersWithTotalExpenditure = Object.keys(data).reduce(function(result, key) {
var currentItem = data[key];
var useName = currentItem.user;
var price = Number(currentItem.price);
if (userName in result) {
result[userName].totalSum += price;
} else {
result[userName] = {
totalSum: price
};
}
return result;
}, {});
var resultObject = {
users: usersWithTotalExpenditure
}
You can use a forEach loop. This relies on Javascripts powerful OR operator, which coerces the first half of the expression to false if the current user's price is not defined (meaning it is a user the loop hasn't encountered before)
`c is your initial object's data, output is empty object`
const c = obj.data;
var output = {};
Object.keys(c).forEach((val) => {
output[c[val]["user"]] = parseInt(output[c[val]["user"]]) + parseInt(c[val]["price"]) || parseInt(c[val]["price"]);
})
I have a series of nested objects like this:
data = {"12345":{"value":{"1":"2","3":"4"}},
{"12346":{"value":{"5":"6","7":"8"}},
{"12347":{"value":{"9":"0","11":"22"}}
I would like to create a function to grab certain objects within this grouping. For example...
grabObject(12345);
would return:
{"value":{"1":"2","3":"4"}}
Any help you could provide would be great.
You don't need anything more than this:
function grabObject(id) {
return data[id];
}
After making some fixes to your syntax, here's a working jsFiddle: http://jsfiddle.net/jfriend00/04no0bvm/
var data = [
{
"12345": {
"value": {
"1": "2",
"3": "4"
}
}
},
{
"12346": {
"value": {
"5": "6",
"7": "8"
}
}
},
{
"12347": {
"value": {
"9": "0",
"11": "22"
}
}
}
];
function grabObject(id) {
var result;
for (i = 0; i < data.length; i++) {
for (var k in data[i]){
if(k == id) {
result = data[i][k];
}
}
}
return result;
}
console.log(grabObject('12345'));
This is the code I tested check and let me know