Projection Operators
Select()
When you wish to retrieve an IEnumerable of a certain document property for example:
[Fact]
public async Task use_select_in_query_for_one_field()
{
theSession.Store(new User { FirstName = "Hank" });
theSession.Store(new User { FirstName = "Bill" });
theSession.Store(new User { FirstName = "Sam" });
theSession.Store(new User { FirstName = "Tom" });
await theSession.SaveChangesAsync();
theSession.Query<User>().OrderBy(x => x.FirstName).Select(x => x.FirstName)
.ShouldHaveTheSameElementsAs("Bill", "Hank", "Sam", "Tom");
}
When you wish to retrieve certain properties and transform them into another type:
[SerializerTypeTargetedFact(RunFor = SerializerType.Newtonsoft)]
public async Task use_select_with_multiple_fields_to_other_type()
{
theSession.Store(new User { FirstName = "Hank", LastName = "Aaron" });
theSession.Store(new User { FirstName = "Bill", LastName = "Laimbeer" });
theSession.Store(new User { FirstName = "Sam", LastName = "Mitchell" });
theSession.Store(new User { FirstName = "Tom", LastName = "Chambers" });
await theSession.SaveChangesAsync();
var users = theSession.Query<User>().Select(x => new User2 { First = x.FirstName, Last = x.LastName }).ToList();
users.Count.ShouldBe(4);
users.Each(x =>
{
x.First.ShouldNotBeNull();
x.Last.ShouldNotBeNull();
});
}
When you wish to retrieve certain properties and transform them into an anonymous type:
[Fact]
public async Task use_select_to_transform_to_an_anonymous_type()
{
theSession.Store(new User { FirstName = "Hank" });
theSession.Store(new User { FirstName = "Bill" });
theSession.Store(new User { FirstName = "Sam" });
theSession.Store(new User { FirstName = "Tom" });
await theSession.SaveChangesAsync();
theSession.Query<User>().OrderBy(x => x.FirstName).Select(x => new { Name = x.FirstName })
.ToArray()
.Select(x => x.Name)
.ShouldHaveTheSameElementsAs("Bill", "Hank", "Sam", "Tom");
}
Marten also allows you to run projection queries on deep (nested) properties:
[Fact]
public void transform_with_deep_properties()
{
var targets = Target.GenerateRandomData(100).ToArray();
theStore.BulkInsert(targets);
var actual = theSession.Query<Target>().Where(x => x.Number == targets[0].Number).Select(x => x.Inner.Number).ToList().Distinct();
var expected = targets.Where(x => x.Number == targets[0].Number).Select(x => x.Inner.Number).Distinct();
actual.ShouldHaveTheSameElementsAs(expected);
}
Chaining other Linq Methods
After calling Select, you'd be able to chain other linq methods such as First()
, FirstOrDefault()
, Single()
and so on, like so:
[Fact]
public async Task use_select_to_another_type_with_first()
{
theSession.Store(new User { FirstName = "Hank" });
theSession.Store(new User { FirstName = "Bill" });
theSession.Store(new User { FirstName = "Sam" });
theSession.Store(new User { FirstName = "Tom" });
await theSession.SaveChangesAsync();
theSession.Query<User>().OrderBy(x => x.FirstName).Select(x => new UserName { Name = x.FirstName })
.FirstOrDefault()
?.Name.ShouldBe("Bill");
}
SelectMany()
::: top As of Marten V4, you can chain SelectMany()
operators N-deep with any possible Where()
/ OrderBy
/ Distinct()
/ etc operators :::
Marten has the ability to use the SelectMany()
operator to issue queries against child collections. You can use SelectMany()
against primitive collections like so:
[Fact]
public async Task can_do_simple_select_many_against_simple_array()
{
var product1 = new Product {Tags = new[] {"a", "b", "c"}};
var product2 = new Product {Tags = new[] {"b", "c", "d"}};
var product3 = new Product {Tags = new[] {"d", "e", "f"}};
using (var session = theStore.LightweightSession())
{
session.Store(product1, product2, product3);
await session.SaveChangesAsync();
}
using (var query = theStore.QuerySession())
{
var distinct = query.Query<Product>().SelectMany(x => x.Tags).Distinct().ToList();
distinct.OrderBy(x => x).ShouldHaveTheSameElementsAs("a", "b", "c", "d", "e", "f");
var names = query.Query<Product>().SelectMany(x => x.Tags).ToList();
names
.Count().ShouldBe(9);
}
}
Or against collections of child documents:
var results = query.Query<Target>()
.SelectMany(x => x.Children)
.Where(x => x.Flag)
.OrderBy(x => x.Id)
.Skip(20)
.Take(15)
.ToList();
A few notes on the SelectMany()
usage and limitations:
- As of 1.2, you are only able to use a single
SelectMany()
operator in a single Linq query. That limitation will be removed in 1.3. - You can use any other Linq operator that Marten supports after the
SelectMany()
in a Linq query, including theStats()
andInclude()
operators Take()
andSkip()
operators in a Linq query that contains aSelectMany()
operator will always apply to the child collection database rather than the parent document regardless of the order in which the operators appear in the Linq query- You cannot use
SelectMany()
with both aDistinct()
and aCount()
operator at this point.