Skip to content

Wildcard Segments

William W. Kimball, Jr., MBA, MSIS edited this page Apr 7, 2021 · 18 revisions
  1. Introduction
  2. Match Every Immediate Child
    1. Match Every Array Element and Every Hash Key
    2. Expand Collector Results
  3. Match Every Leaf Node
    1. No Matter How Deep
    2. Filtering the Traversal
  4. Search Shorthand

Introduction

There are three types of wildcard segments:

  1. Match every immediate child.
  2. Match every leaf node.
  3. A convenience shorthand for Array Element Searches and Hash Attribute Searches.

Match Every Immediate Child

This is a standalone * (single star) as a YAML Path segment. It looks like some.path.* or /some/path/*. More segments may follow it.

This is identical to a Search segment where the search term is [.=~/.*/]. Such a search expression translates to "match every Hash key and every Array element child node, regardless their names or values". As such, every node is returned which is an immediate child of the target. This is not the same as a (deep) traversal, double star segment.

Match Every Array Element and Every Hash Key

When you just want every immediate child:

---
array_values:
  - alpha
  - bravo
  - ''
  - 
  - charlie
  - delta

hash_structure:
  key1: value 1
  key2: value 2
  keyM: ''
  keyN:
  key3: value 3
  key4: value 4

Single star searches return:

  • array_values.* or /array_values/*:
alpha
bravo


charlie
delta

We cannot see it in this visual output, but the null value at 0-based index 3 was returned as a special control-character, NULL, which is printed as the hexadecimal 00. The * operator matches all elements from Arrays, even if they won't print a discernible result (for humans); note that downstream STDOUT parsers can pick up the NULL control-character and the empty-string at index 2 will be just that, an empty-string on its own line.

  • hash_structure.* or /hash_structure/*:
value 1
value 2


value 3
value 4

Notice that the empty value for "keyM" was included in the results, as was the null value for "keyN". In contrast from Array searches, the * segment searches key names, yielding whatever data they identify.

Expand Collector Results

The * segment will also expand Collector results (breaking them out of Array form). If you place this inside a Collector, the results will still be collected into an Array. For example, (array_values.*)+(hash_structure.*) normally returns an Array:

["alpha", "bravo", "charlie", "delta", "value 1", "value 2", "", "value 3", "value 4"]

Should you need to vertically expand the matches (say, to ingress them into an Array in a downstream process which doesn't readily parse JSON), you can add a * segment, like ((array_values.*)+(hash_structure.*))* or /(array_values.*)+(hash_structure.*)/* to produce instead:

alpha
bravo
charlie
delta
value 1
value 2

value 3
value 4

Match Every Leaf Node

This is a standalone ** (double star) as a YAML Path segment. It looks like some.path.** or /some/path/**. More segments may follow it.

This is a traversal segment which causes YAML Path operations to deeply traverse the document from that point. When there are no further segments in the YAML Path, every leaf node (Scalar value) is matched. When the YAML Path has at least one further segment, it (and all further segments) must match subsequent nodes (anywhere deeper than that point in the document) or none are matched. Results can be collected.

No Matter How Deep

When you want every leaf node (Scalar value) within the document from any point defined by the YAML Path prefix, use **. When used at the document root, this will match every leaf node within the entire document.

---
this:
  is:
    a:
      hash: data
    structure: with
  several: levels

another:
  hash:
    - with
    - its
    - own
  child:
    - nodes
    - and
    - data

yet_another_hash:
  - name: except
    property: this
  - name: one
    property: is
  - name: comprised
    property: of
  - name: an
    property: Array
  - name: of
    property: Hashes

Some examples of using **:

  • this.** or /this/**
data
with
levels
  • another.** or /another/**:
with
its
own
nodes
and
data
  • yet_another_hash.** or /yet_another_hash/**:
except
this
one
is
comprised
of
an
Array
of
Hashes
  • ** or /**:
data
with
levels
with
its
own
nodes
and
data
except
this
one
is
comprised
of
an
Array
of
Hashes

Filtering the Traversal

When you're not quite looking for everything in the document but don't know precisely where within the document the data-of-interest resides, you can filter the matches. By adding further YAML Path segments after the ** segment, you restrict matched nodes to only those which also match the following segments. For example:

  • Using the same sample document from above, suppose you know that somewhere within it resides a Hash named a with an immediate child named hash. You want whatever that points at. No matter how shallow or deeply the document, you can find it using **.a.hash or /**/a/hash. This returns:
data
  • What if you just want everything within the document which has a name property? Try **.name or /**/name. With the sample data, this yields:
except
one
comprised
an
of
  • Suppose you know some ancestor and some descendant but you don't care what's between them? You can use multiple traversal segments. Against the sample data, try this.**.a.** or /this/**/a/**:
data

Search Shorthand

The * character now also serves as a wildcard character for key-names, Hash values, and Array value comparisons, converting the segment to a Search Expression. For example, a YAML Path like abc.d* becomes abc[.^d], abc.*f becomes abc[.$f], and abc.*e* becomes abc[.=~/^.*e.*$/], and so on.

Clone this wiki locally