diff --git a/backend/FwLite/FwDataMiniLcmBridge/LexEntryFilterMapProvider.cs b/backend/FwLite/FwDataMiniLcmBridge/LexEntryFilterMapProvider.cs index b52d50de46..e96025705c 100644 --- a/backend/FwLite/FwDataMiniLcmBridge/LexEntryFilterMapProvider.cs +++ b/backend/FwLite/FwDataMiniLcmBridge/LexEntryFilterMapProvider.cs @@ -17,7 +17,7 @@ public class LexEntryFilterMapProvider : EntryFilterMapProvider // ReSharper disable once ConvertClosureToMethodGroup .Select(domain => LcmHelpers.GetSemanticDomainCode(domain)); public override Func? EntrySensesSemanticDomainsConverter => EntryFilter.NormalizeEmptyToNullString; - public override Expression> EntrySensesExampleSentences => e => EmptyToNull(e.AllSenses.SelectMany(s => s.ExamplesOS)); + public override Expression> EntrySensesExampleSentences => e => e.AllSenses.Select(s => EmptyToNull(s.ExamplesOS)); public override Expression> EntrySensesExampleSentencesSentence => (entry, ws) => entry.AllSenses.SelectMany(s => s.ExamplesOS).Select(example => example.PickText(example.Example, ws)); diff --git a/backend/FwLite/LcmCrdt/EntryFilterMapProvider.cs b/backend/FwLite/LcmCrdt/EntryFilterMapProvider.cs index a862a64bff..0d9a5d4e09 100644 --- a/backend/FwLite/LcmCrdt/EntryFilterMapProvider.cs +++ b/backend/FwLite/LcmCrdt/EntryFilterMapProvider.cs @@ -13,7 +13,7 @@ public class EntryFilterMapProvider : EntryFilterMapProvider public override Func? EntrySensesSemanticDomainsConverter => //linq2db treats Sense.SemanticDomains as a table, if we use "null" then it'll write the query we want EntryFilter.NormalizeEmptyToNullString; - public override Expression> EntrySensesExampleSentences => e => e.Senses.SelectMany(s => s.ExampleSentences); + public override Expression> EntrySensesExampleSentences => e => e.Senses.Select(s => s.ExampleSentences); public override Expression> EntrySensesExampleSentencesSentence => (e, ws) => e.Senses.SelectMany(s => s.ExampleSentences).Select(example => Json.Value(example.Sentence, ms => ms[ws])); public override Expression> EntrySensesPartOfSpeechId => e => e.Senses.Select(s => s.PartOfSpeechId); diff --git a/backend/FwLite/MiniLcm.Tests/QueryEntryTestsBase.cs b/backend/FwLite/MiniLcm.Tests/QueryEntryTestsBase.cs index 2ad35320c1..38a37f652a 100644 --- a/backend/FwLite/MiniLcm.Tests/QueryEntryTestsBase.cs +++ b/backend/FwLite/MiniLcm.Tests/QueryEntryTestsBase.cs @@ -5,6 +5,7 @@ public abstract class QueryEntryTestsBase : MiniLcmTestBase private readonly string Apple = "Apple"; private readonly string Peach = "Peach"; private readonly string Banana = "Banana"; + private readonly string Kiwi = "Kiwi"; public override async Task InitializeAsync() { @@ -59,6 +60,27 @@ await Api.CreateEntry(new Entry() } ] }); + await Api.CreateEntry(new Entry() + { + LexemeForm = { { "en", Kiwi } }, + Senses = + [ + new() + { + Gloss = { { "en", "Fruit" } }, + Definition = { { "en", "Fruit, fuzzy with green flesh" } }, + PartOfSpeechId = nounPos.Id, + SemanticDomains = [semanticDomain], + ExampleSentences = + [ + new ExampleSentence() + { + Sentence = { { "en", "I like eating Kiwis, they taste good" } } + }, + ] + } + ] + }); } [Fact] @@ -68,10 +90,18 @@ public async Task CanFilterToMissingSenses() results.Select(e => e.LexemeForm["en"]).Should().BeEquivalentTo(Apple); } + [Fact] + public async Task CanFilterToNotMissingSenses() + { + var results = await Api.GetEntries(new(Filter: new() { GridifyFilter = "Senses!=null" })).ToArrayAsync(); + results.Select(e => e.LexemeForm["en"]).Should().BeEquivalentTo(Kiwi, Peach, Banana); + } + [Fact] public async Task CanFilterToMissingPartOfSpeech() { var results = await Api.GetEntries(new(Filter: new() { GridifyFilter = "Senses.PartOfSpeechId=null" })).ToArrayAsync(); + //does not include entries with no senses results.Select(e => e.LexemeForm["en"]).Should().BeEquivalentTo(Peach); } @@ -79,7 +109,9 @@ public async Task CanFilterToMissingPartOfSpeech() public async Task CanFilterToMissingExamples() { var results = await Api.GetEntries(new(Filter: new() { GridifyFilter = "Senses.ExampleSentences=null" })).ToArrayAsync(); - results.Select(e => e.LexemeForm["en"]).Should().BeEquivalentTo([Apple, Peach]); + //Senses.ExampleSentences=null matches entries which have senses but no examples + //it does not include Apple because it has no senses, to include it a filter Senses=null is needed + results.Select(e => e.LexemeForm["en"]).Should().BeEquivalentTo(Peach, Banana); } [Fact] @@ -100,21 +132,21 @@ public async Task CanFilterToMissingSemanticDomainsWithEmptyArray() public async Task CanFilterSemanticDomainCodeContains() { var results = await Api.GetEntries(new(Filter: new() { GridifyFilter = "Senses.SemanticDomains.Code=*Fruit" })).ToArrayAsync(); - results.Select(e => e.LexemeForm["en"]).Should().BeEquivalentTo(Banana); + results.Select(e => e.LexemeForm["en"]).Should().BeEquivalentTo(Banana, Kiwi); } [Fact] public async Task CanFilterToMissingComplexFormTypes() { var results = await Api.GetEntries(new(Filter: new() { GridifyFilter = "ComplexFormTypes=null" })).ToArrayAsync(); - results.Select(e => e.LexemeForm["en"]).Should().BeEquivalentTo(Apple, Banana); + results.Select(e => e.LexemeForm["en"]).Should().BeEquivalentTo(Apple, Banana, Kiwi); } [Fact] public async Task CanFilterToMissingComplexFormTypesWithEmptyArray() { var results = await Api.GetEntries(new(Filter: new() { GridifyFilter = "ComplexFormTypes=[]" })).ToArrayAsync(); - results.Select(e => e.LexemeForm["en"]).Should().BeEquivalentTo(Apple, Banana); + results.Select(e => e.LexemeForm["en"]).Should().BeEquivalentTo(Apple, Banana, Kiwi); } [Fact] @@ -163,7 +195,7 @@ public async Task CanFilterGlossEmpty() public async Task CanFilterGlossEqualsFruit() { var results = await Api.GetEntries(new(Filter: new() { GridifyFilter = "Senses.Gloss[en]=Fruit" })).ToArrayAsync(); - results.Select(e => e.LexemeForm["en"]).Should().BeEquivalentTo(Banana); + results.Select(e => e.LexemeForm["en"]).Should().BeEquivalentTo(Banana, Kiwi); } [Fact]