diff --git a/src/Umbraco.Core/Constants-Examine.cs b/src/Umbraco.Core/Constants-Examine.cs index 4ff6115749..874965be6e 100644 --- a/src/Umbraco.Core/Constants-Examine.cs +++ b/src/Umbraco.Core/Constants-Examine.cs @@ -11,14 +11,14 @@ namespace Umbraco.Core public static class Examine { /// - /// The alias of the internal member searcher + /// The alias of the internal member indexer /// - public const string InternalMemberSearcher = "InternalMemberSearcher"; + public const string InternalMemberIndexer = "InternalMemberIndexer"; /// - /// The alias of the internal content searcher + /// The alias of the internal content indexer /// - public const string InternalSearcher = "InternalSearcher"; + public const string InternalIndexer = "InternalIndexer"; } } } diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 6d031baee4..3383d97eb1 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -631,6 +631,11 @@ namespace Umbraco.Core return str.ToString(CultureInfo.InvariantCulture); } + public static string ToInvariantString(this long str) + { + return str.ToString(CultureInfo.InvariantCulture); + } + /// /// Compares 2 strings with invariant culture and case ignored /// diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index ee8aa696f2..2b940c8e78 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -56,7 +56,7 @@ True - ..\packages\Examine.2.0.0-beta021\lib\net45\Examine.dll + ..\packages\Examine.2.0.0-beta023\lib\net45\Examine.dll True diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index b6b321f316..cd0344a377 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -31,7 +31,6 @@ namespace Umbraco.Tests.UmbracoExamine Analyzer analyzer = null, IContentService contentService = null, IMediaService mediaService = null, - IDataTypeService dataTypeService = null, IMemberService memberService = null, IUserService userService = null, UmbracoContentIndexerOptions options = null) @@ -107,11 +106,7 @@ namespace Umbraco.Tests.UmbracoExamine == allRecs); } - if (dataTypeService == null) - { - dataTypeService = Mock.Of(); - } - + if (analyzer == null) { analyzer = new StandardAnalyzer(Version.LUCENE_30); @@ -148,7 +143,6 @@ namespace Umbraco.Tests.UmbracoExamine profilingLogger, contentService, mediaService, - dataTypeService, userService, new[] { new DefaultUrlSegmentProvider() }, new UmbracoContentValueSetValidator(options, Mock.Of()), diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index 5167340491..163a2e42b7 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -34,7 +34,7 @@ namespace Umbraco.Tests.UmbracoExamine { using (var luceneDir = new RAMDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, options: new UmbracoContentIndexerOptions(true, false, null))) using (var session = new ThreadScopedIndexSession(indexer.SearcherContext)) { var searcher = indexer.GetSearcher(); @@ -187,7 +187,7 @@ namespace Umbraco.Tests.UmbracoExamine public void Index_Reindex_Content() { using (var luceneDir = new RAMDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, options: new UmbracoContentIndexerOptions(true, false, null))) using (var session = new ThreadScopedIndexSession(indexer.SearcherContext)) { var searcher = indexer.GetSearcher(); diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index 12047f2c5a..b85e8cc2ea 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -41,6 +41,7 @@ namespace Umbraco.Tests.UmbracoExamine m.Name == (string)x.Attribute("nodeName") && m.Path == (string)x.Attribute("path") && m.Properties == new PropertyCollection() && + m.Published == true && m.ContentType == Mock.Of(mt => mt.Icon == "test" && mt.Alias == x.Name.LocalName && diff --git a/src/Umbraco.Tests/packages.config b/src/Umbraco.Tests/packages.config index 63c131688c..8c17514d92 100644 --- a/src/Umbraco.Tests/packages.config +++ b/src/Umbraco.Tests/packages.config @@ -2,7 +2,7 @@ - + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 824214e0e6..2c07423989 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -121,7 +121,7 @@ True - ..\packages\Examine.2.0.0-beta021\lib\net45\Examine.dll + ..\packages\Examine.2.0.0-beta023\lib\net45\Examine.dll True @@ -903,7 +903,7 @@ Form - + diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index 78b0c1d4f1..bf3eca45e6 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -3,7 +3,7 @@ - + diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 596e27e3a5..ca1f9ec251 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -246,14 +246,14 @@ namespace Umbraco.Web.Editors var sb = new StringBuilder(); string type; - var searcher = Constants.Examine.InternalSearcher; + var fields = new[] { "id", "__NodeId" }; //TODO: WE should really just allow passing in a lucene raw query switch (entityType) { case UmbracoEntityTypes.Member: - searcher = Constants.Examine.InternalMemberSearcher; + type = "member"; fields = new[] { "id", "__NodeId", "email", "loginName"}; if (searchFrom != null && searchFrom != Constants.Conventions.MemberTypes.AllMembersListId && searchFrom.Trim() != "-1") @@ -299,7 +299,7 @@ namespace Umbraco.Web.Editors throw new NotSupportedException("The " + typeof(EntityController) + " currently does not support searching against object type " + entityType); } - var internalSearcher = ExamineManager.Instance.SearchProviderCollection[searcher]; + var internalSearcher = ExamineManager.Instance.GetSearcher(Constants.Examine.InternalIndexer); //build a lucene query: // the __nodeName will be boosted 10x without wildcards @@ -314,7 +314,7 @@ namespace Umbraco.Web.Editors if (surroundedByQuotes) { //strip quotes, escape string, the replace again - query = query.Trim(new[] { '\"', '\'' }); + query = query.Trim('\"', '\''); query = Lucene.Net.QueryParsers.QueryParser.Escape(query); @@ -342,7 +342,7 @@ namespace Umbraco.Web.Editors } else { - if (query.Trim(new[] { '\"', '\'' }).IsNullOrWhiteSpace()) + if (query.Trim('\"', '\'').IsNullOrWhiteSpace()) { return new List(); } @@ -390,10 +390,10 @@ namespace Umbraco.Web.Editors sb.Append(type); - var raw = internalSearcher.CreateSearchCriteria().RawQuery(sb.ToString()); + var raw = internalSearcher.CreateCriteria().RawQuery(sb.ToString()).MaxCount(200); //limit results to 200 to avoid huge over processing (CPU) - var result = internalSearcher.Search(raw, 200); + var result = internalSearcher.Find(raw); switch (entityType) { @@ -413,7 +413,7 @@ namespace Umbraco.Web.Editors /// /// /// - private IEnumerable MemberFromSearchResults(ISearchResults results) + private IEnumerable MemberFromSearchResults(ILuceneSearchResults results) { var mapped = Mapper.Map>(results).ToArray(); //add additional data @@ -425,10 +425,10 @@ namespace Umbraco.Web.Editors m.Icon = "icon-user"; } - var searchResult = results.First(x => x.Id.ToInvariantString() == m.Id.ToString()); + var searchResult = results.First(x => x.LongId.ToInvariantString() == m.Id.ToString()); if (searchResult.Fields.ContainsKey("email") && searchResult.Fields["email"] != null) { - m.AdditionalData["Email"] = results.First(x => x.Id.ToInvariantString() == m.Id.ToString()).Fields["email"]; + m.AdditionalData["Email"] = results.First(x => x.LongId.ToInvariantString() == m.Id.ToString()).Fields["email"]; } if (searchResult.Fields.ContainsKey("__key") && searchResult.Fields["__key"] != null) { @@ -447,7 +447,7 @@ namespace Umbraco.Web.Editors /// /// /// - private IEnumerable MediaFromSearchResults(ISearchResults results) + private IEnumerable MediaFromSearchResults(ILuceneSearchResults results) { var mapped = Mapper.Map>(results).ToArray(); //add additional data @@ -467,9 +467,9 @@ namespace Umbraco.Web.Editors /// /// /// - private IEnumerable ContentFromSearchResults(ISearchResults results) + private IEnumerable ContentFromSearchResults(ILuceneSearchResults results) { - var mapped = Mapper.Map>(results).ToArray(); + var mapped = Mapper.Map>(results).ToArray(); //add additional data foreach (var m in mapped) { diff --git a/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs b/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs index f6e13fada4..659cb1bf50 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Linq; using AutoMapper; using Examine; +using Examine.LuceneEngine; +using Examine.LuceneEngine.Providers; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Mapping; @@ -89,8 +91,8 @@ namespace Umbraco.Web.Models.Mapping .AfterMap((result, basic) => { //get the icon if there is one - basic.Icon = result.Fields.ContainsKey(UmbracoContentIndexer.IconFieldName) - ? result.Fields[UmbracoContentIndexer.IconFieldName] + basic.Icon = result.Fields.ContainsKey(BaseUmbracoIndexer.IconFieldName) + ? result.Fields[BaseUmbracoIndexer.IconFieldName] : "icon-document"; basic.Name = result.Fields.ContainsKey("nodeName") ? result.Fields["nodeName"] : "[no name]"; @@ -116,13 +118,13 @@ namespace Umbraco.Web.Models.Mapping } basic.Path = result.Fields.ContainsKey("__Path") ? result.Fields["__Path"] : ""; - if (result.Fields.ContainsKey(UmbracoContentIndexer.NodeTypeAliasFieldName)) + if (result.Fields.ContainsKey(LuceneIndexer.NodeTypeAliasFieldName)) { - basic.AdditionalData.Add("contentType", result.Fields[UmbracoContentIndexer.NodeTypeAliasFieldName]); + basic.AdditionalData.Add("contentType", result.Fields[LuceneIndexer.NodeTypeAliasFieldName]); } }); - config.CreateMap>() + config.CreateMap>() .ConvertUsing(results => results.Select(Mapper.Map).ToList()); } } diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index d4adafad23..25a8239186 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Xml.XPath; using Examine; +using Examine.LuceneEngine; using Examine.LuceneEngine.Providers; using Examine.LuceneEngine.SearchCriteria; using Examine.Providers; @@ -158,7 +159,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache try { //by default use the InternalSearcher - return eMgr.GetSearcher("InternalSearcher"); + return eMgr.GetSearcher(Constants.Examine.InternalIndexer); } catch (FileNotFoundException) { @@ -411,30 +412,17 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache try { //first check in Examine as this is WAY faster - var criteria = searchProvider.CreateSearchCriteria("media"); + var criteria = searchProvider.CreateCriteria("media"); - var filter = criteria.ParentId(parentId).Not().Field(UmbracoContentIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); + var filter = criteria.ParentId(parentId).Not().Field(BaseUmbracoIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); //the above filter will create a query like this, NOTE: That since the use of the wildcard, it automatically escapes it in Lucene. //+(+parentId:3113 -__Path:-1,-21,*) +__IndexType:media - ISearchResults results; + //sort with the Sort field + var results = searchProvider.Find( + filter.And().OrderBy(new SortableField("sortOrder", SortType.Int)).Compile()); - //we want to check if the indexer for this searcher has "sortOrder" flagged as sortable. - //if so, we'll use Lucene to do the sorting, if not we'll have to manually sort it (slower). - var indexer = GetIndexProviderSafe(); - var useLuceneSort = indexer != null && indexer.IndexerData.StandardFields.Any(x => x.Name.InvariantEquals("sortOrder") && x.EnableSorting); - if (useLuceneSort) - { - //we have a sortOrder field declared to be sorted, so we'll use Examine - results = searchProvider.Search( - filter.And().OrderBy(new SortableField("sortOrder", SortType.Int)).Compile()); - } - else - { - results = searchProvider.Search(filter.Compile()); - } - - if (results.Any()) + if (results.Any()) { // var medias = results.Select(ConvertFromSearchResult); var medias = results.Select(x => @@ -446,7 +434,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return CreateFromCacheValues(cacheValues); }); - return useLuceneSort ? medias : medias.OrderBy(x => x.SortOrder); + return medias; } else { @@ -562,17 +550,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache LoadedFromExamine = fromExamine; ValidateAndSetProperty(valueDictionary, val => _id = int.Parse(val), "id", "nodeId", "__NodeId"); //should validate the int! - ValidateAndSetProperty(valueDictionary, val => _key = Guid.Parse(val), "key"); - // wtf are we dealing with templates for medias?! - ValidateAndSetProperty(valueDictionary, val => _templateId = int.Parse(val), "template", "templateId"); + ValidateAndSetProperty(valueDictionary, val => _key = Guid.Parse(val), "key"); ValidateAndSetProperty(valueDictionary, val => _sortOrder = int.Parse(val), "sortOrder"); ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName", "__nodeName"); ValidateAndSetProperty(valueDictionary, val => _urlName = val, "urlName"); - ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", UmbracoContentIndexer.NodeTypeAliasFieldName); + ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", LuceneIndexer.NodeTypeAliasFieldName); ValidateAndSetProperty(valueDictionary, val => _documentTypeId = int.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 - ValidateAndSetProperty(valueDictionary, val => _writerId = int.Parse(val), "writerID"); ValidateAndSetProperty(valueDictionary, val => _creatorId = int.Parse(val), "creatorID", "writerID"); //this is a bit of a hack fix for: U4-1132 ValidateAndSetProperty(valueDictionary, val => _path = val, "path", "__Path"); ValidateAndSetProperty(valueDictionary, val => _createDate = ParseDateTimeValue(val), "createDate"); @@ -668,16 +652,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache public override Guid Key { get { return _key; } } - public override int TemplateId - { - get - { - //TODO: should probably throw a not supported exception since media doesn't actually support this. - return _templateId; - } - } + public override int TemplateId + { + get { return 0; } + } - public override int SortOrder + public override int SortOrder { get { return _sortOrder; } } @@ -704,7 +684,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache public override string WriterName { - get { return _writerName; } + get { return _creatorName; } } public override string CreatorName @@ -714,7 +694,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache public override int WriterId { - get { return _writerId; } + get { return _creatorId; } } public override int CreatorId @@ -739,7 +719,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache public override Guid Version { - get { return _version; } + get { return Guid.Empty; } } public override int Level @@ -808,20 +788,16 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private readonly List _keysAdded = new List(); private int _id; private Guid _key; - private int _templateId; private int _sortOrder; private string _name; private string _urlName; private string _documentTypeAlias; - private int _documentTypeId; - private string _writerName; + private int _documentTypeId; private string _creatorName; - private int _writerId; private int _creatorId; private string _path; private DateTime _createDate; private DateTime _updateDate; - private Guid _version; private int _level; private readonly ICollection _properties; private readonly PublishedContentType _contentType; diff --git a/src/Umbraco.Web/Search/ExamineEvents.cs b/src/Umbraco.Web/Search/ExamineEvents.cs index f2f968801e..7e6a72e8ca 100644 --- a/src/Umbraco.Web/Search/ExamineEvents.cs +++ b/src/Umbraco.Web/Search/ExamineEvents.cs @@ -13,8 +13,6 @@ using Umbraco.Core.Models; using Umbraco.Core.Sync; using Umbraco.Web.Cache; using UmbracoExamine; -using Content = umbraco.cms.businesslogic.Content; -using Document = umbraco.cms.businesslogic.web.Document; namespace Umbraco.Web.Search { @@ -51,7 +49,6 @@ namespace Umbraco.Web.Search CacheRefresherBase.CacheUpdated += PublishedPageCacheRefresherCacheUpdated; CacheRefresherBase.CacheUpdated += MediaCacheRefresherCacheUpdated; CacheRefresherBase.CacheUpdated += MemberCacheRefresherCacheUpdated; - CacheRefresherBase.CacheUpdated += ContentTypeCacheRefresherCacheUpdated; var contentIndexer = ExamineManager.Instance.IndexProviderCollection["InternalIndexer"] as UmbracoContentIndexer; if (contentIndexer != null) @@ -65,24 +62,6 @@ namespace Umbraco.Web.Search } } - /// - /// This is used to refresh content indexers IndexData based on the DataService whenever a content type is changed since - /// properties may have been added/removed - /// - /// - /// - /// - /// See: http://issues.umbraco.org/issue/U4-4798 - /// - static void ContentTypeCacheRefresherCacheUpdated(ContentTypeCacheRefresher sender, CacheRefresherEventArgs e) - { - var indexersToUpdated = ExamineManager.Instance.IndexProviderCollection.OfType(); - foreach (var provider in indexersToUpdated) - { - provider.RefreshIndexerDataFromDataService(); - } - } - static void MemberCacheRefresherCacheUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs e) { switch (e.MessageType) diff --git a/src/Umbraco.Web/Search/ExamineIndexerModel.cs b/src/Umbraco.Web/Search/ExamineIndexerModel.cs index 4fb557d318..c19dc97580 100644 --- a/src/Umbraco.Web/Search/ExamineIndexerModel.cs +++ b/src/Umbraco.Web/Search/ExamineIndexerModel.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Runtime.Serialization; using Examine; @@ -8,8 +9,8 @@ namespace Umbraco.Web.Search [DataContract(Name = "indexer", Namespace = "")] public class ExamineIndexerModel : ExamineSearcherModel { - [DataMember(Name = "indexCriteria")] - public IIndexCriteria IndexCriteria { get; set; } + [DataMember(Name = "fieldDefinitions")] + public IEnumerable FieldDefinitions { get; set; } /// /// The number of docs in the index diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 8834ea4609..793411d8c1 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -114,7 +114,7 @@ ..\packages\dotless.1.4.1.0\lib\dotless.Core.dll - ..\packages\Examine.2.0.0-beta021\lib\net45\Examine.dll + ..\packages\Examine.2.0.0-beta023\lib\net45\Examine.dll True diff --git a/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs b/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs index 31ef4d23f2..cfde906239 100644 --- a/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs +++ b/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs @@ -20,6 +20,9 @@ namespace Umbraco.Web.WebServices [ValidateAngularAntiForgeryToken] public class ExamineManagementApiController : UmbracoAuthorizedApiController { + //TODO: Fix all of this for searchers/indexers that are not configured via code (i.e. the core ones once we do that) + // We will need to be able to search an index directly without having to go through all of the searchers + /// /// Checks if the member internal index is consistent with the data stored in the database /// @@ -74,7 +77,7 @@ namespace Umbraco.Web.WebServices /// public IEnumerable GetIndexerDetails() { - return ExamineManager.Instance.IndexProviderCollection.Select(CreateModel); + return ExamineManager.Instance.IndexProviders.Select(CreateModel); } /// @@ -103,7 +106,7 @@ namespace Umbraco.Web.WebServices return model; } - public ISearchResults GetSearchResults(string searcherName, string query, string queryType) + public ILuceneSearchResults GetSearchResults(string searcherName, string query, string queryType) { if (queryType == null) { @@ -112,7 +115,7 @@ namespace Umbraco.Web.WebServices if (query.IsNullOrWhiteSpace()) - return SearchResults.Empty(); + return LuceneSearchResults.Empty(); LuceneSearcher searcher; var msg = ValidateLuceneSearcher(searcherName, out searcher); @@ -120,11 +123,11 @@ namespace Umbraco.Web.WebServices { if (queryType.InvariantEquals("text")) { - return searcher.Search(query, false); + return searcher.Find(query, false); } if (queryType.InvariantEquals("lucene")) { - return searcher.Search(searcher.CreateSearchCriteria().RawQuery(query)); + return searcher.Find(searcher.CreateCriteria().RawQuery(query)); } throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); } @@ -136,13 +139,14 @@ namespace Umbraco.Web.WebServices /// public HttpResponseMessage PostOptimizeIndex(string indexerName) { - LuceneIndexer indexer; + IExamineIndexer indexer; var msg = ValidateLuceneIndexer(indexerName, out indexer); - if (msg.IsSuccessStatusCode) + var luceneIndexer = indexer as LuceneIndexer; + if (luceneIndexer != null && msg.IsSuccessStatusCode) { try { - indexer.OptimizeIndex(); + luceneIndexer.OptimizeIndex(); } catch (Exception ex) { @@ -225,7 +229,7 @@ namespace Umbraco.Web.WebServices //if its still there then it's not done return found != null ? null - : CreateModel(indexer); + : CreateModel(new KeyValuePair(indexerName, indexer)); } throw new HttpResponseException(msg); } @@ -242,19 +246,19 @@ namespace Umbraco.Web.WebServices if (msg.IsSuccessStatusCode) { var isOptimized = indexer.IsIndexOptimized(); - return !isOptimized + return isOptimized == false ? null - : CreateModel(indexer); + : CreateModel(new KeyValuePair(indexerName, indexer)); } throw new HttpResponseException(msg); } - private ExamineIndexerModel CreateModel(BaseIndexProvider indexer) + private ExamineIndexerModel CreateModel(KeyValuePair indexer) { var indexerModel = new ExamineIndexerModel() { - IndexCriteria = indexer.IndexerData, - Name = indexer.Name + FieldDefinitions = indexer.Value.FieldDefinitions, + Name = indexer.Key }; var props = TypeHelper.CachedDiscoverableProperties(indexer.GetType(), mustWrite: false) //ignore these properties @@ -266,13 +270,13 @@ namespace Umbraco.Web.WebServices var val = p.GetValue(indexer, null); if (val == null) { - LogHelper.Warn("Property value was null when setting up property on indexer: " + indexer.Name + " property: " + p.Name); + LogHelper.Warn("Property value was null when setting up property on indexer: " + indexer.Key + " property: " + p.Name); val = string.Empty; } indexerModel.ProviderProperties.Add(p.Name, val.ToString()); } - var luceneIndexer = indexer as LuceneIndexer; + var luceneIndexer = indexer.Value as LuceneIndexer; if (luceneIndexer != null) { indexerModel.IsLuceneIndex = true; @@ -319,24 +323,17 @@ namespace Umbraco.Web.WebServices return response1; } - private HttpResponseMessage ValidateLuceneIndexer(string indexerName, out LuceneIndexer indexer) - { - if (ExamineManager.Instance.IndexProviderCollection.Any(x => x.Name == indexerName)) - { - indexer = ExamineManager.Instance.IndexProviderCollection[indexerName] as LuceneIndexer; - if (indexer == null) - { - var response1 = Request.CreateResponse(HttpStatusCode.BadRequest); - response1.Content = new StringContent(string.Format("The indexer {0} is not of type {1}", indexerName, typeof(LuceneIndexer))); - response1.ReasonPhrase = "Wrong Indexer Type"; - return response1; - } + private HttpResponseMessage ValidateLuceneIndexer(string indexerName, out T indexer) + where T : class, IExamineIndexer + { + indexer = null; + + if (ExamineManager.Instance.IndexProviders.ContainsKey(indexerName)) + { //return Ok! return Request.CreateResponse(HttpStatusCode.OK); } - - indexer = null; - + var response = Request.CreateResponse(HttpStatusCode.BadRequest); response.Content = new StringContent(string.Format("No indexer found with name = {0}", indexerName)); response.ReasonPhrase = "Indexer Not Found"; diff --git a/src/Umbraco.Web/packages.config b/src/Umbraco.Web/packages.config index a267cb99ce..5b6feac615 100644 --- a/src/Umbraco.Web/packages.config +++ b/src/Umbraco.Web/packages.config @@ -3,7 +3,7 @@ - + diff --git a/src/UmbracoExamine/BaseUmbracoIndexer.cs b/src/UmbracoExamine/BaseUmbracoIndexer.cs index ff4e13de13..f37116e037 100644 --- a/src/UmbracoExamine/BaseUmbracoIndexer.cs +++ b/src/UmbracoExamine/BaseUmbracoIndexer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using Examine.LuceneEngine.Config; @@ -33,8 +34,7 @@ namespace UmbracoExamine /// /// Used to store the path of a content object /// - public const string IndexPathFieldName = "__Path"; - public const string NodeTypeAliasFieldName = "__NodeTypeAlias"; + public const string IndexPathFieldName = "__Path"; public const string IconFieldName = "__Icon"; public const string PublishedFieldName = "__Published"; /// @@ -65,7 +65,7 @@ namespace UmbracoExamine ProfilingLogger = profilingLogger; } - private bool _configBased = false; + private readonly bool _configBased = false; private readonly LocalTempStorageIndexer _localTempStorageIndexer = new LocalTempStorageIndexer(); /// @@ -100,7 +100,7 @@ namespace UmbracoExamine protected ProfilingLogger ProfilingLogger { get; private set; } /// - /// Overridden to ensure that + /// Overridden to ensure that the umbraco system field definitions are in place /// /// /// @@ -114,6 +114,7 @@ namespace UmbracoExamine new FieldDefinition("writerID", FieldDefinitionTypes.Integer), new FieldDefinition("creatorID", FieldDefinitionTypes.Integer), new FieldDefinition("sortOrder", FieldDefinitionTypes.Integer), + new FieldDefinition("template", FieldDefinitionTypes.Integer), new FieldDefinition("createDate", FieldDefinitionTypes.DateTime), new FieldDefinition("updateDate", FieldDefinitionTypes.DateTime), @@ -146,9 +147,8 @@ namespace UmbracoExamine } } - /// - /// If true, the IndexingActionHandler will be run to keep the default index up to date. - /// + [Obsolete("This should not be used, it is used by the configuration based indexes but instead to disable Examine event handlers use the ExamineEvents class instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] public bool EnableDefaultEventHandler { get; protected set; } /// @@ -164,6 +164,9 @@ namespace UmbracoExamine /// /// /// + /// + /// This is ONLY used for configuration based indexes + /// public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { EnableDefaultEventHandler = true; //set to true by default @@ -201,7 +204,7 @@ namespace UmbracoExamine #endregion - public override Lucene.Net.Store.Directory GetLuceneDirectory() + public override Directory GetLuceneDirectory() { //if temp local storage is configured use that, otherwise return the default if (UseTempStorage) @@ -213,19 +216,6 @@ namespace UmbracoExamine } - ///// - ///// Override to check if we can actually initialize. - ///// - ///// - ///// - ///// This check is required since the base examine lib will try to check this method on app startup. If the app - ///// is not ready then we need to deal with it otherwise the base class will throw exceptions since we've bypassed initialization. - ///// - //public override bool IndexExists() - //{ - // return base.IndexExists(); - //} - /// /// override to check if we can actually initialize. /// @@ -254,11 +244,11 @@ namespace UmbracoExamine base.IndexAll(type); } } - + public override void IndexItems(IEnumerable nodes) { if (CanInitialize()) - { + { base.IndexItems(nodes); } } @@ -298,8 +288,6 @@ namespace UmbracoExamine } } - #region Protected - /// /// Returns true if the Umbraco application is in a state that we can initialize the examine indexes /// @@ -334,104 +322,57 @@ namespace UmbracoExamine IndexAll(t); } } - - ///// - ///// Builds an xpath statement to query against Umbraco data for the index type specified, then - ///// initiates the re-indexing of the data matched. - ///// - ///// - //protected override void PerformIndexAll(string type) - //{ - // //NOTE: the logic below is ONLY used for published content, for media and members and non-published content, this method is overridden - // // and we query directly against the umbraco service layer. - - // if (SupportedTypes.Contains(type) == false) - // return; - - // var xPath = "//*[(number(@id) > 0 and (@isDoc or @nodeTypeAlias)){0}]"; //we'll add more filters to this below if needed - - // var sb = new StringBuilder(); - - // //create the xpath statement to match node type aliases if specified - // if (IndexerData.IncludeNodeTypes.Any()) - // { - // sb.Append("("); - // foreach (var field in IndexerData.IncludeNodeTypes) - // { - // //this can be used across both schemas - // const string nodeTypeAlias = "(@nodeTypeAlias='{0}' or (count(@nodeTypeAlias)=0 and name()='{0}'))"; - - // sb.Append(string.Format(nodeTypeAlias, field)); - // sb.Append(" or "); - // } - // sb.Remove(sb.Length - 4, 4); //remove last " or " - // sb.Append(")"); - // } - - // //create the xpath statement to match all children of the current node. - // if (IndexerData.ParentNodeId.HasValue && IndexerData.ParentNodeId.Value > 0) - // { - // if (sb.Length > 0) - // sb.Append(" and "); - // sb.Append("("); - // sb.Append("contains(@path, '," + IndexerData.ParentNodeId.Value + ",')"); //if the path contains comma - id - comma then the nodes must be a child - // sb.Append(")"); - // } - - // //create the full xpath statement to match the appropriate nodes. If there is a filter - // //then apply it, otherwise just select all nodes. - // var filter = sb.ToString(); - // xPath = string.Format(xPath, filter.Length > 0 ? " and " + filter : ""); - - // //raise the event and set the xpath statement to the value returned - // var args = new IndexingNodesEventArgs(IndexerData, xPath, type); - // OnNodesIndexing(args); - // if (args.Cancel) - // { - // return; - // } - - // xPath = args.XPath; - - // ProfilingLogger.Logger.Debug(GetType(), "({0}) PerformIndexAll with XPATH: {1}", () => Name, () => xPath); - - // AddNodesToIndex(xPath, type); - //} - - #endregion - + + /// + /// overridden for logging + /// + /// protected override void OnIndexingError(IndexingErrorEventArgs e) { ProfilingLogger.Logger.Error(GetType(), e.Message, e.Exception); - base.OnIndexingError(e); } + /// + /// Override for logging + /// + /// + protected override void OnIgnoringIndexItem(IndexItemEventArgs e) + { + ProfilingLogger.Logger.Debug(GetType(), "OnIgnoringIndexItem {0} with type {1}", () => e.IndexItem.ValueSet.Id, () => e.IndexItem.ValueSet.IndexCategory); + base.OnIgnoringIndexItem(e); + } + /// /// This ensures that the special __Raw_ fields are indexed /// /// - protected override void OnDocumentWriting(DocumentWritingEventArgs docArgs) { var d = docArgs.Document; - foreach (var f in docArgs.Fields.Where(x => x.Key.StartsWith(RawFieldPrefix))) + + foreach (var f in docArgs.Values.Values.Where(x => x.Key.StartsWith(RawFieldPrefix))) { - d.Add(new Field( - f.Key, - f.Value, - Field.Store.YES, - Field.Index.NO, //don't index this field, we never want to search by it - Field.TermVector.NO)); + if (f.Value.Count > 0) + { + d.Add(new Field( + f.Key, + f.Value[0].ToString(), + Field.Store.YES, + Field.Index.NO, //don't index this field, we never want to search by it + Field.TermVector.NO)); + } } + ProfilingLogger.Logger.Debug(GetType(), "OnDocumentWriting {0} with type {1}", () => docArgs.Values.Id, () => docArgs.Values.ItemType); + base.OnDocumentWriting(docArgs); } - protected override void OnNodeIndexed(IndexedNodeEventArgs e) + protected override void OnItemIndexed(IndexItemEventArgs e) { - ProfilingLogger.Logger.Debug(GetType(), "Index created for node {0}", () => e.NodeId); - base.OnNodeIndexed(e); + ProfilingLogger.Logger.Debug(GetType(), "Index created for node {0}", () => e.IndexItem.Id); + base.OnItemIndexed(e); } protected override void OnIndexDeleted(DeleteIndexEventArgs e) @@ -440,6 +381,7 @@ namespace UmbracoExamine base.OnIndexDeleted(e); } + [Obsolete("This is no longer used, index optimization is no longer managed with the LuceneIndexer")] protected override void OnIndexOptimizing(EventArgs e) { ProfilingLogger.Logger.Debug(GetType(), "Index is being optimized"); @@ -456,81 +398,43 @@ namespace UmbracoExamine base.AddDocument(values); } - /// - /// Overridden for logging. - /// - /// - /// - protected override void AddSingleNodeToIndex(XElement node, string type) - { - ProfilingLogger.Logger.Debug(GetType(), "AddSingleNodeToIndex {0} with type {1}", () => (int)node.Attribute("id"), () => type); - base.AddSingleNodeToIndex(node, type); - } - protected override void OnTransformingIndexValues(TransformingIndexDataEventArgs e) { base.OnTransformingIndexValues(e); - if (e.OriginalValues.ContainsKey("path")) + //ensure special __Path field + if (e.OriginalValues.ContainsKey("path") && e.IndexItem.ValueSet.Values.ContainsKey(IndexPathFieldName) == false) { e.IndexItem.ValueSet.Values[IndexPathFieldName] = new List { e.OriginalValues["path"].First() }; - } - - ////adds the special node type alias property to the index - //fields.Add(NodeTypeAliasFieldName, allValuesForIndexing[NodeTypeAliasFieldName]); - - ////icon - //if (allValuesForIndexing[IconFieldName].IsNullOrWhiteSpace() == false) - //{ - // fields.Add(IconFieldName, allValuesForIndexing[IconFieldName]); - //} - - //return fields; - } - - /// - /// Override this method to strip all html from all user fields before raising the event, then after the event - /// ensure our special Path field is added to the collection - /// - /// - protected override void OnGatheringNodeData(IndexingNodeDataEventArgs e) - { + } + //strip html of all users fields if we detect it has HTML in it. //if that is the case, we'll create a duplicate 'raw' copy of it so that we can return //the value of the field 'as-is'. - // Get all user data that we want to index and store into a dictionary - foreach (var field in IndexerData.UserFields) + foreach (var originalValue in e.OriginalValues) { - if (e.Fields.ContainsKey(field.Name)) + if (originalValue.Value.Any()) { - //check if the field value has html - if (XmlHelper.CouldItBeXml(e.Fields[field.Name])) + var str = originalValue.Value.First() as string; + if (str != null) { - //First save the raw value to a raw field, we will change the policy of this field by detecting the prefix later - e.Fields[RawFieldPrefix + field.Name] = e.Fields[field.Name]; - //now replace the original value with the stripped html - //TODO: This should be done with an analzer?! - e.Fields[field.Name] = e.Fields[field.Name].StripHtml(); + if (XmlHelper.CouldItBeXml(str)) + { + //First save the raw value to a raw field, we will change the policy of this field by detecting the prefix later + e.IndexItem.ValueSet.Values[string.Concat(RawFieldPrefix, originalValue.Key)] = new List { str }; + //now replace the original value with the stripped html + //TODO: This should be done with an analzer?! + e.IndexItem.ValueSet.Values[originalValue.Key] = new List { str.StripHtml() }; + } } - } + } } - base.OnGatheringNodeData(e); - - //ensure the special path and node type alias fields is added to the dictionary to be saved to file - var path = e.Node.Attribute("path").Value; - if (!e.Fields.ContainsKey(IndexPathFieldName)) - e.Fields.Add(IndexPathFieldName, path); - - //this needs to support both schema's so get the nodeTypeAlias if it exists, otherwise the name - var nodeTypeAlias = e.Node.Attribute("nodeTypeAlias") == null ? e.Node.Name.LocalName : e.Node.Attribute("nodeTypeAlias").Value; - if (!e.Fields.ContainsKey(NodeTypeAliasFieldName)) - e.Fields.Add(NodeTypeAliasFieldName, nodeTypeAlias); - - //add icon - var icon = (string)e.Node.Attribute("icon"); - if (!e.Fields.ContainsKey(IconFieldName)) - e.Fields.Add(IconFieldName, icon); + //icon + if (e.OriginalValues.ContainsKey("icon") && e.IndexItem.ValueSet.Values.ContainsKey(IconFieldName) == false) + { + e.IndexItem.ValueSet.Values[IconFieldName] = new List { e.OriginalValues["icon"] }; + } } diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs index 29795610f1..8ab6d2f1f1 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/UmbracoExamine/UmbracoContentIndexer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.Globalization; using System.Linq; using System.Xml; using System.Xml.Linq; @@ -31,9 +32,9 @@ namespace UmbracoExamine { protected IContentService ContentService { get; private set; } protected IMediaService MediaService { get; private set; } - protected IDataTypeService DataTypeService { get; private set; } protected IUserService UserService { get; private set; } - private readonly IEnumerable _urlSegmentProviders; + private readonly IEnumerable _urlSegmentProviders; + private int? _parentId; #region Constructors @@ -45,7 +46,6 @@ namespace UmbracoExamine { ContentService = ApplicationContext.Current.Services.ContentService; MediaService = ApplicationContext.Current.Services.MediaService; - DataTypeService = ApplicationContext.Current.Services.DataTypeService; UserService = ApplicationContext.Current.Services.UserService; _urlSegmentProviders = UrlSegmentProviderResolver.Current.Providers; } @@ -57,7 +57,6 @@ namespace UmbracoExamine ProfilingLogger profilingLogger, IContentService contentService, IMediaService mediaService, - IDataTypeService dataTypeService, IUserService userService, IEnumerable urlSegmentProviders, IValueSetValidator validator, @@ -68,7 +67,6 @@ namespace UmbracoExamine { if (contentService == null) throw new ArgumentNullException("contentService"); if (mediaService == null) throw new ArgumentNullException("mediaService"); - if (dataTypeService == null) throw new ArgumentNullException("dataTypeService"); if (userService == null) throw new ArgumentNullException("userService"); if (urlSegmentProviders == null) throw new ArgumentNullException("urlSegmentProviders"); if (validator == null) throw new ArgumentNullException("validator"); @@ -84,7 +82,6 @@ namespace UmbracoExamine ContentService = contentService; MediaService = mediaService; - DataTypeService = dataTypeService; UserService = userService; _urlSegmentProviders = urlSegmentProviders; } @@ -132,12 +129,7 @@ namespace UmbracoExamine else SupportProtectedContent = false; - base.Initialize(name, config); - - - - } #endregion @@ -159,13 +151,21 @@ namespace UmbracoExamine /// /// If set this will filter the content items allowed to be indexed /// - public int? ParentId { get; protected set; } + public int? ParentId + { + get + { + //fallback to the legacy data + return _parentId ?? (IndexerData == null ? (int?)null : IndexerData.ParentNodeId); + } + protected set { _parentId = value; } + } protected override IEnumerable SupportedTypes { get { - return new string[] { IndexTypes.Content, IndexTypes.Media }; + return new[] { IndexTypes.Content, IndexTypes.Media }; } } @@ -190,21 +190,20 @@ namespace UmbracoExamine var descendantPath = string.Format(@"\-1\,*{0}\,*", nodeId); var rawQuery = string.Format("{0}:{1}", IndexPathFieldName, descendantPath); var searcher = GetSearcher(); - var c = searcher.CreateSearchCriteria(); + var c = searcher.CreateCriteria(); var filtered = c.RawQuery(rawQuery); - var results = searcher.Search(filtered); + var results = searcher.Find(filtered); ProfilingLogger.Logger.Debug(GetType(), string.Format("DeleteFromIndex with query: {0} (found {1} results)", rawQuery, results.TotalItemCount)); //need to create a delete queue item for each one found foreach (var r in results) { - EnqueueIndexOperation(new IndexOperation() + ProcessIndexOperation(new IndexOperation() { Operation = IndexOperationType.Delete, - Item = new IndexItem(null, "", r.Id.ToString()) + Item = new IndexItem(new ValueSet(r.LongId, string.Empty)) }); - //SaveDeleteIndexQueueItem(new KeyValuePair(IndexNodeIdFieldName, r.Id.ToString())); } base.DeleteFromIndex(nodeId); @@ -224,9 +223,9 @@ namespace UmbracoExamine case IndexTypes.Content: var contentParentId = -1; - if (IndexerData.ParentNodeId.HasValue && IndexerData.ParentNodeId.Value > 0) + if (ParentId.HasValue && ParentId.Value > 0) { - contentParentId = IndexerData.ParentNodeId.Value; + contentParentId = ParentId.Value; } IContent[] content; @@ -240,7 +239,7 @@ namespace UmbracoExamine //if specific types are declared we need to post filter them //TODO: Update the service layer to join the cmsContentType table so we can query by content type too - if (IndexerData.IncludeNodeTypes.Any()) + if (IndexerData != null && IndexerData.IncludeNodeTypes.Any()) { content = descendants.Where(x => IndexerData.IncludeNodeTypes.Contains(x.ContentType.Alias)).ToArray(); } @@ -249,7 +248,8 @@ namespace UmbracoExamine content = descendants.ToArray(); } - AddNodesToIndex(GetSerializedContent(content), type); + IndexItems(GetValueSets(content)); + pageIndex++; @@ -260,9 +260,9 @@ namespace UmbracoExamine case IndexTypes.Media: var mediaParentId = -1; - if (IndexerData.ParentNodeId.HasValue && IndexerData.ParentNodeId.Value > 0) + if (ParentId.HasValue && ParentId.Value > 0) { - mediaParentId = IndexerData.ParentNodeId.Value; + mediaParentId = ParentId.Value; } IMedia[] media; @@ -273,7 +273,7 @@ namespace UmbracoExamine //if specific types are declared we need to post filter them //TODO: Update the service layer to join the cmsContentType table so we can query by content type too - if (IndexerData.IncludeNodeTypes.Any()) + if (IndexerData != null && IndexerData.IncludeNodeTypes.Any()) { media = descendants.Where(x => IndexerData.IncludeNodeTypes.Contains(x.ContentType.Alias)).ToArray(); } @@ -281,8 +281,9 @@ namespace UmbracoExamine { media = descendants.ToArray(); } + + IndexItems(GetValueSets(media)); - AddNodesToIndex(GetSerializedMedia(media), type); pageIndex++; } while (media.Length == pageSize); @@ -290,61 +291,77 @@ namespace UmbracoExamine } } - private IEnumerable GetSerializedMedia(IEnumerable media) + private IEnumerable GetValueSets(IEnumerable content) { - var serializer = new EntityXmlSerializer(); - foreach (var m in media) - { - var xml = serializer.Serialize( - MediaService, - DataTypeService, - UserService, - _urlSegmentProviders, - m); - - //add a custom 'icon' attribute - if (m.ContentType.Icon.IsNullOrWhiteSpace() == false) - { - xml.Add(new XAttribute("icon", m.ContentType.Icon)); - } - - - yield return xml; - } - } - - private IEnumerable GetSerializedContent(IEnumerable content) - { - var serializer = new EntityXmlSerializer(); foreach (var c in content) { - var xml = serializer.Serialize( - ContentService, - DataTypeService, - UserService, - _urlSegmentProviders, - c); + var urlValue = c.GetUrlSegment(_urlSegmentProviders); + var values = new Dictionary + { + {"icon", new object[] {c.ContentType.Icon}}, + {PublishedFieldName, new object[] {c.Published ? 1 : 0}}, + {"id", new object[] {c.Id}}, + {"key", new object[] {c.Key}}, + {"parentID", new object[] {c.Level > 1 ? c.ParentId : -1}}, + {"level", new object[] {c.Level}}, + {"creatorID", new object[] {c.CreatorId}}, + {"sortOrder", new object[] {c.SortOrder}}, + {"createDate", new object[] {c.CreateDate}}, + {"updateDate", new object[] {c.UpdateDate}}, + {"nodeName", new object[] {c.Name}}, + {"urlName", new object[] {urlValue}}, + {"path", new object[] {c.Path}}, + {"nodeType", new object[] {c.ContentType.Id}}, + {"creatorName", new object[] {c.GetCreatorProfile(UserService).Name}}, + {"writerName", new object[] {c.GetWriterProfile(UserService).Name}}, + {"writerID", new object[] {c.WriterId}}, + {"version", new object[] {c.Version}}, + {"template", new object[] {c.Template == null ? 0 : c.Template.Id}} + }; - //add a custom 'icon' attribute - xml.Add(new XAttribute("icon", c.ContentType.Icon)); - xml.Add(new XAttribute(PublishedFieldName, c.Published ? 1 : 0)); + foreach (var property in c.Properties.Where(p => p != null && p.Value != null && p.Value.ToString().IsNullOrWhiteSpace() == false)) + { + values.Add(property.Alias, new[] {property.Value}); + } - yield return xml; + var vs = new ValueSet(c.Id, IndexTypes.Content, c.ContentType.Alias, values); + + yield return vs; } } - - /// - /// Used to refresh the current IndexerData from the data in the DataService. This can be used - /// if there are more properties added/removed from the database - /// - public void RefreshIndexerDataFromDataService() + private IEnumerable GetValueSets(IEnumerable media) { - //TODO: This would be much better done if the IndexerData property had read/write locks applied - // to it! Unless we update the base class there's really no way to prevent the IndexerData from being - // changed during an operation that is reading from it. - var newIndexerData = GetIndexerData(IndexSets.Instance.Sets[IndexSetName]); - IndexerData = newIndexerData; + foreach (var m in media) + { + var urlValue = m.GetUrlSegment(_urlSegmentProviders); + var values = new Dictionary + { + {"icon", new object[] {m.ContentType.Icon}}, + {"id", new object[] {m.Id}}, + {"key", new object[] {m.Key}}, + {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, + {"level", new object[] {m.Level}}, + {"creatorID", new object[] {m.CreatorId}}, + {"sortOrder", new object[] {m.SortOrder}}, + {"createDate", new object[] {m.CreateDate}}, + {"updateDate", new object[] {m.UpdateDate}}, + {"nodeName", new object[] {m.Name}}, + {"urlName", new object[] {urlValue}}, + {"path", new object[] {m.Path}}, + {"nodeType", new object[] {m.ContentType.Id}}, + {"creatorName", new object[] {m.GetCreatorProfile(UserService).Name}} + }; + + foreach (var property in m.Properties.Where(p => p != null && p.Value != null && p.Value.ToString().IsNullOrWhiteSpace() == false)) + { + values.Add(property.Alias, new[] { property.Value }); + } + + var vs = new ValueSet(m.Id, IndexTypes.Media, m.ContentType.Alias, values); + + yield return vs; + } } /// @@ -355,6 +372,7 @@ namespace UmbracoExamine /// /// If we cannot initialize we will pass back empty indexer data since we cannot read from the database /// + [Obsolete("IIndexCriteria is obsolete, this method is used only for configuration based indexes it is recommended to configure indexes on startup with code instead of config")] protected override IIndexCriteria GetIndexerData(IndexSet indexSet) { if (CanInitialize()) @@ -364,11 +382,7 @@ namespace UmbracoExamine // the DI ctor is not used. return indexSet.ToIndexCriteria(ApplicationContext.Current.Services.ContentTypeService); } - else - { - return base.GetIndexerData(indexSet); - } - + return base.GetIndexerData(indexSet); } #endregion diff --git a/src/UmbracoExamine/UmbracoExamine.csproj b/src/UmbracoExamine/UmbracoExamine.csproj index 7fa5c1952b..aadd1f0bb5 100644 --- a/src/UmbracoExamine/UmbracoExamine.csproj +++ b/src/UmbracoExamine/UmbracoExamine.csproj @@ -83,7 +83,7 @@ - ..\packages\Examine.2.0.0-beta021\lib\net45\Examine.dll + ..\packages\Examine.2.0.0-beta023\lib\net45\Examine.dll True diff --git a/src/UmbracoExamine/UmbracoMemberIndexer.cs b/src/UmbracoExamine/UmbracoMemberIndexer.cs index eb76d0253b..a2879bd283 100644 --- a/src/UmbracoExamine/UmbracoMemberIndexer.cs +++ b/src/UmbracoExamine/UmbracoMemberIndexer.cs @@ -15,8 +15,6 @@ using Examine.LuceneEngine.Providers; using Lucene.Net.Analysis; using Umbraco.Core.Logging; using Directory = Lucene.Net.Store.Directory; -using IContentService = Umbraco.Core.Services.IContentService; -using IMediaService = Umbraco.Core.Services.IMediaService; namespace UmbracoExamine { @@ -27,7 +25,6 @@ namespace UmbracoExamine public class UmbracoMemberIndexer : BaseUmbracoIndexer { private readonly IMemberService _memberService; - private readonly IDataTypeService _dataTypeService; /// /// Default constructor @@ -36,7 +33,6 @@ namespace UmbracoExamine : base() { _memberService = ApplicationContext.Current.Services.MemberService; - _dataTypeService = ApplicationContext.Current.Services.DataTypeService; } /// @@ -48,21 +44,17 @@ namespace UmbracoExamine /// /// /// - /// public UmbracoMemberIndexer( IEnumerable fieldDefinitions, Directory luceneDirectory, Analyzer analyzer, ProfilingLogger profilingLogger, IValueSetValidator validator, - IMemberService memberService, - IDataTypeService dataTypeService) : + IMemberService memberService) : base(fieldDefinitions, luceneDirectory, analyzer, profilingLogger, validator) { if (memberService == null) throw new ArgumentNullException("memberService"); - if (dataTypeService == null) throw new ArgumentNullException("dataTypeService"); _memberService = memberService; - _dataTypeService = dataTypeService; } /// @@ -104,15 +96,24 @@ namespace UmbracoExamine return indexerData; } + /// + /// Overridden to ensure that the umbraco system field definitions are in place + /// + /// + /// + protected override IEnumerable InitializeFieldDefinitions(IEnumerable originalDefinitions) + { + var result = base.InitializeFieldDefinitions(originalDefinitions).ToList(); + result.Add(new FieldDefinition("__key", FieldDefinitionTypes.Raw)); + return result; + } + /// /// The supported types for this indexer /// protected override IEnumerable SupportedTypes { - get - { - return new string[] { IndexTypes.Member }; - } + get { return new[] {IndexTypes.Member}; } } /// @@ -130,7 +131,7 @@ namespace UmbracoExamine IMember[] members; - if (IndexerData.IncludeNodeTypes.Any()) + if (IndexerData != null && IndexerData.IncludeNodeTypes.Any()) { //if there are specific node types then just index those foreach (var nodeType in IndexerData.IncludeNodeTypes) @@ -140,7 +141,7 @@ namespace UmbracoExamine long total; members = _memberService.GetAll(pageIndex, pageSize, out total, "LoginName", Direction.Ascending, true, null, nodeType).ToArray(); - AddNodesToIndex(GetSerializedMembers(members), type); + IndexItems(GetValueSets(members)); pageIndex++; } while (members.Length == pageSize); @@ -154,50 +155,60 @@ namespace UmbracoExamine int total; members = _memberService.GetAll(pageIndex, pageSize, out total).ToArray(); - AddNodesToIndex(GetSerializedMembers(members), type); + IndexItems(GetValueSets(members)); pageIndex++; } while (members.Length == pageSize); } } - private IEnumerable GetSerializedMembers(IEnumerable members) + private IEnumerable GetValueSets(IEnumerable member) { - var serializer = new EntityXmlSerializer(); - return members.Select(member => serializer.Serialize(_dataTypeService, member)); + foreach (var m in member) + { + var values = new Dictionary + { + {"icon", new object[] {m.ContentType.Icon}}, + {"id", new object[] {m.Id}}, + {"key", new object[] {m.Key}}, + {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, + {"level", new object[] {m.Level}}, + {"creatorID", new object[] {m.CreatorId}}, + {"sortOrder", new object[] {m.SortOrder}}, + {"createDate", new object[] {m.CreateDate}}, + {"updateDate", new object[] {m.UpdateDate}}, + {"nodeName", new object[] {m.Name}}, + {"path", new object[] {m.Path}}, + {"nodeType", new object[] {m.ContentType.Id}}, + {"loginName", new object[] {m.Username}}, + {"email", new object[] {m.Email}}, + }; + + foreach (var property in m.Properties.Where(p => p != null && p.Value != null && p.Value.ToString().IsNullOrWhiteSpace() == false)) + { + values.Add(property.Alias, new[] { property.Value }); + } + + var vs = new ValueSet(m.Id, IndexTypes.Content, m.ContentType.Alias, values); + + yield return vs; + } } protected override void OnTransformingIndexValues(TransformingIndexDataEventArgs e) { base.OnTransformingIndexValues(e); - //adds the special path property to the index - //fields.Add("__key", allValuesForIndexing["__key"]); - } - - /// - /// Add the special __key and _searchEmail fields - /// - /// - protected override void OnGatheringNodeData(IndexingNodeDataEventArgs e) - { - base.OnGatheringNodeData(e); - - if (e.Node.Attribute("key") != null) + if (e.OriginalValues.ContainsKey("key") && e.IndexItem.ValueSet.Values.ContainsKey("__key") == false) { - if (e.Fields.ContainsKey("__key") == false) - e.Fields.Add("__key", e.Node.Attribute("key").Value); + e.IndexItem.ValueSet.Values["__key"] = new List {e.OriginalValues["key"]}; + } + if (e.OriginalValues.ContainsKey("email") && e.IndexItem.ValueSet.Values.ContainsKey("_searchEmail") == false) + { + e.IndexItem.ValueSet.Values["_searchEmail"] = new List { e.OriginalValues["email"].ToString().Replace(".", " ").Replace("@", " ") }; } - if (e.Node.Attribute("email") != null) - { - //NOTE: the single underscore = it's not a 'special' field which means it will be indexed normally - if (e.Fields.ContainsKey("_searchEmail") == false) - e.Fields.Add("_searchEmail", e.Node.Attribute("email").Value.Replace(".", " ").Replace("@", " ")); - } - - if (e.Fields.ContainsKey(IconFieldName) == false) - e.Fields.Add(IconFieldName, (string)e.Node.Attribute("icon")); } + } } diff --git a/src/UmbracoExamine/app.config b/src/UmbracoExamine/app.config index 069bb69de3..23a5733afb 100644 --- a/src/UmbracoExamine/app.config +++ b/src/UmbracoExamine/app.config @@ -12,7 +12,7 @@ - + diff --git a/src/UmbracoExamine/packages.config b/src/UmbracoExamine/packages.config index dde0147e85..dbd8a3b7da 100644 --- a/src/UmbracoExamine/packages.config +++ b/src/UmbracoExamine/packages.config @@ -1,6 +1,6 @@  - +