Merge branch 'ronaldbarendse-v8/bugfix/contentquery-search' into v8/dev

This commit is contained in:
Shannon
2020-01-15 12:38:39 +11:00
8 changed files with 160 additions and 70 deletions

View File

@@ -28,7 +28,7 @@
<dependency id="ClientDependency" version="[1.9.8,1.999999)" />
<dependency id="ClientDependency-Mvc5" version="[1.9.3,1.999999)" />
<dependency id="CSharpTest.Net.Collections" version="[14.906.1403.1082,14.999999)" />
<dependency id="Examine" version="[1.0.1,1.999999)" />
<dependency id="Examine" version="[1.0.2,1.999999)" />
<dependency id="HtmlAgilityPack" version="[1.8.14,1.999999)" />
<dependency id="ImageProcessor" version="[2.7.0.100,2.999999)" />
<dependency id="LightInject.Mvc" version="[2.0.0,2.999999)" />

View File

@@ -49,7 +49,7 @@
</ItemGroup>
<ItemGroup>
<!-- note: NuGet deals with transitive references now -->
<PackageReference Include="Examine" Version="1.0.1" />
<PackageReference Include="Examine" Version="1.0.2" />
<PackageReference Include="Microsoft.SourceLink.GitHub">
<Version>1.0.0-beta2-19324-01</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -112,4 +112,4 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
</Project>

View File

@@ -79,7 +79,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Castle.Core" Version="4.3.1" />
<PackageReference Include="Examine" Version="1.0.1" />
<PackageReference Include="Examine" Version="1.0.2" />
<PackageReference Include="HtmlAgilityPack">
<Version>1.8.14</Version>
</PackageReference>

View File

@@ -88,7 +88,7 @@
<PackageReference Include="CSharpTest.Net.Collections" Version="14.906.1403.1082" />
<PackageReference Include="ClientDependency" Version="1.9.8" />
<PackageReference Include="ClientDependency-Mvc5" Version="1.9.3" />
<PackageReference Include="Examine" Version="1.0.1" />
<PackageReference Include="Examine" Version="1.0.2" />
<PackageReference Include="ImageProcessor.Web" Version="4.10.0.100" />
<PackageReference Include="ImageProcessor.Web.Config" Version="2.5.0.100" />
<PackageReference Include="Microsoft.AspNet.Identity.Owin" Version="2.2.2" />

View File

@@ -2,32 +2,95 @@
using System.Collections.Generic;
using System.Linq;
using Examine;
using Umbraco.Core;
using Examine.LuceneEngine.Providers;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Examine;
using Umbraco.Web.PublishedCache;
namespace Umbraco.Web
{
/// <summary>
/// Extension methods for Examine
/// Extension methods for Examine.
/// </summary>
public static class ExamineExtensions
{
/// <summary>
/// Creates an <see cref="IEnumerable{PublishedSearchResult}" /> containing all content from the <paramref name="cache" />.
/// </summary>
/// <param name="results">The search results.</param>
/// <param name="cache">The cache to fetch the content from.</param>
/// <returns>
/// An <see cref="IEnumerable{PublishedSearchResult}" /> containing all content.
/// </returns>
/// <exception cref="ArgumentNullException">cache</exception>
/// <remarks>
/// Search results are skipped if it can't be fetched from the <paramref name="cache" /> by its integer id.
/// </remarks>
public static IEnumerable<PublishedSearchResult> ToPublishedSearchResults(this IEnumerable<ISearchResult> results, IPublishedCache cache)
{
var list = new List<PublishedSearchResult>();
if (cache == null) throw new ArgumentNullException(nameof(cache));
foreach (var result in results.OrderByDescending(x => x.Score))
var publishedSearchResults = new List<PublishedSearchResult>();
foreach (var result in results)
{
if (!int.TryParse(result.Id, out var intId)) continue; //invalid
var content = cache.GetById(intId);
if (content == null) continue; // skip if this doesn't exist in the cache
list.Add(new PublishedSearchResult(content, result.Score));
if (int.TryParse(result.Id, out var contentId) &&
cache.GetById(contentId) is IPublishedContent content)
{
publishedSearchResults.Add(new PublishedSearchResult(content, result.Score));
}
}
return list;
return publishedSearchResults;
}
/// <summary>
/// Creates an <see cref="IEnumerable{PublishedSearchResult}" /> containing all content, media or members from the <paramref name="snapshot" />.
/// </summary>
/// <param name="results">The search results.</param>
/// <param name="snapshot">The snapshot.</param>
/// <returns>
/// An <see cref="IEnumerable{PublishedSearchResult}" /> containing all content, media or members.
/// </returns>
/// <exception cref="ArgumentNullException">snapshot</exception>
/// <remarks>
/// Search results are skipped if it can't be fetched from the respective cache by its integer id.
/// </remarks>
public static IEnumerable<PublishedSearchResult> ToPublishedSearchResults(this IEnumerable<ISearchResult> results, IPublishedSnapshot snapshot)
{
if (snapshot == null) throw new ArgumentNullException(nameof(snapshot));
var publishedSearchResults = new List<PublishedSearchResult>();
foreach (var result in results)
{
if (int.TryParse(result.Id, out var contentId) &&
result.Values.TryGetValue(LuceneIndex.CategoryFieldName, out var indexType))
{
IPublishedContent content;
switch (indexType)
{
case IndexTypes.Content:
content = snapshot.Content.GetById(contentId);
break;
case IndexTypes.Media:
content = snapshot.Media.GetById(contentId);
break;
case IndexTypes.Member:
content = snapshot.Members.GetById(contentId);
break;
default:
continue;
}
if (content != null)
{
publishedSearchResults.Add(new PublishedSearchResult(content, result.Score));
}
}
}
return publishedSearchResults;
}
}
}

View File

@@ -35,52 +35,63 @@ namespace Umbraco.Web
/// <summary>
/// Searches content.
/// </summary>
/// <param name="term">Term to search.</param>
/// <param name="culture">Optional culture.</param>
/// <param name="indexName">Optional index name.</param>
/// <param name="term">The term to search.</param>
/// <param name="culture">The culture (defaults to a culture insensitive search).</param>
/// <param name="indexName">The name of the index to search (defaults to <see cref="Constants.UmbracoIndexes.ExternalIndexName" />).</param>
/// <returns>
/// The search results.
/// </returns>
/// <remarks>
/// <para>
/// When the <paramref name="culture"/> is not specified or is *, all cultures are searched.
/// When the <paramref name="culture" /> is not specified or is *, all cultures are searched.
/// To search for only invariant documents and fields use null.
/// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents.
/// </para>
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
/// </remarks>
IEnumerable<PublishedSearchResult> Search(string term, string culture = "*", string indexName = null);
IEnumerable<PublishedSearchResult> Search(string term, string culture = "*", string indexName = Constants.UmbracoIndexes.ExternalIndexName);
/// <summary>
/// Searches content.
/// </summary>
/// <param name="term">Term to search.</param>
/// <param name="skip">Numbers of items to skip.</param>
/// <param name="take">Numbers of items to return.</param>
/// <param name="totalRecords">Total number of matching items.</param>
/// <param name="culture">Optional culture.</param>
/// <param name="indexName">Optional index name.</param>
/// <param name="term">The term to search.</param>
/// <param name="skip">The amount of results to skip.</param>
/// <param name="take">The amount of results to take/return.</param>
/// <param name="totalRecords">The total amount of records.</param>
/// <param name="culture">The culture (defaults to a culture insensitive search).</param>
/// <param name="indexName">The name of the index to search (defaults to <see cref="Constants.UmbracoIndexes.ExternalIndexName" />).</param>
/// <returns>
/// The search results.
/// </returns>
/// <remarks>
/// <para>
/// When the <paramref name="culture"/> is not specified or is *, all cultures are searched.
/// When the <paramref name="culture" /> is not specified or is *, all cultures are searched.
/// To search for only invariant documents and fields use null.
/// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents.
/// </para>
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
/// </remarks>
IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = null);
IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = Constants.UmbracoIndexes.ExternalIndexName);
/// <summary>
/// Executes the query and converts the results to PublishedSearchResult.
/// Executes the query and converts the results to <see cref="PublishedSearchResult" />.
/// </summary>
/// <remarks>
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
/// </remarks>
/// <param name="query">The query.</param>
/// <returns>
/// The search results.
/// </returns>
IEnumerable<PublishedSearchResult> Search(IQueryExecutor query);
/// <summary>
/// Executes the query and converts the results to PublishedSearchResult.
/// Executes the query and converts the results to <see cref="PublishedSearchResult" />.
/// </summary>
/// <remarks>
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
/// </remarks>
/// <param name="query">The query.</param>
/// <param name="skip">The amount of results to skip.</param>
/// <param name="take">The amount of results to take/return.</param>
/// <param name="totalRecords">The total amount of records.</param>
/// <returns>
/// The search results.
/// </returns>
IEnumerable<PublishedSearchResult> Search(IQueryExecutor query, int skip, int take, out long totalRecords);
}
}

View File

@@ -183,54 +183,62 @@ namespace Umbraco.Web
#region Search
/// <inheritdoc />
public IEnumerable<PublishedSearchResult> Search(string term, string culture = "*", string indexName = null)
public IEnumerable<PublishedSearchResult> Search(string term, string culture = "*", string indexName = Constants.UmbracoIndexes.ExternalIndexName)
{
return Search(term, 0, 0, out _, culture, indexName);
}
/// <inheritdoc />
public IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = null)
public IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = Constants.UmbracoIndexes.ExternalIndexName)
{
indexName = string.IsNullOrEmpty(indexName)
? Constants.UmbracoIndexes.ExternalIndexName
: indexName;
if (skip < 0)
{
throw new ArgumentOutOfRangeException(nameof(skip), skip, "The value must be greater than or equal to zero.");
}
if (take < 0)
{
throw new ArgumentOutOfRangeException(nameof(take), take, "The value must be greater than or equal to zero.");
}
if (string.IsNullOrEmpty(indexName))
{
indexName = Constants.UmbracoIndexes.ExternalIndexName;
}
if (!_examineManager.TryGetIndex(indexName, out var index) || !(index is IUmbracoIndex umbIndex))
{
throw new InvalidOperationException($"No index found by name {indexName} or is not of type {typeof(IUmbracoIndex)}");
}
var searcher = umbIndex.GetSearcher();
var query = umbIndex.GetSearcher().CreateQuery(IndexTypes.Content);
// default to max 500 results
var count = skip == 0 && take == 0 ? 500 : skip + take;
ISearchResults results;
IQueryExecutor queryExecutor;
if (culture == "*")
{
//search everything
results = searcher.Search(term, count);
// Search everything
queryExecutor = query.ManagedQuery(term);
}
else if (culture.IsNullOrWhiteSpace())
else if (string.IsNullOrWhiteSpace(culture))
{
//only search invariant
var qry = searcher.CreateQuery().Field(UmbracoContentIndex.VariesByCultureFieldName, "n"); //must not vary by culture
qry = qry.And().ManagedQuery(term);
results = qry.Execute(count);
// Only search invariant
queryExecutor = query.Field(UmbracoContentIndex.VariesByCultureFieldName, "n") // Must not vary by culture
.And().ManagedQuery(term);
}
else
{
//search only the specified culture
//get all index fields suffixed with the culture name supplied
var cultureFields = umbIndex.GetCultureAndInvariantFields(culture).ToArray();
var qry = searcher.CreateQuery().ManagedQuery(term, cultureFields);
results = qry.Execute(count);
// Only search the specified culture
var fields = umbIndex.GetCultureAndInvariantFields(culture).ToArray(); // Get all index fields suffixed with the culture name supplied
queryExecutor = query.ManagedQuery(term, fields);
}
var results = skip == 0 && take == 0
? queryExecutor.Execute()
: queryExecutor.Execute(skip + take);
totalRecords = results.TotalItemCount;
return new CultureContextualSearchResults(results.ToPublishedSearchResults(_publishedSnapshot.Content), _variationContextAccessor, culture);
return new CultureContextualSearchResults(results.Skip(skip).ToPublishedSearchResults(_publishedSnapshot.Content), _variationContextAccessor, culture);
}
/// <inheritdoc />
@@ -242,12 +250,23 @@ namespace Umbraco.Web
/// <inheritdoc />
public IEnumerable<PublishedSearchResult> Search(IQueryExecutor query, int skip, int take, out long totalRecords)
{
if (skip < 0)
{
throw new ArgumentOutOfRangeException(nameof(skip), skip, "The value must be greater than or equal to zero.");
}
if (take < 0)
{
throw new ArgumentOutOfRangeException(nameof(take), take, "The value must be greater than or equal to zero.");
}
var results = skip == 0 && take == 0
? query.Execute()
: query.Execute(maxResults: skip + take);
: query.Execute(skip + take);
totalRecords = results.TotalItemCount;
return results.ToPublishedSearchResults(_publishedSnapshot.Content);
return results.Skip(skip).ToPublishedSearchResults(_publishedSnapshot);
}
/// <summary>
@@ -320,9 +339,6 @@ namespace Umbraco.Web
}
}
#endregion
}
}

View File

@@ -63,7 +63,7 @@
<ItemGroup>
<PackageReference Include="ClientDependency" Version="1.9.8" />
<PackageReference Include="CSharpTest.Net.Collections" Version="14.906.1403.1082" />
<PackageReference Include="Examine" Version="1.0.1" />
<PackageReference Include="Examine" Version="1.0.2" />
<PackageReference Include="HtmlAgilityPack" Version="1.8.14" />
<PackageReference Include="ImageProcessor">
<Version>2.7.0.100</Version>
@@ -1279,4 +1279,4 @@
<Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly" />
</SGen>
</Target>
</Project>
</Project>