I'm trying to solve route-violation problem. User defines a route as series of location sequences (in exact order) that some item needs to be seen on. So lets say that correct route is (this is defined by user)
A -> B -> C
I'm trying to write Esper EPL which will fire on all route violations. A is a starting point, which needs to start a pattern (a valid location).
A -> !B -> !C
This will fire when there are all 3 types of events found in ESPER.
But what I want is also to fire when there is only partial combination like
A -> !B
or
!B -> !C
or
B -> !C
because if route is missed on B then I'll probably never come to C and will not be notified of route violation.
Is this even possible with patterns in Esper?
I can see a few options.
Register a separate statement with a pattern or match-recognize for each
Connect each with "or" i.e. "every (..) or every (..) or every (..)
Insert the result of a partial match into a stream for another pattern to continue matching
I think match-recognize has some resumption options around "skip"
Related
can someone please elaborate upon the functionality/conditions of travel(A,B,Visited,Path) and travel(A,B,P,[B|P]) .. this code finds the path between path A and B in a graph
edge(a,b).
edge(b,c).
edge(b,d).
edge(c,d).
edge(d,b).
edge(e,f).
edge(e,g).
edge(e,h).
edge(h,i).
edge(i,g).
edge(b,e).
connectedEdges(X,Y) :- edge(X,Y).
connectedEdges(X,Y) :- edge(Y,X).
path(A,B,Path) :-
travel(A,B,[A],Q),
reverse(Q,Path).
travel(A,B,P,[B|P]) :-
connectedEdges(A,B).
travel(A,B,Visited,Path) :-
connectedEdges(A,C),
C \== B,
\+member(C,Visited),
travel(C,B,[C|Visited],Path).
Lets start with the first travel/4 rule:
travel(A,B,P,[B|P]) :-
connectedEdges(A,B).
"If points A and B are directly connected to each other, then we have found a direct sub-path, and hence can succeed by adding point B to the path which is unified with all the points we have visited thus far."
In other words, since we have solved a sub-problem (by finding a direct connection to 2 nodes), we can essentially state that P (all that we have visited so far), is the tail of the path list [B|P] (the total path is the last node we have visited .... the current destination node, prepended to all the previous nodes we've visited).
Now for the next travel/4 rule:
travel(A,B,Visited,Path) :-
connectedEdges(A,C),
C \== B,
\+member(C,Visited),
travel(C,B,[C|Visited],Path).
It is important to note that this second rule will always be tried as an alternative, whether or not the first rule succeeded. Due to that fact of this implementation, the implication here is that this code can possibly find multiple paths (if more than one exists).
Anyway, in this second rule we find any nodes that are connected to A, other than B. Why?, this is because the first rule above already tried that; in this rule we're searching for alternatives. If that node C hasn't already been visited, we simply try to travel from that point (C) to our destination. Then we recursively query/call travel/4 yet again, until we've found a complete path(s).
Note again, that this implementation may find 0, 1, or more than 1 solution to a given query.
To recapitulate, the first rule is called to find direct connections between points. The second rule is called to find indirect connections between points.
I have the following function for a hacky project:
% The Record variable is some known record with an associated table.
Query = qlc:q([Existing ||
Existing <- mnesia:table(Table),
ExistingFields = record_to_fields(Existing),
RecordFields = record_to_fields(Record),
ExistingFields == RecordFields
]).
The function record_to_fields/1 simply drops the record name and ID from the tuple so that I can compare the fields themselves. If anyone wants context, it's because I pre-generate a unique ID for a record before attempting to insert it into Mnesia, and I want to make sure that a record with identical fields (but different ID) does not exist.
This results in the following (redacted for clarity) stack trace:
{aborted, {{case_clause, {stuff}},
[{db, '-my_func/2-fun-1-',8, ...
Which points to the line where I declare Query, however there is no case clause in sight. What is causing this error?
(Will answer myself, but I appreciate a comment that could explain how I could achieve what I want)
EDIT: this wouldn't be necessary if I could simply mark certain fields as unique, and Mnesia had a dedicated insert/1 or create/1 function.
For your example, I think your solution is clearer anyway (although it seems you can pull the record_to_fields(Record) portion outside the comprehension so it isn't getting calculated over and over.)
Yes, list comprehensions can only have generators and assignments. But you can cheat a little by writing an assignment as a one-element generator. For instance, you can re-write your expression as this:
RecordFields = record_to_fields(Record),
Query = qlc:q([Existing ||
Existing <- mnesia:table(Table),
ExistingFields <- [record_to_fields(Existing)],
ExistingFields == RecordFields
]).
As it turns out, the QLC DSL does not allow assignments, only generators and filters; as per the documentation (emphasis mine):
Syntactically QLCs have the same parts as ordinary list
comprehensions:
[Expression || Qualifier1, Qualifier2, ...]
Expression (the template)
is any Erlang expression. Qualifiers are either filters or generators.
Filters are Erlang expressions returning boolean(). Generators have
the form Pattern <- ListExpression, where ListExpression is an
expression evaluating to a query handle or a list.
Which means we cannot variable assignments within a QLC query.
Thus my only option, insofar as I know, is to simply write out the query as:
Query = qlc:q([Existing ||
Existing <- mnesia:table(Table),
record_to_fields(Existing) == record_to_fields(Record)
]).
I recently started checking new Java 8 features.
I've come across this forEach iterator-which iterates over the Collection.
Let's take I've one ArrayList of type <Integer> having values= {1,2,3,4,5}
list.forEach(i -> System.out.println(i));
This statement iteates over a list and prints the values inside it.
I'd like to know How am I going to specify that I want it to iterate over some specific values only.
Like, I want it to start from 2nd value and iterate it till 2nd last value. or something like that- or on alternate elements.
How am I going to do that?
To iterate on a section of the original list, use the subList method:
list.subList(1, list.length()-1)
.stream() // This line is optional since List already has a foreach method taking a Consumer as parameter
.forEach(...);
This is the concept of streams. After one operation, the results of that operation become the input for the next.
So for your specific example, you can follow #Joni's command. But if you're asking in general, then you can create a filter to only get the values you want to loop over.
For example, if you only wanted to print the even numbers, you could create a filter on the streams before you forEached them. Like this:
List<Integer> intList = Arrays.asList(1,2,3,4,5);
intList.stream()
.filter(e -> (e & 1) == 0)
.forEach(System.out::println);
You can similarly pick out the stuff you want to loop over before reaching your terminal operation (in your case the forEach) on the stream. I suggest you read this stream tutorial to get a better idea of how they work: http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/
I am trying to create a social network-like structure.
I would like to create a timeline of posts which looks like this
(user:Person)-[:POSTED]->(p1:POST)-[:PREV]->[p2:POST]...
My problem is the following.
Assuming a post for a user already exists, I can create a new post by executing the following cypher query
MATCH (user:Person {id:#id})-[rel:POSTED]->(prev_post:POST)
DELETE rel
CREATE (user)-[:POSTED]->(post:POST {post:"#post", created:timestamp()}),
(post)-[:PREV]->(prev_post);
Assuming, the user has not created a post yet, this query fails. So I tried to somehow include both cases (user has no posts / user has at least one post) in one update query (I would like to insert a new post in the "post timeline")
MATCH (user:Person {id:"#id"})
OPTIONAL MATCH (user)-[rel:POSTED]->(prev_post:POST)
CREATE (post:POST {post:"#post2", created:timestamp()})
FOREACH (o IN CASE WHEN rel IS NOT NULL THEN [rel] ELSE [] END |
DELETE rel
)
FOREACH (o IN CASE WHEN prev_post IS NOT NULL THEN [prev_post] ELSE [] END |
CREATE (post)-[:PREV]->(o)
)
MERGE (user)-[:POSTED]->(post)
Is there any kind of if-statement (or some type of CREATE IF NOT NULL) to avoid using a foreach loop two times (the query looks a litte bit complicated and I know that the loop will only run 1 time)?.
However, this was the only solution, I could come up with after studying this SO post. I read in an older post that there is no such thing as an if-statement.
EDIT: The question is: Is it even good to include both cases in one query since I know that the "no-post case" will only occur once and that all other cases are "at least one post"?
Cheers
I've seen a solution to cases like this in some articles. To use a single query for all cases, you could create a special terminating node for the list of posts. A person with no posts would be like:
(:Person)-[:POSTED]->(:PostListEnd)
Now in all cases you can run the query:
MATCH (user:Person {id:#id})-[rel:POSTED]->(prev_post)
DELETE rel
CREATE (user)-[:POSTED]->(post:POST {post:"#post", created:timestamp()}),
(post)-[:PREV]->(prev_post);
Note that the no label is specified for prev_post, so it can match either (:POST) or (:PostListEnd).
After running the query, a person with 1 post will be like:
(:Person)-[:POSTED]->(:POST)-[:PREV]->(:PostListEnd)
Since the PostListEnd node has no info of its own, you can have the same one node for all your users.
I also do not see a better solution than using FOREACH.
However, I think I can make your query a bit more efficient. My solution essentially merges the 2 FOREACH tests into 1, since prev_postand rel must either be both NULL or both non-NULL. It also combines the CREATE and the MERGE (which should have been a CREATE, anyway).
MATCH (user:Person {id:"#id"})
OPTIONAL MATCH (user)-[rel:POSTED]->(prev_post:POST)
CREATE (user)-[:POSTED]->(post:POST {post:"#post2", created:timestamp()})
FOREACH (o IN CASE WHEN prev_post IS NOT NULL THEN [prev_post] ELSE [] END |
DELETE rel
CREATE (post)-[:PREV]->(o)
)
In the Neo4j v3.2 developer manual it specifies how you can create essentially a composite key made of multiple node properties at this link:
CREATE CONSTRAINT ON (n:Person) ASSERT (n.firstname, n.surname) IS NODE KEY
However, this is only available for the Enterprise Edition, not Community.
"CASE" is as close to an if-statement as you're going to get, I think.
The FOREACH probably isn't so bad given that you're likely limited in scope. But I see no particular downside to separating the query into two, especially to keep it readable and given the operations are fairly small.
Just my two cents.
My event stream generally contains an open event followed by a close event. Let's call them O and C, correspndingly. However, there are two particulars:
O may be followed by one or more O before C arrives
C may be missing completely (see below)
It is assumed that C should arrive not later than within time T after some O. Otherwise, C is considered missing. When a C eventually arrives, all open Os arrived earlier than T from this C are considered orphans and are of no interest.
I want esper to fire each pair of O followed by C, where earliest O not farther then T from C is selected. Any Os in between as well as before selected O are skipped.
For example,
O1 O2 O3 ... C
should select (O1,C) if datediff(O1, C) < T
should select (O2,C) if above is false and datediff(O2, C) < T
etc.
A lost my temper in approaching this problem. Looks like my mind is not compatible with esper. Your help is very appritiated.
This could be something like below, the idea is that when Event arrives we want to look at the last 1 minute of events and find the first one that matches the time diff. Have a second statement filter out those without matches, if that is desired.
insert into Pair
select , (select window().firstOf(v => v.time - e2.time < T) from Event.win:time(1 min) as e1) as matched
from Event as e2
select * from Pair where matched is not null