Newbie question: projecting unique ids from a stream of two types of events

Hi all, I’m hoping this has a quick answer that I’m just missing.
I have to admit in advance that I am quite new to Event Store, and a a recent graduate, quite new at many of development topics as well, so I hope this doesn’t seem to idiotic…

I have an issue where we’ve been pushing different types of events into a single stream and need to get a another stream of events with a unique EventId to them.

The case looks like, an event of type ‘Alert’ is stored to the stream with a unique guid, then later on, an event with type ‘ResolvedAlert’ and the same guid is stored to the stream. Up until now, we had simply used a projection to get a state count on the number of unresolved alerts. Now I’ve been asked to display all those unresolved ‘alerts’ and for the life of me I can’t get a projection working to do so.

I’ve tried the following in many forms, but I always seem to run into a race condition of sorts (what order are the events pulled in from the stream?), or some other issue that I can’t identify.

I know I can do this from within the application itself, but it would seem to me this would be better done from inside the Event Store instead (separation of data and what not).

fromStream(‘DVS.TeamDashboard’)

.when({

$init : function(){

return{ resolvedAlerts{} };

},

ResolvedAlert : function(state, event){

state.resolvedAlerts[event.body.EventId] = 1;

},

Alert : function(state, event){

if(state.resolvedAlerts[event.body.EventId] !== 1){

linkTo(“Unresolved Alerts”, event);

}

}

});

I appreciate any help in advance!

Thank you!

A few things.

1) this would make a massive state. Better to use .foreach or state partitioned by.

2) can you give a bit more detail on what you want to go out of the projection with the link to unresolved?

Greg

Thanks for the response Greg!
I was a bit worried that I would end up with some massive ugly thing; there may be a better way to rework this as long as I’m in there.

I’ll take a look at using your suggestions today and post back what get.

As for the second question, really all I want is to end up with a stream of events that are of type ‘Alert’ that have no corresponding ‘ResolvedAlert’ event in the stream. To give a little more detail (I saw my wonderful English above), what we have is a stream that has 4 types of events in it: ‘Alert’, ‘Info’, ‘ResolvedAlert’, ‘ResolvedInfo’. Every ‘ResolvedAlert’ and ‘ResolvedInfo’ has an identical eventId that corresponds to the ‘Alert’ or ‘Info’ that it is meant to resolve. The idea with this project is to develop a small in house team tool that will also demonstrate eventstore’s viability for use in a larger project.

Thanks again,

James

Ok, so foreach didn’t get me where I wanted. I was able to make a projection of all the alerts with corresponding resolvedAlerts, but what I need is the exact opposite. Maybe I’m just struggling to recognize something that a better or veteran programmer takes for granted.

Additionally, I came across the early version of your book, and it appears that what I’m trying to do fits the description of a Reversal Transaction. An Alert is put into the stream, and down line, I want to remove it. Perhaps there is a better way of doing this that with 2 different types of events?

Hmmmm. Are you wanting a stream of events like [pseudo]

1:alert

2:event

Then Projection

if (event happened within timeout) suppressAlert();

Given the above “event stream” and assuming the if check is true, if you look at the “alerts” projection, there are zero alerts.

Is that what you’re trying to achieve?

Pretty much, except that we want our Scrum Master to explicitly resolved an alert as oppose to using a timeout.
However, the ultimate goal is the same: if all of the alerts are resolved, then the projection would return an empty stream.

Why not make a stream per alert?

Sorry, took me a couple days to get back to this project.
If I’m catching on right, I would make a projection to make a stream per alert, and then use the fromCategories to separate out just the alerts I want to see?

I will have to take a look later today.

In the meantime, while I’m sure it’s not the best way to do it, for now I just added a bool field to the Alert type called resolved that defaults to false. I changed the code so that whenever an alert is ‘resolved’ we add another alert to the stream with the same identifying information as the original, but with the bool field set to true.

This allowed me to at least filter out what I didn’t need in the app.

Just wanted to take a second though and thank you for taking the time to help out greenhorn like me.

Thanks!

Hi James,

I'm not sure if I understand your requirments fully, but I think you
need something like this. I would create stream per Alert (eg
'Alert-123467687', 'Alert-123478954', ...), and hold the 'resolved'
boolean in the state state of the projection per stream/alert. Default
is will be false, but when a 'Resolved' event is raised, the boolean
is set to true.

If you need all unresolved alerts, you can partition them in a
separated steam 'UnresolvedAlerts' (²nd projection). You can use this
stream as input of another projection, or handle if from another
application.

I would do something like this:

fromCategory('Alert')
   .foreachStream()
   .when({
        $init: function () {
            return { resolved: false };
        },

        Resolved: function(s, e) {
            s.resolved = true;
            return s;
        },

        Alerted: function(s, e) {
            if (!s.resolved)
            {
                emit(e.streamId, 'Unresolved', {});
            }
            return s;
        },
    });

And to project all 'UnresolvedAlerts' to a single stream:

//pseudo code
fromAll()
   .when({
        Unresolved: function(s, e) {
            linkTo('UnresolvedAlerts', e);
            return s;
        }
    });

That said, I tried the first example yesterday evening, but I received
an error which I posted to the group.
Not sure whether the problem has something to-do with my setup or not.

HTH

Tim

James can you provide you exact events?

I assume you just want to know when unresolved from reading above. The below solution will work quite well. Btw as a pro tip to shorten things you can not return state :slight_smile:

One question: at what time period do you care about non-resolved this seems important. Unresolved is ok if within say 50ms when do you care?

Greg