You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@flink.apache.org by "Ivan Mushketyk (JIRA)" <ji...@apache.org> on 2016/08/23 20:34:22 UTC

[jira] [Comment Edited] (FLINK-3414) Add Scala API for CEP's pattern definition

    [ https://issues.apache.org/jira/browse/FLINK-3414?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15404852#comment-15404852 ] 

Ivan Mushketyk edited comment on FLINK-3414 at 8/23/16 8:34 PM:
----------------------------------------------------------------

I would like to describe my proposal on the case-by-case basis. Feel free to point on any gaps and inconsistencies in this design.
Also, I am not sure what is the best medium for this kind of discussion. I will start it here, but if you find it inconvenient or sub-optimal, I can move it to some other place.

Let's start with a simplest pattern that expects a single event:

{code:java}
Pattern.begin("start").where(evt -> evt.getId() == 42)
{code}

This can be replaced with a much shorter Scala version

{code:java}
pattern "start" _.getId() == 42
{code}

A single function call *pattern* can replace *Pattern.begin* call and *where* condition can be added as an argument to this function call.

A pattern that is expecting a specific type like this one:

{code:java}
Pattern.begin("start").where(evt -> evt.getId() == 42)
       .subtype(SubEvent.class)
{code}

Can be replaced with the following Scala code:

{code:java}
pattern[SubEvent] "start" _.id == 42
{code}

The type of an object that is expected can be added as a type parameter to the "pattern" function.

The *next* function can be replaced with the *->* operator:

{code:java}
Pattern.begin("start").where(evt -> evt.getId() == 42)
       .subtype(SubEvent.class)
       .next("next").where(evt -> evt.getId() > 42)
       .subtype(SubEvent.class)
{code}

as in this code snippet:

{code:java}
pattern[SubEvent] "start" _.id == 42 ->[SubEvent] _.getId() > 42
{code}

or with a function call:

{code:java}
pattern[SubEvent] "start" _.id == 42 next[SubEvent] _.getId() > 42
{code}

A followedBy can be replaced with a *->>* operator:

{code:java}
Pattern.begin("start").where(evt -> evt.getId() == 42)
    .next("middle").subtype(SubEvent.class).where(subEvt -> subEvt.getVolume() >= 10.0)
    .followedBy("end").where(evt -> evt.getName().equals("end"));
{code}

as in the following example:

{code:java}
pattern "start" _.getId() == 42
 ->[SubEvent] "middle" _.getVolume() >= 10.0
 ->> "end" _.getName() == "end"
{code}

A *within* function call can be replaced with *in* function that expects an instance of a FinateDuration class that can provide a more readable code:

{code:java}
pattern "start" -> "middle" _.getName() == "error" ->> "end" _.getName() == "critical" in 10 seconds
{code}

h3. Additional operators

As far as I understand *or* and *not* operators are not yet implemented, but here are few examples for these operators

{code:java}
// Pattern that has an event that is not SubEvent
pattern "start" ! is[SubEvent]
{code}

This example also shows an alternative way of specifying a type of element with the *is* function.

{code:java}
// Pattern that has type SubEvent or has id 42
pattern "start" is[SubEvent] || is _.getId() == 42
{code}

h3. Quantifiers

Quantifiers for patterns are currently in PR, but here how they could be implemented.

{code:java}
pattern "start" is[SubEvent] * // zero or many
pattern "start" is[SubEvent] + // one or many
pattern "start" is[SubEvent] ? // optional
{code}

To mark that a pattern should be repeated specific number of time I suggest to introduce a method "rep" that can be used like this:

{code:java}
pattern "start" is[SubEvent] rep(5) // repeat 5 times
pattern "start" is[SubEvent] rep(3, 10) // repeat from 3 to 10 times inclusive 
{code}


was (Author: ivan.mushketyk):

I would like to describe my proposal on the case-by-case basis. Feel free to point on any gaps and inconsistencies in this design.
Also, I am not sure what is the best medium for this kind of discussion. I will start it here, but if you find it inconvenient or sub-optimal, I can move it to some other place.

Let's start with a simplest pattern that expects a single event:

{code:java}
Pattern.begin("start").where(evt -> evt.getId() == 42)
{code}

This can be replaced with a much shorter Scala version

{code:java}
pattern "start" _.getId() == 42
{code}

A single function call *pattern* can replace *Pattern.begin* call and *where* condition can be added as an argument to this function call.

A pattern that is expecting a specific type like this one:

{code:java}
Pattern.begin("start").where(evt -> evt.getId() == 42)
       .subtype(SubEvent.class)
{code}

Can be replaced with the following Scala code:

{code:java}
pattern[SubEvent] "start" _.id == 42
{code}

The type of an object that is expected can be added as a type parameter to the "pattern" function.

The *next* function can be replaced with the *->* operator:

{code:java}
Pattern.begin("start").where(evt -> evt.getId() == 42)
       .subtype(SubEvent.class)
       .next("next").where(evt -> evt.getId() > 42)
       .subtype(SubEvent.class)
{code}

as in this code snippet:

{code:java}
pattern[SubEvent] "start" _.id == 42 ->[SubEvent] _.getId() > 42
{code}

or with a function call:

{code:java}
pattern[SubEvent] "start" _.id == 42 next[SubEvent] _.getId() > 42
{code}

A followedBy can be replaced with a *->>* operator:

{code:java}
Pattern.begin("start").where(evt -> evt.getId() == 42)
    .next("middle").subtype(SubEvent.class).where(subEvt -> subEvt.getVolume() >= 10.0)
    .followedBy("end").where(evt -> evt.getName().equals("end"));
{code}

as in the following example:

{code:java}
pattern "start" _.getId() == 42
 ->[SubEvent] "middle" _.getVolume() >= 10.0
 ->> "end" _.getName() == "end"
{code}

A *within* function call can be replaced with *in* function that expects an instance of a FinateDuration class that can provide a more readable code:

{code:java}
pattern "start" -> "middle" _.getName() == "error" ->> "end" _.getName() == "critical" in 10 seconds
{code}

h3. Additional operators

As far as I understand *or* and *not* operators are not yet implemented, but here are few examples for these operators

{code:java}
// Pattern that has an event that is not SubEvent
pattern "start" ! is[SubEvent]
{code}

This example also shows an alternative way of specifying a type of element with the *is* function.

{code:java}
// Pattern that has type SubEvent or has id 42
pattern "start" is[SubEvent] || is _.getId() == 42
{code}

> Add Scala API for CEP's pattern definition
> ------------------------------------------
>
>                 Key: FLINK-3414
>                 URL: https://issues.apache.org/jira/browse/FLINK-3414
>             Project: Flink
>          Issue Type: Improvement
>          Components: CEP
>    Affects Versions: 1.0.0
>            Reporter: Till Rohrmann
>            Assignee: Ivan Mushketyk
>            Priority: Minor
>
> Currently, the CEP library only supports a Java API to specify complex event patterns. In order to make it a bit less verbose for Scala users, it would be nice to also add a Scala API for the CEP library. 
> A Scala API would also allow to pass Scala's anonymous functions as filter conditions or as a select function, for example, or to use partial functions to distinguish between different events.
> Furthermore, the Scala API could be designed to feel a bit more like a DSL:
> {code}
> begin "start" where _.id >= 42 -> "middle_1" as classOf[Subclass] || "middle_2" where _.name equals "foobar" -> "end" where x => x.id <= x.volume
> {code}



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)