Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  • First, recognize that the entire ruleset can be seen as a big if-statement looping over the event stream. Seeing event processing as a looping process helps design efficient, effective rulesets.
  • Also, JPexs provide a way of processing arrays in large data sets that amounts to a kind of implicit looping in many cases.
  • Many of the array and map operators like map() and filter() loop over data. 
  • In addition to these implicit forms of looping, KRL functions support recursion and thus can be used to iterate over arguments.

But with all that, we sometimes need explicit loops. KRL loops are a little different than what your previous programming experience might lead you to expect. The KRL foreach statement can only appear just below the select statement like so:

Code Block
languagejavascript
themeConfluencelanguagejavascript
select when pageview url "/archives/"
  foreach [1, 2, 3] setting (x)

...

The value following the keyword foreach can be any KRL expression that yields an array or map.  If the variable f were bound to a JSON data structure, you could use a pick and JPex like so:

Code Block
languagejavascript
themeConfluence
languagejavascript
select when pageview url "/archives/"
  foreach f.pick("$..store") setting (x)

To use the foreach statement with a map, you provide two variables in the setting clause that will be bound the name and value of that entry in the map:

Code Block
languagejavascript
themeConfluence
languagejavascript
select when pageview url "/archives/"
  foreach {"a" : 1, "b" : 2, "c" : 3} setting (n,v)

...

You can have more than one loop in a rule by simply nesting one foreach inside another:

Code Block
languagejavascript
themeConfluence
languagejavascript
select when pageview url "/archives/"
  foreach [1, 2, 3] setting (x)
    foreach ["a", "b", "c"] setting (y)

...

Comparison of FLWOR and KRL

FLWORKRL
ForeachForeach
LetPrelude
WhereRule condition
OrderArray filters and operations
ResultAction

The entire rule body—everything after the select—is executed once for every loop. If the rule condition is true, an action is produced, so a rule with a foreach over a three-element array would produce three actions if the condition were true each time. (Note: KRL optimizes rule preludes by automatically moving expressions that don’t depend on the variable being set in the foreach statement outside the loop during execution so that only those things that really need to be executed multiple times are.)

...

While this kind of data would generally be generated, let’s just declare it, in a variable named items, in the global:

Code Block
language
languagejavascript
themeConfluencejavascript
global {
  items = [{page: "baconsalt.com"           
            content: "Hello World. Go Bacon."
            header: "Bacon Salt Test"
           },
           {page: "craigburton.com"
            content: "Hello World. Burtonian methods."
            header: "Craig Burton Test"
           },
           {page: "kynetx.com"
            content: "Hello World. The World According to Kynetx"
            header: "Kynetx Test"
           }]
}

...

Your first attempt, using foreach might look something like this:

Code Block
language
languagejavascript
themeConfluencejavascript
rule using_foreach {
   select when pageview url ".*"
      foreach items setting (d)
      pre {
         h = d.pick("$.header") + " using foreach";
         c = d.pick("$.content");
         domain = page:url("domain");
      }
      if(domain eq d.pick("$.page")) then
        notify(h,c);
 }

...

The first is to use the full power of array filters to cut the array down to just those members meeting the desired criterion:

Code Block
languagejavascript
themeConfluencelanguagejavascript
rule using_foreach_with_filter {
   select when pageview url ".*"
      foreach items.filter(
               function(x) {page:url("domain") eq
                            x.pick("$.page")}) 
         setting (d)
      pre {
         h = d.pick("$.header") + " using foreach";
         c = d.pick("$.content");
         domain = page:url("domain");
      }
      if(domain eq d.pick("$.page")) then
        notify(h,c);
 }

...

In the following rule, we use the implicit looping and filtering capabilities of a JPex to find just the item we want from the data structure and then pick the pieces we need out of that one item.

Code Block
languagejavascript
themeConfluencelanguagejavascript
rule without_foreach {
   select when pageview url ".*"
   pre {
     dom = page:url("domain");
     item = site_data.pick(
            "$..items[?(@.page eq '"+dom+"')]");
     content = item.pick("$..content");
     header = item.pick("$..header")
        + " without foreach";
   }
   if(dom eq item.pick("$..page")) then
     notify(header,content);
}

...

Suppose we wanted to make changes to a page based on data we retrieved from an online datasource. Assume the datasource returns data like the following and we bind it to a variable named replacements:

Code Block
languagejavascript
themeConfluence
languagejavascript
{"desc": "Data set to test foreach",
 "replacements": [
     {"selector":"#categories",
      "text":"This was the cloud tag"
     },
     {"selector":"#friends",
      "text":"This was a list of friends"
     },
     {"selector":".action-stream",
      "text":"This is where the action stream was"
     }
    ]
}

In this dataset, we have an array of replacements, each of which contains a jQuery selector pattern for elements on a Web page and the text we’re going to use with the action. The following ruleset uses the items in this data structure to prepend the text in each item above to the element on the page that matches the associated selector pattern:

Code Block
languagejavascript
themeConfluence
languagejavascript
rule prepend {
   select when pageview url "windley.com"
     foreach replacements.pick("$.replacements")
             setting (r)
     pre {
       sel = r.pick("$.selector");
       new_text = r.pick("$.text");
     }
     prepend(sel,new_text);
}

...

Using data often requires loops. We'v seen that there are multiple ways to loop in KRL: a ruleset is a loop, each rule can loop explicitly using foreach, and implicit looping is accomplished using operators like pick(), map(), and filter().

...