Parsing JSON objects with arbitrary keys in Logstash - json

Consider a subset of a sample output from http://demo.nginx.com/status:
{
"timestamp": 1516053885198,
"server_zones": {
"hg.nginx.org": {
... // Data for "hg.nginx.org"
},
"trac.nginx.org": {
... // Data for "trac.nginx.org"
}
}
}
The keys "hg.nginx.org" and "track.nginx.org" are quite arbitrary, and I would like to parse them into something meaningful for Elasticsearch. In other words, each key under "server_zones" should be transformed into a separate event. Logstash should thus emit the following events:
[
{
"timestamp": 1516053885198,
"server_zone": "hg.nginx.org",
... // Data for "hg.nginx.org"
},
{
"timestamp": 1516053885198,
"server_zone": "trac.nginx.org",
... // Data for "trac.nginx.org"
}
]
What is the best way to go about doing this?

You can try using the ruby filter. Get the server zones and create a new object using the key value pairs you want to include. From the top of my head, something like below should work. Obviously you then need to map the object to your field in the index. Change the snipped based on your custom format i.e. build the array or object as you want.
filter {
ruby {
code => " time = event.get('timestamp')
myArr = []
event.to_hash.select {|k,v| ['server_zones'].include?(k)}.each do |key,value|
myCustomObject = {}
#map the key value pairs into myCustomObject
myCustomObject[timestamp] = time
myCustomObject[key] = value
myArr.push(myCustomObject) #you'd probably move this out based on nesting level
end
map['my_indexed_field'] = myArr
"
}
}
In the output section use rubydebug for error debugging
output {
stdout { codec => rubydebug }
}

Related

Retrieve values from JSON response, and create a drop-down

I'm trying to get each of of the values inside my JSON file but when I run my API I get [Object Object] instead of what is inside the JSON.
This is my API request:
getAllvalues(): Observable<string[]> {
return this.http
.get<string[]>(this.Url + 'api');
}
my component.ts
this.ddvService.getAllvalues()
.subscribe(response => {
this.slots = response;
console.log (this.slots)
});
Example of my JSON response:
[
{
"AB": "http:"
},
{
"DE": "http:"
},
{
"CE": "http:"
},
{
"HI": "http:"
}
]
How can I get the value inside the JSON, and create a dropdown box with each of them?
Your example JSON is a pretty bad example: each object in the array in the JSON should have at least somewhat matching key names. In your case, the keys are "AB", "DE", "CE", "HI" - all different, which is quite uncommon in real-life. A more realistic JSON response would have matching key names, e.g.:
[
{
"id": "1",
"description": "Some description"
},
{
"id": "2",
"description": "Another description"
}
]
Now to answer your questions:
You are getting [Object Object] because you are trying to use an entire object as a literal value. Instead, you should access the individual keys/values of an object. For example: console.log(slots[0].id) - this should output 1.
Also, as indicated in the comments, replace Observable<string[]> with Observable<any[]> and get<string[]> with get<any[]>.
To create a drop-down in Angular, in your component template you can try this, assuming your slots value is the JSON above:
<select *ngIf="slots" name="slots">
<option *ngFor="let slot of slots" value="slot.id">{{ slot.description }}</option>
</select>
Also, to print the entire object to console in a readable form, instead of just console.log(this.slots);, you can try console.log(JSON.stringify(this.slots));
As mentioned in the comments above it is not ideal to have json like you have, my assumption is you might want to log keys instead of values, since value is same for all the objects in array. In that case you might want to try something like this.
1. Add any[] instead string[].
2.Add nested for loop to console.log your object array.
getAllvalues(): Observable<string[]> {
return this.http
.get<any[]>(this.Url + 'api');
}
this.ddvService.getAllvalues()
.subscribe(response => {
this.slots = response;
for(let i in this.slots)
{
let currentObj = this.slots[i]; // example of first in array { AB : "http:"}
for ( let z in currentObj )
{
if(currentObj[z]=== "http:") // we are trying to find key instead value
{
console.log(z); // it will log AB, DE, CE, HI ..
}
}
}
});

Best Schema for a Data List in JSON for RestFul API to use in Angular

I've been wondering for some days what kind of scheme would be more appropriate to use a data list in json in a web application.
I'm developing a REST Web Application, and im using Angular for front end, i should order, filter and print these data list also in xml ...
For you what scheme is better and why?
1) {
"datas": [
{ "first":"","second":""},
{ "first":"","second":""},
{ "first":"","second":""}
]
}
2) {
"datas": [{
"data": { "first":"","second":""},
"data": { "first":"","second":""},
"data": { "first":"","second":""}
}]
}
3) [
{ "first":"","second":""},
{ "first":"","second":""},
{ "first":"","second":""}
]
Thanks so much.
The first and third notations are quite similar because the third notation is included in your first.
So the question is "Should I return my datas as an array or should I return an object with a property that contain the array ?
It will depend on either you want to have more information alongside your datas or not.
For exemple, if your API might return an error, you will want to manage it from the front end.
In case of error, the JSON will looks like this :
{
"datas": null,
"error": "An error occured because of some reasons..."
}
At the opposite, if everything goes well and your API actually return the results, it will looks like this :
{
"datas": [
{ "first":"","second":""},
{ "first":"","second":""},
{ "first":"","second":""}
],
"error": null
}
Then your front end can use the error property to manage errors sent from the API.
var result = getDatas(); // Load datas from the API
if(result.error){
// Handle the error, display a message to the user, ...
} else {
doSomething(result.datas); // Use your datas
}
If you don't need to have extra properties like error then you can stick with the third schema.
The second notation is invalid. The datas array will contain only one object which will have one property named data. In this case data is a property that is defined multiple times so the object in the array will contain only the last occurence:
var result = {
"datas": [{
"data": { "first":"a","second":"b"},
"data": { "first":"c","second":"d"},
"data": { "first":"e","second":"f"}
}]
}
console.log("Content of result.datas[0].data : ")
console.log(result.datas[0].data)
Obviously the first option would be easy to use. Once you will access datas it'll give you an array. Any operation (filter, sort, print) on that array will be easy in comparison to anything else. Everywhere you just need to pass datas not datas.data.

VueJS - trouble understanding .$set and .$add

I am trying to build an array of objects in VueJS and I am running into some issues with .$set and .$add.
For example, I need the following structure when adding new items/objects:
{
"attendees": {
"a32iaaidASDI": {
"name": "Jane Doe",
"userToken": "a32iaaidASDI",
"agencies": [
{
"name": "Foo Co"
}
]
}
}
}
New objects are added in response to an AJAX call that returns JSON formatted the same as above. Here is my Vue instance:
var vm = new Vue({
el: '#trainingContainer',
data: {
attending: false,
attendees: {}
},
methods: {
setParticipantAttending: function(data)
{
if (data.attending)
{
this.attendees.$add(data.userToken, data);
} else {
this.attendees.$delete(data.userToken);
}
}
}
});
This only works if I start with attendees: {} in my data but when I try attendees.length after adding an attendee, I receive undefined. If I use attendees: [], the new object does not appear to be added. And lastly, if I use .$set(data.userToken, data) it does not add in the 'token':{data..} format required.
What could be the issue here? What is the correct way to use $.add when starting with an empty array of objects?
UPDATE
I found that it works if I set attendees: {} and then, when adding a new object,
if (data.userToken in this.attendees) {
this.attendees.$set(data.userToken, data);
} else {
this.attendees.$add(data.userToken, data);
}
Not sure if there is a better way to accomplish this.
If you set attendees to an empty object ({}) it will not have a length attribute. That attribute is on Arrays.
If you set attendees to an empty array ([]) then you need to use $set (or really, I think you want .push()) – $add is intended for use on objects not on arrays.
I'm not quite following your last question – could you add more context?
EDIT 2:
The answer below was for Vue 1.x. For Vue 2.x and greater use:
this.$set(this.attendees, data.userToken, data);
More information here: https://v2.vuejs.org/v2/api/#Vue-set
EDIT:
Responding to your update, you should be able to just use $set in all cases. I.e. just do this:
this.attendees.$set(data.userToken, data);
As of version 0.6.0, this seems to be the correct way:
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
http://vuejs.org/guide/reactivity.html#Change_Detection_Caveats

Logstash JSON Parse - Ignore or Remove Sub-Tree

I'm sending JSON to logstash with a config like so:
filter {
json {
source => "event"
remove_field => [ "event" ]
}
}
Here is an example JSON object I'm sending:
{
  "#timestamp": "2015-04-07T22:26:37.786Z",
  "type": "event",
  "event": {
    "activityRecord": {
      "id": 68479,
      "completeTime": 1428445597542,
      "data": {
        "2015-03-16": true,
        "2015-03-17": true,
        "2015-03-18": true,
        "2015-03-19": true
      }
    }
  }
}
Because of the arbitrary nature of the activityRecord.data object, I don't want logstash and elasticsearch to index all these date fields. As is, I see activityRecord.data.2015-03-16 as a field to filter on in Kibana.
Is there a way to ignore this sub-tree of data? Or at least delete it after it has already been parsed? I tried remove_field with wildcards and whatnot, but no luck.
Though not entirely intuitive it is documented that subfield references are made with square brackets, e.g. [field][subfield], so that's what you'll have to use with remove_field:
mutate {
remove_field => "[event][activityRecord][data]"
}
To delete fields using wildcard matching you'd have to use a ruby filter.

get MongoDB results as objects instead of array

I'm new to MongoDB, and I'm trying to get results in a different way.
if I execute the query db.collection.find().toArray() I get something like:
[
{
"_id":"34234...",
"first":"Mark",
"last":"Marker"
},
{
"_id": "34235...",
"first":"Adam",
"last":"Smith"
}
]
is there an api that lets you to receive the results as the following?:
{
"results" : {
"34234..." :{
"_id":"34234...",
"first":"Mark",
"last":"Marker"
},
"4235..." :{
"_id": "34235...",
"first":"Adam",
"last":"Smith"
}
}
Or I need to get the results array and iterate every single object and build my response? (I would like to avoid the single cursor iteration)
I don't believe there's a native API function for that. cursor.toArray() goes through each item in the cursor begin with, so I wouldn't worry too much about that. We can just skip the toArray() and do our own iteration:
var obj = {}
db.collection.find().each(function(item){
obj[item._id] = item;
});
I don't think that would really be any slower.