Skip to content

Commit

Permalink
Ability to query for event data for a subset of event types. Closes G…
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremydmiller committed Sep 15, 2024
1 parent 297170e commit f057944
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 3 deletions.
9 changes: 8 additions & 1 deletion docs/events/querying.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ public void can_query_against_event_type()

You can use any Linq operator that Marten supports to query against event data. We think that this functionality is probably more useful for diagnostics or troubleshooting rather than something you would routinely use to support your application. We recommend that you favor event projection views over querying within the raw event table.

With Marten 1.0, you can issue queries with Marten's full Linq support against the raw event data with this method:
You can issue queries with Marten's full Linq support against the raw event data with this method:

<!-- snippet: sample_example_of_querying_for_event_data -->
<a id='snippet-sample_example_of_querying_for_event_data'></a>
Expand All @@ -309,3 +309,10 @@ public void example_of_querying_for_event_data(IDocumentSession session, Guid st
<!-- endSnippet -->

This mechanism will allow you to query by any property of the `IEvent` interface shown above.

## Filter by Event Types <Badge type="tip" text="7.28" />

You can limit the types of events returned by the LINQ query through the `EventTypesAre()` extension method
in a `Where()` clause as shown below:

snippet: sample_using_event_types_are
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using EventSourcingTests.Aggregation;
using Marten;
using Marten.Events;
using Marten.Testing.Harness;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
using Shouldly;
using Weasel.Core;
using Xunit;
using Xunit.Abstractions;

namespace EventSourcingTests;

public class query_against_event_documents_Tests: OneOffConfigurationsContext
public class querying_event_data_with_linq: OneOffConfigurationsContext
{
private readonly ITestOutputHelper _output;
private readonly MembersJoined joined1 = new MembersJoined { Members = new string[] { "Rand", "Matt", "Perrin", "Thom" } };
Expand Down Expand Up @@ -228,11 +231,37 @@ public void can_search_by_stream()
.Count(x => x.StreamId == stream1).ShouldBe(2);
}

[Fact]
public async Task can_search_by_event_types()
{
theSession.Events.StartStream<Quest>(joined1, departed1);
theSession.Events.StartStream<Quest>(joined2, departed2);

theSession.Events.StartStream<SimpleAggregate>(new AEvent(), new BEvent(), new CEvent(), new DEvent());
theSession.Events.StartStream<SimpleAggregate>(new AEvent(), new BEvent(), new DEvent(), new DEvent());
theSession.Events.StartStream<SimpleAggregate>(new AEvent(), new CEvent(), new CEvent(), new DEvent());

await theSession.SaveChangesAsync();

#region sample_using_event_types_are

var raw = await theSession.Events.QueryAllRawEvents()
.Where(x => x.EventTypesAre(typeof(CEvent), typeof(DEvent)))
.ToListAsync();

#endregion

foreach (var e in raw)
{
((e.Data is CEvent) || (e.Data is DEvent)).ShouldBeTrue();
}
}

/*
* MORE!!!
* Async everything
*/
public query_against_event_documents_Tests(ITestOutputHelper output)
public querying_event_data_with_linq(ITestOutputHelper output)
{
_output = output;
theStore.Advanced.Clean.DeleteAllEventData();
Expand Down
2 changes: 2 additions & 0 deletions src/Marten/Events/AggregateToExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,6 @@ public static async Task<T> AggregateToAsync<T>(this IQueryable<IEvent> queryabl
{
return await AggregateToAsync(queryable.As<IMartenQueryable<IEvent>>(), state, token).ConfigureAwait(false);
}


}
42 changes: 42 additions & 0 deletions src/Marten/Events/LinqExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using JasperFx.Core.Reflection;
using Marten.Linq.Members;
using Marten.Linq.Parsing;
using Marten.Linq.Parsing.Methods;
using Weasel.Postgresql.SqlGeneration;

namespace Marten.Events;

public static class LinqExtensions
{
/// <summary>
/// LINQ filter to select only a specified set of event types
/// </summary>
/// <param name="e"></param>
/// <param name="types"></param>
/// <returns></returns>
public static bool EventTypesAre(this IEvent e, params Type[] types)
{
return e.Data.GetType().IsOneOf(types);
}
}

internal class EventTypesAreParser: IMethodCallParser
{
public bool Matches(MethodCallExpression expression)
{
return expression.Method.Name == nameof(LinqExtensions.EventTypesAre) && expression.Method.DeclaringType == typeof(LinqExtensions);
}

public ISqlFragment Parse(IQueryableMemberCollection memberCollection, IReadOnlyStoreOptions options,
MethodCallExpression expression)
{
var types = (Type[])expression.Arguments.Last().Value();
var typeNames = types.Select(x => options.Events.As<EventGraph>().EventMappingFor(x).EventTypeName).ToArray();

var queryableMember = memberCollection.MemberFor(nameof(IEvent.EventTypeName));
return new IsOneOfFilter(queryableMember, new CommandParameter(typeNames));
}
}
2 changes: 2 additions & 0 deletions src/Marten/LinqParsing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq.Expressions;
using System.Reflection;
using JasperFx.Core;
using Marten.Events;
using Marten.Events.Archiving;
using Marten.Linq.CreatedAt;
using Marten.Linq.LastModified;
Expand Down Expand Up @@ -70,6 +71,7 @@ public class LinqParsing: IReadOnlyLinqParsing

// event is archived
new MaybeArchivedMethodCallParser(),
new EventTypesAreParser(),

// last modified
new ModifiedSinceParser(),
Expand Down

0 comments on commit f057944

Please sign in to comment.