Skip to content

Commit

Permalink
adding score and highlights info
Browse files Browse the repository at this point in the history
  • Loading branch information
MikeAlhayek committed Nov 22, 2024
1 parent 5fc7396 commit 620f75c
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -520,13 +520,13 @@ public async Task<IActionResult> Query(AdminQueryViewModel model)

try
{
var elasticTopDocs = await _elasticQueryService.SearchAsync(model.IndexName, tokenizedContent);
var results = await _elasticQueryService.SearchAsync(model.IndexName, tokenizedContent);

if (elasticTopDocs != null)
if (results != null)
{
model.Documents = elasticTopDocs.TopDocs.Where(x => x != null);
model.Fields = elasticTopDocs.Fields;
model.Count = elasticTopDocs.Count;
model.Documents = results.TopDocs?.Where(x => x != null).Select(doc => doc.Data);
model.Fields = results.Fields?.Where(x => x != null).Select(field => field.Data);
model.Count = results.Count;
}
}
catch (Exception e)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
namespace OrchardCore.Search.Elasticsearch;

public class ElasticsearchResult
{
public List<ElasticsearchRecord> TopDocs { get; set; }

public List<ElasticsearchRecord> Fields { get; set; }

public long Count { get; set; }
}

public class ElasticsearchRecord
{
public Dictionary<string, object> Data { get; set; }

public IReadOnlyDictionary<string, IReadOnlyCollection<string>> Highlights { get; set; }

public double? Score { get; set; }

public ElasticsearchRecord()
{
}

public ElasticsearchRecord(Dictionary<string, object> data)
{
Data = data;
}

public bool TryGetDataValue(string key, out object value)
{
if (Data == null)
{
value = null;

return false;
}

return Data.TryGetValue(key, out value);
}

public bool TryGetDataValue<T>(string key, out T value)
{
if (Data == null)
{
value = default;

return false;
}

if (Data.TryGetValue(key, out var obj))
{
if (obj is T typedValue)
{
value = typedValue;

return true;
}

if (typeof(T) == typeof(string))
{
value = (T)(object)obj?.ToString();

return true;
}

// Handle nullable types (e.g., Nullable<T>).
if (Nullable.GetUnderlyingType(typeof(T)) != null)
{
// If the object is null, assign the default value for the nullable type.
if (obj == null)
{
value = default;

return true; // Return true for null values if T is nullable.
}
}

try
{
value = (T)Convert.ChangeType(obj, typeof(T));

return true;
}
catch
{
}
}

value = default;

return false;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public async Task<IEnumerable<ContentPickerResult>> Search(ContentPickerSearchCo
await _elasticIndexManager.SearchAsync(indexName, async elasticClient =>
{
SearchResponse<Dictionary<string, object>> searchResponse = null;
var elasticTopDocs = new ElasticsearchTopDocs();
var elasticTopDocs = new ElasticsearchResult();
var valuesQuery = new TermsQueryField(searchContext.ContentTypes.Select(contentType => FieldValue.String(contentType)).ToArray());
Expand Down Expand Up @@ -93,19 +93,29 @@ await _elasticIndexManager.SearchAsync(indexName, async elasticClient =>
if (searchResponse.IsValidResponse)
{
elasticTopDocs.TopDocs = searchResponse.Documents.ToList();
elasticTopDocs.TopDocs = searchResponse.Documents.Select(doc => new ElasticsearchRecord(doc)).ToList();
}
if (elasticTopDocs.TopDocs != null)
{
foreach (var doc in elasticTopDocs.TopDocs)
{
results.Add(new ContentPickerResult
var result = new ContentPickerResult();
if (doc.TryGetDataValue<string>(nameof(ContentItem.ContentItemId), out var contentItemId))
{
result.ContentItemId = contentItemId;
}
if (doc.TryGetDataValue<string>("Content.ContentItem.DisplayText.keyword", out var keyword))
{
result.DisplayText = keyword;
}
if (doc.TryGetDataValue<bool>("Content.ContentItem.Published", out var published))
{
ContentItemId = doc["ContentItemId"].ToString(),
DisplayText = doc["Content.ContentItem.DisplayText.keyword"].ToString(),
HasPublished = string.Equals("true", doc["Content.ContentItem.Published"].ToString(), StringComparison.OrdinalIgnoreCase)
});
result.HasPublished = published;
}
}
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,35 +501,28 @@ public Task StoreDocumentsAsync(string indexName, IEnumerable<DocumentIndex> ind
);
}

/// <summary>
/// Returns results from a search made with a NEST QueryContainer query.
/// </summary>
/// <param name="indexName"></param>
/// <param name="query"></param>
/// <param name="sort"></param>
/// <param name="from"></param>
/// <param name="size"></param>
/// <returns><see cref="ElasticsearchTopDocs"/>.</returns>
public async Task<ElasticsearchTopDocs> SearchAsync(string indexName, Query query, List<SortOptions> sort, int from, int size)
public async Task<ElasticsearchResult> SearchAsync(ElasticsearchSearchContext context)
{
ArgumentException.ThrowIfNullOrEmpty(indexName);
ArgumentNullException.ThrowIfNull(query);
ArgumentNullException.ThrowIfNull(context);

var elasticTopDocs = new ElasticsearchTopDocs()
var elasticTopDocs = new ElasticsearchResult()
{
TopDocs = [],
};

if (await ExistsAsync(indexName))
if (await ExistsAsync(context.IndexName))
{
var fullIndexName = GetFullIndexName(indexName);
var fullIndexName = GetFullIndexName(context.IndexName);

var searchRequest = new SearchRequest(fullIndexName)
{
Query = query,
From = from,
Size = size,
Sort = sort ?? [],
Query = context.Query,
From = context.From,
Size = context.Size,
Sort = context.Sorts ?? [],
Source = context.Source,
Fields = context.Fields,
Highlight = context.Highlight,
};

var searchResponse = await _elasticClient.SearchAsync<Dictionary<string, object>>(searchRequest);
Expand All @@ -543,23 +536,31 @@ public async Task<ElasticsearchTopDocs> SearchAsync(string indexName, Query quer

while (documents.MoveNext() && hits.MoveNext())
{
var hit = hits.Current;

var document = documents.Current;

if (document != null)
{
elasticTopDocs.TopDocs.Add(document);
elasticTopDocs.TopDocs.Add(new ElasticsearchRecord(document)
{
Score = hit.Score,
Highlights = hit?.Highlight
});

continue;
}

var hit = hits.Current;

var topDoc = new Dictionary<string, object>
{
{ nameof(ContentItem.ContentItemId), hit.Id },
};

elasticTopDocs.TopDocs.Add(topDoc);
elasticTopDocs.TopDocs.Add(new ElasticsearchRecord(topDoc)
{
Score = hit.Score,
Highlights = hit.Highlight
});
}
}

Expand All @@ -569,6 +570,27 @@ public async Task<ElasticsearchTopDocs> SearchAsync(string indexName, Query quer
return elasticTopDocs;
}

/// <summary>
/// Returns results from a search made with a NEST QueryContainer query.
/// </summary>
/// <param name="indexName"></param>
/// <param name="query"></param>
/// <param name="sort"></param>
/// <param name="from"></param>
/// <param name="size"></param>
/// <returns><see cref="ElasticsearchResult"/>.</returns>
public Task<ElasticsearchResult> SearchAsync(string indexName, Query query, IList<SortOptions> sort, int from, int size)
{
var context = new ElasticsearchSearchContext(indexName, query)
{
Sorts = sort,
From = from,
Size = size,
};

return SearchAsync(context);
}

/// <summary>
/// Returns results from a search made with NEST Fluent DSL query.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using System.Text;
using Elastic.Clients.Elasticsearch;
using Elastic.Clients.Elasticsearch.QueryDsl;
using Microsoft.Extensions.Logging;
using OrchardCore.ContentManagement;

namespace OrchardCore.Search.Elasticsearch.Core.Services;

public class ElasticsearchQueryService
{
private readonly ElasticsearchIndexManager _elasticIndexManager;
Expand All @@ -22,9 +20,9 @@ public ElasticsearchQueryService(
_logger = logger;
}

public async Task<IList<string>> ExecuteQueryAsync(string indexName, Query query, List<SortOptions> sort, int from, int size)
public async Task<IList<string>> GetContentItemIdsAsync(ElasticsearchSearchContext request)
{
var results = await _elasticIndexManager.SearchAsync(indexName, query, sort, from, size);
var results = await _elasticIndexManager.SearchAsync(request);

if (results?.TopDocs is null || results.TopDocs.Count == 0)
{
Expand All @@ -35,68 +33,44 @@ public async Task<IList<string>> ExecuteQueryAsync(string indexName, Query query

foreach (var item in results.TopDocs)
{
contentItemIds.Add(item.GetValueOrDefault(nameof(ContentItem.ContentItemId)).ToString());
if (!item.TryGetDataValue<string>(nameof(ContentItem.ContentItemId), out var contentItemId))
{
continue;
}

contentItemIds.Add(contentItemId);
}

return contentItemIds;
}

public async Task<ElasticsearchTopDocs> SearchAsync(string indexName, string query)
public Task<ElasticsearchResult> SearchAsync(string indexName, string query)
{
ArgumentException.ThrowIfNullOrEmpty(indexName);

var elasticTopDocs = new ElasticsearchTopDocs();
ArgumentException.ThrowIfNullOrEmpty(query);

try
{
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(query));
var deserializedSearchRequest = _client.RequestResponseSerializer.Deserialize<SearchRequest>(stream);

var searchRequest = new SearchRequest(_elasticIndexManager.GetFullIndexName(indexName))
{
Query = deserializedSearchRequest.Query,
From = deserializedSearchRequest.From,
Size = deserializedSearchRequest.Size,
Fields = deserializedSearchRequest.Fields,
Sort = deserializedSearchRequest.Sort,
Source = deserializedSearchRequest.Source,
};

var searchResponse = await _client.SearchAsync<Dictionary<string, object>>(searchRequest);

var hits = new List<Dictionary<string, object>>();
var request = _client.RequestResponseSerializer.Deserialize<SearchRequest>(stream);

foreach (var hit in searchResponse.Hits)
var searchTask = _elasticIndexManager.SearchAsync(new ElasticsearchSearchContext(indexName, request.Query)
{
if (hit.Fields != null)
{
var row = new Dictionary<string, object>();

foreach (var keyValuePair in hit.Fields)
{
row[keyValuePair.Key] = keyValuePair.Value;
}

hits.Add(row);
}
}

if (searchResponse.IsValidResponse)
{
elasticTopDocs.Count = searchResponse.Total;
elasticTopDocs.TopDocs = new List<Dictionary<string, object>>(searchResponse.Documents);
elasticTopDocs.Fields = hits;
}
else
{
_logger.LogError("Received failure response from Elasticsearch.");
}
From = request.From,
Size = request.Size,
Fields = request.Fields,
Sorts = request.Sort,
Source = request.Source,
Highlight = request.Highlight,
});

return searchTask;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while querying elastic with exception: {Message}", ex.Message);
}

return elasticTopDocs;
return Task.FromResult(new ElasticsearchResult());
}
}
Loading

0 comments on commit 620f75c

Please sign in to comment.