Skip to content
38 changes: 35 additions & 3 deletions Moriyama.AzureSearch.Umbraco.Application/AzureSearchClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public IList<string> Filters
private string _searchTerm = "*";
private IList<string> _orderBy;
private IList<string> _facets;
private QueryType _queryType = QueryType.Simple;

private bool _content;
private bool _media;
Expand Down Expand Up @@ -69,7 +70,7 @@ public IAzureSearchClient DocumentTypes(IEnumerable<string> typeAliases)
return this;
}

private SearchParameters GetSearchParameters()
public SearchParameters GetSearchParameters()
{
var sp = new SearchParameters();

Expand All @@ -94,6 +95,7 @@ private SearchParameters GetSearchParameters()
sp.Skip = (_page - 1) * _pageSize;
sp.OrderBy = _orderBy;
sp.Facets = _facets;
sp.QueryType = _queryType;

return sp;
}
Expand All @@ -104,10 +106,20 @@ public ISearchResult Results()
return Results(sp);
}

private ISearchResult Results(SearchParameters sp)
public ISearchResult Results(SearchParameters sp)
{
var client = GetClient();
var config = GetConfiguration();
sp.HighlightFields = new List<string>();

foreach (var field in config.SearchFields.Where(f => f.IsSearchable))
{
sp.HighlightFields.Add(field.Name);
}

sp.HighlightPreTag = "<match>";
sp.HighlightPostTag = "</match>";

ISearchIndexClient indexClient = client.Indexes.GetClient(config.IndexName);
var startTime = DateTime.UtcNow;
var response = indexClient.Documents.Search(_searchTerm, sp);
Expand All @@ -117,7 +129,10 @@ private ISearchResult Results(SearchParameters sp)

foreach (var result in response.Results)
{
results.Content.Add(FromDocument(result.Document, result.Score));
var doc = FromDocument(result.Document, result.Score);

doc.Properties.Add("__match", result.Highlights);
results.Content.Add(doc);
}

if (response.Facets != null)
Expand Down Expand Up @@ -277,6 +292,12 @@ public IAzureSearchClient Filter(string field, bool value)
return this;
}

public IAzureSearchClient Filter(string filter)
{
_filters.Add(filter);
return this;
}

public IAzureSearchClient Facet(string facet)
{
_facets.Add(facet);
Expand All @@ -293,6 +314,12 @@ public IAzureSearchClient Facets(string[] facets)
return this;
}

public IAzureSearchClient SearchIn(string field, IEnumerable<string> values)
{
_filters.Add($"search.in({field}, '{string.Join(",", values)}')");
return this;
}

public IAzureSearchClient Any(string field)
{
_filters.Add(string.Format("{0}/any()", field));
Expand Down Expand Up @@ -407,5 +434,10 @@ public IList<SuggestResult> Suggest(string value, int count, bool fuzzy = true)

return indexClient.Documents.Suggest(value, "sg", sp).Results;
}

public void SetQueryType(QueryType type)
{
_queryType = type;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ private string SessionFile(string sessionId)
return Path.Combine(path, sessionId + ".json");
}

public event EventHandler<Index> CreatingIndex;

public string DropCreateIndex()
{
var serviceClient = GetClient();
Expand All @@ -56,6 +58,7 @@ public string DropCreateIndex()

try
{
CreatingIndex?.Invoke(this, definition);
serviceClient.Indexes.Create(definition);
}
catch (Exception ex)
Expand Down Expand Up @@ -368,6 +371,7 @@ private Document GetDocumentToIndex(IContentBase content, SearchField[] searchFi
{"Name", content.Name},
{"SortOrder", content.SortOrder},
{"Level", content.Level},
{"SearchablePath", content.Path.TrimStart('-') },
{"Path", content.Path.Split(',') },
{"ParentId", content.ParentId},
{"UpdateDate", content.UpdateDate},
Expand Down Expand Up @@ -503,10 +507,10 @@ private void SetCustomFieldParsers(AzureSearchConfig azureSearchConfig)
public Field[] GetStandardUmbracoFields()
{
// Key field has to be a string....
return new[]
{
new Field("Id", DataType.String) { IsKey = true, IsFilterable = true, IsSortable = true },
var key = new Field("Id", DataType.String) { IsKey = true, IsFilterable = true, IsSortable = true };

var fields = new []
{
new Field("Name", DataType.String) { IsFilterable = true, IsSortable = true, IsSearchable = true, IsRetrievable = true},
new Field("Key", DataType.String) { IsSearchable = true, IsRetrievable = true},

Expand All @@ -520,6 +524,7 @@ public Field[] GetStandardUmbracoFields()
new Field("Published", DataType.Boolean) { IsFilterable = true, IsFacetable = true },
new Field("Trashed", DataType.Boolean) { IsFilterable = true, IsFacetable = true },

new Field("SearchablePath", DataType.String) { IsSearchable = true, IsFilterable = true},
new Field("Path", DataType.Collection(DataType.String)) { IsSearchable = true, IsFilterable = true },
new Field("Template", DataType.String) { IsSearchable = true, IsFacetable = true },
new Field("Icon", DataType.String) { IsSearchable = true, IsFacetable = true },
Expand All @@ -537,6 +542,11 @@ public Field[] GetStandardUmbracoFields()
new Field("WriterId", DataType.Int32) { IsSortable = true, IsFacetable = true },
new Field("CreatorId", DataType.Int32) { IsSortable = true, IsFacetable = true }
};

var sorted = new List<Field>(fields.OrderBy(f => f.Name));
sorted.Insert(0, key);

return sorted.ToArray();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Examine;
using System.Linq;
using Moriyama.AzureSearch.Umbraco.Application.Interfaces;
using Newtonsoft.Json;
using Umbraco.Core;
using SearchResult = Examine.SearchResult;

namespace Moriyama.AzureSearch.Umbraco.Application.Examine
{
public class AzureExamineSearchResults : ISearchResults
{
public AzureExamineSearchResults(ISearchResult results)
{
_azureResults = results;

if (results != null)
TotalItemCount = results.Count;
}

private readonly ISearchResult _azureResults;

public IEnumerator<SearchResult> GetEnumerator()
{
using (var iterator = _azureResults?.Content?.GetEnumerator())
{
while (iterator != null && iterator.MoveNext())
{
yield return Convert(iterator.Current);
}
}
}

private SearchResult Convert(ISearchContent azureResult)
{
if (azureResult == null) return null;

var result = new SearchResult
{
Id = azureResult.Id,
Score = (float) azureResult.Score,
};

if (result.Fields == null) return result;

var indexType = "content";
if (azureResult.IsMedia)
{
indexType = "media";
}

if (azureResult.IsMember)
{
indexType = "member";
}

result.Fields.Add("__IndexType", indexType);
result.Fields.Add("__NodeId", azureResult.Id.ToString());
result.Fields.Add("__Path", $"-{azureResult.SearchablePath}");
result.Fields.Add("__NodeTypeAlias", azureResult.ContentTypeAlias?.ToLower());
result.Fields.Add("__Key", azureResult.Key);
result.Fields.Add("id", azureResult.Id.ToString());
result.Fields.Add("nodeName", azureResult.Name);
result.Fields.Add("createDate", azureResult.CreateDate.ToString("yyyyMMddHHmmsss"));

if (azureResult.Properties == null) return result;

foreach (var prop in azureResult.Properties)
{
if (prop.Key == null || prop.Value == null) continue;

result.Fields[prop.Key] = GetPropertyString(prop.Value);
}

var icon = "";
object iconObj = null;
if (azureResult.Properties?.TryGetValue("Icon", out iconObj) == true)
{
icon = iconObj.ToString();
}

result.Fields.Add("__Icon", icon);

return result;
}

private static string GetPropertyString(object value)
{
if (value == null) return string.Empty;

var type = value.GetType();
if (type == typeof(string) || !type.IsEnumerable())
{
return value.ToString();
}

switch (value)
{
case IEnumerable<object> enumerable:
var values = enumerable.Select(i => i?.ToString());
return string.Join(", ", values);

default:
return JsonConvert.SerializeObject(value);
}
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

public IEnumerable<SearchResult> Skip(int skip)
{
using (var enumerator = GetEnumerator())
{
while (enumerator.MoveNext())
{
yield return enumerator.Current;
}
}
}

public int TotalItemCount
{
get;
}
}
}

Original file line number Diff line number Diff line change
@@ -1,49 +1,79 @@
using Examine;
using System;
using System.Collections.Generic;
using Examine;
using Examine.SearchCriteria;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Examine.LuceneEngine;
using Examine.LuceneEngine.Config;
using Examine.LuceneEngine.SearchCriteria;
using Umbraco.Web.Models.ContentEditing;
using UmbracoExamine;
using Lucene.Net.Search;
using Microsoft.Azure.Search.Models;
using Newtonsoft.Json;
using StackExchange.Profiling;
using Umbraco.Core.Logging;

namespace Moriyama.AzureSearch.Umbraco.Application.Examine
{
public class DummyUmbracoExamineSearcher : UmbracoExamineSearcher
public partial class DummyUmbracoExamineSearcher : UmbracoExamineSearcher
{
public override ISearchResults Search(ISearchCriteria searchParams)
public override ISearchResults Search(ISearchCriteria searchCriteria)
{
// Doing this will make Umbraco fallback to the database.
// We could in future implement this to make it come from Azure search.
throw new FileNotFoundException("");

//// Video Nasty
//var s = searchParams.ToString();
//int id = 0;
try
{
if (searchCriteria != null)
{
var client = AzureSearchContext.Instance?.GetSearchClient();
if (client != null)
{
client.SetQueryType(QueryType.Full);
client.Filter("Published", true);
client.Filter("Trashed", false);

//try
//{
// s = s.Substring(s.IndexOf("NodeId:") + 7);
// s = s.Substring(0, s.IndexOf(" "));
var indexSet = IndexSets.Instance?.Sets?[IndexSetName];
if (indexSet != null)
{
client.Filter(GetExcludedDocTypesFilter(indexSet));
}

var query = GetLuceneQuery(searchCriteria);
var azQuery = client.Term(query);
var azureResults = azQuery?.Results();

// int.TryParse(s, out id);
ISearchResults azureExamineResults = new AzureExamineSearchResults(azureResults);
return azureExamineResults;
}
}
}
catch (Exception ex)
{
LogHelper.Error(GetType(), ex.Message, ex);
}

// if(id > 0)
// {
// var client = AzureSearchContext.Instance;
// var media = client.SearchClient.Media().Filter("Id", id.ToString()).Results();

// if(media.Count == 1)
// {
// var mediaItem = media.Content[0];
// Doing this will make Umbraco fallback to the database.
throw new FileNotFoundException("");
}

// }
// }
private static string GetLuceneQuery(ISearchCriteria searchCriteria)
{
// this line can be used when examine dependency is updated
//if (searchCriteria is LuceneSearchCriteria criteria) return criteria.Query?.ToString();

//}
//catch (Exception ex)
//{
var query = Regex.Match(searchCriteria.ToString(), "LuceneQuery: (.*\\)) }");
return query.Success && query.Groups.Count > 0 ? query.Groups[1].Value : string.Empty;;
}

//}
private static string GetExcludedDocTypesFilter(IndexSet indexSet)
{
var excludeDocs = indexSet?.ExcludeNodeTypes?.ToList();
if (excludeDocs?.Any() == false) return string.Empty;

//throw new FileNotFoundException("");
var docNames = excludeDocs?.Select(i => i?.Name) ?? Enumerable.Empty<string>();
return $"not search.in(ContentTypeAlias, '{string.Join(", ", docNames)}')";
}
}
}

Loading