Drools Window length and from behaviour - window

I am using the following rule to fire an alert in case there is 1 green packet sent followed by a red packet
package com.myspace.floodmonitoringrules;
import java.lang.Number;
import java.util.ArrayList;
rule "rule3"
dialect "mvel"
enabled true
when
w : ArrayList( size() == 1 ) from collect ( devicestatus( flood_status_color == "green" ) over window:length (1))
ds : devicestatus( flood_status_color == "red" )
then
ds.setFlood_alert( "WindowDetected" );
end
However the rule is getting fired after sending any red packet wven if no green packets sent before.
This is when i define ksession as statful.
AND when i define it as stateless the rule is not getting fired at all!
i need to understand the behavior as i am new to drools.

This should fit your requirenments
declare Status #role (event) end
rule "rule3"
dialect "mvel"
enabled true
when
$list : List() from collect (Status() over window:length (2))
$g : Status( floodStatusColor == "green" ) from $list
$r : Status( floodStatusColor == "red", this after $g ) from $list
then
System.out.println("WindowDetected")
end
test
#DroolsSession("test.drl")
public class PlaygroundTest {
#Rule
public DroolsAssert drools = new DroolsAssert();
#Test
public void testIt() {
drools.insertAndFire(new Status("red"));
drools.advanceTime(1, SECONDS);
drools.insertAndFire(new Status("green"));
drools.advanceTime(1, SECONDS);
drools.insertAndFire(new Status("green"));
drools.advanceTime(1, SECONDS);
drools.insertAndFire(new Status("red"));
drools.advanceTime(1, SECONDS);
drools.insertAndFire(new Status("green"));
drools.advanceTime(1, SECONDS);
drools.insertAndFire(new Status("red"));
drools.advanceTime(1, SECONDS);
drools.insertAndFire(new Status("red"));
drools.advanceTime(1, SECONDS);
drools.insertAndFire(new Status("red"));
drools.advanceTime(1, SECONDS);
drools.insertAndFire(new Status("green"));
drools.advanceTime(1, SECONDS);
drools.insertAndFire(new Status("red"));
}
output
00:00:00 --> inserted: Status[floodStatusColor=red]
00:00:00 --> fireAllRules
00:00:01 --> inserted: Status[floodStatusColor=green]
00:00:01 --> fireAllRules
00:00:02 --> inserted: Status[floodStatusColor=green]
00:00:02 --> fireAllRules
00:00:03 --> inserted: Status[floodStatusColor=red]
00:00:03 --> fireAllRules
00:00:03 <-- 'rule3' has been activated by the tuple [InitialFactImpl, ArrayList, Status, Status]
WindowDetected
00:00:04 --> inserted: Status[floodStatusColor=green]
00:00:04 --> fireAllRules
00:00:05 --> inserted: Status[floodStatusColor=red]
00:00:05 --> fireAllRules
00:00:05 <-- 'rule3' has been activated by the tuple [InitialFactImpl, ArrayList, Status, Status]
WindowDetected
00:00:06 --> inserted: Status[floodStatusColor=red]
00:00:06 --> fireAllRules
00:00:07 --> inserted: Status[floodStatusColor=red]
00:00:07 --> fireAllRules
00:00:08 --> inserted: Status[floodStatusColor=green]
00:00:08 --> fireAllRules
00:00:09 --> inserted: Status[floodStatusColor=red]
00:00:09 --> fireAllRules
00:00:09 <-- 'rule3' has been activated by the tuple [InitialFactImpl, ArrayList, Status, Status]
WindowDetected
You may want to look at similar question, more complicated and interesting.
alternative solution without sliding windows if you do not need older events for other rules
rule "rule3"
when
$g : Status( floodStatusColor == "green" )
$r : Status( floodStatusColor == "red", this after $g )
then
System.out.println("WindowDetected");
end
rule "rule3 cleanup"
salience -1
when
$e : Status()
Status( this after $e )
then
delete($e);
end

I added retract( $g ); to your code then it is working perfectly now.
rule "rule3"
dialect "mvel"
enabled true
when
$list : List() from collect (Status() over window:length (2))
$g : Status( floodStatusColor == "green" ) from $list
$r : Status( floodStatusColor == "red", this after $g ) from $list
then
System.out.println("WindowDetected")
retract( $g );
end

Related

Filtering with comparing each element of a Flux to a single Mono

I am trying to use a Mono of a username to filter out every element of a Flux (the flux having multiple courses) and I am using Cassandra as backend , here is the schema:
CREATE TABLE main.courses_by_user (
course_creator text PRIMARY KEY,
courseid timeuuid,
description text,
enrollmentkey text
) WITH additional_write_policy = '99PERCENTILE'
AND bloom_filter_fp_chance = 0.01
AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'}
AND comment = ''
AND compaction = {'class': 'org.apache.cassandra.db.compaction.UnifiedCompactionStrategy'}
AND compression = {'chunk_length_in_kb': '64', 'class': 'org.apache.cassandra.io.compress.LZ4Compressor'}
AND crc_check_chance = 1.0
AND default_time_to_live = 0
AND gc_grace_seconds = 864000
AND max_index_interval = 2048
AND memtable_flush_period_in_ms = 0
AND min_index_interval = 128
AND read_repair = 'BLOCKING'
AND speculative_retry = '99PERCENTILE';
course_creator | courseid | description | enrollmentkey
----------------+--------------------------------------+-------------+---------------
rascall | b7757e80-0c24-11ed-aec5-23fe9d87e512 | Cosmology | hubble
dibiasky | b7757e81-0c24-11ed-aec5-23fe9d87e512 | astronomy | thebigbang
michaeljburry | b7753060-0c24-11ed-aec5-23fe9d87e512 | Lol | enter
Noam Chomsky | 6c1a4800-09ac-11ed-ada9-83d934863d60 | Hi | Bye
I am using zipWith to pair the Flux of courses with the Mono of user, here is the code
public Flux<CourseByCreator> getMyCourses(Principal placeholder){
Mono<User> principal = Mono.just(new User("dibiasky", "whatifitsnotequal","Kate" ,"Dibiasky"));
return allCreatedCourses = this.courseByCreatorRepository.findAll()
.zipWith(principal)
.flatMap(tuple -> {
if(tuple.getT1().getCourseCreator().equals(tuple.getT2().getUsername())){
System.out.println(tuple.getT2().getUsername());
return Flux.just(tuple.getT1());
}else{
return Flux.empty();
}
}).log();
}
For some reason I am not getting an empty return result despite the user have one matching username and the courses have one matching course with the same creator
What am I doing wrong here?
Flux.zipWith:
Zip this Flux with another Publisher source, that is to say wait for both to emit one element and combine these elements once into a Tuple2. The operator will continue doing so until any of the sources completes.
The mono will emit one then complete.
I'd rather first resolve the principal, then filter the courses:
return Mono
.just(new User("dibiasky", "whatifitsnotequal","Kate" ,"Dibiasky"))
.flatMapMany(principal -> {
return courseByCreatorRepository
.findAll()
.filter(course -> couse.getCourseCreator().equals(principal.getUsername()));
})
.log();
public Flux<CourseByCreator> getMyCourses(Principal placeholder){
Mono<User> principal = Mono.just(new User("dibiasky", "whatifitsnotequal","Kate" ,"Dibiasky"));
return this.courseByCreatorRepository.findAll()
.filterWhen(course -> principal
.filter(user -> user.getUsername().equals(course. getCourseCreator()))
.hasElement()
)
.log();
}

Laravel calculate total balance

Here is my Table:
| id | type | balance
| ----|--------| -------
| 1 | credit | 2400
| 2 | credit | 4800
| 3 | debit | 1200
The calculated amount should be 6000. (2400 + 4800 - 1200) = 6000
How can I do this using Eloquent or collection?
Using laravel collection and one sql query.
return Model::all()->reduce(function ($carry, $item) {
return $item->type == 'credit'
? $carry + $item->balance : $carry - $item->balance;
},0);
You can do by this using Eloquent:
Credits
$totalCredits = Model::where('type', 'credit')->sum('balance');
Debits
$totalDebits = Model::where('type', 'debit')->sum('balance');
Balances
$Total = $totalCredits - $totalDebits
If you want SUM only then do this
DB::table("table")->get()->sum("balance")

Concatenate rows based on row comparisons using LINQ

I tried doing this in SQL for about a month now, but I think it might be easier to do it with .NET linq.
The basics are as follows:
The query is supposed to return data from a date range, and return a concatenated list of player names and player times.
The concatenation would ONLY occur if the playEnd was within 30 minutes of the next players playStart.
So if I have data like this:
Name PlayDate PlayStart PlayEnd
----------------------------------------------------
player1 | 10/8/2018 | 08:00:00 | 09:00:00
player2 | 10/8/2018 | 09:10:00 | 10:10:00
player3 | 10/9/2018 | 10:40:00 | 11:30:00
player4 | 10/11/2018 | 08:30:00 | 08:37:00
player5 | 10/11/2018 | 08:40:00 | 08:50:00
player6 | 10/12/2018 | 09:00:00 | 09:45:00
player7 | 10/12/2018 | 09:50:00 | 10:10:00
player8 | 10/12/2018 | 10:30:00 | 12:20:00
player1 and player2 play times would be concatenated together like: player1, player2 = 8:00:00 - 10:10:00 for 10/8/2018
player3 would just be: player3 = 10:40:00 - 11:30:00 for 10/9/2018
player4 and player5 play times would be concatenated like: player4, player5 = 08:30:00 - 08:50:00 for 10/11/2018
player6 and player7 and player8 play times would be concatenated like: player6, player7, player8 = 09:00:00 - 12:20:00 for 10/12/2018
I've tried modifying the query below in many ways, but I just don't know how to compare one row of data with the next and then combine the two (or more) if needed.
var query = from pl in players
select new PlaySession
{
Name = pl.Name,
PlayDate = pl.PlayDate,
PlayStart = pl.PlayStartTime,
PlayEnd = pl.PlayEndTime
};
var grouped = query
.OrderBy(r => r.Name)
.ThenBy(r => r.PlayDate)
.ThenBy(r => r.PlayStart)
Now this is where I get confused:
I need to figure out the following:
how to compare PlayDates of the various rows to make sure that they are the same date, like this: row1.PlayDate == row2.PlayDate
how to compare one rows PlayEnd with the next rows PlayStart, something like this: row2.PlayStart - row1.PlayEnd < 30 minutes
Is there a way to compare values across rows using LINQ?
Thanks!
As per as I am concern, thing should be like as follows:
List<ViewModel> playersGroupList = Players.GroupBy(p => p.PlayDate).Select(group => new ViewModel
{
PlayDate = group.Key,
Names = String.Join("-", group.Select(g => g.Name).ToArray()),
PlayDuration = group.Select(g => g.PlayStart).First() + "-" + group.Select(g => g.PlayEnd).Last()
}).ToList();
And here ViewModel is as follows:
public class ViewModel
{
public string PlayDate {get set;}
public string Names {get set;}
public string PlayDuration {get set;}
}
Note: Some adjudgement may be needed to fulfill your point to point requirement but actual implementation should be as shown.

Efficient use of Perl hash

I'm using a hash to abbreviate state names
%STATEABBRIVATE = ('ALABAMA' => 'AL',
...);
Some of my input sets already have abbreviated state names. Would it be more efficient to use an if defined $STATEABBRIVATE{$state} or to add another 51 matched pairs 'AL'=>'AL' to the hash?
If you want to verify that the state really exists, using AL => 'AL' might be the easiest way.
To keep your code DRY (Don't Repeat Yourself), you can just
my %STATEABBRIVATE = ( ALABAMA => 'AL',
...
);
my #abbrevs = values %STATEABBRIVATE;
#STATEABBRIVATE{#abbrevs} = #abbrevs;
If you're concenrned about performance, the bottleneck is probably somewhere else:
#! /usr/bin/perl
use warnings;
use strict;
use Benchmark qw{ cmpthese };
use Test::More;
my %hash = qw( Alabama AL Alaska AK Arizona AZ Arkansas AR California CA
Colorado CO Connecticut CT Delaware DE Florida FL
Georgia GA Hawaii HI Idaho ID Illinois IL Indiana IN
Iowa IA Kansas KS Kentucky KY Louisiana LA Maine ME
Maryland MD Massachusetts MA Michigan MI Minnesota MN
Mississippi MS Missouri MO Montana MT Nebraska NE
Nevada NV Ohio OH Oklahoma OK Oregon OR Pennsylvania PA
Tennessee TN Texas TX Utah UT Vermont VT Virginia VA
Washington WA Wisconsin WI Wyoming WY );
$hash{'West Virginia'} = 'WV';
$hash{'South Dakota'} = 'SD';
$hash{'South Carolina'} = 'SC';
$hash{'Rhode Island'} = 'RI';
$hash{'North Dakota'} = 'ND';
$hash{'North Carolina'} = 'NC';
$hash{'New York'} = 'NY';
$hash{'New Mexico'} = 'NM';
$hash{'New Jersey'} = 'NJ';
$hash{'New Hampshire'} = 'NH';
my %larger = %hash;
#larger{ values %hash } = values %hash;
sub def {
my $state = shift;
return defined $hash{$state} ? $hash{$state} : $state
}
sub ex {
my $state = shift;
return exists $hash{$state} ? $hash{$state} : $state
}
sub hash {
my $state = shift;
return $larger{$state}
}
is(def($_), ex($_), "def-ex-$_") for keys %larger;
is(def($_), hash($_), "def-hash-$_") for keys %larger;
done_testing();
cmpthese(-1,
{ hash => sub { map hash($_), keys %larger },
ex => sub { map ex($_), keys %larger },
def => sub { map def($_), keys %larger },
});
Results:
Rate def ex hash
def 27307/s -- -2% -11%
ex 27926/s 2% -- -9%
hash 30632/s 12% 10% --
Both if defined $STATEABBRIVATE{$state} and any hash lookups are going to be constant time (i.e. O(1) operations). In fact, defined() probably uses a hash table lookup behind the scenes anyway. So, my prediction is that the difference in performance is going to be negligible, even with large data sets. This is, at best, an educated guess.

Count from each distinct date, fill in missing dates with zero

I'm trying to create an Eloquent query that gets the total number of posts made each distinct day, and if the date is missing, fill it in with a value of zero.
For example, if my table looks like this:
+----+---------------------+
| id | date |
+----+---------------------+
| 1 | 2015-01-01 00:00:00 |
| 2 | 2015-01-01 01:53:18 |
| 3 | 2015-01-01 02:41:26 |
| 4 | 2015-01-02 12:51:01 |
| 5 | 2015-01-05 08:24:12 |
+----+---------------------+
This would output:
2015-01-01 : 3
2015-01-02 : 1
2015-01-05 : 1
Notice, however, that the days 03-04 are missing. How can I include these dates, but give them the value 0 such that I end up with an output like:
2015-01-01 : 3
2015-01-02 : 1
2015-01-03 : 0
2015-01-04 : 0
2015-01-05 : 1
Here is my current query:
$posts = Post::select(array(
DB::raw('DATE(`created_at`) as `date`'),
DB::raw('COUNT(*)as `count`')
))
->where('created_at', '>', Carbon::today()->subWeek())
->groupBy('date')
->orderBy('date', 'DESC')
->lists('count', 'date');
Thanks!
In your SQL results you can generate some "fake-data" in your rows, but u can not generate "fake-rows", exept joining to some "fake(temporary)-table".
In your case ll be much easier to apply some logic around sql-result.
Replace your code with this:
$order = 'DESC';
$endDate = Carbon::today();
$startDate = Carbon::today()->subWeek();
$dateInc = ($order == 'DESC') ? -1 : 1;
$dateCycleHolder = clone ($dateInc > 0 ? $startDate : $endDate);
$dateCycleEnd = clone ($dateInc > 0 ? $endDate : $startDate);
$posts = Post::select(array(
DB::raw('DATE(`created_at`) as `date`'),
DB::raw('COUNT(*)as `count`')
))
->where('created_at', '>', $startDate)
->groupBy('date')
->orderBy('date', $order)
->lists('count', 'date');
$postsFinal = new \Illuminate\Database\Eloquent\Collection();
while ($dateCycleHolder->ne($dateCycleEnd)) {
$dateCurr = $dateCycleHolder->format('Y-m-d');
$postsFinal->put($dateCurr, $posts->get($dateCurr, 0));
$dateCycleHolder->addDay($dateInc);
}
$dateCurr = $dateCycleHolder->format('Y-m-d');
$postsFinal->put($dateCurr, $posts->get($dateCurr, 0));
$posts = $postsFinal;
its alittle bit flexible, you can change values of this things:
$order = 'DESC';
$endDate = Carbon::today();
$startDate = Carbon::today()->subWeek();

Resources