Skip to content

Commit

Permalink
Better exception for unusable LINQ combination. Closes GH-2649
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremydmiller committed Dec 4, 2023
1 parent 4d97010 commit 2c8ef2c
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 6 deletions.
12 changes: 10 additions & 2 deletions docs/documents/full-text.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ var result = await session
.Where(x => x.UserName.NgramSearch(term))
.ToListAsync();
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/DocumentDbTests/Indexes/NgramSearchTests.cs#L52-L57' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ngram_search' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/DocumentDbTests/Indexes/NgramSearchTests.cs#L67-L72' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ngram_search' title='Start of snippet'>anchor</a></sup>
<a id='snippet-sample_ngram_search-1'></a>
```cs
var store = DocumentStore.For(_ =>
Expand Down Expand Up @@ -350,5 +350,13 @@ var result = await session
.Where(x => x.UserName.NgramSearch(term))
.ToListAsync();
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/DocumentDbTests/Indexes/NgramSearchTests.cs#L67-L97' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ngram_search-1' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/DocumentDbTests/Indexes/NgramSearchTests.cs#L82-L112' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ngram_search-1' title='Start of snippet'>anchor</a></sup>
<a id='snippet-sample_ngram_search-2'></a>
```cs
var result = await session
.Query<User>()
.Where(x => x.Address.Line1.NgramSearch(term))
.ToListAsync();
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/DocumentDbTests/Indexes/NgramSearchTests.cs#L145-L150' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ngram_search-2' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
12 changes: 11 additions & 1 deletion docs/documents/indexing/computed-indexes.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,17 @@ CREATE INDEX mt_doc_user_idx_first_namelast_name ON public.mt_doc_user USING btr

As of Marten V7, you can specify multi-field computed indexes through anonymous types like so:

snippet: sample_multi_column_index
<!-- snippet: sample_multi_column_index -->
<a id='snippet-sample_multi_column_index'></a>
```cs
var store = DocumentStore.For(opts =>
{
// This creates a single index against both FirstName and ListName
opts.Schema.For<User>().Index(x => new { x.FirstName, x.LastName });
});
```
<sup><a href='https://github.com/JasperFx/marten/blob/master/src/DocumentDbTests/Indexes/computed_indexes.cs#L153-L161' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_multi_column_index' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## Customizing a Calculated Index

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Marten;
using Marten.Exceptions;
using Marten.Testing.Documents;
using Marten.Testing.Harness;
using Shouldly;

namespace LinqTests.Bugs;

public class Bug_2649_select_and_distinct_string_with_ordering : BugIntegrationContext
{
[Fact]
public async Task can_query_with_this_combination()
{
await theStore.BulkInsertAsync(Target.GenerateRandomData(100).ToArray());

var ex = await Should.ThrowAsync<BadLinqExpressionException>(async () =>
{
var query = await theSession.Query<Target>()
.OrderBy(x=> x.String, StringComparer.InvariantCultureIgnoreCase)
.Select(x => x.String)
.Distinct()
.ToListAsync();
});


}
}
7 changes: 7 additions & 0 deletions src/Marten/Linq/CollectionUsage.Compilation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System;
using System.Diagnostics;
using System.Linq;
using JasperFx.Core.Reflection;
using Marten.Exceptions;
using Marten.Internal;
Expand Down Expand Up @@ -51,6 +52,12 @@ public Statement BuildTopStatement(IMartenSession session, IQueryableMemberColle

if (IsDistinct)
{
if (SelectExpression != null && OrderingExpressions.Any(x => x.IsTransformed))
{
throw new BadLinqExpressionException(
"Marten is unable to build a query with a Distinct() + Select() + a 'transformed' OrderBy(). You will have to resort to SQL for this query");
}

statement.ApplySqlOperator("DISTINCT");
}

Expand Down
6 changes: 6 additions & 0 deletions src/Marten/Linq/Parsing/Operators/Ordering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ public Ordering(Expression expression, OrderingDirection direction)

public CasingRule CasingRule { get; set; } = CasingRule.CaseSensitive;

/// <summary>
/// Refers to whether or not this ordering is transformed such that it cannot
/// be combined with a Distinct(Select()) usage
/// </summary>
public bool IsTransformed { get; set; }

public string BuildExpression(IQueryableMemberCollection collection)
{
var member = collection.MemberFor(Expression, "Invalid OrderBy() expression");
Expand Down
8 changes: 5 additions & 3 deletions src/Marten/Linq/Parsing/Operators/OrderingOperator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@ public OrderingOperator(string methodName, OrderingDirection direction): base(me
public override void Apply(ILinqQuery query, MethodCallExpression expression)
{
var usage = query.CollectionUsageFor(expression);
var newOrdering = new Ordering(expression.Arguments[1], Direction);
var ordering = new Ordering(expression.Arguments[1], Direction);
if (expression.Arguments.Count == 3)
{
var comparer = expression.Arguments[2].Value() as StringComparer;

if (comparer == StringComparer.OrdinalIgnoreCase || comparer == StringComparer.CurrentCultureIgnoreCase ||
comparer == StringComparer.InvariantCultureIgnoreCase)
{
newOrdering.CasingRule = CasingRule.CaseInsensitive;
ordering.CasingRule = CasingRule.CaseInsensitive;
}

ordering.IsTransformed = true;
}

usage.OrderingExpressions.Insert(0, newOrdering);
usage.OrderingExpressions.Insert(0, ordering);
}
}

0 comments on commit 2c8ef2c

Please sign in to comment.