diff --git a/core/Piranha.Azure.Search/Content.cs b/core/Piranha.Azure.Search/Content.cs index 19ced70ac..d61576cd9 100644 --- a/core/Piranha.Azure.Search/Content.cs +++ b/core/Piranha.Azure.Search/Content.cs @@ -10,9 +10,8 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using Microsoft.Azure.Search; -using Microsoft.Azure.Search.Models; -using Microsoft.Spatial; +using Azure.Search.Documents.Indexes; +using Azure.Search.Documents.Indexes.Models; using Piranha.Models; namespace Piranha.Azure.Search @@ -20,7 +19,6 @@ namespace Piranha.Azure.Search /// /// Search index model for Azure Search. /// - [SerializePropertyNamesAsCamelCase] public class Content { /// @@ -32,46 +30,37 @@ public class Content /// /// Gets/sets the content slug. /// - [IsSearchable] - [Analyzer(AnalyzerName.AsString.StandardLucene)] + [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.StandardLucene)] public string Slug { get; set; } /// /// Gets/sets the content type. /// - [IsFilterable] + [SimpleField(IsFilterable = true)] public string ContentType { get; set; } /// /// Gets/sets the main title. /// - [IsSearchable] - [IsSortable] - [Analyzer(AnalyzerName.AsString.StandardLucene)] + [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.StandardLucene, IsSortable = true)] public string Title { get; set; } /// /// Gets/sets the optional category. /// - [IsFilterable] - [IsSearchable] - [IsSortable] - [Analyzer(AnalyzerName.AsString.StandardLucene)] + [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.StandardLucene, IsFilterable = true, IsSortable = true)] public string Category { get; set; } /// /// Gets/sets the optional tags. /// - [IsFilterable] - [IsSearchable] - [Analyzer(AnalyzerName.AsString.StandardLucene)] + [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.StandardLucene, IsFilterable = true, IsFacetable = true)] public IList Tags { get; set; } = new List(); /// /// Gets/sets the main body. /// - [IsSearchable] - [Analyzer(AnalyzerName.AsString.StandardLucene)] + [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.StandardLucene)] public string Body { get; set; } } } \ No newline at end of file diff --git a/core/Piranha.Azure.Search/Piranha.Azure.Search.csproj b/core/Piranha.Azure.Search/Piranha.Azure.Search.csproj index 712b81312..bb278dd08 100644 --- a/core/Piranha.Azure.Search/Piranha.Azure.Search.csproj +++ b/core/Piranha.Azure.Search/Piranha.Azure.Search.csproj @@ -6,8 +6,8 @@ + - diff --git a/core/Piranha.Azure.Search/PiranhaSearchExtensions.cs b/core/Piranha.Azure.Search/PiranhaSearchExtensions.cs index 944f9d202..eca9b6cc4 100644 --- a/core/Piranha.Azure.Search/PiranhaSearchExtensions.cs +++ b/core/Piranha.Azure.Search/PiranhaSearchExtensions.cs @@ -19,13 +19,14 @@ public static class PiranhaSearchExtensions /// Adds the Azure Search module. /// /// The service builder - /// The unique name of the azure search service + /// The url of the azure search service /// The admin api key + /// The name of the search index /// The services public static PiranhaServiceBuilder UseAzureSearch(this PiranhaServiceBuilder serviceBuilder, - string serviceName, string apiKey) + string serviceUrl, string apiKey, string index) { - serviceBuilder.Services.AddPiranhaAzureSearch(serviceName, apiKey); + serviceBuilder.Services.AddPiranhaAzureSearch(serviceUrl, apiKey, index); return serviceBuilder; } @@ -34,17 +35,18 @@ public static PiranhaServiceBuilder UseAzureSearch(this PiranhaServiceBuilder se /// Adds the Azure Search module. /// /// The current service collection - /// The unique name of the azure search service + /// The url of the azure search service /// The admin api key + /// The name of the search index /// The services public static IServiceCollection AddPiranhaAzureSearch(this IServiceCollection services, - string serviceName, string apiKey) + string serviceUrl, string apiKey, string index) { // Add the identity module App.Modules.Register(); // Register the search service - services.AddSingleton(new AzureSearchService(serviceName, apiKey)); + services.AddSingleton(new AzureSearchService(serviceUrl, apiKey, index)); return services; } diff --git a/core/Piranha.Azure.Search/Services/AzureSearchService.cs b/core/Piranha.Azure.Search/Services/AzureSearchService.cs index 03d2cb198..ec4b23bd5 100644 --- a/core/Piranha.Azure.Search/Services/AzureSearchService.cs +++ b/core/Piranha.Azure.Search/Services/AzureSearchService.cs @@ -8,13 +8,17 @@ * */ +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Microsoft.Azure.Search; -using Microsoft.Azure.Search.Models; +using Azure; +using Azure.Search.Documents; +using Azure.Search.Documents.Indexes; +using Azure.Search.Documents.Indexes.Models; +using Azure.Search.Documents.Models; using Piranha.Extend; using Piranha.Models; @@ -25,37 +29,38 @@ namespace Piranha.Azure.Search.Services /// public class AzureSearchService : ISearch { - private readonly string _serviceName = ""; + private readonly string _serviceUrl = ""; private readonly string _apiKey = ""; + private readonly string _index = ""; /// /// Default constructor. /// - /// The search service name + /// The search service url /// The admin api key - public AzureSearchService(string serviceName, string apiKey) + /// Name of the search index + public AzureSearchService(string serviceUrl, string apiKey, string index) { - _serviceName = serviceName; + _serviceUrl = serviceUrl; _apiKey = apiKey; + _index = index.ToLowerInvariant(); // Make sure the search indexes are up to date - CreateIndexes(); + CreateIndex(_index); } /// /// Creates the main search indexes. /// - public void CreateIndexes() + private void CreateIndex(string indexName) { - using (var client = CreateClient()) - { - var contentIndex = new Index() - { - Name = "content", - Fields = FieldBuilder.BuildForType() - }; - client.Indexes.CreateOrUpdate(contentIndex); - } + var indexClient = CreateSearchIndexClient(); + FieldBuilder fieldBuilder = new FieldBuilder(); + var searchFields = fieldBuilder.Build(typeof(Content)); + + var definition = new SearchIndex(indexName, searchFields); + + indexClient.CreateOrUpdateIndex(definition); } /// @@ -65,41 +70,36 @@ public void CreateIndexes() /// The page public async Task SavePageAsync(PageBase page) { - using (var client = CreateClient()) - { - var indexClient = client.Indexes.GetClient("content"); - var body = new StringBuilder(); - - foreach (var block in page.Blocks) - { - if (block is ISearchable searchableBlock) - { - body.AppendLine(searchableBlock.GetIndexedContent()); - } - } - - var cleanHtml = new Regex("<[^>]*(>|$)"); - var cleanSpaces = new Regex("[\\s\\r\\n]+"); + var client = CreateSearchClient(); - var cleaned = cleanSpaces.Replace(cleanHtml.Replace(body.ToString(), " "), " ").Trim(); + var body = new StringBuilder(); - var actions = new IndexAction[] + foreach (var block in page.Blocks) + { + if (block is ISearchable searchableBlock) { - IndexAction.MergeOrUpload( - new Content - { - Slug = page.Slug, - ContentId = page.Id.ToString(), - ContentType = "page", - Title = page.Title, - Body = cleaned - } - ) - }; - var batch = IndexBatch.New(actions); - - await indexClient.Documents.IndexAsync(batch); + body.AppendLine(searchableBlock.GetIndexedContent()); + } } + + var cleanHtml = new Regex("<[^>]*(>|$)"); + var cleanSpaces = new Regex("[\\s\\r\\n]+"); + + var cleaned = cleanSpaces.Replace(cleanHtml.Replace(body.ToString(), " "), " ").Trim(); + + IndexDocumentsBatch batch = IndexDocumentsBatch.Create( + IndexDocumentsAction.MergeOrUpload( + new Content + { + Slug = page.Slug, + ContentId = page.Id.ToString(), + ContentType = "page", + Title = page.Title, + Body = cleaned + } + ) + ); + await client.IndexDocumentsAsync(batch); } /// @@ -108,14 +108,9 @@ public async Task SavePageAsync(PageBase page) /// The page to delete public async Task DeletePageAsync(PageBase page) { - using (var client = CreateClient()) - { - var indexClient = client.Indexes.GetClient("content"); - - var batch = IndexBatch.Delete("contentId", new List { page.Id.ToString() }); - - await indexClient.Documents.IndexAsync(batch); - } + var client = CreateSearchClient(); + var batch = IndexDocumentsBatch.Delete("contentId", new List { page.Id.ToString() }); + await client.IndexDocumentsAsync(batch); } /// @@ -125,43 +120,37 @@ public async Task DeletePageAsync(PageBase page) /// The post public async Task SavePostAsync(PostBase post) { - using (var client = CreateClient()) - { - var indexClient = client.Indexes.GetClient("content"); - var body = new StringBuilder(); + var client = CreateSearchClient(); + var body = new StringBuilder(); - foreach (var block in post.Blocks) + foreach (var block in post.Blocks) + { + if (block is ISearchable searchableBlock) { - if (block is ISearchable searchableBlock) - { - body.AppendLine(searchableBlock.GetIndexedContent()); - } + body.AppendLine(searchableBlock.GetIndexedContent()); } - - var cleanHtml = new Regex("<[^>]*(>|$)"); - var cleanSpaces = new Regex("[\\s\\r\\n]+"); - - var cleaned = cleanSpaces.Replace(cleanHtml.Replace(body.ToString(), " "), " ").Trim(); - - var actions = new IndexAction[] - { - IndexAction.MergeOrUpload( - new Content - { - Slug = post.Slug, - ContentId = post.Id.ToString(), - ContentType = "post", - Title = post.Title, - Category = post.Category.Title, - Tags = post.Tags.Select(t => t.Title).ToList(), - Body = cleaned - } - ) - }; - var batch = IndexBatch.New(actions); - - await indexClient.Documents.IndexAsync(batch); } + + var cleanHtml = new Regex("<[^>]*(>|$)"); + var cleanSpaces = new Regex("[\\s\\r\\n]+"); + + var cleaned = cleanSpaces.Replace(cleanHtml.Replace(body.ToString(), " "), " ").Trim(); + + IndexDocumentsBatch batch = IndexDocumentsBatch.Create( + IndexDocumentsAction.MergeOrUpload( + new Content + { + Slug = post.Slug, + ContentId = post.Id.ToString(), + ContentType = "post", + Title = post.Title, + Category = post.Category.Title, + Tags = post.Tags.Select(t => t.Title).ToList(), + Body = cleaned + } + ) + ); + await client.IndexDocumentsAsync(batch); } /// @@ -170,22 +159,27 @@ public async Task SavePostAsync(PostBase post) /// The post to delete public async Task DeletePostAsync(PostBase post) { - using (var client = CreateClient()) - { - var indexClient = client.Indexes.GetClient("content"); - - var batch = IndexBatch.Delete("contentId", new List { post.Id.ToString() }); + var client = CreateSearchClient(); + var batch = IndexDocumentsBatch.Delete("contentId", new List { post.Id.ToString() }); + await client.IndexDocumentsAsync(batch); + } - await indexClient.Documents.IndexAsync(batch); - } + /// + /// Creates the SearchIndexClient. + /// + private SearchIndexClient CreateSearchIndexClient() + { + SearchIndexClient indexClient = new SearchIndexClient(new Uri(_serviceUrl), new AzureKeyCredential(_apiKey)); + return indexClient; } /// - /// Creates the search client. + /// Creates the SearchClient. /// - private SearchServiceClient CreateClient() + private SearchClient CreateSearchClient() { - return new SearchServiceClient(_serviceName, new SearchCredentials(_apiKey)); + SearchClient searchClient = new SearchClient(new Uri(_serviceUrl), _index, new AzureKeyCredential(_apiKey)); + return searchClient; } } } \ No newline at end of file