How to check if a field has any subfields

I get input that sometimes looks like this:

{
  "baz": {
    "foo": "bar",
    "data": {}
  }
}

sometimes it looks like this:

{
  "baz": {
    "foo": "bar",
    "data": {
      "fizz": "buzz",
      "fizzy": "buzzy"
    }
  }
}

and sometimes it looks like this:

{
  "baz": {
    "foo": "bar",
    "data": {
      "fuzz": "bizz",
      "fuzzy": "bizzy"
    }
  }
}

I do not know the keys that could be contained in the baz.data object in beforehand.

If, and only if, the baz.data object contains actual content, I want to redact the original data by replacing it with the pre-prepared static object { "notice": "this data has been redacted" }, so that I end up with the following:

{
  "baz": {
    "foo": "bar",
    "data": {
      "notice": "this data has been redacted"
    }
  }
}

Now I figured out how to replace the content of the baz.data object. The only part I haven't figured out is how to check if the baz.data object is empty or not.

Currently my code looks like this:

mutate {
  add_field => { "[@metadata][redacted]" => '{ "notice": "this data has been redacted" }' }
}

if [baz][data] =~ /.+/ {
  json {
    source => "[@metadata][redacted]"
    target => "[baz][data]"
  }
}

The problem is that the above code will never replace the object. I am guessing it's because the [baz][data] field itself is empty, even though it contains subfields.

I previously tried the following conditional:

if [baz][data] {

However the above code will always insert the object, apparently because the [baz][data] field exists, despite not having any sub-fields.

I also tried the following:

if [baz][data] and [baz][data] != "" {

The above conditions always end up true, I guess because an empty object does not equal an empty string.

So how do I properly check if [baz][data] has any nested subfields, when I do not know the names of the subfields?

Personally I would use ruby

input { generator { count => 1 lines => [ '{}', '{ "data": "" }', '{ "data": { "foo": "bar" } }', '{ "data": { } }'  ] codec => json } }
filter {
    ruby {
        code => '
            d = event.get("data")
            if d and d.is_a? Hash and !d.empty?
                event.set("data", { "notice" => "this data has been redacted" })
            end
        '
    }
}
output { stdout { codec => rubydebug { metadata => false } } }

produces events with

      "data" => "",
      "data" => {
    "notice" => "this data has been redacted"
},
      "data" => {},

You can do the same without using ruby, but I think it is more obscure

    if [data] {
        mutate { add_field => { "[@metadata][JSON]" => "{}" } }
        json { source => "[@metadata][JSON]" target => "[@metadata][emptyHash]" }
        if [data] != [@metadata][emptyHash] {
            mutate { remove_field => [ "data" ] }
            mutate { add_field => { "[@metadata][otherJSON]" => '{ "notice": "this data has been redacted" }' } }
            json { source => "[@metadata][otherJSON]" target => "[data]" }
        }
    }

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.