diff --git a/Blog/bin/Blog.dll b/Blog/bin/Blog.dll index ed1bd54..fea599a 100644 Binary files a/Blog/bin/Blog.dll and b/Blog/bin/Blog.dll differ diff --git a/Blog/bin/Blog.pdb b/Blog/bin/Blog.pdb index 7788790..c18a06a 100644 Binary files a/Blog/bin/Blog.pdb and b/Blog/bin/Blog.pdb differ diff --git a/Blog/bin/Umbraco.Examine.Linq.dll b/Blog/bin/Umbraco.Examine.Linq.dll index 0347dc5..ea895a1 100644 Binary files a/Blog/bin/Umbraco.Examine.Linq.dll and b/Blog/bin/Umbraco.Examine.Linq.dll differ diff --git a/Blog/bin/Umbraco.Examine.Linq.pdb b/Blog/bin/Umbraco.Examine.Linq.pdb index e9a8ea1..b780f1b 100644 Binary files a/Blog/bin/Umbraco.Examine.Linq.pdb and b/Blog/bin/Umbraco.Examine.Linq.pdb differ diff --git a/Blog/obj/Debug/Blog.csprojResolveAssemblyReference.cache b/Blog/obj/Debug/Blog.csprojResolveAssemblyReference.cache index 930869a..2ac13ba 100644 Binary files a/Blog/obj/Debug/Blog.csprojResolveAssemblyReference.cache and b/Blog/obj/Debug/Blog.csprojResolveAssemblyReference.cache differ diff --git a/Blog/obj/Debug/Blog.dll b/Blog/obj/Debug/Blog.dll index ed1bd54..fea599a 100644 Binary files a/Blog/obj/Debug/Blog.dll and b/Blog/obj/Debug/Blog.dll differ diff --git a/Blog/obj/Debug/Blog.pdb b/Blog/obj/Debug/Blog.pdb index 7788790..c18a06a 100644 Binary files a/Blog/obj/Debug/Blog.pdb and b/Blog/obj/Debug/Blog.pdb differ diff --git a/Umbraco.Examine.Linq.Sandbox/App_Data/Umbraco.sdf b/Umbraco.Examine.Linq.Sandbox/App_Data/Umbraco.sdf index 4701571..348d908 100644 Binary files a/Umbraco.Examine.Linq.Sandbox/App_Data/Umbraco.sdf and b/Umbraco.Examine.Linq.Sandbox/App_Data/Umbraco.sdf differ diff --git a/Umbraco.Examine.Linq.Sandbox/Umbraco.Examine.Linq.Sandbox.csproj b/Umbraco.Examine.Linq.Sandbox/Umbraco.Examine.Linq.Sandbox.csproj index dfe9622..77168e5 100644 --- a/Umbraco.Examine.Linq.Sandbox/Umbraco.Examine.Linq.Sandbox.csproj +++ b/Umbraco.Examine.Linq.Sandbox/Umbraco.Examine.Linq.Sandbox.csproj @@ -219,6 +219,1315 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -253,6 +1562,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Web.config diff --git a/Umbraco.Examine.Linq.Sandbox/Views/BlogOverview.cshtml b/Umbraco.Examine.Linq.Sandbox/Views/BlogOverview.cshtml index dd0d340..7264b99 100644 --- a/Umbraco.Examine.Linq.Sandbox/Views/BlogOverview.cshtml +++ b/Umbraco.Examine.Linq.Sandbox/Views/BlogOverview.cshtml @@ -1,5 +1,6 @@ @using ConcreteContentTypes.Core.Extensions -@using UmbracoNS = Umbraco; +@using UmbracoNS = Umbraco +@using Umbraco.Examine.Linq.Extensions @inherits Umbraco.Web.Mvc.UmbracoTemplatePage @@ -7,6 +8,9 @@ Layout = "Master.cshtml"; var CurrentPage = (Umbraco.Core.Models.IPublishedContent)base.CurrentPage; + + var results = new Umbraco.Examine.Linq.Index(new Umbraco.Examine.Linq.Sandbox.Mapper.ConcreteMapper()) + .Where(c => c.CreateDate < DateTime.Now); }
@@ -15,13 +19,14 @@
- @foreach (var post in new UmbracoNS.Examine.Linq.Sandbox.Repositories.BlogRepository().GetAllBlogPosts()) + @*foreach (var post in new UmbracoNS.Examine.Linq.Sandbox.Repositories.BlogRepository().GetAllBlogPosts())*@ + @foreach (var post in results) {
diff --git a/Umbraco.Examine.Linq.Sandbox/media/created-packages/LinqToExamine_1.2.zip b/Umbraco.Examine.Linq.Sandbox/media/created-packages/LinqToExamine_1.2.zip new file mode 100644 index 0000000..7de21fd Binary files /dev/null and b/Umbraco.Examine.Linq.Sandbox/media/created-packages/LinqToExamine_1.2.zip differ diff --git a/Umbraco.Examine.Linq/ExpressionTreeVisitor.cs b/Umbraco.Examine.Linq/ExpressionTreeVisitor.cs index 8e455fb..3cb8b53 100644 --- a/Umbraco.Examine.Linq/ExpressionTreeVisitor.cs +++ b/Umbraco.Examine.Linq/ExpressionTreeVisitor.cs @@ -50,6 +50,7 @@ protected override System.Linq.Expressions.Expression VisitBinaryExpression(Syst currentPart.Append("eq"); break; case ExpressionType.NotEqual: + currentPart.Append("ne"); inverseMode = true; break; @@ -156,8 +157,16 @@ protected override Expression VisitConstantExpression(ConstantExpression express if(expression.Value is string) { value = expression.Value.ToString(); - if (value.Contains(' ')) - value = string.Format("\"{0}\"", value); + operation = currentPart.ToString().Substring(currentPart.Length - 2); + if (operation == "eq" || operation == "ne") + { + if (value.Contains(' ')) + value = string.Format("\"{0}\"", value); + } + else + { + operation = ""; + } } else if (expression.Value is string[]) value = string.Join(" ", (string[])expression.Value); @@ -165,56 +174,80 @@ protected override Expression VisitConstantExpression(ConstantExpression express value = string.Join(" ", (IEnumerable)expression.Value); else if (expression.Value is DateTime) { - var formattedDateTime = ((DateTime)expression.Value).ToString("yyyyMMddHHmmssfff", CultureInfo.InvariantCulture);//((DateTime)expression.Value).ToString("o"); + string fieldName = currentPart.ToString().Substring(0, currentPart.Length - 3); //-3 to remove colon + + string minRange = shouldFormatLegacyDate(fieldName) || true ? "00000000000000000" : formatDateTime(fieldName, DateTime.MinValue); + string maxRange = shouldFormatLegacyDate(fieldName) || true ? "99999999999999999" : formatDateTime(fieldName, DateTime.MaxValue); + operation = currentPart.ToString().Substring(currentPart.Length - 2); + if (operation == "eq") { - value = formattedDateTime; + var formattedDateTime = formatDateTime(fieldName, (DateTime)expression.Value); + value = $"\"{formattedDateTime}\""; } else if (operation == "ne") { - string fieldName = currentPart.ToString().Substring(0, currentPart.Length - 2); + var formattedDateTime = formatDateTime("createDate", (DateTime)expression.Value, true); //we will do a < and then > operation = "gt"; - handleRangeOperation(formattedDateTime, "\\-99999999999999999", "99999999999999999", operation, ref value); + handleRangeOperation(formattedDateTime, minRange, maxRange, operation, ref value); //we want to now create the greater than expression value += " OR " + fieldName; operation = "lt"; - handleRangeOperation(formattedDateTime, "\\-99999999999999999", "99999999999999999", operation, ref value); + formattedDateTime = formatDateTime(fieldName, (DateTime)expression.Value); + handleRangeOperation(formattedDateTime, minRange, maxRange, operation, ref value); } else { - handleRangeOperation(formattedDateTime, "\\-99999999999999999", "99999999999999999", operation, ref value); + var formattedDateTime = formatDateTime(fieldName, (DateTime)expression.Value); + if (operation == "gt") + { + formattedDateTime = formatDateTime(fieldName, (DateTime)expression.Value, true); + handleRangeOperation(formattedDateTime, minRange, maxRange, operation, ref value); + } + else + handleRangeOperation(formattedDateTime, minRange, maxRange, operation, ref value); } } else if(expression.Value is int || expression.Value is double) { - var formattedInt = expression.Value is double ? (Convert.ToInt64((double)expression.Value)).ToString() : ((int)expression.Value).ToString(); operation = currentPart.ToString().Substring(currentPart.Length - 2); if(operation == "eq") { + var formattedInt = expression.Value is double ? (Convert.ToInt64((double)expression.Value)).ToString() : ((int)expression.Value).ToString(); value = formattedInt; } else if (operation == "ne") { + var formattedGt = expression.Value is double ? (Convert.ToInt64((double)expression.Value - 1)).ToString() : ((int)expression.Value - 1).ToString(); + var formattedLt = expression.Value is double ? (Convert.ToInt64((double)expression.Value + 1)).ToString() : ((int)expression.Value + 1).ToString(); string fieldName = currentPart.ToString().Substring(0, currentPart.Length - 2); //we will do a < and then > operation = "gt"; - handleRangeOperation(formattedInt, "\\-99999999999999999", "99999999999999999", operation, ref value); + handleRangeOperation(formattedGt, "\\-99999999999999999", "99999999999999999", operation, ref value); //we want to now create the greater than expression value += " OR " + fieldName; operation = "lt"; - handleRangeOperation(formattedInt, "\\-99999999999999999", "99999999999999999", operation, ref value); + handleRangeOperation(formattedLt, "\\-99999999999999999", "99999999999999999", operation, ref value); } else { + string formattedInt = ""; + if(operation == "gt") + formattedInt = expression.Value is double ? (Convert.ToInt64((double)expression.Value - 1)).ToString() : ((int)expression.Value - 1).ToString(); + else if(operation == "lt") + formattedInt = expression.Value is double ? (Convert.ToInt64((double)expression.Value + 1)).ToString() : ((int)expression.Value + 1).ToString(); handleRangeOperation(formattedInt, "\\-99999999999999999", "99999999999999999", operation, ref value); } } else if (expression.Value is bool || expression.Value is Boolean) - value = ((bool)expression.Value) ? "1" : "0"; + { + operation = currentPart.ToString().Substring(currentPart.Length - 2); + value = ((bool)expression.Value) && !inverseMode ? "1" : "0"; + } else value = expression.Value.ToString(); @@ -235,7 +268,7 @@ protected override Expression VisitMethodCallExpression(MethodCallExpression exp { case "Contains": bracketsEnabled = false; - currentPart.Append(inverseMode ? "-" : "+"); + currentPart.Append(inverseMode ? "-" : ""); VisitExpression(expression.Object); if(expression.Arguments.Count == 3) { @@ -297,29 +330,48 @@ protected override Expression VisitMethodCallExpression(MethodCallExpression exp } bracketsEnabled = true; break; + case "IsWithinRange": + //in this instance, the first argument is an expression to the value, + //so we will disable brackets and visit the expression + bracketsEnabled = false; + DateTime fromDate = (DateTime)((ConstantExpression)expression.Arguments[1]).Value; + DateTime toDate = (DateTime)((ConstantExpression)expression.Arguments[2]).Value; + VisitExpression(expression.Arguments[0]); + var fieldName = currentPart.ToString().Split(':')[0]; + //below we offset the date by a minute to be inclusive of the dates being filtered. Lucene doesn't include the min and max in the results + currentPart.AppendFormat("[{0} TO {1}]", formatDateTime("", fromDate.AddMinutes(-1), true), formatDateTime(fieldName, toDate.AddMinutes(1))); + bracketsEnabled = true; + break; + case "StartsWith": + bracketsEnabled = false; + VisitExpression(expression.Object); + currentPart.Append("^"); + VisitExpression(expression.Arguments[0]); + bracketsEnabled = true; + break; + case "Equals": + bracketsEnabled = false; + VisitExpression(expression.Object); + VisitExpression(expression.Arguments[0]); + bracketsEnabled = true; + break; case "Boost": bracketsEnabled = false; VisitExpression(expression.Arguments[0]); VisitExpression(expression.Object); - addEndBracket(); + //addEndBracket(); currentPart.AppendFormat("^{0}", expression.Arguments[1]); bracketsEnabled = false; break; case "Fuzzy": bracketsEnabled = false; fuzzy = (double)((ConstantExpression)expression.Arguments[1]).Value; - if(expression.Arguments.Any()) + if (expression.Arguments.Any()) VisitExpression(expression.Arguments[0]); VisitExpression(expression.Object); fuzzy = 0; bracketsEnabled = false; break; - case "Equals": - bracketsEnabled = false; - VisitExpression(expression.Object); - VisitExpression(expression.Arguments[0]); - bracketsEnabled = true; - break; } query.Append(currentPart); @@ -329,6 +381,35 @@ protected override Expression VisitMethodCallExpression(MethodCallExpression exp return expression; } + protected string formatDateTime(string fieldName, DateTime date, bool isFrom = false) + { + + return shouldFormatLegacyDate(fieldName) + ? toDateLegacy(date) + : isFrom ? toDateFromFormat(date) : toDateISO8601(date); + } + + protected bool shouldFormatLegacyDate(string fieldName) + { + var legacyDateTimeFields = new[] { "createDate", "updateDate" }; + return legacyDateTimeFields.Contains(fieldName); + } + + protected string toDateISO8601(DateTime date) + { + return date.ToString("yyyy-MM-ddTHH:mm:ss"); + } + + protected string toDateLegacy(DateTime date) + { + return date.ToString("yyyyMMddHHmmssfff", CultureInfo.InvariantCulture); + } + + protected string toDateFromFormat(DateTime date) + { + return date.ToString("yyyy-MM-ddTHHmmssfff"); + } + protected bool handleRangeOperation(string formattedValue, string defaultMinValue, string defaultMaxValue, string operation, ref string value) { switch (operation) @@ -337,14 +418,12 @@ protected bool handleRangeOperation(string formattedValue, string defaultMinValu value += string.Format("[{0} TO {1}]", defaultMinValue, formattedValue); break; case "lt": - formattedValue = (Int64.Parse(formattedValue) - 1).ToString();//-1 on the formattedValue to do less than value += string.Format("[{0} TO {1}]", defaultMinValue, formattedValue); break; case "ge": value += string.Format("[{0} TO {1}]", formattedValue, defaultMaxValue); break; case "gt": - formattedValue = (Int64.Parse(formattedValue) + 1).ToString();//-1 on the formattedValue to do less than value += string.Format("[{0} TO {1}]", formattedValue, defaultMaxValue); break; default: //is equals diff --git a/Umbraco.Examine.Linq/Extensions/ClassTypes.cs b/Umbraco.Examine.Linq/Extensions/ClassTypes.cs index 38ee79c..f2ec301 100644 --- a/Umbraco.Examine.Linq/Extensions/ClassTypes.cs +++ b/Umbraco.Examine.Linq/Extensions/ClassTypes.cs @@ -8,9 +8,9 @@ namespace Umbraco.Examine.Linq.Extensions { public static class ClassTypes { - //public static DateTime Boost(this DateTime value, int boost) - //{ - // return value; - //} + public static bool IsWithinRange(this DateTime value, DateTime from, DateTime to) + { + return value > from && value < to; + } } } diff --git a/Umbraco.Examine.Linq/Index.cs b/Umbraco.Examine.Linq/Index.cs index add8579..8fc638b 100644 --- a/Umbraco.Examine.Linq/Index.cs +++ b/Umbraco.Examine.Linq/Index.cs @@ -14,13 +14,13 @@ namespace Umbraco.Examine.Linq public class Index : QueryableBase { public Index(IMapper mapper) - : base(new DefaultQueryProvider(typeof(Index<>), QueryParser.CreateDefault(), new Executor(new UmbracoSearch("ExternalSearcher"), mapper))) + : base(new DefaultQueryProvider(typeof(Index<>), QueryParser.CreateDefault(), new Executor(new LuceneSearch("ExternalSearcher"), mapper))) { } public Index(string indexName = "ExternalSearcher", IMapper mapper = null) - : base(new DefaultQueryProvider(typeof(Index<>), QueryParser.CreateDefault(), new Executor(new UmbracoSearch(indexName), mapper))) + : base(new DefaultQueryProvider(typeof(Index<>), QueryParser.CreateDefault(), new Executor(new LuceneSearch(indexName), mapper))) { } diff --git a/Umbraco.Examine.Linq/SearchProviders/UmbracoSearch.cs b/Umbraco.Examine.Linq/SearchProviders/ExamineSearch.cs similarity index 90% rename from Umbraco.Examine.Linq/SearchProviders/UmbracoSearch.cs rename to Umbraco.Examine.Linq/SearchProviders/ExamineSearch.cs index fd9580f..1386053 100644 --- a/Umbraco.Examine.Linq/SearchProviders/UmbracoSearch.cs +++ b/Umbraco.Examine.Linq/SearchProviders/ExamineSearch.cs @@ -10,18 +10,18 @@ namespace Umbraco.Examine.Linq.SearchProviders { - public class UmbracoSearch : ISearcher + public class ExamineSearch : ISearcher { protected string IndexName { get; set; } public static Dictionary searchQueryCache { get; set; } - static UmbracoSearch() + static ExamineSearch() { searchQueryCache = new Dictionary(); } - public UmbracoSearch(string indexName) + public ExamineSearch(string indexName) { IndexName = indexName; } diff --git a/Umbraco.Examine.Linq/SearchProviders/LuceneSearch.cs b/Umbraco.Examine.Linq/SearchProviders/LuceneSearch.cs new file mode 100644 index 0000000..f5b8534 --- /dev/null +++ b/Umbraco.Examine.Linq/SearchProviders/LuceneSearch.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Examine; +using Examine.LuceneEngine.Providers; +using Examine.SearchCriteria; + +namespace Umbraco.Examine.Linq.SearchProviders +{ + public class LuceneSearch : ISearcher + { + protected string IndexName { get; set; } + + public static Dictionary searchQueryCache { get; set; } + + static LuceneSearch() + { + searchQueryCache = new Dictionary(); + } + + public LuceneSearch(string indexName) + { + IndexName = indexName; + } + + public IEnumerable Search(string query) + { + LuceneSearcher searcher = ExamineManager.Instance.SearchProviderCollection[IndexName] as LuceneSearcher; + return searcher.Search(searcher.CreateSearchCriteria().RawQuery(query)).ToList(); + } + } +} diff --git a/Umbraco.Examine.Linq/Umbraco.Examine.Linq.csproj b/Umbraco.Examine.Linq/Umbraco.Examine.Linq.csproj index e96a0f1..65d0b90 100644 --- a/Umbraco.Examine.Linq/Umbraco.Examine.Linq.csproj +++ b/Umbraco.Examine.Linq/Umbraco.Examine.Linq.csproj @@ -200,7 +200,8 @@ - + +