diff --git a/src/Umbraco.Web/ITypedPublishedContentQuery.cs b/src/Umbraco.Web/ITypedPublishedContentQuery.cs
index 893c036958..f075727b5c 100644
--- a/src/Umbraco.Web/ITypedPublishedContentQuery.cs
+++ b/src/Umbraco.Web/ITypedPublishedContentQuery.cs
@@ -50,6 +50,18 @@ namespace Umbraco.Web
///
IEnumerable TypedSearch(string term, bool useWildCards = true, string searchProvider = null);
+ ///
+ /// Searches content
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ IEnumerable TypedSearch(int skip, int take, out int totalRecords, string term, bool useWildCards = true, string searchProvider = null);
+
///
/// Searhes content
///
@@ -57,5 +69,16 @@ namespace Umbraco.Web
///
///
IEnumerable TypedSearch(Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null);
+
+ ///
+ /// Searhes content
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ IEnumerable TypedSearch(int skip, int take, out int totalrecords, Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs
index bfe8fb5260..8cbbfd3fda 100644
--- a/src/Umbraco.Web/PublishedContentQuery.cs
+++ b/src/Umbraco.Web/PublishedContentQuery.cs
@@ -1,7 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
using System.Xml.XPath;
+using Examine;
+using Examine.LuceneEngine.Providers;
+using Examine.LuceneEngine.SearchCriteria;
+using Examine.Providers;
+using Examine.SearchCriteria;
using umbraco;
using Umbraco.Core;
using Umbraco.Core.Configuration;
@@ -375,7 +381,7 @@ namespace Umbraco.Web
///
///
///
- public dynamic Search(Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null)
+ public dynamic Search(ISearchCriteria criteria, BaseSearchProvider searchProvider = null)
{
return _dynamicContentQuery == null
? new DynamicPublishedContentList(
@@ -383,6 +389,51 @@ namespace Umbraco.Web
: _dynamicContentQuery.Search(criteria, searchProvider);
}
+ ///
+ /// Searches content
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public IEnumerable TypedSearch(int skip, int take, out int totalRecords, string term, bool useWildCards = true, string searchProvider = null)
+ {
+ if (_typedContentQuery != null) return _typedContentQuery.TypedSearch(skip, take, out totalRecords, term, useWildCards, searchProvider);
+
+ var searcher = ExamineManager.Instance.DefaultSearchProvider;
+ if (string.IsNullOrEmpty(searchProvider) == false)
+ searcher = ExamineManager.Instance.SearchProviderCollection[searchProvider];
+
+ //if both are zero, use the native Examine API
+ if (skip == 0 && take == 0)
+ {
+ var results = searcher.Search(term, useWildCards);
+ totalRecords = results.TotalItemCount;
+ return results.ConvertSearchResultToPublishedContent(_contentCache);
+ }
+
+ var luceneSearcher = searcher as BaseLuceneSearcher;
+ //if the searcher is not a base lucene searcher, we'll have to use Linq Take (edge case)
+ if (luceneSearcher == null)
+ {
+ var results = searcher.Search(term, useWildCards);
+ totalRecords = results.TotalItemCount;
+ return results
+ .Skip(skip) //uses Examine Skip
+ .ConvertSearchResultToPublishedContent(_contentCache)
+ .Take(take); //uses Linq Take
+ }
+
+ //create criteria for all fields
+ var allSearchFieldCriteria = SearchAllFields(term, useWildCards, luceneSearcher);
+
+ return TypedSearch(skip, take, out totalRecords, allSearchFieldCriteria, searcher);
+ }
+
+
///
/// Searches content
///
@@ -391,16 +442,51 @@ namespace Umbraco.Web
///
///
public IEnumerable TypedSearch(string term, bool useWildCards = true, string searchProvider = null)
- {
- if (_typedContentQuery != null) return _typedContentQuery.TypedSearch(term, useWildCards, searchProvider);
-
- var searcher = Examine.ExamineManager.Instance.DefaultSearchProvider;
- if (string.IsNullOrEmpty(searchProvider) == false)
- searcher = Examine.ExamineManager.Instance.SearchProviderCollection[searchProvider];
-
- var results = searcher.Search(term, useWildCards);
- return results.ConvertSearchResultToPublishedContent(_contentCache);
+ {
+ int total;
+ return TypedSearch(0, 0, out total, term, useWildCards, searchProvider);
}
+
+ ///
+ /// Searhes content
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public IEnumerable TypedSearch(int skip, int take, out int totalRecords, ISearchCriteria criteria, BaseSearchProvider searchProvider = null)
+ {
+ if (_typedContentQuery != null) return _typedContentQuery.TypedSearch(skip, take, out totalRecords, criteria, searchProvider);
+
+ var s = ExamineManager.Instance.DefaultSearchProvider;
+ if (searchProvider != null)
+ s = searchProvider;
+
+ //if both are zero, use the simple native Examine API
+ if (skip == 0 && take == 0)
+ {
+ var r = s.Search(criteria);
+ totalRecords = r.TotalItemCount;
+ return r.ConvertSearchResultToPublishedContent(_contentCache);
+ }
+
+ var maxResults = skip + take;
+
+ var results = s.Search(criteria,
+ //don't return more results than we need for the paging
+ //this is the 'trick' - we need to be able to load enough search results to fill
+ //all items to the maxResults
+ maxResults: maxResults);
+
+ totalRecords = results.TotalItemCount;
+
+ //use examine to skip, this will ensure the lucene data is not loaded for those items
+ var records = results.Skip(skip);
+
+ return records.ConvertSearchResultToPublishedContent(_contentCache);
+ }
///
/// Searhes content
@@ -410,14 +496,54 @@ namespace Umbraco.Web
///
public IEnumerable TypedSearch(Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null)
{
- if (_typedContentQuery != null) return _typedContentQuery.TypedSearch(criteria, searchProvider);
+ var total = 0;
+ return TypedSearch(0, 0, out total, criteria, searchProvider);
+ }
- var s = Examine.ExamineManager.Instance.DefaultSearchProvider;
- if (searchProvider != null)
- s = searchProvider;
+ ///
+ /// Helper method to create an ISearchCriteria for searching all fields in a
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// This is here because some of this stuff is internal in Examine
+ ///
+ private ISearchCriteria SearchAllFields(string searchText, bool useWildcards, BaseLuceneSearcher searcher)
+ {
+ var sc = searcher.CreateSearchCriteria();
- var results = s.Search(criteria);
- return results.ConvertSearchResultToPublishedContent(_contentCache);
+ if (_examineGetSearchFields == null)
+ {
+ //get the GetSearchFields method from BaseLuceneSearcher
+ _examineGetSearchFields = typeof(BaseLuceneSearcher).GetMethod("GetSearchFields", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
+ }
+ //get the results of searcher.BaseLuceneSearcher() using ugly reflection since it's not public
+ var searchFields = (IEnumerable)_examineGetSearchFields.Invoke(searcher, null);
+
+ //this is what Examine does internally to create ISearchCriteria for searching all fields
+ var strArray = searchText.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ sc = useWildcards == false
+ ? sc.GroupedOr(searchFields, strArray).Compile()
+ : sc.GroupedOr(searchFields, strArray.Select(x => new CustomExamineValue(Examineness.ComplexWildcard, x.MultipleCharacterWildcard().Value)).ToArray()).Compile();
+ return sc;
+ }
+
+ private static MethodInfo _examineGetSearchFields;
+
+ //support class since Examine doesn't expose it's own ExamineValue class publicly
+ private class CustomExamineValue : IExamineValue
+ {
+ public CustomExamineValue(Examineness vagueness, string value)
+ {
+ this.Examineness = vagueness;
+ this.Value = value;
+ this.Level = 1f;
+ }
+ public Examineness Examineness { get; private set; }
+ public string Value { get; private set; }
+ public float Level { get; private set; }
}
#endregion
diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs
index d94476b31c..90bc0a436b 100644
--- a/src/Umbraco.Web/UmbracoHelper.cs
+++ b/src/Umbraco.Web/UmbracoHelper.cs
@@ -1251,24 +1251,53 @@ namespace Umbraco.Web
public IEnumerable TypedSearch(string term, bool useWildCards = true, string searchProvider = null)
{
return ContentQuery.TypedSearch(term, useWildCards, searchProvider);
- }
-
- ///
- /// Searhes content
- ///
- ///
+ }
+
+ ///
+ /// Searches content
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
///
///
- public IEnumerable TypedSearch(Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null)
+ public IEnumerable TypedSearch(int skip, int take, out int totalRecords, string term, bool useWildCards = true, string searchProvider = null)
+ {
+ return ContentQuery.TypedSearch(skip, take, out totalRecords, term, useWildCards, searchProvider);
+ }
+
+ ///
+ /// Searhes content
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public IEnumerable TypedSearch(int skip, int take, out int totalRecords, Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null)
{
- return ContentQuery.TypedSearch(criteria, searchProvider);
- }
-
- #endregion
-
- #region Xml
-
- public dynamic ToDynamicXml(string xml)
+ return ContentQuery.TypedSearch(skip, take, out totalRecords, criteria, searchProvider);
+ }
+
+ ///
+ /// Searhes content
+ ///
+ ///
+ ///
+ ///
+ public IEnumerable TypedSearch(Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null)
+ {
+ return ContentQuery.TypedSearch(criteria, searchProvider);
+ }
+
+ #endregion
+
+ #region Xml
+
+ public dynamic ToDynamicXml(string xml)
{
if (string.IsNullOrWhiteSpace(xml)) return null;
var xElement = XElement.Parse(xml);
@@ -1439,8 +1468,8 @@ namespace Umbraco.Web
///
public IHtmlString Truncate(string html, int length, bool addElipsis, bool treatTagsAsContent)
{
- return _stringUtilities.Truncate(html, length, addElipsis, treatTagsAsContent);
- }
+ return _stringUtilities.Truncate(html, length, addElipsis, treatTagsAsContent);
+ }
#region Truncate by Words
///
@@ -1451,7 +1480,7 @@ namespace Umbraco.Web
int length = _stringUtilities.WordsToLength(html, words);
return Truncate(html, length, true, false);
- }
+ }
///
/// Truncates a string to a given amount of words, can add a elipsis at the end (...). Method checks for open html tags, and makes sure to close them
@@ -1481,12 +1510,12 @@ namespace Umbraco.Web
int length = _stringUtilities.WordsToLength(html.ToHtmlString(), words);
return Truncate(html, length, addElipsis, false);
- }
- #endregion
+ }
#endregion
-
+ #endregion
+
#region If
-
+
///
/// If the test is true, the string valueIfTrue will be returned, otherwise the valueIfFalse will be returned.
///