From 3fdfead729636a773c51f49f02847af54f0687fc Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Fri, 17 Aug 2012 16:31:42 -0700 Subject: [PATCH] Improving Content Query Custom extensions IStartingWith, IsEndingWith, IsContaining New IContentQuery.WhereAny to prevent the limitation from QueryOver to handle IEnumerable.Any() in expression queries --HG-- branch : 1.x --- .hgsubstate | 2 +- .../ContentManagement/ContentQueryTests.cs | 54 +++++++++++++ .../Orchard.Tags/Services/TagService.cs | 4 +- .../DefaultContentManager.cs | 3 + .../ContentManagement/DefaultContentQuery.cs | 16 +++- src/Orchard/ContentManagement/Diagram.cd | 77 ------------------- .../ContentManagement/IContentQuery.cs | 1 + .../RestrictionExtensions.cs | 53 +++++++++++++ src/Orchard/Orchard.Framework.csproj | 2 +- 9 files changed, 128 insertions(+), 84 deletions(-) delete mode 100644 src/Orchard/ContentManagement/Diagram.cd create mode 100644 src/Orchard/ContentManagement/RestrictionExtensions.cs diff --git a/.hgsubstate b/.hgsubstate index 392ce4ccb..fac0bee43 100644 --- a/.hgsubstate +++ b/.hgsubstate @@ -1,5 +1,5 @@ 1f8f582859916d411b5333dd0f32f57da0e72ee8 src/Orchard.Web/Modules/Orchard.AntiSpam -bb8c612e72535cbb28b03efb5f7c52e79ced61cd src/Orchard.Web/Modules/Orchard.Autoroute +5257b79de7ea0cdcd1bebc04f922fe9bbb127944 src/Orchard.Web/Modules/Orchard.Autoroute 2ca2dbbf97b7219f9150d46d52f62bc405c17673 src/Orchard.Web/Modules/Orchard.ContentPermissions e6e70d69f37e48ea6c8784e83a9af4103f5d0dde src/Orchard.Web/Modules/Orchard.ContentPicker b5ccdaed10ff8bcd5bc94d46541209873ae0f722 src/Orchard.Web/Modules/Orchard.CustomForms diff --git a/src/Orchard.Tests/ContentManagement/ContentQueryTests.cs b/src/Orchard.Tests/ContentManagement/ContentQueryTests.cs index ef6141fb2..e2dfcbac3 100644 --- a/src/Orchard.Tests/ContentManagement/ContentQueryTests.cs +++ b/src/Orchard.Tests/ContentManagement/ContentQueryTests.cs @@ -16,6 +16,7 @@ using Orchard.Tests.ContentManagement.Records; using Orchard.Tests.ContentManagement.Models; using Orchard.DisplayManagement.Implementation; using Orchard.Tests.Stubs; +using NHibernate.Impl; namespace Orchard.Tests.ContentManagement { [TestFixture] @@ -451,6 +452,59 @@ namespace Orchard.Tests.ContentManagement { Assert.That(listTwo.Count(), Is.EqualTo(2)); Assert.That(listThree.Count(), Is.EqualTo(1)); } + + [Test] + public void IsStartingExtensionShouldBeUsed() { + _manager.Create("gamma", init => { init.Record.Frap = "one"; }); + _manager.Create("gamma", init => { init.Record.Frap = "two"; }); + _manager.Create("gamma", init => { init.Record.Frap = "three"; }); + _manager.Create("gamma", init => { init.Record.Frap = "four"; }); + _session.Flush(); + + var result = _manager.Query() + .Where(x => x.Frap.IsStartingWith("t")) + .List(); + + Assert.That(result.Count(), Is.EqualTo(2)); + Assert.That(result.Count(x => x.Get().Record.Frap == "two"), Is.EqualTo(1)); + Assert.That(result.Count(x => x.Get().Record.Frap == "three"), Is.EqualTo(1)); + } + + [Test] + public void IsEndingExtensionShouldBeUsed() { + _manager.Create("gamma", init => { init.Record.Frap = "one"; }); + _manager.Create("gamma", init => { init.Record.Frap = "two"; }); + _manager.Create("gamma", init => { init.Record.Frap = "three"; }); + _manager.Create("gamma", init => { init.Record.Frap = "four"; }); + _session.Flush(); + + var result = _manager.Query() + .Where(x => x.Frap.IsEndingWith("e")) + .List(); + + Assert.That(result.Count(), Is.EqualTo(2)); + Assert.That(result.Count(x => x.Get().Record.Frap == "one"), Is.EqualTo(1)); + Assert.That(result.Count(x => x.Get().Record.Frap == "three"), Is.EqualTo(1)); + } + + [Test] + public void IsContainingExtensionShouldBeUsed() { + _manager.Create("gamma", init => { init.Record.Frap = "one"; }); + _manager.Create("gamma", init => { init.Record.Frap = "two"; }); + _manager.Create("gamma", init => { init.Record.Frap = "three"; }); + _manager.Create("gamma", init => { init.Record.Frap = "four"; }); + _session.Flush(); + + var result = _manager.Query() + .Where(x => x.Frap.IsContaining("o")) + .List(); + + Assert.That(result.Count(), Is.EqualTo(3)); + Assert.That(result.Count(x => x.Get().Record.Frap == "one"), Is.EqualTo(1)); + Assert.That(result.Count(x => x.Get().Record.Frap == "two"), Is.EqualTo(1)); + Assert.That(result.Count(x => x.Get().Record.Frap == "four"), Is.EqualTo(1)); + } + } } diff --git a/src/Orchard.Web/Modules/Orchard.Tags/Services/TagService.cs b/src/Orchard.Web/Modules/Orchard.Tags/Services/TagService.cs index bd8a7e05b..4a93f6199 100644 --- a/src/Orchard.Web/Modules/Orchard.Tags/Services/TagService.cs +++ b/src/Orchard.Web/Modules/Orchard.Tags/Services/TagService.cs @@ -143,8 +143,8 @@ namespace Orchard.Tags.Services { public int GetTaggedContentItemCount(int tagId, VersionOptions options) { return _orchardServices.ContentManager - .HqlQuery() - .Where(tpr => tpr.ContentPartRecord().Property("Tags", "tags"), x => x.Eq("TagRecord.Id", tagId)) + .Query() + .WhereAny(part => part.Tags, tag => tag.Id == tagId) .Count(); } diff --git a/src/Orchard/ContentManagement/DefaultContentManager.cs b/src/Orchard/ContentManagement/DefaultContentManager.cs index cfd91b0b3..419dadcb2 100644 --- a/src/Orchard/ContentManagement/DefaultContentManager.cs +++ b/src/Orchard/ContentManagement/DefaultContentManager.cs @@ -34,6 +34,9 @@ namespace Orchard.ContentManagement { private const string Published = "Published"; private const string Draft = "Draft"; + static DefaultContentManager() { + RestrictionExtensions.RegisterExtensions(); + } public DefaultContentManager( IComponentContext context, IRepository contentTypeRepository, diff --git a/src/Orchard/ContentManagement/DefaultContentQuery.cs b/src/Orchard/ContentManagement/DefaultContentQuery.cs index fd0def33b..069e3cc9a 100644 --- a/src/Orchard/ContentManagement/DefaultContentQuery.cs +++ b/src/Orchard/ContentManagement/DefaultContentQuery.cs @@ -103,7 +103,14 @@ namespace Orchard.ContentManagement { } private void Where(Expression> predicate) where TRecord : ContentPartRecord { - BindPartQueryOver().Where(predicate); + var processedCriteria = NHibernate.Impl.ExpressionProcessor.ProcessExpression(predicate); + BindPartQueryOver().Where(processedCriteria); + //BindPartQueryOver().WithSubquery.WhereSome(.Where(predicate); + } + + private void WhereAny(Expression>> selector, Expression> predicate) where TRecord : ContentPartRecord { + //var address = BindPartQueryOver() QueryOver.Of().Where(selector); + BindPartQueryOver().JoinQueryOver(selector).Where(predicate); } private void OrderBy(Expression> keySelector) where TRecord : ContentPartRecord { @@ -256,6 +263,11 @@ namespace Orchard.ContentManagement { return this; } + IContentQuery IContentQuery.WhereAny(Expression>> selector, Expression> predicate) { + _query.WhereAny(selector, predicate); + return this; + } + IContentQuery IContentQuery.OrderBy(Expression> keySelector) { _query.OrderBy(keySelector); return this; @@ -293,8 +305,6 @@ namespace Orchard.ContentManagement { .GroupBy(item => item.Segments.FirstOrDefault()) .ToDictionary(grouping => grouping.Key, StringComparer.InvariantCultureIgnoreCase); - var aggregatedProperty = false; - // locate hints that match properties in the ContentItemVersionRecord foreach (var hit in contentItemVersionMetadata.PropertyNames.Where(hintDictionary.ContainsKey).SelectMany(key => hintDictionary[key])) { contentItemVersionCriteria.UnderlyingCriteria.SetFetchMode(hit.Hint, FetchMode.Eager); diff --git a/src/Orchard/ContentManagement/Diagram.cd b/src/Orchard/ContentManagement/Diagram.cd deleted file mode 100644 index 6fc3fb202..000000000 --- a/src/Orchard/ContentManagement/Diagram.cd +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - AAACAAAAAAACAAhAABAAAAAAAAAAQABAAAAQAAAEBAA= - Models\ContentItem.cs - - - - - - - - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAA= - Models\ContentPart.cs - - - - - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA= - Models\ContentPart.cs - - - - - - AAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAA= - Models\Records\ContentItemRecord.cs - - - - - - - - - AAACAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA= - Models\Records\ContentTypeRecord.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAA= - Models\IContent.cs - - - - - - AAAAEAAAAAAAACBBAIAAAAAAAAEAAEgAAACAAAAAAAA= - Models\IContentManager.cs - - - - - - AAAAEAAAAAEAAABBAIAAAIAAACEAAAQAAACAAAAAAAg= - Models\Driver\IContentHandler.cs - - - - \ No newline at end of file diff --git a/src/Orchard/ContentManagement/IContentQuery.cs b/src/Orchard/ContentManagement/IContentQuery.cs index 946f47de7..6421f3c24 100644 --- a/src/Orchard/ContentManagement/IContentQuery.cs +++ b/src/Orchard/ContentManagement/IContentQuery.cs @@ -32,6 +32,7 @@ namespace Orchard.ContentManagement { new IContentQuery ForVersion(VersionOptions options); IContentQuery Where(Expression> predicate); + IContentQuery WhereAny(Expression>> selector, Expression> predicate); IContentQuery OrderBy(Expression> keySelector); IContentQuery OrderByDescending(Expression> keySelector); diff --git a/src/Orchard/ContentManagement/RestrictionExtensions.cs b/src/Orchard/ContentManagement/RestrictionExtensions.cs new file mode 100644 index 000000000..15324392f --- /dev/null +++ b/src/Orchard/ContentManagement/RestrictionExtensions.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using NHibernate.Criterion; +using NHibernate.Impl; +using Orchard.ContentManagement.Records; + +namespace Orchard.ContentManagement { + public static class RestrictionExtensions { + public static void RegisterExtensions() { + ExpressionProcessor.RegisterCustomMethodCall(() => RestrictionExtensions.IsStartingWith("", ""), RestrictionExtensions.ProcessIsStartingWith); + ExpressionProcessor.RegisterCustomMethodCall(() => RestrictionExtensions.IsEndingWith("", ""), RestrictionExtensions.ProcessIsEndingWith); + ExpressionProcessor.RegisterCustomMethodCall(() => RestrictionExtensions.IsContaining("", ""), RestrictionExtensions.ProcessIsContaining); + } + + public static bool IsStartingWith(this string item, string value) { + throw new NotImplementedException("Do not use this method directly"); + } + + public static bool IsEndingWith(this string item, string value) { + throw new NotImplementedException("Do not use this method directly"); + } + + public static bool IsContaining(this string item, string value) { + throw new NotImplementedException("Do not use this method directly"); + } + + public static ICriterion ProcessIsStartingWith(MethodCallExpression methodCallExpression) { + ExpressionProcessor.ProjectionInfo projection = ExpressionProcessor.FindMemberProjection(methodCallExpression.Arguments[0]); + string value = (string)ExpressionProcessor.FindValue(methodCallExpression.Arguments[1]); + MatchMode matchMode = MatchMode.Start; + return projection.Create(s => Restrictions.InsensitiveLike(s, value, matchMode), p => Restrictions.InsensitiveLike(p, value, matchMode)); + } + + + public static ICriterion ProcessIsEndingWith(MethodCallExpression methodCallExpression) { + ExpressionProcessor.ProjectionInfo projection = ExpressionProcessor.FindMemberProjection(methodCallExpression.Arguments[0]); + string value = (string)ExpressionProcessor.FindValue(methodCallExpression.Arguments[1]); + MatchMode matchMode = MatchMode.End; + return projection.Create(s => Restrictions.InsensitiveLike(s, value, matchMode), p => Restrictions.InsensitiveLike(p, value, matchMode)); + } + + + public static ICriterion ProcessIsContaining(MethodCallExpression methodCallExpression) { + ExpressionProcessor.ProjectionInfo projection = ExpressionProcessor.FindMemberProjection(methodCallExpression.Arguments[0]); + string value = (string)ExpressionProcessor.FindValue(methodCallExpression.Arguments[1]); + MatchMode matchMode = MatchMode.Anywhere; + return projection.Create(s => Restrictions.InsensitiveLike(s, value, matchMode), p => Restrictions.InsensitiveLike(p, value, matchMode)); + } + } +} diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 7b76f8631..963bb3b27 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -171,6 +171,7 @@ + @@ -906,7 +907,6 @@ -