StreamInsight, event matching - complex-event-processing

I ran into a situation with StreamInsight where I have 1 input source with different types of events that need to be treated differently, but are eventually matched with other events from the same source.
I created a (MUCH) simpler scenario below where an input source generates random numbers (for simplicity sake, 1s and 0s). If the number is even, we want to save it until further notice (unknown duration). If the number is odd, we want to match it with n-1 from the even stream and then remove n-1 from the even stream. If there is no match, the odd number is simply processed through as is with no further calculation. I have everything working as expected up to the point of removing a matching n-1 from the even stream. A match is made, and the match gets pushed to the output adapter, but remains available for another join to be made to the given event. What I have gathered of several days worth of experimentation and research, is somehow I need to clip the even stream event duration (ClipEventDuration), presumably as part of the filter in GenerateEvenJoinQuery, however, everything I have
tried has produced no change, or undesired results. I have also tried changing the evenStream to an Interval shape with even less luck. Any help or suggestions would be greatly appreciated.
For example, given a simplified list:
[ 1, 0, 1, 1, 0, 0, 1, 1, 1 ]
I would expect the output to look like:
[ 1, 100, 1, 100, 100, 1 ]
I would also be accepting of as the real scenario I'm working with, the first output isn't actually possible:
Note the 2nd and 3rd 0s are joined to a single 1.
[ 1, 100, 1, 100, 1, 1 ]
...
CepStream<int> inputStream = CepStream<int>.Create(
app
, "ATGInputStream"
, typeof(InputAdapterFactory)
, new InputConfig()
, EventShape.Point);
var everythingFilter = from e in inputStream select e;
Query everythingQuery = GenerateEverythingQuery(app, inputStream);
var everythingStream = everythingQuery.ToStream<int>("everythingStream");
Query oddQuery = GenerateOddQuery(app, everythingStream);
var oddAts = new AdvanceTimeSettings(new AdvanceTimeGenerationSettings(1, TimeSpan.FromTicks(-1), false), null, AdvanceTimePolicy.Drop);
var oddStream = oddQuery.ToStream<int>("oddStream", oddAts);
// only inject a cti in to even when we need it
var ats = new AdvanceTimeSettings(null, new AdvanceTimeImportSettings("oddStream"), AdvanceTimePolicy.Adjust);
Query evenQuery = GenerateEvenQuery(app, everythingStream);
var evenStream = evenQuery.ToStream<int>("evenStream", ats);
Query joinQuery = GenerateOddEvenJoinQuery(app, evenStream, oddStream);
var joinStream = joinQuery.ToStream<int>("joinStream");
...
private Query GenerateOddEvenJoinQuery(Application app, CepStream<int> evenStream, CepStream<int> oddStream) {
// (o * e) + 100 is an easy way to tell we had a match
var filter = (from o in oddStream
from e in evenStream
where e == (o - 1)
select (o * e) + 100);
// LEFT ANTI SEMI JOIN
var filter2 = from o in oddStream
where (from e in evenStream where e == o - 1 select e).IsEmpty()
select o;
var joinFilter = filter.Union(filter2);
return joinFilter.ToQuery(
app
, "Number Join Query"
, "Joins number streams."
, EventShape.Point
, StreamEventOrder.FullyOrdered);
}
private Query GenerateEvenQuery(Application app, CepStream<int> stream) {
var evenFilter = (from e in stream where e % 2 == 0 select e).AlterEventDuration(e => TimeSpan.MaxValue);
return evenFilter.ToQuery(
app
, "EvenQuery"
, ""
, EventShape.Edge
, StreamEventOrder.FullyOrdered);
}
private Query GenerateOddQuery(Application app, CepStream<int> stream) {
var filter = (from f in stream where (f % 2) == 1 select f);
return filter.ToQuery(
app
, "OddQuery"
, "Queries for odd numbers in stream."
, EventShape.Point
, StreamEventOrder.FullyOrdered);
}
private Query GenerateEverythingQuery(Application app, CepStream<int> stream) {
var everythingFilter = from e in stream select e;
return everythingFilter.ToQuery(
app
, "EverythingQuery"
, "Queries everything from the input stream."
, EventShape.Point
, StreamEventOrder.FullyOrdered);
}
SOLUTION:
While I was hoping for something a little more elaborate and potentially faster, the delayed processing may help with performance.
public Program() {
public Program() {
...
var stream = CepStream<RandomNumber>.Create(
app
, "StaticInputStream"
, typeof(StaticInputAdapterFactory)
, new InputConfig()
, EventShape.Point);
var processedStream = stream.Scan(new StreamMatcher());
Query consoleQuery = GenerateConsoleOutputQuery(app, processedStream);
...
}
private Query GenerateConsoleOutputQuery(Application app, CepStream<int> stream) {
var filter = from f in stream select f;
return filter.ToQuery(
app
, "Console Output Query"
, "Queries for messages to output to the console."
, typeof(OutputAdapterFactory)
, new OutputConfig()
, EventShape.Point
, StreamEventOrder.FullyOrdered);
}
public class StreamMatcher : CepPointStreamOperator<RandomNumber, int> {
private List<int> unmatched = new List<int>();
public override bool IsEmpty {
get { return false; }
}
public override IEnumerable<int> ProcessEvent(PointEvent<RandomNumber> inputEvent) {
if(inputEvent.Payload.value % 2 == 0) {
unmatched.Add(inputEvent.Payload.value);
} else {
var result = inputEvent.Payload.value;
int match = -1;
try {
match = (from f in unmatched where f == result - 1 select f).Take(1).Single();
unmatched.Remove(match);
} catch { }
if(match > -1) {
result += match + 100;
}
yield return result;
}
}
}
}
public class RandomNumber {
public int value { get; set; }
public DateTime timeStamp { get; set; }
}

You might consider using a UDSO ( User defined stream operator), where you can keep the state of your zeros.
void Main()
{
var randomNumbers = new []
{
new RandomNumber(){ Value = 1, TimeStamp = DateTime.Parse("2012-01-01 10:01:00 AM") },
new RandomNumber(){ Value = 0, TimeStamp = DateTime.Parse("2012-01-01 10:02:00 AM") },
new RandomNumber(){ Value = 0, TimeStamp = DateTime.Parse("2012-01-01 10:02:00 AM") },
new RandomNumber(){ Value = 1, TimeStamp = DateTime.Parse("2012-01-01 10:03:00 AM") },
new RandomNumber(){ Value = 1, TimeStamp = DateTime.Parse("2012-01-01 10:04:00 AM") },
new RandomNumber(){ Value = 0, TimeStamp = DateTime.Parse("2012-01-01 10:05:00 AM") },
};
var stream = randomNumbers.ToPointStream(Application,
e=> PointEvent.CreateInsert(e.TimeStamp ,e),
AdvanceTimeSettings.IncreasingStartTime) ;
var query = stream.Scan(new MyCalculation());
query.Dump();
}
public class MyCalculation : CepPointStreamOperator<RandomNumber,string>
{
private Queue<int> _queue = new Queue<int>() ;
public override bool IsEmpty
{
get { return false; }
}
public override IEnumerable<string> ProcessEvent(PointEvent<RandomNumber> inputEvent)
{
if (inputEvent.Payload.Value % 2 == 0)
{
_queue.Enqueue(inputEvent.Payload.Value);
}
else
{
var result= inputEvent.Payload.Value.ToString() ;
var list = _queue.ToArray();
for (int i = 0; i < list.Count(); i++)
{
result += list[i];
}
yield return result ;
}
}
}
public class RandomNumber
{
public int Value {get;set;}
public DateTime TimeStamp {get;set;}
}

Related

How to combine GroupedObservables in rx.net?

I have one observable that I use GroupBy on to get a number of streams. I actually want a Scan result over each sub-stream. Let's say the observable is over product prices and the scan result is average price per product type.
I have another stream of events pertaining to those 'products' (let's say "show product price" events) and I want to combine it with the previous stream's latest product price. So the Scan output per group needs to be combined with each element of the event stream to get the latest average price for that event's product.
For some reason I cannot get the right syntax and I have been bashing away at this all day. Can someone please help?
Update
I am adding the code below to illustrate the approximate intent.
public class Node
{
private List<int> Details = new List<int>();
public void AddInfo(int x)
{
Details.Add(x );
}
public Node(int x)
{
Details.Add(x);
}
public int Index => Details[0]%10; //just to simplify the grouping and debugging
public int Latest => Details.Last();
}
public class Message
{
private static Random _random = new Random();
public int MessageNodeInfo { get; private set; }
public Message()
{
MessageNodeInfo = _random.Next();
}
}
public class AccumulatingInfoTest
{
private static Random _random=new Random();
private IObservable<Message> MessageStream()
{
TimeSpan timeSpan = TimeSpan.FromSeconds(0.5);
var ret= Observable.Generate(0,
_ => { return true; },
_ => { return 0; },
_ => { return new Message(); },
_=> timeSpan)
.Publish()
.RefCount();
return ret;
}
public class ArbitraryCommonClass
{
public int K { get; set; }
public Message M { get; set; }
public Node D { get; set; }
public ArbitraryCommonClass Combine(ArbitraryCommonClass a)
{
return new ArbitraryCommonClass()
{
K = this.K,
M = this.M ?? a.M,
D = this.D ?? a.D
};
}
}
public void Start()
{
var inputStream = MessageStream();
inputStream.Subscribe(y => Console.WriteLine("Input: K " + y.MessageNodeInfo % 10 + " V " + y.MessageNodeInfo));
var nodeInfoStream = inputStream
.Select(nodeInfo => new Node(nodeInfo.MessageNodeInfo))
.GroupBy(node => node.Index)
.Select(groupedObservable => new
{
Key = groupedObservable.Key,
Observable = groupedObservable
.Scan(
(nodeAcc, node) => { nodeAcc.AddInfo(node.Latest); return nodeAcc; }
)
.Select(a => new ArbitraryCommonClass() { K = a.Index, M = (Message)null, D = a })
}
);
var groupedMessageStream =
inputStream
.GroupBy(
m => new Node(m.MessageNodeInfo).Index
)
.Select(a => new
{
Key =a.Key,
Observable = a.Select(b => new ArbitraryCommonClass() { K = a.Key, M = b, D = null })
});
var combinedStreams = nodeInfoStream
.Merge(groupedMessageStream)
.GroupBy(s => s.Key)
.Select(grp => grp
.Scan(
(state, next) => new { Key = state.Key, Observable = Observable.CombineLatest(state.Observable, next.Observable, (x, y) => { return x.Combine(y); }) }
)
)
.Merge()
.SelectMany(x => x.Observable.Select(a=>a));
combinedStreams.Where(x=>x.M!=null).Subscribe(x => Console.WriteLine(x.K + " " + x.M.MessageNodeInfo + " " + x.D.Latest));
}
}
Assuming the following class:
public class Product
{
public string Type { get; set; } = "Default";
public decimal Price { get; set; }
}
Here's a use of GroupBy with Scan (shows the average product price grouped by type). The trick is to Select over the grouped observable to get to the individual groupings, do whatever, then (presumably) merge them back together. You could collapse the Select and the Merge into a single SelectMany, but it can be easier to read when separated:
var productSubject = new Subject<Product>();
var printSignal = new Subject<Unit>();
var latestAverages = productSubject.GroupBy(p => p.Type)
.Select(g => g
.Scan((0, 0.0m), (state, item) => (state.Item1 + 1, state.Item2 + item.Price)) //hold in state the count and the running total for each group
.Select(t => (g.Key, t.Item2 / t.Item1)) //divide to get the average
)
.Merge()
.Scan(ImmutableDictionary<string, decimal>.Empty, (state, t) => state.SetItem(t.Key, t.Item2)); //Finally, cache the average by group.
printSignal.WithLatestFrom(latestAverages, (_, d) => d)
.Subscribe(avgs =>
{
foreach (var avg in avgs)
{
Console.WriteLine($"ProductType: {avg.Key}. Average: {avg.Value}");
}
Console.WriteLine();
});
var productsList = new List<Product>()
{
new Product { Price = 1.00m },
new Product { Price = 2.00m },
new Product { Price = 3.00m },
new Product { Price = 2.00m, Type = "Alternate" },
new Product { Price = 4.00m, Type = "Alternate" },
new Product { Price = 6.00m, Type = "Alternate" },
};
productsList.ForEach(p => productSubject.OnNext(p));
printSignal.OnNext(Unit.Default);
productSubject.OnNext(new Product { Price = 4.0m });
printSignal.OnNext(Unit.Default);
productSubject.OnNext(new Product { Price = 8.0m, Type = "Alternate" });
printSignal.OnNext(Unit.Default);
This uses nuget package System.Collections.Immutable.

concat results of select with complex types

I have bellow class :
public class KardeksKalaComplexColumnInfoM{
private decimal _Qty;
public decimal Qty
{
get { return _Qty; }
set
{
if (_Qty != value)
{
_Qty = value;
this.RaisePropertyChanged("Qty");
this.RaisePropertyChanged("Total");
}
}
}
private decimal _Fee;
public decimal Fee
{
get { return _Fee; }
set
{
if (_Fee != value)
{
_Fee = value;
this.RaisePropertyChanged("Fee");
this.RaisePropertyChanged("Total");
}
}
}
public decimal Total
{
get { return Qty * Fee; }
}
}
and a context contains buys and sales,i run bellow code and results commented in three last lines:
var q_buys = (
from
r_d in _db.BuyDetails
select new KardeksKalaDetailM()
{
ID = r_d.ID,
Date = r_d.Date,
in = new KardeksKalaComplexColumnInfoM() {Qty = r_d.Qty, Fee = r_d.Fee },
});
var q_sales = (
from
r_d in _db.SaleDetails
select new KardeksKalaDetailM()
{
ID = r_d.ID,
Date = r_d.Date,
in = new KardeksKalaComplexColumnInfoM() { Qty=0 , Fee=0 },
});
var q1=q_buys.ToList(); // Ok
var q2=q_sales.ToList(); // Ok
var result=q_buys.Concat(q_sales).ToList(); // Error
when I change the KardeksKalaComplexColumnInfoM filed types to double the last line's error solved but when them types are decimal it has bellow error:
An exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll but was not handled in user code
Additional information: The specified cast from a materialized 'System.Int32' type to the 'System.Int64' type is not valid.
how can resolve last line problem?

When using NumericField, get absolutely nothing back every time

Been playing with Lucene.NET the last two days.
After reading up on Dates, I was led to believe that Dates are best converted to Milliseconds, and stored in NumericField, with Indexing=true, and Store=No.
But now nothing ever returns - it must be something basic, but I'm just not seeing it.
The saving code is as follows:
...
else if (type == typeof (DateTime?))
{
var typedValue = (DateTime?) value;
field = numericField = new NumericField(documentFieldName, 4, Field.Store.YES, true);
long milliseconds = typedValue.HasValue?(typedValue.Value.Date.Ticks/TimeSpan.TicksPerMillisecond):0;
numericField.SetLongValue(milliseconds);
doc.Add(numericField);
}
...
else
{
field = stringField = new Field(
documentFieldName,
(value != null)?value.ToString():string.Empty,
Store.YES,
Field.Index.ANALYZED) ;
doc.Add(stringField);
}
// Write the Document to the catalog
indexWriter.AddDocument(doc);
When I query for docs against the values saved in Field ... no problem.
When I query for documents by matching against the values in NumericFields, nothing returns.
Where did I go wrong?
Thanks for your help.
Lucene.Net.Analysis.Analyzer analyzer = new Lucene.Net.Analysis.Standard.StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30);
var q2 = NumericRangeQuery.NewLongRange("Val", 3, 3, true, true);
var uxy2 = documentSearchManagementService.Search("Students", termQuery, "Id");
Using:
public ScoredDocumentResult[] Search(string indexName, Query query, params string[] hitFieldNamesToReturn)
{
if (_configuration.IndexRootDirectory.IsNullOrEmpty())
{
throw new Exception("Configuration.IndexRootDirectory has not been configued yet.");
}
indexName.ValidateIsNotNullOrEmpty("indexName");
hitFieldNamesToReturn.ValidateIsNotDefault("hitFieldNamesToReturn");
//Specify the index file location where the indexes are to be stored
string indexFileLocation = Path.Combine(_configuration.IndexRootDirectory, indexName);
Lucene.Net.Store.Directory luceneDirectory = Lucene.Net.Store.FSDirectory.Open(indexFileLocation);
IndexSearcher indexSearcher = new IndexSearcher(luceneDirectory);
TopScoreDocCollector topScoreDocCollector = TopScoreDocCollector.Create(10, true);
indexSearcher.Search(query, topScoreDocCollector);
List<ScoredDocumentResult> results = new List<ScoredDocumentResult>();
foreach (var scoreDoc in topScoreDocCollector.TopDocs(0, 10).ScoreDocs)
{
ScoredDocumentResult resultItem = new ScoredDocumentResult();
Lucene.Net.Documents.Document doc = indexSearcher.Doc(scoreDoc.Doc);
resultItem.Score = scoreDoc.Score;
List<ScoredDocumentFieldResult> fields = new List<ScoredDocumentFieldResult>();
foreach (string fieldName in hitFieldNamesToReturn)
{
string fieldValue = doc.Get(fieldName);
fields.Add(new ScoredDocumentFieldResult{Key= fieldName,Value=fieldValue});
}
resultItem.FieldValues = fields.ToArray();
results.Add(resultItem);
}
indexSearcher.Close();
return results.ToArray();
}

GWT: multiple column sorting

I am trying to do multiple columns sorting in my application .
Like i have firstname , last name columns
Right now , when i click on firstname header , it sorts as per firstname , when i click on lastname column it sorts as per lastname column..
what i need is when i click on firstname header it should sort on the basis of firstname and then if i click on lastname(with shift or any other option) header it should sort on the basis of both firstname and lastname , firstname as primary column and last name as sub sorting column
here is what i have now
private void sortTableUsers(List<UserDTO> userList){
ListDataProvider<UserDTO> dataProvider = new ListDataProvider<UserDTO>();
dataProvider.addDataDisplay(usersTable);
List<UserDTO> list = dataProvider.getList();
for (UserDTO UserDTO : userList) {
list.add(UserDTO);
}
final ListHandler<UserDTO> columnSortHandler = new ListHandler<UserDTO>(list);
columnSortHandler.setComparator(firstNameColumn,new Comparator<UserDTO>() {
public int compare(UserDTO o1,UserDTO o2) {
if (o1 == o2) {
return 0;
}
// Compare the firstname columns.
if (o1 != null) {
return (o2 != null) ? o1.getUser().getFirstName().compareTo(o2.getUser().getFirstName()) : 1;
}
return -1;
}
});
columnSortHandler.setComparator(lastNameColumn,new Comparator<UserDTO>() {
public int compare(UserDTO o1,UserDTO o2) {
if (o1 == o2) {
return 0;
}
// Compare the lastname columns.
if (o1 != null) {
return (o2 != null) ? o1.getUser().getLastName().compareTo(o2.getUser().getLastName()) : 1;
}
return -1;
}
});
usersTable.getColumnSortList().push(firstNameColumn);
usersTable.getColumnSortList().push(middleNameColumn);
}
Well, you need a different comparator for each column, the second comparator is the one that you need to change.
First it must sort for FirstName,and if the firstnames are equal, then go on and compare the last names too.
I'm not using your DTO, and i don't check for nulls, but it's the same thing, you get the idea
ArrayList<Map> list = new ArrayList<Map>();
ListHandler<Map> _sortHandler = new ListHandler<Map>(list);
Column columnDefinitionFirstName = null; // create your column first name
columnDefinitionFirstName.setSortable(true);
//
_sortHandler.setComparator(columnDefinitionFirstName, new Comparator<Map>()
{
public int compare(Map o1, Map o2)
{
int res = 0;
String object1 = (String) o1.get("FIRST_NAME");
String object2 = (String) o2.get("FIRST_NAME");
res = object1.compareTo(object2);
return res;
}
});
Column columnDefinitionLastName = null; // create your column last name
columnDefinitionLastName.setSortable(true);
_sortHandler.setComparator(columnDefinitionLastName, new Comparator<Map>()
{
public int compare(Map o1, Map o2)
{
int res = 0;
String object1 = (String) o1.get("FIRST_NAME");
String object2 = (String) o2.get("FIRST_NAME");
res = object1.compareTo(object2);
if(res == 0)
{
String object11 = (String) o1.get("LAST_NAME");
String object22 = (String) o2.get("LAST_NAME");
res = object11.compareTo(object22);
}
return res;
}
});

Quickbooks CustomerQuery with Iterator returning duplicate records

I have a strange issue, and I am not sure where the error is
I am working on C#, and while running a query with filters against the Customer object, I get a strange series of results
If the filter I set should return 8 records, then my implementation returns 8, 8, 3, 1, 1, 1 records (in that order, in every subsequent call, and all those are duplicate)...
This is how I implemented the Iterator
var customerQuery = new CustomerQuery()
{
ChunkSize = "8",
Item = Guid.NewGuid().ToString("N"),
IncludeFinancialIndicator = false,
IncludeFinancialIndicatorSpecified = true,
MinimumBalanceSpecified = true,
MinimumBalance = 92,
SynchronizedFilterSpecified = true,
SynchronizedFilter = SynchronizedFilterEnumType.Synchronized,
};
var results = new List<Customer>();
int count;
//Loop until find all the results.
do
{
IEnumerable<Customer> partialResult = customerQuery.ExecuteQuery<Customer>(context);
count = partialResult.Count();
//First pass here returns 8 records
//Second pass returns same 8 records again
//third pass returns 3 records
//Three more passes with 1 record each
results.AddRange(partialResult);
} while (count > 0);
Am I doing something wrong or missing anything?
Thanks!
Edit:
this is the code implementing the Paging option....
var customerSet = new List<Customer>();
List<Customer> customerQueryResult = null;
int startPage = 1;
var qbdCustomerQuery = new CustomerQuery();
do
{
qbdCustomerQuery.ChunkSize = "10";
qbdCustomerQuery.MinimumBalanceSpecified = true;
qbdCustomerQuery.MinimumBalance = 92;
qbdCustomerQuery.ItemElementName = ItemChoiceType4.StartPage;
qbdCustomerQuery.Item = startPage.ToString();
customerQueryResult = qbdCustomerQuery.ExecuteQuery<Customer>(context).ToList();
if (customerQueryResult.Count > 0) { customerSet.AddRange(customerQueryResult); }
startPage++;
} while (customerQueryResult.Count > 0);
If I set ChunkSize to "100" I get the 8 expected records, but when trying to run it with ChunkSize = "10" I get 8,8,1 (duplicate records)