Skip to content

Search Keyword: has_child

William W. Kimball, Jr., MBA, MSIS edited this page May 4, 2021 · 7 revisions
  1. Introduction
    1. Syntax
    2. Sample Data
  2. Match Parent Hashes Lacking a Specific Child Key
  3. Match Parent Arrays Lacking a Specific Element
  4. Less Useful Against Single-Dimension Arrays

Introduction

The [has_child(NAME)] search keyword will match nodes which have -- or do not have, when inverted with ! -- a child node with a given key name or element value. This differs from a search like [.=SOME_VALUE] (from Search Expressions) in that [has_child(NAME)] returns the matching parent (with all of its children) rather than just any matching children nodes. This keyword is particularly useful for its inverted mode, enabling users to find nodes that are missing an expected child, usually to add it as with default data. By contrast, the similar search expression cannot match what isn't there.

Syntax

[has_child(NAME)] requires one parameter, NAME. This parameter specifies either:

  • The exact -- case-sensitive -- name of the required child key when searching Hash (map/dict) data.
  • The exact -- case-sensitive -- value of the required element when searching Array (sequence/list) data.

Sample Data

To illustrate, the example commands below will use this sample data as has_child-examples.yaml:

---
hash_of_hashes:
  one:
    child_one: value1.1
    child_two: value1.2
  two:
    child_one: value2.1
    child_three: value2.3
  three:
    child_one: value3.1
    child_two: value3.2

array_of_hashes:
  - id: one
    child_one: value1.1
    child_two: value1.2
  - id: two
    child_one: value2.1
    child_three: value2.3
  - id: three
    child_one: value3.1
    child_two: value3.2

simple_array:
  - value1.1
  - value1.2
  - value2.1
  - value2.3
  - value3.1
  - value3.2

array_of_arrays:
  - - value1.1
    - value1.2
  - - value2.1
    - value2.3
  - - value3.1
    - value3.2

Match Parent Hashes Lacking a Specific Child Key

Imagine a scenario where you needed to match Hash nodes lacking one of these keys, like child_two. For the various representations of the data, the inverted form of [has_child(NAME)] solves this need:

$ yaml-get --query='hash_of_hashes.*[!has_child(child_two)]' has_child-examples.yaml
{"child_one": "value2.1", "child_three": "value2.3"}

$ yaml-get --query='/array_of_hashes/*[!has_child(child_two)]' has_child-examples.yaml
{"id": "two", "child_one": "value2.1", "child_three": "value2.3"}

Notice that the selectors are functionally identical -- disregarding that the two examples are in alternating dot- and forward-slash-notation -- whether searching a Hash of Hashes or an Array of Hashes. In both cases, we instruct the YAML Path processor to move to the hash_of_hashes and array_of_hashes parent nodes and then run the search against each of its immediate child nodes.

In this use-case, each of these YAML Paths matches exactly one parent Hash, the one lacking a child key named child_two. This is not possible with any of the Search Expressions, so the [has_child(NAME)] search keyword fits this need. If you needed to know only the identifier of the matches, you could add the [name()] search keyword for the first query and simply select the id field of the second, like so:

$ yaml-get --query='/hash_of_hashes/*[!has_child(child_two)][name()]' has_child-examples.yaml
two

$ yaml-get --query='array_of_hashes.*[!has_child(child_two)].id' has_child-examples.yaml
two

Match Parent Arrays Lacking a Specific Element

The [has_child(NAME)] keyword can also be used to match Arrays (sequences/lists) which do -- or do not, when inverted -- have a particular element by value. Take note: this is for selecting the entire matching Array, not only the matching child element. When it comes to matching such elements, you can use a search expression for an identical result. Suppose you needed to find whichever Arrays in your data are missing a particular value, like "value2.1". In this case, using a search keyword or a search expression (with a descendant search) can yield the same result:

$ yaml-get --query='/array_of_arrays/*[!has_child(value2.1)]' has_child-examples.yaml
["value1.1", "value1.2"]
["value3.1", "value3.2"]

$ yaml-get --query='array_of_arrays[*!=value2.1]' has_child-examples.yaml
["value1.1", "value1.2"]
["value3.1", "value3.2"]

Notice that the entirety of the Arrays which lack the sought value are selected. In this specific case, use whichever YAML Path segments are most intuitive to you. If we take this a step further and need the index of matches rather than the data, add the [name()] keyword:

$ yaml-get --query='array_of_arrays.*[!has_child(value2.1)][name()]' has_child-examples.yaml
0
2

$ yaml-get --query='/array_of_arrays[*!=value2.1][name()]' has_child-examples.yaml
0
2

We could go a step further and imagine that we need only the index of the first match. To achieve this, simply use a Collector and index the first result:

$ yaml-get --query='(/array_of_arrays/*[!has_child(value2.1)][name()])[0]' has_child-examples.yaml
0

$ yaml-get --query='(array_of_arrays[*!=value2.1][name()])[0]' has_child-examples.yaml
0

What if we only wanted the index of the last match? Using the same technique, just select the last index of the collected result:

$ yaml-get --query='(array_of_arrays.*[!has_child(value2.1)][name()])[-1]' has_child-examples.yaml
2

$ yaml-get --query='(/array_of_arrays[*!=value2.1][name()])[-1]' has_child-examples.yaml
2

To work with the selected data rather than its index, just drop the [name()] keyword.

Less Useful Against Single-Dimension Arrays

Be aware that when you want to check whether a single-dimension Array -- like simple_array in the sample data -- contains a particular value, the [has_child(NAME)] keyword is not particularly useful. When doing so, you'll always:

  • get back the entire Array when there is a match; or
  • get nothing at all back when nothing matches.

This is because search keywords operate at the parent node. So, when the parent node has a matching child, you get back the entire parent node. For example:

$ yaml-get --query='/simple_array[has_child(value1.1)]' has_child-examples.yaml
["value1.1", "value1.2", "value2.1", "value2.3", "value3.1", "value3.2"]

$ yaml-get --query='/simple_array[!has_child(value1.3)]' has_child-examples.yaml
["value1.1", "value1.2", "value2.1", "value2.3", "value3.1", "value3.2"]
Clone this wiki locally