-
- {{searcher.name}}
-
+
+
+
+
+
+
Search
+
Search the index and view the results
-
-
-
- -
- Search tools
+
+
+
+
+
+
+ ← Back to overview
+
+
+
+
+
+
+
+
+
+
{{ vm.selectedIndex.name }}
+
+
+
+
+
+
+
+
+
+
Health status
+
The health status of the index and if it can be read
+
+
+
+
+
+
+
+
+
+
+
+
{{vm.selectedIndex.healthStatus}}
+
+ The index cannot be read and will need to be rebuilt
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Search
+
Search the index and view the results
+
+
+
+
+
+
+
+
+
+
+
-
-
-
- | Score |
- Id |
- Values |
-
-
-
-
- | {{result.Score}} |
- {{result.Id}} |
-
-
- {{key}}:
- {{val}}
-
- |
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index info
+
Lists the properties of the index
+
+
+
+
+
+
+
+
+
+ | {{key}} |
+ {{val}} |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Tools
+
Tools to manage the index
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation
+
+
+
+
+
+ This index cannot be rebuilt because it has no assigned IIndexPopulator
-
-
-
- Provider properties
-
-
-
- | {{key}} |
- {{val}} |
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagementresults.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagementresults.html
new file mode 100644
index 0000000000..1df737816f
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagementresults.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ | Field |
+ Value |
+
+
+
+
+ | {{key}} |
+ {{val}} |
+
+
+
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemgmt.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemgmt.controller.js
deleted file mode 100644
index f4e78a9dc3..0000000000
--- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemgmt.controller.js
+++ /dev/null
@@ -1,128 +0,0 @@
-function ExamineMgmtController($scope, umbRequestHelper, $log, $http, $q, $timeout) {
-
- $scope.indexerDetails = [];
- $scope.searcherDetails = [];
- $scope.loading = true;
-
- function checkProcessing(indexer, checkActionName) {
- umbRequestHelper.resourcePromise(
- $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl",
- checkActionName,
- { indexerName: indexer.name })),
- 'Failed to check index processing')
- .then(function(data) {
-
- if (data !== null && data !== "null") {
-
- //copy all resulting properties
- for (var k in data) {
- indexer[k] = data[k];
- }
- indexer.isProcessing = false;
- } else {
- $timeout(function() {
- //don't continue if we've tried 100 times
- if (indexer.processingAttempts < 100) {
- checkProcessing(indexer, checkActionName);
- //add an attempt
- indexer.processingAttempts++;
- } else {
- //we've exceeded 100 attempts, stop processing
- indexer.isProcessing = false;
- }
- },
- 1000);
- }
- });
- }
-
- $scope.search = function(searcher, e) {
- if (e && e.keyCode !== 13) {
- return;
- }
-
- umbRequestHelper.resourcePromise(
- $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl",
- "GetSearchResults",
- {
- searcherName: searcher.name,
- query: encodeURIComponent(searcher.searchText),
- queryType: searcher.searchType
- })),
- 'Failed to search')
- .then(function(searchResults) {
- searcher.isSearching = true;
- searcher.searchResults = searchResults;
- });
- }
-
- $scope.toggle = function(provider, propName) {
- if (provider[propName] !== undefined) {
- provider[propName] = !provider[propName];
- } else {
- provider[propName] = true;
- }
- }
-
- $scope.rebuildIndex = function(indexer) {
- if (confirm("This will cause the index to be rebuilt. " +
- "Depending on how much content there is in your site this could take a while. " +
- "It is not recommended to rebuild an index during times of high website traffic " +
- "or when editors are editing content.")) {
-
- indexer.isProcessing = true;
- indexer.processingAttempts = 0;
-
- umbRequestHelper.resourcePromise(
- $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl",
- "PostRebuildIndex",
- { indexerName: indexer.name })),
- 'Failed to rebuild index')
- .then(function() {
-
- //rebuilding has started, nothing is returned accept a 200 status code.
- //lets poll to see if it is done.
- $timeout(function() {
- checkProcessing(indexer, "PostCheckRebuildIndex");
- },
- 1000);
-
- });
- }
- }
-
- $scope.closeSearch = function(searcher) {
- searcher.isSearching = true;
- }
-
- //go get the data
-
- //combine two promises and execute when they are both done
- $q.all([
-
- //get the indexer details
- umbRequestHelper.resourcePromise(
- $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetIndexerDetails")),
- 'Failed to retrieve indexer details')
- .then(function(data) {
- $scope.indexerDetails = data;
- }),
-
- //get the searcher details
- umbRequestHelper.resourcePromise(
- $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearcherDetails")),
- 'Failed to retrieve searcher details')
- .then(function(data) {
- $scope.searcherDetails = data;
- for (var s in $scope.searcherDetails) {
- $scope.searcherDetails[s].searchType = "text";
- }
- })
- ])
- .then(function() {
- //all init loading is complete
- $scope.loading = false;
- });
-}
-
-angular.module("umbraco").controller("Umbraco.Dashboard.ExamineMgmtController", ExamineMgmtController);
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI/Global.asax b/src/Umbraco.Web.UI/Global.asax
index 53d2d54050..08312748b9 100644
--- a/src/Umbraco.Web.UI/Global.asax
+++ b/src/Umbraco.Web.UI/Global.asax
@@ -1,2 +1 @@
<%@ Application Inherits="Umbraco.Web.UmbracoApplication" Language="C#" %>
-
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
index 48a8b9208d..2823908a92 100644
--- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
+++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
@@ -88,10 +88,9 @@
-
+
-
diff --git a/src/Umbraco.Web.UI/config/ExamineIndex.Release.config b/src/Umbraco.Web.UI/config/ExamineIndex.Release.config
index 300f75ba33..ec0d18aa2d 100644
--- a/src/Umbraco.Web.UI/config/ExamineIndex.Release.config
+++ b/src/Umbraco.Web.UI/config/ExamineIndex.Release.config
@@ -7,22 +7,4 @@ Index/Search providers can be defined in the UmbracoSettings.config
More information and documentation can be found on GitHub: https://github.com/Shazwazza/Examine/
-->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
diff --git a/src/Umbraco.Web.UI/config/ExamineIndex.config b/src/Umbraco.Web.UI/config/ExamineIndex.config
index 8d8dcaaf6d..833a65c8d1 100644
--- a/src/Umbraco.Web.UI/config/ExamineIndex.config
+++ b/src/Umbraco.Web.UI/config/ExamineIndex.config
@@ -4,27 +4,11 @@ Umbraco examine is an extensible indexer and search engine.
This configuration file can be extended to create your own index sets.
Index/Search providers can be defined in the UmbracoSettings.config
-More information and documentation can be found on GitHub: https://github.com/Shazwazza/Examine/
+More information and documentation can be found on Our:
+https://our.umbraco.com/Documentation/Reference/Searching/Examine/
+https://our.umbraco.com/Documentation/Reference/Config/ExamineIndex/
+https://our.umbraco.com/Documentation/Reference/Config/ExamineSettings/
+
-->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
diff --git a/src/Umbraco.Web.UI/config/ExamineSettings.Release.config b/src/Umbraco.Web.UI/config/ExamineSettings.Release.config
index 31e1193ee9..6b3aaa0372 100644
--- a/src/Umbraco.Web.UI/config/ExamineSettings.Release.config
+++ b/src/Umbraco.Web.UI/config/ExamineSettings.Release.config
@@ -7,34 +7,14 @@ Index sets can be defined in the ExamineIndex.config if you're using the standar
More information and documentation can be found on GitHub: https://github.com/Shazwazza/Examine/
-->
+
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Umbraco.Web.UI/config/ExamineSettings.config b/src/Umbraco.Web.UI/config/ExamineSettings.config
index 18c5658731..f0fd2b69fc 100644
--- a/src/Umbraco.Web.UI/config/ExamineSettings.config
+++ b/src/Umbraco.Web.UI/config/ExamineSettings.config
@@ -4,38 +4,20 @@ Umbraco examine is an extensible indexer and search engine.
This configuration file can be extended to add your own search/index providers.
Index sets can be defined in the ExamineIndex.config if you're using the standard provider model.
-More information and documentation can be found on GitHub: https://github.com/Shazwazza/Examine/
+More information and documentation can be found on Our:
+https://our.umbraco.com/Documentation/Reference/Searching/Examine/
+https://our.umbraco.com/Documentation/Reference/Config/ExamineIndex/
+https://our.umbraco.com/Documentation/Reference/Config/ExamineSettings/
-->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs
index 02dede1da9..990aa32ddd 100644
--- a/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs
+++ b/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs
@@ -11,6 +11,7 @@ using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Changes;
using Umbraco.Core.Sync;
+using Umbraco.Examine;
using Umbraco.Web.Cache;
using Umbraco.Web.Composing;
using Umbraco.Web.Routing;
@@ -48,7 +49,7 @@ namespace Umbraco.Web.Components
private BackgroundTaskRunner
_processTaskRunner;
private bool _started;
private IBackgroundTask[] _tasks;
- private IExamineManager _examineManager;
+ private IndexRebuilder _indexRebuilder;
public override void Compose(Composition composition)
{
@@ -87,13 +88,13 @@ namespace Umbraco.Web.Components
//rebuild indexes if the server is not synced
// NOTE: This will rebuild ALL indexes including the members, if developers want to target specific
// indexes then they can adjust this logic themselves.
- () => ExamineComponent.RebuildIndexes(false, _examineManager, _logger)
+ () => ExamineComponent.RebuildIndexes(_indexRebuilder, _logger, false, 5000)
}
});
});
}
- public void Initialize(IRuntimeState runtime, IServerRegistrar serverRegistrar, IServerMessenger serverMessenger, IServerRegistrationService registrationService, ILogger logger, IExamineManager examineManager)
+ public void Initialize(IRuntimeState runtime, IServerRegistrar serverRegistrar, IServerMessenger serverMessenger, IServerRegistrationService registrationService, ILogger logger, IndexRebuilder indexRebuilder)
{
_registrar = serverRegistrar as DatabaseServerRegistrar;
if (_registrar == null) throw new Exception("panic: registar.");
@@ -101,12 +102,10 @@ namespace Umbraco.Web.Components
_messenger = serverMessenger as BatchedDatabaseServerMessenger;
if (_messenger == null) throw new Exception("panic: messenger");
- _messenger.Startup();
-
_runtime = runtime;
_logger = logger;
_registrationService = registrationService;
- _examineManager = examineManager;
+ _indexRebuilder = indexRebuilder;
_touchTaskRunner = new BackgroundTaskRunner("ServerRegistration",
new BackgroundTaskRunnerOptions { AutoStart = true }, logger);
@@ -115,6 +114,9 @@ namespace Umbraco.Web.Components
//We will start the whole process when a successful request is made
UmbracoModule.RouteAttempt += RegisterBackgroundTasksOnce;
+
+ // must come last, as it references some _variables
+ _messenger.Startup();
}
///
diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs
index 5444aadcec..4d7a21ffef 100644
--- a/src/Umbraco.Web/Editors/EntityController.cs
+++ b/src/Umbraco.Web/Editors/EntityController.cs
@@ -15,6 +15,7 @@ using Umbraco.Core.Models;
using Constants = Umbraco.Core.Constants;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using System.Web.Http.Controllers;
+using Examine;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Xml;
using Umbraco.Web.Models.Mapping;
@@ -53,12 +54,13 @@ namespace Umbraco.Web.Editors
}
}
- private readonly UmbracoTreeSearcher _treeSearcher = new UmbracoTreeSearcher();
+ private readonly UmbracoTreeSearcher _treeSearcher;
private readonly SearchableTreeCollection _searchableTreeCollection;
- public EntityController(SearchableTreeCollection searchableTreeCollection)
+ public EntityController(SearchableTreeCollection searchableTreeCollection, UmbracoTreeSearcher treeSearcher)
{
_searchableTreeCollection = searchableTreeCollection;
+ _treeSearcher = treeSearcher;
}
///
@@ -121,9 +123,8 @@ namespace Umbraco.Web.Editors
return result;
var allowedSections = Security.CurrentUser.AllowedSections.ToArray();
- var searchableTrees = _searchableTreeCollection.AsReadOnlyDictionary();
- foreach (var searchableTree in searchableTrees)
+ foreach (var searchableTree in _searchableTreeCollection.SearchableApplicationTrees)
{
if (allowedSections.Contains(searchableTree.Value.AppAlias))
{
@@ -411,22 +412,18 @@ namespace Umbraco.Web.Editors
Direction orderDirection = Direction.Ascending,
string filter = "")
{
- int intId;
-
- if (int.TryParse(id, out intId))
+ if (int.TryParse(id, out var intId))
{
return GetPagedChildren(intId, type, pageNumber, pageSize, orderBy, orderDirection, filter);
}
- Guid guidId;
- if (Guid.TryParse(id, out guidId))
+ if (Guid.TryParse(id, out _))
{
//Not supported currently
throw new HttpResponseException(HttpStatusCode.NotFound);
}
- Udi udiId;
- if (Udi.TryParse(id, out udiId))
+ if (Udi.TryParse(id, out _))
{
//Not supported currently
throw new HttpResponseException(HttpStatusCode.NotFound);
@@ -444,8 +441,7 @@ namespace Umbraco.Web.Editors
//the EntityService cannot search members of a certain type, this is currently not supported and would require
//quite a bit of plumbing to do in the Services/Repository, we'll revert to a paged search
- long total;
- var searchResult = _treeSearcher.ExamineSearch(Umbraco, filter ?? "", type, pageSize, pageNumber - 1, out total, id);
+ var searchResult = _treeSearcher.ExamineSearch(filter ?? "", type, pageSize, pageNumber - 1, out long total, id);
return new PagedResult(total, pageNumber, pageSize)
{
@@ -481,8 +477,7 @@ namespace Umbraco.Web.Editors
var objectType = ConvertToObjectType(type);
if (objectType.HasValue)
{
- long totalRecords;
- var entities = Services.EntityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out totalRecords, orderBy, orderDirection, filter);
+ var entities = Services.EntityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out var totalRecords, orderBy, orderDirection, filter);
if (totalRecords == 0)
{
@@ -595,15 +590,11 @@ namespace Umbraco.Web.Editors
///
///
///
- private IEnumerable ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null)
+ private IEnumerable ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null)
{
- long total;
- return _treeSearcher.ExamineSearch(Umbraco, query, entityType, 200, 0, out total, searchFrom);
+ return _treeSearcher.ExamineSearch(query, entityType, 200, 0, out _, searchFrom);
}
-
-
-
private IEnumerable GetResultForChildren(int id, UmbracoEntityTypes entityType)
{
var objectType = ConvertToObjectType(entityType);
diff --git a/src/Umbraco.Web/Editors/ExamineManagementController.cs b/src/Umbraco.Web/Editors/ExamineManagementController.cs
index 829c4ccf74..67209c91bd 100644
--- a/src/Umbraco.Web/Editors/ExamineManagementController.cs
+++ b/src/Umbraco.Web/Editors/ExamineManagementController.cs
@@ -9,12 +9,18 @@ using System.Web.Http;
using Examine;
using Examine.LuceneEngine;
using Examine.LuceneEngine.Providers;
+using Lucene.Net.Analysis;
+using Lucene.Net.QueryParsers;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
+using Umbraco.Examine;
+using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.Search;
+using SearchResult = Umbraco.Web.Models.ContentEditing.SearchResult;
+using Version = Lucene.Net.Util.Version;
namespace Umbraco.Web.Editors
{
@@ -24,26 +30,26 @@ namespace Umbraco.Web.Editors
private readonly IExamineManager _examineManager;
private readonly ILogger _logger;
private readonly IRuntimeCacheProvider _runtimeCacheProvider;
+ private readonly IndexRebuilder _indexRebuilder;
+
public ExamineManagementController(IExamineManager examineManager, ILogger logger,
- IRuntimeCacheProvider runtimeCacheProvider)
+ IRuntimeCacheProvider runtimeCacheProvider,
+ IndexRebuilder indexRebuilder)
{
_examineManager = examineManager;
_logger = logger;
_runtimeCacheProvider = runtimeCacheProvider;
+ _indexRebuilder = indexRebuilder;
}
///
/// Get the details for indexers
///
///
- public IEnumerable GetIndexerDetails()
+ public IEnumerable GetIndexerDetails()
{
- return _examineManager.IndexProviders.Select(CreateModel).OrderBy(x =>
- {
- //order by name , but strip the "Indexer" from the end if it exists
- return x.Name.TrimEnd("Indexer");
- });
+ return _examineManager.Indexes.Select(CreateModel).OrderBy(x => x.Name.TrimEnd("Indexer"));
}
///
@@ -53,229 +59,189 @@ namespace Umbraco.Web.Editors
public IEnumerable GetSearcherDetails()
{
var model = new List(
- _examineManager.IndexProviders.Select(indexer =>
- {
- var searcher = indexer.Value.GetSearcher();
- var searcherName = (searcher as BaseLuceneSearcher)?.Name ?? string.Concat(indexer.Key, "Searcher");
-
- var indexerModel = new ExamineSearcherModel
- {
- Name = searcherName
- };
- var props = TypeHelper.CachedDiscoverableProperties(searcher.GetType(), mustWrite: false)
- //ignore these properties
- .Where(x => new[] { "Description" }.InvariantContains(x.Name) == false)
- .Where(x => x.GetCustomAttribute()
- ?.State != EditorBrowsableState.Never)
- .OrderBy(x => x.Name);
- foreach (var p in props)
- {
- indexerModel.ProviderProperties.Add(p.Name, p.GetValue(searcher, null)?.ToString());
- }
-
- return indexerModel;
- }).OrderBy(x =>
- {
- //order by name , but strip the "Searcher" from the end if it exists
- return x.Name.TrimEnd("Searcher");
- }));
+ _examineManager.RegisteredSearchers.Select(searcher => new ExamineSearcherModel { Name = searcher.Name })
+ .OrderBy(x => x.Name.TrimEnd("Searcher"))); //order by name , but strip the "Searcher" from the end if it exists
return model;
}
- public ISearchResults GetSearchResults(string searcherName, string query, string queryType)
+ public SearchResults GetSearchResults(string searcherName, string query, int pageIndex = 0, int pageSize = 20)
{
- if (queryType == null)
- {
- throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
- }
-
if (query.IsNullOrWhiteSpace())
+ return SearchResults.Empty();
+
+ var msg = ValidateSearcher(searcherName, out var searcher);
+ if (!msg.IsSuccessStatusCode)
+ throw new HttpResponseException(msg);
+
+ var results = TryParseLuceneQuery(query)
+ ? searcher.Search(searcher.CreateCriteria().RawQuery(query), maxResults: pageSize * (pageIndex + 1))
+ : searcher.Search(query, true, maxResults: pageSize * (pageIndex + 1));
+
+ var pagedResults = results.Skip(pageIndex * pageSize);
+
+ return new SearchResults
{
- return LuceneSearchResults.Empty();
- }
+ TotalRecords = results.TotalItemCount,
+ Results = pagedResults.Select(x => new SearchResult
+ {
+ Id = x.Id,
+ Score = x.Score,
+ //order the values by key
+ Values = new Dictionary(x.Values.OrderBy(y => y.Key).ToDictionary(y => y.Key, y => y.Value))
+ })
+ };
+ }
- var msg = ValidateLuceneSearcher(searcherName, out var searcher);
- if (msg.IsSuccessStatusCode)
+ private bool TryParseLuceneQuery(string query)
+ {
+ //TODO: I'd assume there would be a more strict way to parse the query but not that i can find yet, for now we'll
+ // also do this rudimentary check
+ if (!query.Contains(":"))
+ return false;
+
+ try
{
- if (queryType.InvariantEquals("text"))
- {
- return searcher.Search(query, false);
- }
-
- if (queryType.InvariantEquals("lucene"))
- {
- return searcher.Search(searcher.CreateCriteria().RawQuery(query));
- }
-
- throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
+ //This will pass with a plain old string without any fields, need to figure out a way to have it properly parse
+ var parsed = new QueryParser(Version.LUCENE_30, "nodeName", new KeywordAnalyzer()).Parse(query);
+ return true;
+ }
+ catch (ParseException)
+ {
+ return false;
+ }
+ catch (Exception)
+ {
+ return false;
}
-
- throw new HttpResponseException(msg);
}
///
/// Check if the index has been rebuilt
///
- ///
+ ///
///
///
/// This is kind of rudimentary since there's no way we can know that the index has rebuilt, we
/// have a listener for the index op complete so we'll just check if that key is no longer there in the runtime cache
///
- public ExamineIndexerModel PostCheckRebuildIndex(string indexerName)
+ public ExamineIndexModel PostCheckRebuildIndex(string indexName)
{
- var msg = ValidateLuceneIndexer(indexerName, out LuceneIndexer indexer);
- if (msg.IsSuccessStatusCode)
- {
- var cacheKey = "temp_indexing_op_" + indexerName;
- var found = ApplicationCache.RuntimeCache.GetCacheItem(cacheKey);
+ var validate = ValidateIndex(indexName, out var index);
+ if (!validate.IsSuccessStatusCode)
+ throw new HttpResponseException(validate);
- //if its still there then it's not done
- return found != null
- ? null
- : CreateModel(new KeyValuePair(indexerName, indexer));
- }
+ validate = ValidatePopulator(indexName);
+ if (!validate.IsSuccessStatusCode)
+ throw new HttpResponseException(validate);
+
+ var cacheKey = "temp_indexing_op_" + indexName;
+ var found = ApplicationCache.RuntimeCache.GetCacheItem(cacheKey);
+
+ //if its still there then it's not done
+ return found != null
+ ? null
+ : CreateModel(index);
- throw new HttpResponseException(msg);
}
///
- /// Rebuilds the index
+ /// Rebuilds the index
///
- ///
+ ///
///
- public HttpResponseMessage PostRebuildIndex(string indexerName)
+ public HttpResponseMessage PostRebuildIndex(string indexName)
{
- var msg = ValidateLuceneIndexer(indexerName, out LuceneIndexer indexer);
- if (msg.IsSuccessStatusCode)
+ var validate = ValidateIndex(indexName, out var index);
+ if (!validate.IsSuccessStatusCode)
+ return validate;
+
+ validate = ValidatePopulator(indexName);
+ if (!validate.IsSuccessStatusCode)
+ return validate;
+
+ _logger.Info("Rebuilding index '{IndexName}'", indexName);
+
+ //remove it in case there's a handler there alraedy
+ index.IndexOperationComplete -= Indexer_IndexOperationComplete;
+
+ //now add a single handler
+ index.IndexOperationComplete += Indexer_IndexOperationComplete;
+
+ try
{
- _logger.Info("Rebuilding index '{IndexerName}'", indexerName);
-
- //remove it in case there's a handler there alraedy
- indexer.IndexOperationComplete -= Indexer_IndexOperationComplete;
-
- //now add a single handler
- indexer.IndexOperationComplete += Indexer_IndexOperationComplete;
-
- var cacheKey = "temp_indexing_op_" + indexer.Name;
+ //clear and replace
+ index.CreateIndex();
+ var cacheKey = "temp_indexing_op_" + index.Name;
//put temp val in cache which is used as a rudimentary way to know when the indexing is done
- ApplicationCache.RuntimeCache.InsertCacheItem(cacheKey, () => "tempValue", TimeSpan.FromMinutes(5),
- false);
+ ApplicationCache.RuntimeCache.InsertCacheItem(cacheKey, () => "tempValue", TimeSpan.FromMinutes(5));
- try
- {
- indexer.RebuildIndex();
- }
- catch (Exception ex)
- {
- //ensure it's not listening
- indexer.IndexOperationComplete -= Indexer_IndexOperationComplete;
- Logger.Error(ex, "An error occurred rebuilding index");
- var response = Request.CreateResponse(HttpStatusCode.Conflict);
- response.Content =
- new
- StringContent($"The index could not be rebuilt at this time, most likely there is another thread currently writing to the index. Error: {ex}");
- response.ReasonPhrase = "Could Not Rebuild";
- return response;
- }
+ _indexRebuilder.RebuildIndex(indexName);
+
+ ////populate it
+ //foreach (var populator in _populators.Where(x => x.IsRegistered(indexName)))
+ // populator.Populate(index);
+
+ return Request.CreateResponse(HttpStatusCode.OK);
+ }
+ catch (Exception ex)
+ {
+ //ensure it's not listening
+ index.IndexOperationComplete -= Indexer_IndexOperationComplete;
+ Logger.Error(ex, "An error occurred rebuilding index");
+ var response = Request.CreateResponse(HttpStatusCode.Conflict);
+ response.Content =
+ new
+ StringContent($"The index could not be rebuilt at this time, most likely there is another thread currently writing to the index. Error: {ex}");
+ response.ReasonPhrase = "Could Not Rebuild";
+ return response;
}
-
- return msg;
}
- private ExamineIndexerModel CreateModel(KeyValuePair indexerKeyVal)
+
+
+ private ExamineIndexModel CreateModel(IIndex index)
{
- var indexer = indexerKeyVal.Value;
- var indexName = indexerKeyVal.Key;
- var indexerModel = new ExamineIndexerModel
+ var indexName = index.Name;
+
+ if (!(index is IIndexDiagnostics indexDiag))
+ indexDiag = new GenericIndexDiagnostics(index);
+
+
+ var isHealth = indexDiag.IsHealthy();
+ var properties = new Dictionary
{
- FieldDefinitions = indexer.FieldDefinitionCollection,
- Name = indexName
+ [nameof(IIndexDiagnostics.DocumentCount)] = indexDiag.DocumentCount,
+ [nameof(IIndexDiagnostics.FieldCount)] = indexDiag.FieldCount,
+ };
+ foreach (var p in indexDiag.Metadata)
+ properties[p.Key] = p.Value;
+
+ var indexerModel = new ExamineIndexModel
+ {
+ Name = indexName,
+ HealthStatus = isHealth.Success ? (isHealth.Result ?? "Healthy") : (isHealth.Result ?? "Unhealthy"),
+ ProviderProperties = properties,
+ CanRebuild = _indexRebuilder.CanRebuild(indexName)
};
- var props = TypeHelper.CachedDiscoverableProperties(indexer.GetType(), mustWrite: false)
- //ignore these properties
- .Where(x => new[] { "IndexerData", "Description", "WorkingFolder" }
- .InvariantContains(x.Name) == false)
- .OrderBy(x => x.Name);
-
- foreach (var p in props)
- {
- var val = p.GetValue(indexer, null);
- if (val == null)
- {
- // Do not warn for new new attribute that is optional
- if (string.Equals(p.Name, "DirectoryFactory", StringComparison.InvariantCultureIgnoreCase) == false)
- {
- Logger
- .Warn("Property value was null when setting up property on indexer: " + indexName +
- " property: " + p.Name);
- }
-
- val = string.Empty;
- }
-
- indexerModel.ProviderProperties.Add(p.Name, val.ToString());
- }
-
- if (indexer is LuceneIndexer luceneIndexer)
- {
- indexerModel.IsLuceneIndex = true;
-
- if (luceneIndexer.IndexExists())
- {
- indexerModel.IsHealthy = luceneIndexer.IsHealthy(out var indexError);
-
- if (indexerModel.IsHealthy == false)
- {
- //we cannot continue at this point
- indexerModel.Error = indexError.ToString();
- return indexerModel;
- }
-
- indexerModel.DocumentCount = luceneIndexer.GetIndexDocumentCount();
- indexerModel.FieldCount = luceneIndexer.GetIndexFieldCount();
- }
- else
- {
- indexerModel.DocumentCount = 0;
- indexerModel.FieldCount = 0;
- }
- }
return indexerModel;
}
- private HttpResponseMessage ValidateLuceneSearcher(string searcherName, out LuceneSearcher searcher)
+ private HttpResponseMessage ValidateSearcher(string searcherName, out ISearcher searcher)
{
- foreach (var indexer in _examineManager.IndexProviders)
+ //try to get the searcher from the indexes
+ if (_examineManager.TryGetIndex(searcherName, out var index))
{
- var s = indexer.Value.GetSearcher();
- var sName = (s as BaseLuceneSearcher)?.Name ?? string.Concat(indexer.Key, "Searcher");
- if (sName != searcherName)
- {
- continue;
- }
-
- searcher = s as LuceneSearcher;
-
- //Found it, return OK
- if (searcher != null)
- {
- return Request.CreateResponse(HttpStatusCode.OK);
- }
-
- //Return an error since it's not the right type
- var response = Request.CreateResponse(HttpStatusCode.BadRequest);
- response.Content =
- new StringContent($"The searcher {searcherName} is not of type {typeof(LuceneSearcher)}");
- response.ReasonPhrase = "Wrong Searcher Type";
- return response;
+ searcher = index.GetSearcher();
+ return Request.CreateResponse(HttpStatusCode.OK);
}
- searcher = null;
+
+ //if we didn't find anything try to find it by an explicitly declared searcher
+ if (_examineManager.TryGetSearcher(searcherName, out searcher))
+ return Request.CreateResponse(HttpStatusCode.OK);
var response1 = Request.CreateResponse(HttpStatusCode.BadRequest);
response1.Content = new StringContent($"No searcher found with name = {searcherName}");
@@ -283,29 +249,38 @@ namespace Umbraco.Web.Editors
return response1;
}
- private HttpResponseMessage ValidateLuceneIndexer(string indexerName, out T indexer)
- where T : class, IIndexer
+ private HttpResponseMessage ValidatePopulator(string indexName)
{
- indexer = null;
+ if (_indexRebuilder.CanRebuild(indexName))
+ return Request.CreateResponse(HttpStatusCode.OK);
- if (_examineManager.IndexProviders.ContainsKey(indexerName)
- && _examineManager.IndexProviders[indexerName] is T casted)
+ var response = Request.CreateResponse(HttpStatusCode.BadRequest);
+ response.Content = new StringContent($"The index {indexName} cannot be rebuilt because it does not have an associated {typeof(IIndexPopulator)}");
+ response.ReasonPhrase = "Index cannot be rebuilt";
+ return response;
+ }
+
+ private HttpResponseMessage ValidateIndex(string indexName, out IIndex index)
+ {
+ index = null;
+
+ if (_examineManager.TryGetIndex(indexName, out index))
{
//return Ok!
- indexer = casted;
return Request.CreateResponse(HttpStatusCode.OK);
}
var response = Request.CreateResponse(HttpStatusCode.BadRequest);
- response.Content = new StringContent($"No indexer found with name = {indexerName} of type {typeof(T)}");
- response.ReasonPhrase = "Indexer Not Found";
+ response.Content = new StringContent($"No index found with name = {indexName}");
+ response.ReasonPhrase = "Index Not Found";
return response;
}
- //static listener so it's not GC'd
private void Indexer_IndexOperationComplete(object sender, EventArgs e)
{
- var indexer = (LuceneIndexer) sender;
+ var indexer = (LuceneIndex)sender;
+
+ _logger.Debug("Logging operation completed for index {IndexName}", indexer.Name);
//ensure it's not listening anymore
indexer.IndexOperationComplete -= Indexer_IndexOperationComplete;
diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs
index f614d38ecb..0d0438138a 100644
--- a/src/Umbraco.Web/Editors/TemplateQueryController.cs
+++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs
@@ -59,8 +59,7 @@ namespace Umbraco.Web.Editors
public QueryResultModel PostTemplateQuery(QueryModel model)
{
- var umbraco = new UmbracoHelper(UmbracoContext, Services, ApplicationCache);
-
+
var queryResult = new QueryResultModel();
var sb = new StringBuilder();
@@ -72,7 +71,7 @@ namespace Umbraco.Web.Editors
timer.Start();
- var currentPage = umbraco.ContentAtRoot().FirstOrDefault();
+ var currentPage = Umbraco.ContentAtRoot().FirstOrDefault();
timer.Stop();
var pointerNode = currentPage;
@@ -80,7 +79,7 @@ namespace Umbraco.Web.Editors
// adjust the "FROM"
if (model != null && model.Source != null && model.Source.Id > 0)
{
- var targetNode = umbraco.Content(model.Source.Id);
+ var targetNode = Umbraco.Content(model.Source.Id);
if (targetNode != null)
{
diff --git a/src/Umbraco.Web/ExamineExtensions.cs b/src/Umbraco.Web/ExamineExtensions.cs
index fa5ce64426..f1ed6c0659 100644
--- a/src/Umbraco.Web/ExamineExtensions.cs
+++ b/src/Umbraco.Web/ExamineExtensions.cs
@@ -12,7 +12,7 @@ namespace Umbraco.Web
///
public static class ExamineExtensions
{
- public static IEnumerable ToPublishedSearchResults(this IEnumerable results, IPublishedCache cache)
+ public static IEnumerable ToPublishedSearchResults(this IEnumerable results, IPublishedCache cache)
{
var list = new List();
diff --git a/src/Umbraco.Web/ExamineStartup.cs b/src/Umbraco.Web/ExamineStartup.cs
deleted file mode 100644
index f3a8bc3ef5..0000000000
--- a/src/Umbraco.Web/ExamineStartup.cs
+++ /dev/null
@@ -1,212 +0,0 @@
-// fixme - Examine is borked
-// this cannot compile because it seems to require some changes that are not in Examine v2
-// this should *not* exist, see ExamineComponent instead, which handles everything about Examine
-
-//using System;
-//using System.Collections.Generic;
-//using System.Linq;
-//using Examine;
-//using Examine.Config;
-//using Examine.LuceneEngine;
-//using Examine.LuceneEngine.Providers;
-//using Examine.Providers;
-//using Lucene.Net.Index;
-//using Lucene.Net.Store;
-//using Umbraco.Core;
-//using Umbraco.Core.Logging;
-//using UmbracoExamine;
-
-//namespace Umbraco.Web
-//{
-// ///
-// /// Used to configure Examine during startup for the web application
-// ///
-// internal class ExamineStartup
-// {
-// private readonly List _indexesToRebuild = new List();
-// private readonly ApplicationContext _appCtx;
-// private readonly ProfilingLogger _profilingLogger;
-// private static bool _isConfigured = false;
-
-// //this is used if we are not the MainDom, in which case we need to ensure that if indexes need rebuilding that this
-// //doesn't occur since that should only occur when we are MainDom
-// private bool _disableExamineIndexing = false;
-
-// public ExamineStartup(ApplicationContext appCtx)
-// {
-// _appCtx = appCtx;
-// _profilingLogger = appCtx.ProfilingLogger;
-// }
-
-// ///
-// /// Called during the initialize operation of the boot manager process
-// ///
-// public void Initialize()
-// {
-// //We want to manage Examine's appdomain shutdown sequence ourselves so first we'll disable Examine's default behavior
-// //and then we'll use MainDom to control Examine's shutdown
-// ExamineManager.DisableDefaultHostingEnvironmentRegistration();
-
-// //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the appdomain
-// //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock
-// //which simply checks the existence of the lock file
-// DirectoryTracker.DefaultLockFactory = d =>
-// {
-// var simpleFsLockFactory = new NoPrefixSimpleFsLockFactory(d);
-// return simpleFsLockFactory;
-// };
-
-// //This is basically a hack for this item: http://issues.umbraco.org/issue/U4-5976
-// // when Examine initializes it will try to rebuild if the indexes are empty, however in many cases not all of Examine's
-// // event handlers will be assigned during bootup when the rebuilding starts which is a problem. So with the examine 0.1.58.2941 build
-// // it has an event we can subscribe to in order to cancel this rebuilding process, but what we'll do is cancel it and postpone the rebuilding until the
-// // boot process has completed. It's a hack but it works.
-// ExamineManager.Instance.BuildingEmptyIndexOnStartup += OnInstanceOnBuildingEmptyIndexOnStartup;
-
-// //let's deal with shutting down Examine with MainDom
-// var examineShutdownRegistered = _appCtx.MainDom.Register(() =>
-// {
-// using (_profilingLogger.TraceDuration("Examine shutting down"))
-// {
-// //Due to the way Examine's own IRegisteredObject works, we'll first run it with immediate=false and then true so that
-// //it's correct subroutines are executed (otherwise we'd have to run this logic manually ourselves)
-// ExamineManager.Instance.Stop(false);
-// ExamineManager.Instance.Stop(true);
-// }
-// });
-// if (examineShutdownRegistered)
-// {
-// _profilingLogger.Logger.Debug("Examine shutdown registered with MainDom");
-// }
-// else
-// {
-// _profilingLogger.Logger.Debug("Examine shutdown not registered, this appdomain is not the MainDom");
-
-// //if we could not register the shutdown examine ourselves, it means we are not maindom! in this case all of examine should be disabled
-// //from indexing anything on startup!!
-// _disableExamineIndexing = true;
-// Suspendable.ExamineEvents.SuspendIndexers();
-// }
-// }
-
-// ///
-// /// Called during the Complete operation of the boot manager process
-// ///
-// public void Complete()
-// {
-// EnsureUnlockedAndConfigured();
-
-// //Ok, now that everything is complete we'll check if we've stored any references to index that need rebuilding and run them
-// // (see the initialize method for notes) - we'll ensure we remove the event handler too in case examine manager doesn't actually
-// // initialize during startup, in which case we want it to rebuild the indexes itself.
-// ExamineManager.Instance.BuildingEmptyIndexOnStartup -= OnInstanceOnBuildingEmptyIndexOnStartup;
-
-// //don't do anything if we have disabled this
-// if (_disableExamineIndexing == false)
-// {
-// foreach (var indexer in _indexesToRebuild)
-// {
-// indexer.RebuildIndex();
-// }
-// }
-// }
-
-// ///
-// /// Called to perform the rebuilding indexes on startup if the indexes don't exist
-// ///
-// public void RebuildIndexes()
-// {
-// //don't do anything if we have disabled this
-// if (_disableExamineIndexing) return;
-
-// EnsureUnlockedAndConfigured();
-
-// //If the developer has explicitly opted out of rebuilding indexes on startup then we
-// // should adhere to that and not do it, this means that if they are load balancing things will be
-// // out of sync if they are auto-scaling but there's not much we can do about that.
-// if (ExamineSettings.Instance.RebuildOnAppStart == false) return;
-
-// foreach (var indexer in GetIndexesForColdBoot())
-// {
-// indexer.RebuildIndex();
-// }
-// }
-
-// ///
-// /// The method used to create indexes on a cold boot
-// ///
-// ///
-// /// A cold boot is when the server determines it will not (or cannot) process instructions in the cache table and
-// /// will rebuild it's own caches itself.
-// ///
-// public IEnumerable GetIndexesForColdBoot()
-// {
-// // NOTE: This is IMPORTANT! ... we don't want to rebuild any index that is already flagged to be re-indexed
-// // on startup based on our _indexesToRebuild variable and how Examine auto-rebuilds when indexes are empty.
-// // This callback is used above for the DatabaseServerMessenger startup options.
-
-// // all indexes
-// IEnumerable indexes = ExamineManager.Instance.IndexProviderCollection;
-
-// // except those that are already flagged
-// // and are processed in Complete()
-// if (_indexesToRebuild.Any())
-// indexes = indexes.Except(_indexesToRebuild);
-
-// // return
-// foreach (var index in indexes)
-// yield return index;
-// }
-
-// ///
-// /// Must be called to configure each index and ensure it's unlocked before any indexing occurs
-// ///
-// ///
-// /// Indexing rebuilding can occur on a normal boot if the indexes are empty or on a cold boot by the database server messenger. Before
-// /// either of these happens, we need to configure the indexes.
-// ///
-// private void EnsureUnlockedAndConfigured()
-// {
-// if (_isConfigured) return;
-
-// _isConfigured = true;
-
-// foreach (var luceneIndexer in ExamineManager.Instance.IndexProviderCollection.OfType())
-// {
-// //We now need to disable waiting for indexing for Examine so that the appdomain is shutdown immediately and doesn't wait for pending
-// //indexing operations. We used to wait for indexing operations to complete but this can cause more problems than that is worth because
-// //that could end up halting shutdown for a very long time causing overlapping appdomains and many other problems.
-// luceneIndexer.WaitForIndexQueueOnShutdown = false;
-
-// //we should check if the index is locked ... it shouldn't be! We are using simple fs lock now and we are also ensuring that
-// //the indexes are not operational unless MainDom is true so if _disableExamineIndexing is false then we should be in charge
-// if (_disableExamineIndexing == false)
-// {
-// var dir = luceneIndexer.GetLuceneDirectory();
-// if (IndexWriter.IsLocked(dir))
-// {
-// _profilingLogger.Logger.Info("Forcing index " + luceneIndexer.IndexSetName + " to be unlocked since it was left in a locked state");
-// IndexWriter.Unlock(dir);
-// }
-// }
-// }
-// }
-
-// private void OnInstanceOnBuildingEmptyIndexOnStartup(object sender, BuildingEmptyIndexOnStartupEventArgs args)
-// {
-// //store the indexer that needs rebuilding because it's empty for when the boot process
-// // is complete and cancel this current event so the rebuild process doesn't start right now.
-// args.Cancel = true;
-// _indexesToRebuild.Add((BaseIndexProvider)args.Indexer);
-
-// //check if the index is rebuilding due to an error and log it
-// if (args.IsHealthy == false)
-// {
-// var baseIndex = args.Indexer as BaseIndexProvider;
-// var name = baseIndex != null ? baseIndex.Name : "[UKNOWN]";
-
-// _profilingLogger.Logger.Error(string.Format("The index {0} is rebuilding due to being unreadable/corrupt", name), args.UnhealthyException);
-// }
-// }
-// }
-//}
diff --git a/src/Umbraco.Web/IPublishedContentQuery.cs b/src/Umbraco.Web/IPublishedContentQuery.cs
index 055dcbe181..3ab1c5e3ae 100644
--- a/src/Umbraco.Web/IPublishedContentQuery.cs
+++ b/src/Umbraco.Web/IPublishedContentQuery.cs
@@ -39,7 +39,7 @@ namespace Umbraco.Web
///
/// Searches content.
///
- IEnumerable Search(int skip, int take, out int totalRecords, string term, bool useWildCards = true, string indexName = null);
+ IEnumerable Search(int skip, int take, out long totalRecords, string term, bool useWildCards = true, string indexName = null);
///
/// Searches content.
@@ -49,6 +49,6 @@ namespace Umbraco.Web
///
/// Searches content.
///
- IEnumerable Search(int skip, int take, out int totalRecords, Examine.SearchCriteria.ISearchCriteria criteria, Examine.ISearcher searchProvider = null);
+ IEnumerable Search(int skip, int take, out long totalRecords, Examine.SearchCriteria.ISearchCriteria criteria, Examine.ISearcher searcher = null);
}
}
diff --git a/src/Umbraco.Web/Models/ContentEditing/SearchResult.cs b/src/Umbraco.Web/Models/ContentEditing/SearchResult.cs
new file mode 100644
index 0000000000..1cdd539165
--- /dev/null
+++ b/src/Umbraco.Web/Models/ContentEditing/SearchResult.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Umbraco.Web.Models.ContentEditing
+{
+ [DataContract(Name = "result", Namespace = "")]
+ public class SearchResult
+ {
+ [DataMember(Name = "id")]
+ public string Id { get; set; }
+
+ [DataMember(Name = "score")]
+ public float Score { get; set; }
+
+ [DataMember(Name = "fieldCount")]
+ public int FieldCount => Values?.Count ?? 0;
+
+ [DataMember(Name = "values")]
+ public IReadOnlyDictionary Values { get; set; }
+ }
+}
diff --git a/src/Umbraco.Web/Models/ContentEditing/SearchResultItem.cs b/src/Umbraco.Web/Models/ContentEditing/SearchResultEntity.cs
similarity index 86%
rename from src/Umbraco.Web/Models/ContentEditing/SearchResultItem.cs
rename to src/Umbraco.Web/Models/ContentEditing/SearchResultEntity.cs
index 5ba89806d0..a1cca9763d 100644
--- a/src/Umbraco.Web/Models/ContentEditing/SearchResultItem.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/SearchResultEntity.cs
@@ -3,7 +3,7 @@
namespace Umbraco.Web.Models.ContentEditing
{
[DataContract(Name = "searchResult", Namespace = "")]
- public class SearchResultItem : EntityBasic
+ public class SearchResultEntity : EntityBasic
{
///
/// The score of the search result
diff --git a/src/Umbraco.Web/Models/ContentEditing/SearchResults.cs b/src/Umbraco.Web/Models/ContentEditing/SearchResults.cs
new file mode 100644
index 0000000000..f791d55cab
--- /dev/null
+++ b/src/Umbraco.Web/Models/ContentEditing/SearchResults.cs
@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+
+namespace Umbraco.Web.Models.ContentEditing
+{
+ [DataContract(Name = "results", Namespace = "")]
+ public class SearchResults
+ {
+ public static SearchResults Empty() => new SearchResults
+ {
+ Results = Enumerable.Empty(),
+ TotalRecords = 0
+ };
+
+ [DataMember(Name = "totalRecords")]
+ public long TotalRecords { get; set; }
+
+ [DataMember(Name = "results")]
+ public IEnumerable Results { get; set; }
+ }
+}
diff --git a/src/Umbraco.Web/Models/ContentEditing/TreeSearchResult.cs b/src/Umbraco.Web/Models/ContentEditing/TreeSearchResult.cs
index 964ed62c88..22c177190d 100644
--- a/src/Umbraco.Web/Models/ContentEditing/TreeSearchResult.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/TreeSearchResult.cs
@@ -30,6 +30,6 @@ namespace Umbraco.Web.Models.ContentEditing
public string JsFormatterMethod { get; set; }
[DataMember(Name = "results")]
- public IEnumerable Results { get; set; }
+ public IEnumerable Results { get; set; }
}
}
diff --git a/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs
index bfabfd799a..3e42178fbd 100644
--- a/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs
+++ b/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs
@@ -84,7 +84,7 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore());
- CreateMap()
+ CreateMap()
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(ObjectTypes.GetUdiType(src.NodeObjectType), src.Key)))
.ForMember(dest => dest.Icon, opt => opt.MapFrom(src=> GetContentTypeIcon(src)))
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
@@ -107,7 +107,7 @@ namespace Umbraco.Web.Models.Mapping
}
});
- CreateMap()
+ CreateMap()
//default to document icon
.ForMember(dest => dest.Score, opt => opt.MapFrom(result => result.Score))
.ForMember(dest => dest.Udi, opt => opt.Ignore())
@@ -123,22 +123,22 @@ namespace Umbraco.Web.Models.Mapping
.AfterMap((src, dest) =>
{
//get the icon if there is one
- dest.Icon = src.Fields.ContainsKey(UmbracoExamineIndexer.IconFieldName)
- ? src.Fields[UmbracoExamineIndexer.IconFieldName]
+ dest.Icon = src.Values.ContainsKey(UmbracoExamineIndexer.IconFieldName)
+ ? src.Values[UmbracoExamineIndexer.IconFieldName]
: "icon-document";
- dest.Name = src.Fields.ContainsKey("nodeName") ? src.Fields["nodeName"] : "[no name]";
- if (src.Fields.ContainsKey(UmbracoExamineIndexer.NodeKeyFieldName))
+ dest.Name = src.Values.ContainsKey("nodeName") ? src.Values["nodeName"] : "[no name]";
+ if (src.Values.ContainsKey(UmbracoExamineIndexer.NodeKeyFieldName))
{
Guid key;
- if (Guid.TryParse(src.Fields[UmbracoExamineIndexer.NodeKeyFieldName], out key))
+ if (Guid.TryParse(src.Values[UmbracoExamineIndexer.NodeKeyFieldName], out key))
{
dest.Key = key;
//need to set the UDI
- if (src.Fields.ContainsKey(LuceneIndexer.CategoryFieldName))
+ if (src.Values.ContainsKey(LuceneIndex.CategoryFieldName))
{
- switch (src.Fields[LuceneIndexer.CategoryFieldName])
+ switch (src.Values[LuceneIndex.CategoryFieldName])
{
case IndexTypes.Member:
dest.Udi = new GuidUdi(Constants.UdiEntityType.Member, dest.Key);
@@ -154,10 +154,10 @@ namespace Umbraco.Web.Models.Mapping
}
}
- if (src.Fields.ContainsKey("parentID"))
+ if (src.Values.ContainsKey("parentID"))
{
int parentId;
- if (int.TryParse(src.Fields["parentID"], out parentId))
+ if (int.TryParse(src.Values["parentID"], out parentId))
{
dest.ParentId = parentId;
}
@@ -166,19 +166,19 @@ namespace Umbraco.Web.Models.Mapping
dest.ParentId = -1;
}
}
- dest.Path = src.Fields.ContainsKey(UmbracoExamineIndexer.IndexPathFieldName) ? src.Fields[UmbracoExamineIndexer.IndexPathFieldName] : "";
+ dest.Path = src.Values.ContainsKey(UmbracoExamineIndexer.IndexPathFieldName) ? src.Values[UmbracoExamineIndexer.IndexPathFieldName] : "";
- if (src.Fields.ContainsKey(LuceneIndexer.ItemTypeFieldName))
+ if (src.Values.ContainsKey(LuceneIndex.ItemTypeFieldName))
{
- dest.AdditionalData.Add("contentType", src.Fields[LuceneIndexer.ItemTypeFieldName]);
+ dest.AdditionalData.Add("contentType", src.Values[LuceneIndex.ItemTypeFieldName]);
}
});
- CreateMap>()
- .ConvertUsing(results => results.Select(Mapper.Map).ToList());
+ CreateMap>()
+ .ConvertUsing(results => results.Select(Mapper.Map).ToList());
- CreateMap, IEnumerable>()
- .ConvertUsing(results => results.Select(Mapper.Map).ToList());
+ CreateMap, IEnumerable>()
+ .ConvertUsing(results => results.Select(Mapper.Map).ToList());
}
///
diff --git a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs
index 163284cf86..56d00e8ec9 100644
--- a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs
+++ b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs
@@ -80,7 +80,7 @@ namespace Umbraco.Web.Mvc
/// Exposes an UmbracoHelper
///
protected UmbracoHelper Umbraco => _helper
- ?? (_helper = new UmbracoHelper(Current.UmbracoContext, Current.Services, Current.ApplicationCache));
+ ?? (_helper = new UmbracoHelper(Current.UmbracoContext, Current.Services));
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
diff --git a/src/Umbraco.Web/Mvc/PluginController.cs b/src/Umbraco.Web/Mvc/PluginController.cs
index f2742ce9f0..2b4ebc3f71 100644
--- a/src/Umbraco.Web/Mvc/PluginController.cs
+++ b/src/Umbraco.Web/Mvc/PluginController.cs
@@ -82,7 +82,7 @@ namespace Umbraco.Web.Mvc
get
{
return _umbracoHelper
- ?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services, ApplicationCache));
+ ?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services));
}
internal set // tests
{
diff --git a/src/Umbraco.Web/Mvc/UmbracoController.cs b/src/Umbraco.Web/Mvc/UmbracoController.cs
index 563b315de5..b8b55ae35d 100644
--- a/src/Umbraco.Web/Mvc/UmbracoController.cs
+++ b/src/Umbraco.Web/Mvc/UmbracoController.cs
@@ -83,7 +83,7 @@ namespace Umbraco.Web.Mvc
/// Gets the Umbraco helper.
///
public UmbracoHelper Umbraco => _umbracoHelper
- ?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services, ApplicationCache));
+ ?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services));
///
/// Gets the web security helper.
diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs
index 6fa6750031..bd1dd1ed6b 100644
--- a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs
+++ b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs
@@ -98,8 +98,8 @@ namespace Umbraco.Web.Mvc
if (content == null && model is IContentModel)
content = ((IContentModel) model).Content;
_helper = content == null
- ? new UmbracoHelper(UmbracoContext, Services, ApplicationCache)
- : new UmbracoHelper(UmbracoContext, Services, ApplicationCache, content);
+ ? new UmbracoHelper(UmbracoContext, Services)
+ : new UmbracoHelper(UmbracoContext, Services, content);
return _helper;
}
}
diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs
index 0f3211b743..99972a27cf 100644
--- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs
+++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs
@@ -1,15 +1,9 @@
-using System;
-using System.Linq;
-using System.Text;
+using System.Linq;
using Umbraco.Core.Logging;
using Examine;
using Lucene.Net.Documents;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using Umbraco.Core;
using Umbraco.Core.PropertyEditors;
-using Umbraco.Core.Xml;
-using Umbraco.Examine;
namespace Umbraco.Web.PropertyEditors
{
@@ -25,84 +19,7 @@ namespace Umbraco.Web.PropertyEditors
: base(logger)
{ }
- internal void DocumentWriting(object sender, Examine.LuceneEngine.DocumentWritingEventArgs e)
- {
- foreach (var value in e.ValueSet.Values)
- {
- //if there is a value, it's a string and it's detected as json
- if (value.Value.Count > 0 && value.Value[0] != null && (value.Value[0] is string firstVal) && firstVal.DetectIsJson())
- {
- try
- {
- //TODO: We should deserialize this to Umbraco.Core.Models.GridValue instead of doing the below
- var json = JsonConvert.DeserializeObject(firstVal);
-
- //check if this is formatted for grid json
- if (json.HasValues && json.TryGetValue("name", out _) && json.TryGetValue("sections", out _))
- {
- //get all values and put them into a single field (using JsonPath)
- var sb = new StringBuilder();
- foreach (var row in json.SelectTokens("$.sections[*].rows[*]"))
- {
- var rowName = row["name"].Value();
- var areaVals = row.SelectTokens("$.areas[*].controls[*].value");
-
- foreach (var areaVal in areaVals)
- {
- //TODO: If it's not a string, then it's a json formatted value -
- // we cannot really index this in a smart way since it could be 'anything'
- if (areaVal.Type == JTokenType.String)
- {
- var str = areaVal.Value();
- str = XmlHelper.CouldItBeXml(str) ? str.StripHtml() : str;
- sb.Append(str);
- sb.Append(" ");
-
- //add the row name as an individual field
- e.Document.Add(
- new Field(
- $"{value.Key}.{rowName}", str, Field.Store.YES, Field.Index.ANALYZED));
- }
-
- }
- }
-
- if (sb.Length > 0)
- {
- //First save the raw value to a raw field
- e.Document.Add(
- new Field(
- $"{UmbracoExamineIndexer.RawFieldPrefix}{value.Key}",
- firstVal, Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO));
-
- //now replace the original value with the combined/cleaned value
- e.Document.RemoveField(value.Key);
- e.Document.Add(
- new Field(
- value.Key,
- sb.ToString(), Field.Store.YES, Field.Index.ANALYZED));
- }
- }
- }
- catch (InvalidCastException)
- {
- //swallow...on purpose, there's a chance that this isn't the json format we are looking for
- // and we don't want that to affect the website.
- }
- catch (JsonException)
- {
- //swallow...on purpose, there's a chance that this isn't json and we don't want that to affect
- // the website.
- }
- catch (ArgumentException)
- {
- //swallow on purpose to prevent this error:
- // Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.
- }
- }
-
- }
- }
+ public override IPropertyIndexValueFactory PropertyIndexValueFactory => new GridPropertyIndexValueFactory();
///
/// Overridden to ensure that the value is validated
diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValueFactory.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValueFactory.cs
new file mode 100644
index 0000000000..c8235d1d11
--- /dev/null
+++ b/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValueFactory.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Text;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Umbraco.Core;
+using Umbraco.Core.PropertyEditors;
+using Umbraco.Core.Xml;
+using Umbraco.Examine;
+
+namespace Umbraco.Web.PropertyEditors
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using Umbraco.Core.Models;
+
+ ///
+ /// Parses the grid value into indexable values
+ ///
+ public class GridPropertyIndexValueFactory : IPropertyIndexValueFactory
+ {
+ public IEnumerable>> GetIndexValues(Property property, string culture, string segment, bool published)
+ {
+ var result = new List>>();
+
+ var val = property.GetValue(culture, segment, published);
+
+ //if there is a value, it's a string and it's detected as json
+ if (val is string rawVal && rawVal.DetectIsJson())
+ {
+ try
+ {
+ var gridVal = JsonConvert.DeserializeObject(rawVal);
+
+ //get all values and put them into a single field (using JsonPath)
+ var sb = new StringBuilder();
+ foreach (var row in gridVal.Sections.SelectMany(x => x.Rows))
+ {
+ var rowName = row.Name;
+
+ foreach (var control in row.Areas.SelectMany(x => x.Controls))
+ {
+ var controlVal = control.Value;
+
+ //TODO: If it's not a string, then it's a json formatted value -
+ // we cannot really index this in a smart way since it could be 'anything'
+ if (controlVal.Type == JTokenType.String)
+ {
+ var str = controlVal.Value();
+ str = XmlHelper.CouldItBeXml(str) ? str.StripHtml() : str;
+ sb.Append(str);
+ sb.Append(" ");
+
+ //add the row name as an individual field
+ result.Add(new KeyValuePair>($"{property.Alias}.{rowName}", new[] { str }));
+ }
+ }
+ }
+
+ if (sb.Length > 0)
+ {
+ //First save the raw value to a raw field
+ result.Add(new KeyValuePair>($"{UmbracoExamineIndexer.RawFieldPrefix}{property.Alias}", new[] { rawVal }));
+
+ //index the property with the combined/cleaned value
+ result.Add(new KeyValuePair>(property.Alias, new[] { sb.ToString() }));
+ }
+ }
+ catch (InvalidCastException)
+ {
+ //swallow...on purpose, there's a chance that this isn't the json format we are looking for
+ // and we don't want that to affect the website.
+ }
+ catch (JsonException)
+ {
+ //swallow...on purpose, there's a chance that this isn't json and we don't want that to affect
+ // the website.
+ }
+ catch (ArgumentException)
+ {
+ //swallow on purpose to prevent this error:
+ // Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs
index f7d886f637..07c5aac5bb 100644
--- a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs
+++ b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs
@@ -42,6 +42,8 @@ namespace Umbraco.Web.PropertyEditors
: Current.Services.ContentTypeService.Get(contentTypeAlias);
}
+ //fixme: Need to add a custom IPropertyIndexValueFactory for this editor
+
#region Pre Value Editor
protected override IConfigurationEditor CreateConfigurationEditor() => new NestedContentConfigurationEditor();
diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs
index 0b091e0d47..2deb8b8444 100644
--- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs
+++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs
@@ -6,6 +6,7 @@ using Umbraco.Core.Macros;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
+using Umbraco.Examine;
using Umbraco.Web.Macros;
namespace Umbraco.Web.PropertyEditors
@@ -31,6 +32,7 @@ namespace Umbraco.Web.PropertyEditors
protected override IConfigurationEditor CreateConfigurationEditor() => new RichTextConfigurationEditor();
+ public override IPropertyIndexValueFactory PropertyIndexValueFactory => new RichTextPropertyIndexValueFactory();
///
/// A custom value editor to ensure that macro syntax is parsed when being persisted and formatted correctly for display in the editor
@@ -90,6 +92,21 @@ namespace Umbraco.Web.PropertyEditors
return parsed;
}
}
+
+ internal class RichTextPropertyIndexValueFactory : IPropertyIndexValueFactory
+ {
+ public IEnumerable>> GetIndexValues(Property property, string culture, string segment, bool published)
+ {
+ var val = property.GetValue(culture, segment, published);
+
+ if (!(val is string strVal)) yield break;
+
+ //index the stripped html values
+ yield return new KeyValuePair>(property.Alias, new object[] { strVal.StripHtml() });
+ //store the raw value
+ yield return new KeyValuePair>($"{UmbracoExamineIndexer.RawFieldPrefix}{property.Alias}", new object[] { strVal });
+ }
+ }
}
diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs
index cd2f4e5384..5a0fc87cb0 100644
--- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs
+++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs
@@ -21,13 +21,11 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
{
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
private readonly ServiceContext _services;
- private readonly CacheHelper _appCache;
- public MacroContainerValueConverter(IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services, CacheHelper appCache)
+ public MacroContainerValueConverter(IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services)
{
_umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
_services = services ?? throw new ArgumentNullException(nameof(services));
- _appCache = appCache ?? throw new ArgumentNullException(nameof(appCache));
}
public override bool IsConverter(PublishedPropertyType propertyType)
@@ -50,7 +48,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
{
var sb = new StringBuilder();
- var umbracoHelper = new UmbracoHelper(umbracoContext, _services, _appCache);
+ var umbracoHelper = new UmbracoHelper(umbracoContext, _services);
MacroTagParser.ParseMacros(
source,
//callback for when text block is found
diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksLegacyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksLegacyValueConverter.cs
index 407ea481c4..6c2a4331d0 100644
--- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksLegacyValueConverter.cs
+++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksLegacyValueConverter.cs
@@ -24,13 +24,11 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
private readonly ILogger _logger;
private readonly ServiceContext _services;
- private readonly CacheHelper _appCache;
- public RelatedLinksLegacyValueConverter(IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services, CacheHelper appCache, ILogger logger)
+ public RelatedLinksLegacyValueConverter(IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services, ILogger logger)
{
_umbracoContextAccessor = umbracoContextAccessor;
_services = services;
- _appCache = appCache;
_logger = logger;
}
@@ -56,7 +54,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
//update the internal links if we have a context
if (UmbracoContext.Current != null)
{
- var helper = new UmbracoHelper(_umbracoContextAccessor.UmbracoContext, _services, _appCache);
+ var helper = new UmbracoHelper(_umbracoContextAccessor.UmbracoContext, _services);
foreach (var a in obj)
{
var type = a.Value("type");
diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs
index 74a5fb313c..7a089b6f9e 100644
--- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs
+++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs
@@ -22,7 +22,6 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
{
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
private readonly ServiceContext _services;
- private readonly CacheHelper _appCache;
public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType)
{
@@ -31,11 +30,10 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
return PropertyCacheLevel.Snapshot;
}
- public RteMacroRenderingValueConverter(IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services, CacheHelper appCache)
+ public RteMacroRenderingValueConverter(IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services)
{
_umbracoContextAccessor = umbracoContextAccessor;
_services = services;
- _appCache = appCache;
}
// NOT thread-safe over a request because it modifies the
@@ -49,7 +47,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
{
var sb = new StringBuilder();
- var umbracoHelper = new UmbracoHelper(umbracoContext, _services, _appCache);
+ var umbracoHelper = new UmbracoHelper(umbracoContext, _services);
MacroTagParser.ParseMacros(
source,
//callback for when text block is found
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs
index 49e0d1e9d2..7a8ce65ae3 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs
@@ -57,7 +57,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
ValidateAndSetProperty(valueDictionary, val => _sortOrder = Int32.Parse(val), "sortOrder");
ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName", "__nodeName");
ValidateAndSetProperty(valueDictionary, val => _urlName = val, "urlName");
- ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", LuceneIndexer.ItemTypeFieldName);
+ ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", LuceneIndex.ItemTypeFieldName);
ValidateAndSetProperty(valueDictionary, val => _documentTypeId = Int32.Parse(val), "nodeType");
//ValidateAndSetProperty(valueDictionary, val => _writerName = val, "writerName");
ValidateAndSetProperty(valueDictionary, val => _creatorName = val, "creatorName", "writerName"); //this is a bit of a hack fix for: U4-1132
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
index 79ff0c33a8..d681d9296e 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
@@ -38,7 +38,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
// method GetExamineManagerSafe().
//
private readonly ISearcher _searchProvider;
- private readonly IIndexer _indexProvider;
+ private readonly IIndex _indexProvider;
private readonly XmlStore _xmlStore;
private readonly PublishedContentTypeCache _contentTypeCache;
@@ -240,8 +240,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
try
{
- //by default use the internal index
- return eMgr.GetSearcher(Constants.Examine.InternalIndexer);
+ if (eMgr.TryGetIndex(Constants.Examine.InternalIndexer, out var index))
+ return index.GetSearcher();
+ throw new InvalidOperationException($"No index found by name {Constants.Examine.InternalIndexer}");
}
catch (FileNotFoundException)
{
@@ -353,13 +354,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
return null;
}
- internal CacheValues ConvertFromSearchResult(SearchResult searchResult)
+ internal CacheValues ConvertFromSearchResult(ISearchResult searchResult)
{
// note: fixing fields in 7.x, removed by Shan for 8.0
return new CacheValues
{
- Values = searchResult.Fields,
+ Values = searchResult.Values,
FromExamine = true
};
}
diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs
index f0ddf62074..2bc0d7be3f 100644
--- a/src/Umbraco.Web/PublishedContentExtensions.cs
+++ b/src/Umbraco.Web/PublishedContentExtensions.cs
@@ -282,16 +282,15 @@ namespace Umbraco.Web
#region Search
- public static IEnumerable Search(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null)
+ public static IEnumerable SearchDescendants(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null)
{
- //TODO: we should pass in the IExamineManager?
+ //fixme: pass in the IExamineManager
- var searcher = string.IsNullOrEmpty(indexName)
- ? ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer)
- : ExamineManager.Instance.GetSearcher(indexName);
+ indexName = string.IsNullOrEmpty(indexName) ? Constants.Examine.ExternalIndexer : indexName;
+ if (!ExamineManager.Instance.TryGetIndex(indexName, out var index))
+ throw new InvalidOperationException("No index found with name " + indexName);
- if (searcher == null)
- throw new InvalidOperationException("No searcher found for index " + indexName);
+ var searcher = index.GetSearcher();
var t = term.Escape().Value;
if (useWildCards)
@@ -303,21 +302,15 @@ namespace Umbraco.Web
return content.Search(crit, searcher);
}
- public static IEnumerable SearchDescendants(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null)
- {
- return content.Search(term, useWildCards, indexName);
- }
-
public static IEnumerable SearchChildren(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null)
{
- //TODO: we should pass in the IExamineManager?
+ //fixme: pass in the IExamineManager
- var searcher = string.IsNullOrEmpty(indexName)
- ? ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer)
- : ExamineManager.Instance.GetSearcher(indexName);
+ indexName = string.IsNullOrEmpty(indexName) ? Constants.Examine.ExternalIndexer : indexName;
+ if (!ExamineManager.Instance.TryGetIndex(indexName, out var index))
+ throw new InvalidOperationException("No index found with name " + indexName);
- if (searcher == null)
- throw new InvalidOperationException("No searcher found for index " + indexName);
+ var searcher = index.GetSearcher();
var t = term.Escape().Value;
if (useWildCards)
@@ -329,13 +322,17 @@ namespace Umbraco.Web
return content.Search(crit, searcher);
}
- public static IEnumerable Search(this IPublishedContent content, Examine.SearchCriteria.ISearchCriteria criteria, Examine.ISearcher searchProvider = null)
+ public static IEnumerable Search(this IPublishedContent content, Examine.SearchCriteria.ISearchCriteria criteria, ISearcher searchProvider = null)
{
- //TODO: we should pass in the IExamineManager?
+ //fixme: pass in the IExamineManager
- var s = searchProvider ?? ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer);
-
- var results = s.Search(criteria);
+ if (searchProvider == null)
+ {
+ if (!ExamineManager.Instance.TryGetIndex(Constants.Examine.ExternalIndexer, out var index))
+ throw new InvalidOperationException("No index found with name " + Constants.Examine.ExternalIndexer);
+ searchProvider = index.GetSearcher();
+ }
+ var results = searchProvider.Search(criteria);
return results.ToPublishedSearchResults(UmbracoContext.Current.ContentCache);
}
diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs
index e0fac5773f..04611000b9 100644
--- a/src/Umbraco.Web/PublishedContentQuery.cs
+++ b/src/Umbraco.Web/PublishedContentQuery.cs
@@ -187,42 +187,43 @@ namespace Umbraco.Web
}
///
- public IEnumerable Search(int skip, int take, out int totalRecords, string term, bool useWildCards = true, string indexName = null)
+ public IEnumerable Search(int skip, int take, out long totalRecords, string term, bool useWildCards = true, string indexName = null)
{
- //TODO: Can we inject IExamineManager?
+ //fixme: inject IExamineManager
- var indexer = string.IsNullOrEmpty(indexName)
- ? Examine.ExamineManager.Instance.GetIndexer(Constants.Examine.ExternalIndexer)
- : Examine.ExamineManager.Instance.GetIndexer(indexName);
+ indexName = string.IsNullOrEmpty(indexName)
+ ? Constants.Examine.ExternalIndexer
+ : indexName;
- if (indexer == null) throw new InvalidOperationException("No index found by name " + indexName);
+ if (!ExamineManager.Instance.TryGetIndex(indexName, out var index))
+ throw new InvalidOperationException($"No index found by name {indexName}");
- var searcher = indexer.GetSearcher();
+ var searcher = index.GetSearcher();
- if (skip == 0 && take == 0)
- {
- var results = searcher.Search(term, useWildCards);
- totalRecords = results.TotalItemCount;
- return results.ToPublishedSearchResults(_contentCache);
- }
+ var results = skip == 0 && take == 0
+ ? searcher.Search(term, true)
+ : searcher.Search(term, true, maxResults: skip + take);
- var criteria = SearchAllFields(term, useWildCards, searcher, indexer);
- return Search(skip, take, out totalRecords, criteria, searcher);
+ totalRecords = results.TotalItemCount;
+ return results.ToPublishedSearchResults(_contentCache);
}
///
- public IEnumerable Search(ISearchCriteria criteria, Examine.ISearcher searchProvider = null)
+ public IEnumerable Search(ISearchCriteria criteria, ISearcher searchProvider = null)
{
return Search(0, 0, out _, criteria, searchProvider);
}
///
- public IEnumerable Search(int skip, int take, out int totalRecords, ISearchCriteria criteria, Examine.ISearcher searchProvider = null)
+ public IEnumerable Search(int skip, int take, out long totalRecords, ISearchCriteria criteria, ISearcher searcher = null)
{
-
- //TODO: Can we inject IExamineManager?
-
- var searcher = searchProvider ?? Examine.ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer);
+ //fixme: inject IExamineManager
+ if (searcher == null)
+ {
+ if (!ExamineManager.Instance.TryGetIndex(Constants.Examine.ExternalIndexer, out var index))
+ throw new InvalidOperationException($"No index found by name {Constants.Examine.ExternalIndexer}");
+ searcher = index.GetSearcher();
+ }
var results = skip == 0 && take == 0
? searcher.Search(criteria)
@@ -232,28 +233,6 @@ namespace Umbraco.Web
return results.ToPublishedSearchResults(_contentCache);
}
- ///
- /// Creates an ISearchCriteria for searching all fields in a .
- ///
- private ISearchCriteria SearchAllFields(string searchText, bool useWildcards, Examine.ISearcher searcher, Examine.IIndexer indexer)
- {
- var sc = searcher.CreateCriteria();
-
- //if we're dealing with a lucene searcher, we can get all of it's indexed fields,
- //else we can get the defined fields for the index.
- var searchFields = (searcher is BaseLuceneSearcher luceneSearcher)
- ? luceneSearcher.GetAllIndexedFields()
- : indexer.FieldDefinitionCollection.Keys;
-
- //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 => (IExamineValue)new ExamineValue(Examineness.ComplexWildcard, x.MultipleCharacterWildcard().Value)).ToArray()).Compile();
- return sc;
- }
-
#endregion
}
diff --git a/src/Umbraco.Web/RelatedLinksTypeConverter.cs b/src/Umbraco.Web/RelatedLinksTypeConverter.cs
index 9ed9398d2e..647959b920 100644
--- a/src/Umbraco.Web/RelatedLinksTypeConverter.cs
+++ b/src/Umbraco.Web/RelatedLinksTypeConverter.cs
@@ -92,7 +92,7 @@ namespace Umbraco.Web
}
//DO NOT assign to _umbracoHelper variable, this is a singleton class and we cannot assign this based on an UmbracoHelper which is request based
- return new UmbracoHelper(UmbracoContext.Current, Current.Services, Current.ApplicationCache);
+ return new UmbracoHelper(UmbracoContext.Current, Current.Services);
}
}
}
diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs
index 97cebdb076..63849034a4 100644
--- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs
+++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs
@@ -130,6 +130,8 @@ namespace Umbraco.Web.Runtime
composition.Container.RegisterCollectionBuilder()
.Add(() => typeLoader.GetTypes()); // fixme which searchable trees?!
+ composition.Container.Register(new PerRequestLifeTime());
+
composition.Container.RegisterCollectionBuilder()
.Add(() => typeLoader.GetTypes());
diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs
index 7a036ef712..d8c1016c3e 100644
--- a/src/Umbraco.Web/Search/ExamineComponent.cs
+++ b/src/Umbraco.Web/Search/ExamineComponent.cs
@@ -1,14 +1,11 @@
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
-using System.Xml.Linq;
using Examine;
using Examine.LuceneEngine;
using Examine.LuceneEngine.Providers;
-using Lucene.Net.Documents;
using Lucene.Net.Index;
using Umbraco.Core;
using Umbraco.Core.Cache;
@@ -19,14 +16,16 @@ using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Changes;
-using Umbraco.Core.Services.Implement;
-using Umbraco.Core.Strings;
using Umbraco.Core.Sync;
using Umbraco.Web.Cache;
-using Umbraco.Web.Composing;
-using Umbraco.Web.PropertyEditors;
using Umbraco.Examine;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Web.Scheduling;
+using System.Threading.Tasks;
+using Examine.LuceneEngine.Directories;
+using LightInject;
+using Umbraco.Core.Composing;
+using Umbraco.Core.Strings;
namespace Umbraco.Web.Search
{
@@ -37,24 +36,77 @@ namespace Umbraco.Web.Search
public sealed class ExamineComponent : UmbracoComponentBase, IUmbracoCoreComponent
{
private IExamineManager _examineManager;
+ private IContentValueSetBuilder _contentValueSetBuilder;
+ private IPublishedContentValueSetBuilder _publishedContentValueSetBuilder;
+ private IValueSetBuilder _mediaValueSetBuilder;
+ private IValueSetBuilder _memberValueSetBuilder;
private static bool _disableExamineIndexing = false;
private static volatile bool _isConfigured = false;
private static readonly object IsConfiguredLocker = new object();
private IScopeProvider _scopeProvider;
- private UrlSegmentProviderCollection _urlSegmentProviders;
private ServiceContext _services;
+ private static BackgroundTaskRunner _rebuildOnStartupRunner;
+ private static readonly object RebuildLocker = new object();
// the default enlist priority is 100
// enlist with a lower priority to ensure that anything "default" runs after us
// but greater that SafeXmlReaderWriter priority which is 60
private const int EnlistPriority = 80;
- internal void Initialize(IRuntimeState runtime, MainDom mainDom, PropertyEditorCollection propertyEditors, IExamineManager examineManager, ProfilingLogger profilingLogger, IScopeProvider scopeProvider, UrlSegmentProviderCollection urlSegmentProviderCollection, ServiceContext services)
+ public override void Compose(Composition composition)
+ {
+ base.Compose(composition);
+
+ //fixme: I cannot do this since RegisterSingleton acts like TryRegisterSingleton and only allows one
+ //composition.Container.RegisterSingleton();
+ //composition.Container.RegisterSingleton