diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index 7387a8281f..c4169147f8 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -13,7 +13,7 @@ using System.Runtime.InteropServices; // Umbraco Cms [assembly: InternalsVisibleTo("Umbraco.Web")] [assembly: InternalsVisibleTo("Umbraco.Web.UI")] -[assembly: InternalsVisibleTo("UmbracoExamine")] +[assembly: InternalsVisibleTo("Umbraco.Examine")] [assembly: InternalsVisibleTo("Umbraco.Tests")] [assembly: InternalsVisibleTo("Umbraco.Tests.Benchmarks")] diff --git a/src/UmbracoExamine/BaseUmbracoIndexer.cs b/src/Umbraco.Examine/BaseUmbracoIndexer.cs similarity index 97% rename from src/UmbracoExamine/BaseUmbracoIndexer.cs rename to src/Umbraco.Examine/BaseUmbracoIndexer.cs index 3f5a9bc22e..24d3168240 100644 --- a/src/UmbracoExamine/BaseUmbracoIndexer.cs +++ b/src/Umbraco.Examine/BaseUmbracoIndexer.cs @@ -1,459 +1,459 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Text; -using Examine.LuceneEngine.Config; -using Examine.LuceneEngine.Providers; -using Examine.Providers; -using Lucene.Net.Analysis; -using Lucene.Net.Index; -using Umbraco.Core; -using Examine; -using System.IO; -using System.Xml; -using System.Xml.Linq; -using Examine.LuceneEngine; -using Examine.LuceneEngine.Faceting; -using Examine.LuceneEngine.Indexing; -using Lucene.Net.Documents; -using Lucene.Net.Store; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; -using Umbraco.Core.Xml; -using UmbracoExamine.LocalStorage; -using Directory = Lucene.Net.Store.Directory; - -namespace UmbracoExamine -{ - /// - /// An abstract provider containing the basic functionality to be able to query against - /// Umbraco data. - /// - public abstract class BaseUmbracoIndexer : LuceneIndexer - { - // note - // wrapping all operations that end up calling base.SafelyProcessQueueItems in a safe call - // context because they will fork a thread/task/whatever which should *not* capture our - // call context (and the database it can contain)! ideally we should be able to override - // SafelyProcessQueueItems but that's not possible in the current version of Examine. - - /// - /// Used to store the path of a content object - /// - public const string IndexPathFieldName = "__Path"; - public const string NodeKeyFieldName = "__Key"; - public const string IconFieldName = "__Icon"; - public const string PublishedFieldName = "__Published"; - /// - /// The prefix added to a field when it is duplicated in order to store the original raw value. - /// - public const string RawFieldPrefix = "__Raw_"; - - /// - /// Default constructor - /// - protected BaseUmbracoIndexer() - : base() - { - ProfilingLogger = Current.ProfilingLogger; - _configBased = true; - } - - protected BaseUmbracoIndexer( - IEnumerable fieldDefinitions, - Directory luceneDirectory, - Analyzer defaultAnalyzer, - ProfilingLogger profilingLogger, - IValueSetValidator validator = null, - FacetConfiguration facetConfiguration = null, IDictionary> indexValueTypes = null) - : base(fieldDefinitions, luceneDirectory, defaultAnalyzer, validator, facetConfiguration, indexValueTypes) - { - if (profilingLogger == null) throw new ArgumentNullException("profilingLogger"); - ProfilingLogger = profilingLogger; - } - - private readonly bool _configBased = false; - private LocalTempStorageIndexer _localTempStorageIndexer; - - /// - /// A type that defines the type of index for each Umbraco field (non user defined fields) - /// Alot of standard umbraco fields shouldn't be tokenized or even indexed, just stored into lucene - /// for retreival after searching. - /// - [Obsolete("IndexFieldPolicies is not really used apart for some legacy reasons - use FieldDefinition's instead")] - internal static readonly List IndexFieldPolicies - = new List - { - new StaticField("id", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), - new StaticField("key", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), - new StaticField( "version", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), - new StaticField( "parentID", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), - new StaticField( "level", FieldIndexTypes.NOT_ANALYZED, true, "NUMBER"), - new StaticField( "writerID", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), - new StaticField( "creatorID", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), - new StaticField( "nodeType", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), - new StaticField( "template", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), - new StaticField( "sortOrder", FieldIndexTypes.NOT_ANALYZED, true, "NUMBER"), - new StaticField( "createDate", FieldIndexTypes.NOT_ANALYZED, false, "DATETIME"), - new StaticField( "updateDate", FieldIndexTypes.NOT_ANALYZED, false, "DATETIME"), - new StaticField( "nodeName", FieldIndexTypes.ANALYZED, false, string.Empty), - new StaticField( "urlName", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), - new StaticField( "writerName", FieldIndexTypes.ANALYZED, false, string.Empty), - new StaticField( "creatorName", FieldIndexTypes.ANALYZED, false, string.Empty), - new StaticField( "nodeTypeAlias", FieldIndexTypes.ANALYZED, false, string.Empty), - new StaticField( "path", FieldIndexTypes.NOT_ANALYZED, false, string.Empty) - }; - - protected ProfilingLogger ProfilingLogger { get; private set; } - - /// - /// Overridden to ensure that the umbraco system field definitions are in place - /// - /// - /// - protected override IEnumerable InitializeFieldDefinitions(IEnumerable originalDefinitions) - { - var fd = base.InitializeFieldDefinitions(originalDefinitions).ToList(); - fd.AddRange(new[] - { - new FieldDefinition("parentID", FieldDefinitionTypes.Integer), - new FieldDefinition("level", FieldDefinitionTypes.Integer), - 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), - - new FieldDefinition("key", FieldDefinitionTypes.Raw), - new FieldDefinition("version", FieldDefinitionTypes.Raw), - new FieldDefinition("nodeType", FieldDefinitionTypes.Raw), - new FieldDefinition("template", FieldDefinitionTypes.Raw), - new FieldDefinition("urlName", FieldDefinitionTypes.Raw), - new FieldDefinition("path", FieldDefinitionTypes.Raw), - - new FieldDefinition(IndexPathFieldName, FieldDefinitionTypes.Raw), - new FieldDefinition(NodeTypeAliasFieldName, FieldDefinitionTypes.Raw), - new FieldDefinition(IconFieldName, FieldDefinitionTypes.Raw) - }); - return fd; - } - - public bool UseTempStorage - { - get { return _localTempStorageIndexer != null && _localTempStorageIndexer.LuceneDirectory != null; } - } - - public string TempStorageLocation - { - get - { - if (UseTempStorage == false) return string.Empty; - return _localTempStorageIndexer.TempPath; - } - } - - [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; } - - /// - /// the supported indexable types - /// - protected abstract IEnumerable SupportedTypes { get; } - - #region Initialize - - - /// - /// Setup the properties for the indexer from the provider settings - /// - /// - /// - /// - /// 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 - bool enabled; - if (bool.TryParse(config["enableDefaultEventHandler"], out enabled)) - { - EnableDefaultEventHandler = enabled; - } - - ProfilingLogger.Logger.Debug(GetType(), "{0} indexer initializing", () => name); - - base.Initialize(name, config); - - //NOTES: useTempStorage is obsolete, tempStorageDirectory is obsolete, both have been superceded by Examine Core's IDirectoryFactory - // tempStorageDirectory never actually got finished in Umbraco Core but accidentally got shipped (it's only enabled on the searcher - // and not the indexer). So this whole block is just legacy - - //detect if a dir factory has been specified, if so then useTempStorage will not be used (deprecated) - - if (config["directoryFactory"] == null && config["useTempStorage"] != null) - { - throw new NotImplementedException("Fix how local temp storage works and is synced with Examine v2.0 - since a writer is always open we cannot snapshot it, we need to use the same logic in AzureDirectory"); - - _localTempStorageIndexer = new LocalTempStorageIndexer(); - - var fsDir = base.GetLuceneDirectory() as FSDirectory; - if (fsDir != null) - { - //Use the temp storage directory which will store the index in the local/codegen folder, this is useful - // for websites that are running from a remove file server and file IO latency becomes an issue - var attemptUseTempStorage = config["useTempStorage"].TryConvertTo(); - if (attemptUseTempStorage) - { - - var indexSet = IndexSets.Instance.Sets[IndexSetName]; - var configuredPath = indexSet.IndexPath; - - _localTempStorageIndexer.Initialize(config, configuredPath, fsDir, IndexingAnalyzer, attemptUseTempStorage.Result); - } - } - - } - } - - #endregion - - - public override Directory GetLuceneDirectory() - { - //if temp local storage is configured use that, otherwise return the default - if (UseTempStorage) - { - return _localTempStorageIndexer.LuceneDirectory; - } - - return base.GetLuceneDirectory(); - - } - - /// - /// override to check if we can actually initialize. - /// - /// - /// This check is required since the base examine lib will try to rebuild on startup - /// - public override void RebuildIndex() - { - if (CanInitialize()) - { - ProfilingLogger.Logger.Debug(GetType(), "Rebuilding index"); - using (new SafeCallContext()) - { - base.RebuildIndex(); - } - } - } - - /// - /// override to check if we can actually initialize. - /// - /// - /// This check is required since the base examine lib will try to rebuild on startup - /// - public override void IndexAll(string type) - { - if (CanInitialize()) - { - using (new SafeCallContext()) - { - base.IndexAll(type); - } - } - } - - public override void IndexItems(IEnumerable nodes) - { - if (CanInitialize()) - { - using (new SafeCallContext()) - { - base.IndexItems(nodes); - } - } - } - - [Obsolete("Use ValueSets with IndexItems instead")] - public override void ReIndexNode(XElement node, string type) - { - if (CanInitialize()) - { - if (SupportedTypes.Contains(type) == false) - return; - - if (node.Attribute("id") != null) - { - ProfilingLogger.Logger.Debug(GetType(), "ReIndexNode {0} with type {1}", () => node.Attribute("id"), () => type); - using (new SafeCallContext()) - { - base.ReIndexNode(node, type); - } - } - else - { - ProfilingLogger.Logger.Error(GetType(), "ReIndexNode cannot proceed, the format of the XElement is invalid", - new XmlException("XElement is invalid, the xml has not id attribute")); - } - } - } - - /// - /// override to check if we can actually initialize. - /// - /// - /// This check is required since the base examine lib will try to rebuild on startup - /// - public override void DeleteFromIndex(string nodeId) - { - if (CanInitialize()) - { - using (new SafeCallContext()) - { - base.DeleteFromIndex(nodeId); - } - } - } - - /// - /// Returns true if the Umbraco application is in a state that we can initialize the examine indexes - /// - protected bool CanInitialize() - { - // only affects indexers that are config file based, if an index was created via code then - // this has no effect, it is assumed the index would not be created if it could not be initialized - return _configBased == false || Current.RuntimeState.Level == RuntimeLevel.Run; - } - - /// - /// Reindexes all supported types - /// - protected override void PerformIndexRebuild() - { - foreach (var t in SupportedTypes) - { - IndexAll(t); - } - } - - /// - /// 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.Values.Values.Where(x => x.Key.StartsWith(RawFieldPrefix))) - { - 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 OnItemIndexed(IndexItemEventArgs e) - { - ProfilingLogger.Logger.Debug(GetType(), "Index created for node {0}", () => e.IndexItem.Id); - base.OnItemIndexed(e); - } - - protected override void OnIndexDeleted(DeleteIndexEventArgs e) - { - ProfilingLogger.Logger.Debug(GetType(), "Index deleted for term: {0} with value {1}", () => e.DeletedTerm.Key, () => e.DeletedTerm.Value); - 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"); - base.OnIndexOptimizing(e); - } - - /// - /// Overridden for logging. - /// - /// - protected override void AddDocument(ValueSet values) - { - ProfilingLogger.Logger.Debug(GetType(), "AddDocument {0} with type {1}", () => values.Id, () => values.ItemType); - base.AddDocument(values); - } - - protected override void OnTransformingIndexValues(TransformingIndexDataEventArgs e) - { - base.OnTransformingIndexValues(e); - - //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() }; - } - - //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'. - foreach (var originalValue in e.OriginalValues) - { - if (originalValue.Value.Any()) - { - var str = originalValue.Value.First() as string; - if (str != null) - { - 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() }; - } - } - } - } - - //icon - if (e.OriginalValues.ContainsKey("icon") && e.IndexItem.ValueSet.Values.ContainsKey(IconFieldName) == false) - { - e.IndexItem.ValueSet.Values[IconFieldName] = new List { e.OriginalValues["icon"] }; - } - } - } -} +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using Examine.LuceneEngine.Config; +using Examine.LuceneEngine.Providers; +using Examine.Providers; +using Lucene.Net.Analysis; +using Lucene.Net.Index; +using Umbraco.Core; +using Examine; +using System.IO; +using System.Xml; +using System.Xml.Linq; +using Examine.LuceneEngine; +using Examine.LuceneEngine.Faceting; +using Examine.LuceneEngine.Indexing; +using Lucene.Net.Documents; +using Lucene.Net.Store; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; +using Umbraco.Core.Xml; +using Umbraco.Examine.LocalStorage; +using Directory = Lucene.Net.Store.Directory; + +namespace Umbraco.Examine +{ + /// + /// An abstract provider containing the basic functionality to be able to query against + /// Umbraco data. + /// + public abstract class BaseUmbracoIndexer : LuceneIndexer + { + // note + // wrapping all operations that end up calling base.SafelyProcessQueueItems in a safe call + // context because they will fork a thread/task/whatever which should *not* capture our + // call context (and the database it can contain)! ideally we should be able to override + // SafelyProcessQueueItems but that's not possible in the current version of Examine. + + /// + /// Used to store the path of a content object + /// + public const string IndexPathFieldName = "__Path"; + public const string NodeKeyFieldName = "__Key"; + public const string IconFieldName = "__Icon"; + public const string PublishedFieldName = "__Published"; + /// + /// The prefix added to a field when it is duplicated in order to store the original raw value. + /// + public const string RawFieldPrefix = "__Raw_"; + + /// + /// Default constructor + /// + protected BaseUmbracoIndexer() + : base() + { + ProfilingLogger = Current.ProfilingLogger; + _configBased = true; + } + + protected BaseUmbracoIndexer( + IEnumerable fieldDefinitions, + Directory luceneDirectory, + Analyzer defaultAnalyzer, + ProfilingLogger profilingLogger, + IValueSetValidator validator = null, + FacetConfiguration facetConfiguration = null, IDictionary> indexValueTypes = null) + : base(fieldDefinitions, luceneDirectory, defaultAnalyzer, validator, facetConfiguration, indexValueTypes) + { + if (profilingLogger == null) throw new ArgumentNullException("profilingLogger"); + ProfilingLogger = profilingLogger; + } + + private readonly bool _configBased = false; + private LocalTempStorageIndexer _localTempStorageIndexer; + + /// + /// A type that defines the type of index for each Umbraco field (non user defined fields) + /// Alot of standard umbraco fields shouldn't be tokenized or even indexed, just stored into lucene + /// for retreival after searching. + /// + [Obsolete("IndexFieldPolicies is not really used apart for some legacy reasons - use FieldDefinition's instead")] + internal static readonly List IndexFieldPolicies + = new List + { + new StaticField("id", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), + new StaticField("key", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), + new StaticField( "version", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), + new StaticField( "parentID", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), + new StaticField( "level", FieldIndexTypes.NOT_ANALYZED, true, "NUMBER"), + new StaticField( "writerID", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), + new StaticField( "creatorID", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), + new StaticField( "nodeType", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), + new StaticField( "template", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), + new StaticField( "sortOrder", FieldIndexTypes.NOT_ANALYZED, true, "NUMBER"), + new StaticField( "createDate", FieldIndexTypes.NOT_ANALYZED, false, "DATETIME"), + new StaticField( "updateDate", FieldIndexTypes.NOT_ANALYZED, false, "DATETIME"), + new StaticField( "nodeName", FieldIndexTypes.ANALYZED, false, string.Empty), + new StaticField( "urlName", FieldIndexTypes.NOT_ANALYZED, false, string.Empty), + new StaticField( "writerName", FieldIndexTypes.ANALYZED, false, string.Empty), + new StaticField( "creatorName", FieldIndexTypes.ANALYZED, false, string.Empty), + new StaticField( "nodeTypeAlias", FieldIndexTypes.ANALYZED, false, string.Empty), + new StaticField( "path", FieldIndexTypes.NOT_ANALYZED, false, string.Empty) + }; + + protected ProfilingLogger ProfilingLogger { get; private set; } + + /// + /// Overridden to ensure that the umbraco system field definitions are in place + /// + /// + /// + protected override IEnumerable InitializeFieldDefinitions(IEnumerable originalDefinitions) + { + var fd = base.InitializeFieldDefinitions(originalDefinitions).ToList(); + fd.AddRange(new[] + { + new FieldDefinition("parentID", FieldDefinitionTypes.Integer), + new FieldDefinition("level", FieldDefinitionTypes.Integer), + 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), + + new FieldDefinition("key", FieldDefinitionTypes.Raw), + new FieldDefinition("version", FieldDefinitionTypes.Raw), + new FieldDefinition("nodeType", FieldDefinitionTypes.Raw), + new FieldDefinition("template", FieldDefinitionTypes.Raw), + new FieldDefinition("urlName", FieldDefinitionTypes.Raw), + new FieldDefinition("path", FieldDefinitionTypes.Raw), + + new FieldDefinition(IndexPathFieldName, FieldDefinitionTypes.Raw), + new FieldDefinition(NodeTypeAliasFieldName, FieldDefinitionTypes.Raw), + new FieldDefinition(IconFieldName, FieldDefinitionTypes.Raw) + }); + return fd; + } + + public bool UseTempStorage + { + get { return _localTempStorageIndexer != null && _localTempStorageIndexer.LuceneDirectory != null; } + } + + public string TempStorageLocation + { + get + { + if (UseTempStorage == false) return string.Empty; + return _localTempStorageIndexer.TempPath; + } + } + + [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; } + + /// + /// the supported indexable types + /// + protected abstract IEnumerable SupportedTypes { get; } + + #region Initialize + + + /// + /// Setup the properties for the indexer from the provider settings + /// + /// + /// + /// + /// 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 + bool enabled; + if (bool.TryParse(config["enableDefaultEventHandler"], out enabled)) + { + EnableDefaultEventHandler = enabled; + } + + ProfilingLogger.Logger.Debug(GetType(), "{0} indexer initializing", () => name); + + base.Initialize(name, config); + + //NOTES: useTempStorage is obsolete, tempStorageDirectory is obsolete, both have been superceded by Examine Core's IDirectoryFactory + // tempStorageDirectory never actually got finished in Umbraco Core but accidentally got shipped (it's only enabled on the searcher + // and not the indexer). So this whole block is just legacy + + //detect if a dir factory has been specified, if so then useTempStorage will not be used (deprecated) + + if (config["directoryFactory"] == null && config["useTempStorage"] != null) + { + throw new NotImplementedException("Fix how local temp storage works and is synced with Examine v2.0 - since a writer is always open we cannot snapshot it, we need to use the same logic in AzureDirectory"); + + _localTempStorageIndexer = new LocalTempStorageIndexer(); + + var fsDir = base.GetLuceneDirectory() as FSDirectory; + if (fsDir != null) + { + //Use the temp storage directory which will store the index in the local/codegen folder, this is useful + // for websites that are running from a remove file server and file IO latency becomes an issue + var attemptUseTempStorage = config["useTempStorage"].TryConvertTo(); + if (attemptUseTempStorage) + { + + var indexSet = IndexSets.Instance.Sets[IndexSetName]; + var configuredPath = indexSet.IndexPath; + + _localTempStorageIndexer.Initialize(config, configuredPath, fsDir, IndexingAnalyzer, attemptUseTempStorage.Result); + } + } + + } + } + + #endregion + + + public override Directory GetLuceneDirectory() + { + //if temp local storage is configured use that, otherwise return the default + if (UseTempStorage) + { + return _localTempStorageIndexer.LuceneDirectory; + } + + return base.GetLuceneDirectory(); + + } + + /// + /// override to check if we can actually initialize. + /// + /// + /// This check is required since the base examine lib will try to rebuild on startup + /// + public override void RebuildIndex() + { + if (CanInitialize()) + { + ProfilingLogger.Logger.Debug(GetType(), "Rebuilding index"); + using (new SafeCallContext()) + { + base.RebuildIndex(); + } + } + } + + /// + /// override to check if we can actually initialize. + /// + /// + /// This check is required since the base examine lib will try to rebuild on startup + /// + public override void IndexAll(string type) + { + if (CanInitialize()) + { + using (new SafeCallContext()) + { + base.IndexAll(type); + } + } + } + + public override void IndexItems(IEnumerable nodes) + { + if (CanInitialize()) + { + using (new SafeCallContext()) + { + base.IndexItems(nodes); + } + } + } + + [Obsolete("Use ValueSets with IndexItems instead")] + public override void ReIndexNode(XElement node, string type) + { + if (CanInitialize()) + { + if (SupportedTypes.Contains(type) == false) + return; + + if (node.Attribute("id") != null) + { + ProfilingLogger.Logger.Debug(GetType(), "ReIndexNode {0} with type {1}", () => node.Attribute("id"), () => type); + using (new SafeCallContext()) + { + base.ReIndexNode(node, type); + } + } + else + { + ProfilingLogger.Logger.Error(GetType(), "ReIndexNode cannot proceed, the format of the XElement is invalid", + new XmlException("XElement is invalid, the xml has not id attribute")); + } + } + } + + /// + /// override to check if we can actually initialize. + /// + /// + /// This check is required since the base examine lib will try to rebuild on startup + /// + public override void DeleteFromIndex(string nodeId) + { + if (CanInitialize()) + { + using (new SafeCallContext()) + { + base.DeleteFromIndex(nodeId); + } + } + } + + /// + /// Returns true if the Umbraco application is in a state that we can initialize the examine indexes + /// + protected bool CanInitialize() + { + // only affects indexers that are config file based, if an index was created via code then + // this has no effect, it is assumed the index would not be created if it could not be initialized + return _configBased == false || Current.RuntimeState.Level == RuntimeLevel.Run; + } + + /// + /// Reindexes all supported types + /// + protected override void PerformIndexRebuild() + { + foreach (var t in SupportedTypes) + { + IndexAll(t); + } + } + + /// + /// 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.Values.Values.Where(x => x.Key.StartsWith(RawFieldPrefix))) + { + 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 OnItemIndexed(IndexItemEventArgs e) + { + ProfilingLogger.Logger.Debug(GetType(), "Index created for node {0}", () => e.IndexItem.Id); + base.OnItemIndexed(e); + } + + protected override void OnIndexDeleted(DeleteIndexEventArgs e) + { + ProfilingLogger.Logger.Debug(GetType(), "Index deleted for term: {0} with value {1}", () => e.DeletedTerm.Key, () => e.DeletedTerm.Value); + 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"); + base.OnIndexOptimizing(e); + } + + /// + /// Overridden for logging. + /// + /// + protected override void AddDocument(ValueSet values) + { + ProfilingLogger.Logger.Debug(GetType(), "AddDocument {0} with type {1}", () => values.Id, () => values.ItemType); + base.AddDocument(values); + } + + protected override void OnTransformingIndexValues(TransformingIndexDataEventArgs e) + { + base.OnTransformingIndexValues(e); + + //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() }; + } + + //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'. + foreach (var originalValue in e.OriginalValues) + { + if (originalValue.Value.Any()) + { + var str = originalValue.Value.First() as string; + if (str != null) + { + 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() }; + } + } + } + } + + //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/DataServices/UmbracoContentService.cs b/src/Umbraco.Examine/DataServices/UmbracoContentService.cs similarity index 96% rename from src/UmbracoExamine/DataServices/UmbracoContentService.cs rename to src/Umbraco.Examine/DataServices/UmbracoContentService.cs index a6b1c5feab..5f3fba6505 100644 --- a/src/UmbracoExamine/DataServices/UmbracoContentService.cs +++ b/src/Umbraco.Examine/DataServices/UmbracoContentService.cs @@ -1,115 +1,115 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core; -using System.Xml.Linq; -using System.Collections; -using System.Xml.XPath; -using Examine.LuceneEngine; -using Umbraco.Core.Logging; -using Umbraco.Core.Scoping; -using Umbraco.Core.Services; -using Umbraco.Core.Models; - -namespace UmbracoExamine.DataServices -{ - public class UmbracoContentService - { - private readonly IScopeProvider _scopeProvider; - private readonly ServiceContext _services; - private readonly ILogger _logger; - - public UmbracoContentService(IScopeProvider scopeProvider, ServiceContext services, ILogger logger) - { - _scopeProvider = scopeProvider; - _services = services; - _logger = logger; - } - - /// - /// removes html markup from a string - /// - /// - /// - public string StripHtml(string value) - { - return value.StripHtml(); - } - - /// - /// Gets published content by xpath - /// - /// - /// - public XDocument GetPublishedContentByXPath(string xpath) - { - //TODO: Remove the need for this, the best way would be to remove all requirements of examine based on Xml but that - // would take some time. Another way in the in-term would be to add a static delegate to this class which can be set - // on the WebBootManager to set how to get the XmlNodeByXPath but that is still ugly :( - return LegacyLibrary.GetXmlNodeByXPath(xpath).ToXDocument(); - } - - /// - /// This is quite an intensive operation... - /// get all root content, then get the XML structure for all children, - /// then run xpath against the navigator that's created - /// - /// - /// - [Obsolete("This should no longer be used, latest content will be indexed by using the IContentService directly")] - public XDocument GetLatestContentByXPath(string xpath) - { - var xmlContent = XDocument.Parse(""); - foreach (var c in _services.ContentService.GetRootContent()) - { - xmlContent.Root.Add(c.ToDeepXml(_services.PackagingService)); - } - var result = ((IEnumerable)xmlContent.XPathEvaluate(xpath)).Cast(); - return result.ToXDocument(); - } - - /// - /// Check if the node is protected - /// - /// - /// - /// - public bool IsProtected(int nodeId, string path) - { - return _services.PublicAccessService.IsProtected(path.EnsureEndsWith("," + nodeId)); - } - - /// - /// Returns a list of all of the user defined property names in Umbraco - /// - /// - - public IEnumerable GetAllUserPropertyNames() - { - using (var scope = _scopeProvider.CreateScope()) - { - try - { - var result = scope.Database.Fetch("select distinct alias from cmsPropertyType order by alias"); - scope.Complete(); - return result; - } - catch (Exception ex) - { - _logger.Error("EXCEPTION OCCURRED reading GetAllUserPropertyNames", ex); - scope.Complete(); - return Enumerable.Empty(); - } - } - } - - /// - /// Returns a list of all system field names in Umbraco - /// - /// - public IEnumerable GetAllSystemPropertyNames() - { - return UmbracoContentIndexer.IndexFieldPolicies.Select(x => x.Name); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core; +using System.Xml.Linq; +using System.Collections; +using System.Xml.XPath; +using Examine.LuceneEngine; +using Umbraco.Core.Logging; +using Umbraco.Core.Scoping; +using Umbraco.Core.Services; +using Umbraco.Core.Models; + +namespace Umbraco.Examine.DataServices +{ + public class UmbracoContentService + { + private readonly IScopeProvider _scopeProvider; + private readonly ServiceContext _services; + private readonly ILogger _logger; + + public UmbracoContentService(IScopeProvider scopeProvider, ServiceContext services, ILogger logger) + { + _scopeProvider = scopeProvider; + _services = services; + _logger = logger; + } + + /// + /// removes html markup from a string + /// + /// + /// + public string StripHtml(string value) + { + return value.StripHtml(); + } + + /// + /// Gets published content by xpath + /// + /// + /// + public XDocument GetPublishedContentByXPath(string xpath) + { + //TODO: Remove the need for this, the best way would be to remove all requirements of examine based on Xml but that + // would take some time. Another way in the in-term would be to add a static delegate to this class which can be set + // on the WebBootManager to set how to get the XmlNodeByXPath but that is still ugly :( + return LegacyLibrary.GetXmlNodeByXPath(xpath).ToXDocument(); + } + + /// + /// This is quite an intensive operation... + /// get all root content, then get the XML structure for all children, + /// then run xpath against the navigator that's created + /// + /// + /// + [Obsolete("This should no longer be used, latest content will be indexed by using the IContentService directly")] + public XDocument GetLatestContentByXPath(string xpath) + { + var xmlContent = XDocument.Parse(""); + foreach (var c in _services.ContentService.GetRootContent()) + { + xmlContent.Root.Add(c.ToDeepXml(_services.PackagingService)); + } + var result = ((IEnumerable)xmlContent.XPathEvaluate(xpath)).Cast(); + return result.ToXDocument(); + } + + /// + /// Check if the node is protected + /// + /// + /// + /// + public bool IsProtected(int nodeId, string path) + { + return _services.PublicAccessService.IsProtected(path.EnsureEndsWith("," + nodeId)); + } + + /// + /// Returns a list of all of the user defined property names in Umbraco + /// + /// + + public IEnumerable GetAllUserPropertyNames() + { + using (var scope = _scopeProvider.CreateScope()) + { + try + { + var result = scope.Database.Fetch("select distinct alias from cmsPropertyType order by alias"); + scope.Complete(); + return result; + } + catch (Exception ex) + { + _logger.Error("EXCEPTION OCCURRED reading GetAllUserPropertyNames", ex); + scope.Complete(); + return Enumerable.Empty(); + } + } + } + + /// + /// Returns a list of all system field names in Umbraco + /// + /// + public IEnumerable GetAllSystemPropertyNames() + { + return UmbracoContentIndexer.IndexFieldPolicies.Select(x => x.Name); + } + } +} diff --git a/src/UmbracoExamine/DataServices/UmbracoMediaService.cs b/src/Umbraco.Examine/DataServices/UmbracoMediaService.cs similarity index 94% rename from src/UmbracoExamine/DataServices/UmbracoMediaService.cs rename to src/Umbraco.Examine/DataServices/UmbracoMediaService.cs index 8f50410be0..436cd99b9e 100644 --- a/src/UmbracoExamine/DataServices/UmbracoMediaService.cs +++ b/src/Umbraco.Examine/DataServices/UmbracoMediaService.cs @@ -1,45 +1,45 @@ -using System; -using System.Collections; -using System.Linq; -using System.Xml.Linq; -using System.Xml.XPath; -using Examine.LuceneEngine; -using Umbraco.Core.Services; -using Umbraco.Core.Models; - -namespace UmbracoExamine.DataServices -{ - - /// - /// Data service used to query for media - /// - [Obsolete("This should no longer be used, latest content will be indexed by using the IMediaService directly")] - public class UmbracoMediaService - { - private readonly ServiceContext _services; - - public UmbracoMediaService(ServiceContext services) - { - _services = services; - } - - /// - /// This is quite an intensive operation... - /// get all root media, then get the XML structure for all children, - /// then run xpath against the navigator that's created - /// - /// - /// - [Obsolete("This should no longer be used, latest content will be indexed by using the IMediaService directly")] - public XDocument GetLatestMediaByXpath(string xpath) - { - var xmlMedia = XDocument.Parse(""); - foreach (var m in _services.MediaService.GetRootMedia()) - { - xmlMedia.Root.Add(m.ToDeepXml(_services.PackagingService)); - } - var result = ((IEnumerable)xmlMedia.XPathEvaluate(xpath)).Cast(); - return result.ToXDocument(); - } - } -} +using System; +using System.Collections; +using System.Linq; +using System.Xml.Linq; +using System.Xml.XPath; +using Examine.LuceneEngine; +using Umbraco.Core.Services; +using Umbraco.Core.Models; + +namespace Umbraco.Examine.DataServices +{ + + /// + /// Data service used to query for media + /// + [Obsolete("This should no longer be used, latest content will be indexed by using the IMediaService directly")] + public class UmbracoMediaService + { + private readonly ServiceContext _services; + + public UmbracoMediaService(ServiceContext services) + { + _services = services; + } + + /// + /// This is quite an intensive operation... + /// get all root media, then get the XML structure for all children, + /// then run xpath against the navigator that's created + /// + /// + /// + [Obsolete("This should no longer be used, latest content will be indexed by using the IMediaService directly")] + public XDocument GetLatestMediaByXpath(string xpath) + { + var xmlMedia = XDocument.Parse(""); + foreach (var m in _services.MediaService.GetRootMedia()) + { + xmlMedia.Root.Add(m.ToDeepXml(_services.PackagingService)); + } + var result = ((IEnumerable)xmlMedia.XPathEvaluate(xpath)).Cast(); + return result.ToXDocument(); + } + } +} diff --git a/src/UmbracoExamine/DeletePolicyTracker.cs b/src/Umbraco.Examine/DeletePolicyTracker.cs similarity index 96% rename from src/UmbracoExamine/DeletePolicyTracker.cs rename to src/Umbraco.Examine/DeletePolicyTracker.cs index 484217aac3..2c851ed39b 100644 --- a/src/UmbracoExamine/DeletePolicyTracker.cs +++ b/src/Umbraco.Examine/DeletePolicyTracker.cs @@ -2,7 +2,7 @@ using System.IO; using Lucene.Net.Index; -namespace UmbracoExamine +namespace Umbraco.Examine { internal sealed class DeletePolicyTracker { diff --git a/src/UmbracoExamine/ExamineIndexCollectionAccessor.cs b/src/Umbraco.Examine/ExamineIndexCollectionAccessor.cs similarity index 93% rename from src/UmbracoExamine/ExamineIndexCollectionAccessor.cs rename to src/Umbraco.Examine/ExamineIndexCollectionAccessor.cs index 8106c2bb40..806cbeadc7 100644 --- a/src/UmbracoExamine/ExamineIndexCollectionAccessor.cs +++ b/src/Umbraco.Examine/ExamineIndexCollectionAccessor.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Examine; -namespace UmbracoExamine +namespace Umbraco.Examine { /// /// Default implementation of IExamineIndexCollectionAccessor to return indexes from Examinemanager diff --git a/src/UmbracoExamine/IExamineIndexCollectionAccessor.cs b/src/Umbraco.Examine/IExamineIndexCollectionAccessor.cs similarity index 91% rename from src/UmbracoExamine/IExamineIndexCollectionAccessor.cs rename to src/Umbraco.Examine/IExamineIndexCollectionAccessor.cs index 9682136677..4c55abc789 100644 --- a/src/UmbracoExamine/IExamineIndexCollectionAccessor.cs +++ b/src/Umbraco.Examine/IExamineIndexCollectionAccessor.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Examine; -namespace UmbracoExamine +namespace Umbraco.Examine { /// /// Returns a collection of IExamineIndexer diff --git a/src/UmbracoExamine/IndexTypes.cs b/src/Umbraco.Examine/IndexTypes.cs similarity index 93% rename from src/UmbracoExamine/IndexTypes.cs rename to src/Umbraco.Examine/IndexTypes.cs index fb68d1dceb..fab72eca25 100644 --- a/src/UmbracoExamine/IndexTypes.cs +++ b/src/Umbraco.Examine/IndexTypes.cs @@ -1,39 +1,39 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Lucene.Net.Store; - -namespace UmbracoExamine -{ - /// - /// The index types stored in the Lucene Index - /// - public static class IndexTypes - { - - /// - /// The content index type - /// - /// - /// Is lower case because the Standard Analyzer requires lower case - /// - public const string Content = "content"; - - /// - /// The media index type - /// - /// - /// Is lower case because the Standard Analyzer requires lower case - /// - public const string Media = "media"; - - /// - /// The member index type - /// - /// - /// Is lower case because the Standard Analyzer requires lower case - /// - public const string Member = "member"; - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Lucene.Net.Store; + +namespace Umbraco.Examine +{ + /// + /// The index types stored in the Lucene Index + /// + public static class IndexTypes + { + + /// + /// The content index type + /// + /// + /// Is lower case because the Standard Analyzer requires lower case + /// + public const string Content = "content"; + + /// + /// The media index type + /// + /// + /// Is lower case because the Standard Analyzer requires lower case + /// + public const string Media = "media"; + + /// + /// The member index type + /// + /// + /// Is lower case because the Standard Analyzer requires lower case + /// + public const string Member = "member"; + } +} diff --git a/src/UmbracoExamine/LegacyExtensions.cs b/src/Umbraco.Examine/LegacyExtensions.cs similarity index 98% rename from src/UmbracoExamine/LegacyExtensions.cs rename to src/Umbraco.Examine/LegacyExtensions.cs index 64b1eb0778..cf1eebb5e4 100644 --- a/src/UmbracoExamine/LegacyExtensions.cs +++ b/src/Umbraco.Examine/LegacyExtensions.cs @@ -3,7 +3,7 @@ using Examine; using Examine.LuceneEngine.Config; using Umbraco.Core.Services; -namespace UmbracoExamine +namespace Umbraco.Examine { internal static class LegacyExtensions { diff --git a/src/UmbracoExamine/LegacyLibrary.cs b/src/Umbraco.Examine/LegacyLibrary.cs similarity index 96% rename from src/UmbracoExamine/LegacyLibrary.cs rename to src/Umbraco.Examine/LegacyLibrary.cs index b92712c8a4..5f3f296c9d 100644 --- a/src/UmbracoExamine/LegacyLibrary.cs +++ b/src/Umbraco.Examine/LegacyLibrary.cs @@ -1,47 +1,47 @@ -using System; -using System.Reflection; -using System.Xml.XPath; - -namespace UmbracoExamine -{ - /// - /// This is only used for backward compatibility to get access to the umbraco.library object but this needs to be done - /// via reflection because of the circular reference we have between Umbraco.Web and UmbracoExamine. - /// - internal static class LegacyLibrary - { - private static volatile Type _libraryType; - private static readonly object Locker = new object(); - private static Type LibraryType - { - get - { - if (_libraryType == null) - { - lock (Locker) - { - if (_libraryType == null) - { - var ass = Assembly.Load("Umbraco.Web"); - if (ass == null) - throw new InvalidOperationException("Could not load assembly Umbraco.Web.dll, the Umbraco.Web.dll needs to be loaded in the current app domain"); - var lib = ass.GetType("umbraco.library"); - if (lib == null) - throw new InvalidOperationException("Could not load type umbraco.library, the Umbraco.Web.dll needs to be loaded in the current app domain"); - _libraryType = lib; - } - } - } - return _libraryType; - } - } - - - internal static XPathNodeIterator GetXmlNodeByXPath(string xpathQuery) - { - var meth = LibraryType.GetMethod("GetXmlNodeByXPath", BindingFlags.Public | BindingFlags.Static); - return (XPathNodeIterator)meth.Invoke(null, new object[] { xpathQuery }); - } - - } -} +using System; +using System.Reflection; +using System.Xml.XPath; + +namespace Umbraco.Examine +{ + /// + /// This is only used for backward compatibility to get access to the umbraco.library object but this needs to be done + /// via reflection because of the circular reference we have between Umbraco.Web and UmbracoExamine. + /// + internal static class LegacyLibrary + { + private static volatile Type _libraryType; + private static readonly object Locker = new object(); + private static Type LibraryType + { + get + { + if (_libraryType == null) + { + lock (Locker) + { + if (_libraryType == null) + { + var ass = Assembly.Load("Umbraco.Web"); + if (ass == null) + throw new InvalidOperationException("Could not load assembly Umbraco.Web.dll, the Umbraco.Web.dll needs to be loaded in the current app domain"); + var lib = ass.GetType("umbraco.library"); + if (lib == null) + throw new InvalidOperationException("Could not load type umbraco.library, the Umbraco.Web.dll needs to be loaded in the current app domain"); + _libraryType = lib; + } + } + } + return _libraryType; + } + } + + + internal static XPathNodeIterator GetXmlNodeByXPath(string xpathQuery) + { + var meth = LibraryType.GetMethod("GetXmlNodeByXPath", BindingFlags.Public | BindingFlags.Static); + return (XPathNodeIterator)meth.Invoke(null, new object[] { xpathQuery }); + } + + } +} diff --git a/src/UmbracoExamine/LocalStorage/AzureLocalStorageDirectory.cs b/src/Umbraco.Examine/LocalStorage/AzureLocalStorageDirectory.cs similarity index 97% rename from src/UmbracoExamine/LocalStorage/AzureLocalStorageDirectory.cs rename to src/Umbraco.Examine/LocalStorage/AzureLocalStorageDirectory.cs index 5f9ea6ca34..1d750424b1 100644 --- a/src/UmbracoExamine/LocalStorage/AzureLocalStorageDirectory.cs +++ b/src/Umbraco.Examine/LocalStorage/AzureLocalStorageDirectory.cs @@ -5,7 +5,7 @@ using System.IO; using System.Web; using Umbraco.Core; -namespace UmbracoExamine.LocalStorage +namespace Umbraco.Examine.LocalStorage { /// /// When running on Azure websites, we can use the local user's storage space diff --git a/src/UmbracoExamine/LocalStorage/CodeGenLocalStorageDirectory.cs b/src/Umbraco.Examine/LocalStorage/CodeGenLocalStorageDirectory.cs similarity index 96% rename from src/UmbracoExamine/LocalStorage/CodeGenLocalStorageDirectory.cs rename to src/Umbraco.Examine/LocalStorage/CodeGenLocalStorageDirectory.cs index 8e29dad8c9..0678ca359e 100644 --- a/src/UmbracoExamine/LocalStorage/CodeGenLocalStorageDirectory.cs +++ b/src/Umbraco.Examine/LocalStorage/CodeGenLocalStorageDirectory.cs @@ -4,7 +4,7 @@ using System.ComponentModel; using System.IO; using System.Web; -namespace UmbracoExamine.LocalStorage +namespace Umbraco.Examine.LocalStorage { /// /// Use the ASP.Net CodeGen folder to store the index files diff --git a/src/UmbracoExamine/LocalStorage/DirectoryTracker.cs b/src/Umbraco.Examine/LocalStorage/DirectoryTracker.cs similarity index 95% rename from src/UmbracoExamine/LocalStorage/DirectoryTracker.cs rename to src/Umbraco.Examine/LocalStorage/DirectoryTracker.cs index 4b067d1a7a..5a6d4f6d2f 100644 --- a/src/UmbracoExamine/LocalStorage/DirectoryTracker.cs +++ b/src/Umbraco.Examine/LocalStorage/DirectoryTracker.cs @@ -3,7 +3,7 @@ using System.Collections.Concurrent; using System.IO; using Lucene.Net.Store; -namespace UmbracoExamine.LocalStorage +namespace Umbraco.Examine.LocalStorage { [Obsolete("This is used only for configuration based indexes")] internal class DirectoryTracker diff --git a/src/UmbracoExamine/LocalStorage/ILocalStorageDirectory.cs b/src/Umbraco.Examine/LocalStorage/ILocalStorageDirectory.cs similarity index 88% rename from src/UmbracoExamine/LocalStorage/ILocalStorageDirectory.cs rename to src/Umbraco.Examine/LocalStorage/ILocalStorageDirectory.cs index 7eb532b7e9..aec007ec60 100644 --- a/src/UmbracoExamine/LocalStorage/ILocalStorageDirectory.cs +++ b/src/Umbraco.Examine/LocalStorage/ILocalStorageDirectory.cs @@ -1,7 +1,7 @@ using System.Collections.Specialized; using System.IO; -namespace UmbracoExamine.LocalStorage +namespace Umbraco.Examine.LocalStorage { /// /// Used to resolve the local storage folder diff --git a/src/UmbracoExamine/LocalStorage/LocalStorageType.cs b/src/Umbraco.Examine/LocalStorage/LocalStorageType.cs similarity index 65% rename from src/UmbracoExamine/LocalStorage/LocalStorageType.cs rename to src/Umbraco.Examine/LocalStorage/LocalStorageType.cs index 87a289dca8..e1d2e0f943 100644 --- a/src/UmbracoExamine/LocalStorage/LocalStorageType.cs +++ b/src/Umbraco.Examine/LocalStorage/LocalStorageType.cs @@ -1,4 +1,4 @@ -namespace UmbracoExamine.LocalStorage +namespace Umbraco.Examine.LocalStorage { public enum LocalStorageType { diff --git a/src/UmbracoExamine/LocalStorage/LocalTempStorageDirectory.cs b/src/Umbraco.Examine/LocalStorage/LocalTempStorageDirectory.cs similarity index 99% rename from src/UmbracoExamine/LocalStorage/LocalTempStorageDirectory.cs rename to src/Umbraco.Examine/LocalStorage/LocalTempStorageDirectory.cs index b77870c188..6319dc8878 100644 --- a/src/UmbracoExamine/LocalStorage/LocalTempStorageDirectory.cs +++ b/src/Umbraco.Examine/LocalStorage/LocalTempStorageDirectory.cs @@ -4,7 +4,7 @@ using System.Linq; using Lucene.Net.Store; using Directory = Lucene.Net.Store.Directory; -namespace UmbracoExamine.LocalStorage +namespace Umbraco.Examine.LocalStorage { /// /// Used to read data from local temp storage and write to both local storage and main storage diff --git a/src/UmbracoExamine/LocalStorage/LocalTempStorageDirectoryTracker.cs b/src/Umbraco.Examine/LocalStorage/LocalTempStorageDirectoryTracker.cs similarity index 95% rename from src/UmbracoExamine/LocalStorage/LocalTempStorageDirectoryTracker.cs rename to src/Umbraco.Examine/LocalStorage/LocalTempStorageDirectoryTracker.cs index 481eec1696..d519925c07 100644 --- a/src/UmbracoExamine/LocalStorage/LocalTempStorageDirectoryTracker.cs +++ b/src/Umbraco.Examine/LocalStorage/LocalTempStorageDirectoryTracker.cs @@ -2,7 +2,7 @@ using System.IO; using Lucene.Net.Store; -namespace UmbracoExamine.LocalStorage +namespace Umbraco.Examine.LocalStorage { internal class LocalTempStorageDirectoryTracker { diff --git a/src/UmbracoExamine/LocalStorage/LocalTempStorageIndexer.cs b/src/Umbraco.Examine/LocalStorage/LocalTempStorageIndexer.cs similarity index 99% rename from src/UmbracoExamine/LocalStorage/LocalTempStorageIndexer.cs rename to src/Umbraco.Examine/LocalStorage/LocalTempStorageIndexer.cs index 2e05eb2bb8..93102578dd 100644 --- a/src/UmbracoExamine/LocalStorage/LocalTempStorageIndexer.cs +++ b/src/Umbraco.Examine/LocalStorage/LocalTempStorageIndexer.cs @@ -15,7 +15,7 @@ using Umbraco.Core.IO; using Umbraco.Core.Logging; using Directory = System.IO.Directory; -namespace UmbracoExamine.LocalStorage +namespace Umbraco.Examine.LocalStorage { internal enum InitializeDirectoryFlags { diff --git a/src/UmbracoExamine/LocalStorage/MultiIndexLock.cs b/src/Umbraco.Examine/LocalStorage/MultiIndexLock.cs similarity index 97% rename from src/UmbracoExamine/LocalStorage/MultiIndexLock.cs rename to src/Umbraco.Examine/LocalStorage/MultiIndexLock.cs index f7a74c1285..51abd23b2c 100644 --- a/src/UmbracoExamine/LocalStorage/MultiIndexLock.cs +++ b/src/Umbraco.Examine/LocalStorage/MultiIndexLock.cs @@ -1,6 +1,6 @@ using Lucene.Net.Store; -namespace UmbracoExamine.LocalStorage +namespace Umbraco.Examine.LocalStorage { /// /// Lock that wraps multiple locks diff --git a/src/UmbracoExamine/LocalStorage/MultiIndexLockFactory.cs b/src/Umbraco.Examine/LocalStorage/MultiIndexLockFactory.cs similarity index 96% rename from src/UmbracoExamine/LocalStorage/MultiIndexLockFactory.cs rename to src/Umbraco.Examine/LocalStorage/MultiIndexLockFactory.cs index af99c7f95c..085cf1580e 100644 --- a/src/UmbracoExamine/LocalStorage/MultiIndexLockFactory.cs +++ b/src/Umbraco.Examine/LocalStorage/MultiIndexLockFactory.cs @@ -1,7 +1,7 @@ using System; using Lucene.Net.Store; -namespace UmbracoExamine.LocalStorage +namespace Umbraco.Examine.LocalStorage { /// /// Lock factory that wraps multiple factories diff --git a/src/UmbracoExamine/LocalStorage/MultiIndexOutput.cs b/src/Umbraco.Examine/LocalStorage/MultiIndexOutput.cs similarity index 97% rename from src/UmbracoExamine/LocalStorage/MultiIndexOutput.cs rename to src/Umbraco.Examine/LocalStorage/MultiIndexOutput.cs index 3fea50e38c..bb6b061528 100644 --- a/src/UmbracoExamine/LocalStorage/MultiIndexOutput.cs +++ b/src/Umbraco.Examine/LocalStorage/MultiIndexOutput.cs @@ -2,7 +2,7 @@ using System.Linq; using Lucene.Net.Store; -namespace UmbracoExamine.LocalStorage +namespace Umbraco.Examine.LocalStorage { public class MultiIndexOutput : IndexOutput { diff --git a/src/UmbracoExamine/LoggingLevel.cs b/src/Umbraco.Examine/LoggingLevel.cs similarity index 87% rename from src/UmbracoExamine/LoggingLevel.cs rename to src/Umbraco.Examine/LoggingLevel.cs index f4daae513f..7ab8fbfad8 100644 --- a/src/UmbracoExamine/LoggingLevel.cs +++ b/src/Umbraco.Examine/LoggingLevel.cs @@ -1,13 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace UmbracoExamine -{ - [Obsolete("This object is no longer used since we support the log levels that are available with LogHelper")] - public enum LoggingLevel - { - Verbose, Normal - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Examine +{ + [Obsolete("This object is no longer used since we support the log levels that are available with LogHelper")] + public enum LoggingLevel + { + Verbose, Normal + } +} diff --git a/src/UmbracoExamine/Properties/AssemblyInfo.cs b/src/Umbraco.Examine/Properties/AssemblyInfo.cs similarity index 96% rename from src/UmbracoExamine/Properties/AssemblyInfo.cs rename to src/Umbraco.Examine/Properties/AssemblyInfo.cs index 5bd98bacd2..c08fd23daf 100644 --- a/src/UmbracoExamine/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Examine/Properties/AssemblyInfo.cs @@ -1,10 +1,10 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -[assembly: AssemblyTitle("Umbraco.Examine")] -[assembly: AssemblyDescription("Umbraco Examine")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyProduct("Umbraco CMS")] - -// Umbraco Cms -[assembly: InternalsVisibleTo("Umbraco.Tests")] +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Umbraco.Examine")] +[assembly: AssemblyDescription("Umbraco Examine")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("Umbraco CMS")] + +// Umbraco Cms +[assembly: InternalsVisibleTo("Umbraco.Tests")] diff --git a/src/UmbracoExamine/StaticField.cs b/src/Umbraco.Examine/StaticField.cs similarity index 92% rename from src/UmbracoExamine/StaticField.cs rename to src/Umbraco.Examine/StaticField.cs index c39eaff70d..237cbaaf26 100644 --- a/src/UmbracoExamine/StaticField.cs +++ b/src/Umbraco.Examine/StaticField.cs @@ -1,21 +1,21 @@ -using Examine; -using Examine.LuceneEngine; - -namespace UmbracoExamine -{ - internal class StaticField : IIndexField - { - public StaticField(string name, FieldIndexTypes indexType, bool enableSorting, string type) - { - Type = type; - EnableSorting = enableSorting; - IndexType = indexType; - Name = name; - } - - public string Name { get; set; } - public FieldIndexTypes IndexType { get; private set; } - public bool EnableSorting { get; set; } - public string Type { get; set; } - } -} +using Examine; +using Examine.LuceneEngine; + +namespace Umbraco.Examine +{ + internal class StaticField : IIndexField + { + public StaticField(string name, FieldIndexTypes indexType, bool enableSorting, string type) + { + Type = type; + EnableSorting = enableSorting; + IndexType = indexType; + Name = name; + } + + public string Name { get; set; } + public FieldIndexTypes IndexType { get; private set; } + public bool EnableSorting { get; set; } + public string Type { get; set; } + } +} diff --git a/src/UmbracoExamine/StaticFieldCollection.cs b/src/Umbraco.Examine/StaticFieldCollection.cs similarity index 96% rename from src/UmbracoExamine/StaticFieldCollection.cs rename to src/Umbraco.Examine/StaticFieldCollection.cs index 8b277767d8..2579299fc1 100644 --- a/src/UmbracoExamine/StaticFieldCollection.cs +++ b/src/Umbraco.Examine/StaticFieldCollection.cs @@ -1,6 +1,6 @@ using System.Collections.ObjectModel; -namespace UmbracoExamine +namespace Umbraco.Examine { internal class StaticFieldCollection : KeyedCollection { diff --git a/src/UmbracoExamine/UmbracoExamine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj similarity index 91% rename from src/UmbracoExamine/UmbracoExamine.csproj rename to src/Umbraco.Examine/Umbraco.Examine.csproj index 3435f0f03e..b7b8b3217a 100644 --- a/src/UmbracoExamine/UmbracoExamine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -1,68 +1,68 @@ - - - - v4.6.1 - false - {07FBC26B-2927-4A22-8D96-D644C667FECC} - Library - UmbracoExamine - UmbracoExamine - ..\ - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\UmbracoExamine.XML - false - - - - - - - - - - - - - - - - - - - - - - - - - - Umbraco.Core - - - - - - Properties\SolutionInfo.cs - - - - - + + + + v4.6.1 + false + {07FBC26B-2927-4A22-8D96-D644C667FECC} + Library + Umbraco.Examine + Umbraco.Examine + ..\ + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\Umbraco.Examine.xml + false + + + + + + + + + + + + + + + + + + + + + + + + + + Umbraco.Core + + + + + + Properties\SolutionInfo.cs + + + + + \ No newline at end of file diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs similarity index 97% rename from src/UmbracoExamine/UmbracoContentIndexer.cs rename to src/Umbraco.Examine/UmbracoContentIndexer.cs index 0b41bc86fe..26ba96121c 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -1,403 +1,403 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using Examine; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Services; -using Umbraco.Core.Strings; -using Examine.LuceneEngine.Config; -using Examine.LuceneEngine.Faceting; -using Examine.LuceneEngine.Indexing; -using Examine.LuceneEngine.Providers; -using Lucene.Net.Analysis; -using Lucene.Net.Store; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Scoping; -using IContentService = Umbraco.Core.Services.IContentService; -using IMediaService = Umbraco.Core.Services.IMediaService; - - -namespace UmbracoExamine -{ - /// - /// An indexer for Umbraco content and media - /// - public class UmbracoContentIndexer : BaseUmbracoIndexer - { - protected IContentService ContentService { get; } - protected IMediaService MediaService { get; } - protected IUserService UserService { get; } - - private readonly IEnumerable _urlSegmentProviders; - private readonly IScopeProvider _scopeProvider; - private int? _parentId; - - #region Constructors - - // default - bad, should inject instead - // usage: none - public UmbracoContentIndexer() - { - ContentService = Current.Services.ContentService; - MediaService = Current.Services.MediaService; - UserService = Current.Services.UserService; - - _urlSegmentProviders = Current.UrlSegmentProviders; - _scopeProvider = Current.ScopeProvider; - - InitializeQueries(); - } - - // usage: IndexInitializer (tests) - public UmbracoContentIndexer( - IEnumerable fieldDefinitions, - Directory luceneDirectory, - Analyzer defaultAnalyzer, - ProfilingLogger profilingLogger, - IContentService contentService, - IMediaService mediaService, - IUserService userService, - IEnumerable urlSegmentProviders, - IValueSetValidator validator, - UmbracoContentIndexerOptions options, - IScopeProvider scopeProvider, - FacetConfiguration facetConfiguration = null, - IDictionary> indexValueTypes = null) - : base(fieldDefinitions, luceneDirectory, defaultAnalyzer, profilingLogger, validator, facetConfiguration, indexValueTypes) - { - if (validator == null) throw new ArgumentNullException(nameof(validator)); - if (options == null) throw new ArgumentNullException(nameof(options)); - - SupportProtectedContent = options.SupportProtectedContent; - SupportUnpublishedContent = options.SupportUnpublishedContent; - ParentId = options.ParentId; - //backward compat hack: - IndexerData = new IndexCriteria(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), - //hack to set the parent Id for backwards compat, when using this ctor the IndexerData will (should) always be null - options.ParentId); - - ContentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); - MediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); - UserService = userService ?? throw new ArgumentNullException(nameof(userService)); - _urlSegmentProviders = urlSegmentProviders ?? throw new ArgumentNullException(nameof(urlSegmentProviders)); - _scopeProvider = scopeProvider ?? throw new ArgumentNullException(nameof(scopeProvider)); - - InitializeQueries(); - } - - private void InitializeQueries() - { - if (_publishedQuery == null) - _publishedQuery = Current.DatabaseContext.Query().Where(x => x.Published); - } - - #endregion - - #region Initialize - - /// - /// Set up all properties for the indexer based on configuration information specified. This will ensure that - /// all of the folders required by the indexer are created and exist. This will also create an instruction - /// file declaring the computer name that is part taking in the indexing. This file will then be used to - /// determine the master indexer machine in a load balanced environment (if one exists). - /// - /// The friendly name of the provider. - /// A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider. - /// - /// The name of the provider is null. - /// - /// - /// The name of the provider has a length of zero. - /// - /// - /// An attempt is made to call on a provider after the provider has already been initialized. - /// - - public override void Initialize(string name, NameValueCollection config) - { - - //check if there's a flag specifying to support unpublished content, - //if not, set to false; - bool supportUnpublished; - if (config["supportUnpublished"] != null && bool.TryParse(config["supportUnpublished"], out supportUnpublished)) - SupportUnpublishedContent = supportUnpublished; - else - SupportUnpublishedContent = false; - - - //check if there's a flag specifying to support protected content, - //if not, set to false; - bool supportProtected; - if (config["supportProtected"] != null && bool.TryParse(config["supportProtected"], out supportProtected)) - SupportProtectedContent = supportProtected; - else - SupportProtectedContent = false; - - base.Initialize(name, config); - } - - #endregion - - #region Properties - - /// - /// By default this is false, if set to true then the indexer will include indexing content that is flagged as publicly protected. - /// This property is ignored if SupportUnpublishedContent is set to true. - /// - public bool SupportProtectedContent { get; protected set; } - - /// - /// Determines if the manager will call the indexing methods when content is saved or deleted as - /// opposed to cache being updated. - /// - public bool SupportUnpublishedContent { get; protected set; } - - /// - /// If set this will filter the content items allowed to be indexed - /// - 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 => new[] {IndexTypes.Content, IndexTypes.Media}; - - #endregion - - #region Public methods - - /// - /// Deletes a node from the index. - /// - /// - /// When a content node is deleted, we also need to delete it's children from the index so we need to perform a - /// custom Lucene search to find all decendents and create Delete item queues for them too. - /// - /// ID of the node to delete - public override void DeleteFromIndex(string nodeId) - { - //find all descendants based on path - var descendantPath = $@"\-1\,*{nodeId}\,*"; - var rawQuery = $"{IndexPathFieldName}:{descendantPath}"; - var searcher = GetSearcher(); - var c = searcher.CreateCriteria(); - var filtered = c.RawQuery(rawQuery); - var results = searcher.Find(filtered); - - ProfilingLogger.Logger.Debug(GetType(), $"DeleteFromIndex with query: {rawQuery} (found {results.TotalItemCount} results)"); - - //need to create a delete queue item for each one found - foreach (var r in results) - { - ProcessIndexOperation(new IndexOperation() - { - Operation = IndexOperationType.Delete, - Item = new IndexItem(new ValueSet(r.LongId, string.Empty)) - }); - } - - base.DeleteFromIndex(nodeId); - } - #endregion - - #region Protected - - /// - /// This is a static query, it's parameters don't change so store statically - /// - private static IQuery _publishedQuery; - - protected override void PerformIndexAll(string type) - { - const int pageSize = 10000; - var pageIndex = 0; - - switch (type) - { - case IndexTypes.Content: - var contentParentId = -1; - if (ParentId.HasValue && ParentId.Value > 0) - { - contentParentId = ParentId.Value; - } - IContent[] content; - - do - { - long total; - - IEnumerable descendants; - if (SupportUnpublishedContent) - { - descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total); - } - else - { - //add the published filter - descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "Path", Direction.Ascending, true, _publishedQuery); - } - - //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 != null && IndexerData.IncludeNodeTypes.Any()) - { - content = descendants.Where(x => IndexerData.IncludeNodeTypes.Contains(x.ContentType.Alias)).ToArray(); - } - else - { - content = descendants.ToArray(); - } - - IndexItems(GetValueSets(content)); - - pageIndex++; - } while (content.Length == pageSize); - - break; - case IndexTypes.Media: - var mediaParentId = -1; - - if (ParentId.HasValue && ParentId.Value > 0) - { - mediaParentId = ParentId.Value; - } - - // merge note: 7.5 changes this to use mediaService.GetPagedXmlEntries but we cannot merge the - // change here as mediaService does not provide access to Xml in v8 - and actually Examine should - // not assume that Umbraco provides Xml at all. - - IMedia[] media; - - do - { - long total; - var descendants = MediaService.GetPagedDescendants(mediaParentId, pageIndex, pageSize, out total); - - //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 != null && IndexerData.IncludeNodeTypes.Any()) - { - media = descendants.Where(x => IndexerData.IncludeNodeTypes.Contains(x.ContentType.Alias)).ToArray(); - } - else - { - media = descendants.ToArray(); - } - - IndexItems(GetValueSets(media)); - - pageIndex++; - } while (media.Length == pageSize); - - break; - } - } - - private IEnumerable GetValueSets(IEnumerable content) - { - foreach (var c in content) - { - 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?.Id ?? 0}} - }; - - foreach (var property in c.Properties.Where(p => p?.Value != null && p.Value.ToString().IsNullOrWhiteSpace() == false)) - { - values.Add(property.Alias, new[] {property.Value}); - } - - var vs = new ValueSet(c.Id, IndexTypes.Content, c.ContentType.Alias, values); - - yield return vs; - } - } - - private IEnumerable GetValueSets(IEnumerable media) - { - 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?.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; - } - } - - /// - /// Creates an IIndexCriteria object based on the indexSet passed in and our DataService - /// - /// - /// - /// - /// 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()) - { - //NOTE: We are using a singleton here because: This is only ever used for configuration based scenarios, this is never - // used when the index is configured via code (the default), in which case IIndexCriteria is never used. When this is used - // the DI ctor is not used. - return indexSet.ToIndexCriteria(Current.Services.ContentTypeService); - } - return base.GetIndexerData(indexSet); - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using Examine; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using Examine.LuceneEngine.Config; +using Examine.LuceneEngine.Faceting; +using Examine.LuceneEngine.Indexing; +using Examine.LuceneEngine.Providers; +using Lucene.Net.Analysis; +using Lucene.Net.Store; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Scoping; +using IContentService = Umbraco.Core.Services.IContentService; +using IMediaService = Umbraco.Core.Services.IMediaService; + + +namespace Umbraco.Examine +{ + /// + /// An indexer for Umbraco content and media + /// + public class UmbracoContentIndexer : BaseUmbracoIndexer + { + protected IContentService ContentService { get; } + protected IMediaService MediaService { get; } + protected IUserService UserService { get; } + + private readonly IEnumerable _urlSegmentProviders; + private readonly IScopeProvider _scopeProvider; + private int? _parentId; + + #region Constructors + + // default - bad, should inject instead + // usage: none + public UmbracoContentIndexer() + { + ContentService = Current.Services.ContentService; + MediaService = Current.Services.MediaService; + UserService = Current.Services.UserService; + + _urlSegmentProviders = Current.UrlSegmentProviders; + _scopeProvider = Current.ScopeProvider; + + InitializeQueries(); + } + + // usage: IndexInitializer (tests) + public UmbracoContentIndexer( + IEnumerable fieldDefinitions, + Directory luceneDirectory, + Analyzer defaultAnalyzer, + ProfilingLogger profilingLogger, + IContentService contentService, + IMediaService mediaService, + IUserService userService, + IEnumerable urlSegmentProviders, + IValueSetValidator validator, + UmbracoContentIndexerOptions options, + IScopeProvider scopeProvider, + FacetConfiguration facetConfiguration = null, + IDictionary> indexValueTypes = null) + : base(fieldDefinitions, luceneDirectory, defaultAnalyzer, profilingLogger, validator, facetConfiguration, indexValueTypes) + { + if (validator == null) throw new ArgumentNullException(nameof(validator)); + if (options == null) throw new ArgumentNullException(nameof(options)); + + SupportProtectedContent = options.SupportProtectedContent; + SupportUnpublishedContent = options.SupportUnpublishedContent; + ParentId = options.ParentId; + //backward compat hack: + IndexerData = new IndexCriteria(Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), Enumerable.Empty(), + //hack to set the parent Id for backwards compat, when using this ctor the IndexerData will (should) always be null + options.ParentId); + + ContentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); + MediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); + UserService = userService ?? throw new ArgumentNullException(nameof(userService)); + _urlSegmentProviders = urlSegmentProviders ?? throw new ArgumentNullException(nameof(urlSegmentProviders)); + _scopeProvider = scopeProvider ?? throw new ArgumentNullException(nameof(scopeProvider)); + + InitializeQueries(); + } + + private void InitializeQueries() + { + if (_publishedQuery == null) + _publishedQuery = Current.DatabaseContext.Query().Where(x => x.Published); + } + + #endregion + + #region Initialize + + /// + /// Set up all properties for the indexer based on configuration information specified. This will ensure that + /// all of the folders required by the indexer are created and exist. This will also create an instruction + /// file declaring the computer name that is part taking in the indexing. This file will then be used to + /// determine the master indexer machine in a load balanced environment (if one exists). + /// + /// The friendly name of the provider. + /// A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider. + /// + /// The name of the provider is null. + /// + /// + /// The name of the provider has a length of zero. + /// + /// + /// An attempt is made to call on a provider after the provider has already been initialized. + /// + + public override void Initialize(string name, NameValueCollection config) + { + + //check if there's a flag specifying to support unpublished content, + //if not, set to false; + bool supportUnpublished; + if (config["supportUnpublished"] != null && bool.TryParse(config["supportUnpublished"], out supportUnpublished)) + SupportUnpublishedContent = supportUnpublished; + else + SupportUnpublishedContent = false; + + + //check if there's a flag specifying to support protected content, + //if not, set to false; + bool supportProtected; + if (config["supportProtected"] != null && bool.TryParse(config["supportProtected"], out supportProtected)) + SupportProtectedContent = supportProtected; + else + SupportProtectedContent = false; + + base.Initialize(name, config); + } + + #endregion + + #region Properties + + /// + /// By default this is false, if set to true then the indexer will include indexing content that is flagged as publicly protected. + /// This property is ignored if SupportUnpublishedContent is set to true. + /// + public bool SupportProtectedContent { get; protected set; } + + /// + /// Determines if the manager will call the indexing methods when content is saved or deleted as + /// opposed to cache being updated. + /// + public bool SupportUnpublishedContent { get; protected set; } + + /// + /// If set this will filter the content items allowed to be indexed + /// + 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 => new[] {IndexTypes.Content, IndexTypes.Media}; + + #endregion + + #region Public methods + + /// + /// Deletes a node from the index. + /// + /// + /// When a content node is deleted, we also need to delete it's children from the index so we need to perform a + /// custom Lucene search to find all decendents and create Delete item queues for them too. + /// + /// ID of the node to delete + public override void DeleteFromIndex(string nodeId) + { + //find all descendants based on path + var descendantPath = $@"\-1\,*{nodeId}\,*"; + var rawQuery = $"{IndexPathFieldName}:{descendantPath}"; + var searcher = GetSearcher(); + var c = searcher.CreateCriteria(); + var filtered = c.RawQuery(rawQuery); + var results = searcher.Find(filtered); + + ProfilingLogger.Logger.Debug(GetType(), $"DeleteFromIndex with query: {rawQuery} (found {results.TotalItemCount} results)"); + + //need to create a delete queue item for each one found + foreach (var r in results) + { + ProcessIndexOperation(new IndexOperation() + { + Operation = IndexOperationType.Delete, + Item = new IndexItem(new ValueSet(r.LongId, string.Empty)) + }); + } + + base.DeleteFromIndex(nodeId); + } + #endregion + + #region Protected + + /// + /// This is a static query, it's parameters don't change so store statically + /// + private static IQuery _publishedQuery; + + protected override void PerformIndexAll(string type) + { + const int pageSize = 10000; + var pageIndex = 0; + + switch (type) + { + case IndexTypes.Content: + var contentParentId = -1; + if (ParentId.HasValue && ParentId.Value > 0) + { + contentParentId = ParentId.Value; + } + IContent[] content; + + do + { + long total; + + IEnumerable descendants; + if (SupportUnpublishedContent) + { + descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total); + } + else + { + //add the published filter + descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "Path", Direction.Ascending, true, _publishedQuery); + } + + //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 != null && IndexerData.IncludeNodeTypes.Any()) + { + content = descendants.Where(x => IndexerData.IncludeNodeTypes.Contains(x.ContentType.Alias)).ToArray(); + } + else + { + content = descendants.ToArray(); + } + + IndexItems(GetValueSets(content)); + + pageIndex++; + } while (content.Length == pageSize); + + break; + case IndexTypes.Media: + var mediaParentId = -1; + + if (ParentId.HasValue && ParentId.Value > 0) + { + mediaParentId = ParentId.Value; + } + + // merge note: 7.5 changes this to use mediaService.GetPagedXmlEntries but we cannot merge the + // change here as mediaService does not provide access to Xml in v8 - and actually Examine should + // not assume that Umbraco provides Xml at all. + + IMedia[] media; + + do + { + long total; + var descendants = MediaService.GetPagedDescendants(mediaParentId, pageIndex, pageSize, out total); + + //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 != null && IndexerData.IncludeNodeTypes.Any()) + { + media = descendants.Where(x => IndexerData.IncludeNodeTypes.Contains(x.ContentType.Alias)).ToArray(); + } + else + { + media = descendants.ToArray(); + } + + IndexItems(GetValueSets(media)); + + pageIndex++; + } while (media.Length == pageSize); + + break; + } + } + + private IEnumerable GetValueSets(IEnumerable content) + { + foreach (var c in content) + { + 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?.Id ?? 0}} + }; + + foreach (var property in c.Properties.Where(p => p?.Value != null && p.Value.ToString().IsNullOrWhiteSpace() == false)) + { + values.Add(property.Alias, new[] {property.Value}); + } + + var vs = new ValueSet(c.Id, IndexTypes.Content, c.ContentType.Alias, values); + + yield return vs; + } + } + + private IEnumerable GetValueSets(IEnumerable media) + { + 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?.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; + } + } + + /// + /// Creates an IIndexCriteria object based on the indexSet passed in and our DataService + /// + /// + /// + /// + /// 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()) + { + //NOTE: We are using a singleton here because: This is only ever used for configuration based scenarios, this is never + // used when the index is configured via code (the default), in which case IIndexCriteria is never used. When this is used + // the DI ctor is not used. + return indexSet.ToIndexCriteria(Current.Services.ContentTypeService); + } + return base.GetIndexerData(indexSet); + } + + #endregion + } +} diff --git a/src/UmbracoExamine/UmbracoContentIndexerOptions.cs b/src/Umbraco.Examine/UmbracoContentIndexerOptions.cs similarity index 96% rename from src/UmbracoExamine/UmbracoContentIndexerOptions.cs rename to src/Umbraco.Examine/UmbracoContentIndexerOptions.cs index 125d33cef3..507556cdef 100644 --- a/src/UmbracoExamine/UmbracoContentIndexerOptions.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexerOptions.cs @@ -1,6 +1,6 @@ using System; -namespace UmbracoExamine +namespace Umbraco.Examine { /// /// Options used to configure the umbraco content indexer diff --git a/src/UmbracoExamine/UmbracoContentValueSetValidator.cs b/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs similarity index 99% rename from src/UmbracoExamine/UmbracoContentValueSetValidator.cs rename to src/Umbraco.Examine/UmbracoContentValueSetValidator.cs index 76929aca82..2d414cc38f 100644 --- a/src/UmbracoExamine/UmbracoContentValueSetValidator.cs +++ b/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs @@ -5,7 +5,7 @@ using Examine.LuceneEngine.Providers; using Umbraco.Core; using Umbraco.Core.Services; -namespace UmbracoExamine +namespace Umbraco.Examine { /// /// Used to validate a ValueSet for content - based on permissions, parent id, etc.... diff --git a/src/UmbracoExamine/UmbracoExamineSearcher.cs b/src/Umbraco.Examine/UmbracoExamineSearcher.cs similarity index 96% rename from src/UmbracoExamine/UmbracoExamineSearcher.cs rename to src/Umbraco.Examine/UmbracoExamineSearcher.cs index 606303e8d6..c62c763cf8 100644 --- a/src/UmbracoExamine/UmbracoExamineSearcher.cs +++ b/src/Umbraco.Examine/UmbracoExamineSearcher.cs @@ -1,191 +1,191 @@ -using System; -using System.ComponentModel; -using System.IO; -using System.Linq; -using System.Security; -using System.Web; -using System.Web.Compilation; -using Examine; -using Examine.LuceneEngine.Config; -using Examine.Providers; -using Examine.SearchCriteria; -using Lucene.Net.Index; -using Lucene.Net.Store; -using Umbraco.Core; -using Examine.LuceneEngine; -using Examine.LuceneEngine.Providers; -using Examine.LuceneEngine.SearchCriteria; -using Lucene.Net.Analysis; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; -using UmbracoExamine.LocalStorage; -using Directory = Lucene.Net.Store.Directory; - - -namespace UmbracoExamine -{ - /// - /// An Examine searcher which uses Lucene.Net as the - /// - public class UmbracoExamineSearcher : LuceneSearcher - { - - private Lazy _localTempDirectory; - private LocalStorageType _localStorageType = LocalStorageType.Sync; - private string _name; - private readonly bool _configBased = false; - - /// - /// Default constructor for config based construction - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public UmbracoExamineSearcher() - : base() - { - _configBased = true; - } - - /// - /// Constructor to allow for creating an indexer at runtime - /// - /// - /// - public UmbracoExamineSearcher(DirectoryInfo indexPath, Analyzer analyzer) - : base(indexPath, analyzer) - { - _configBased = false; - } - - /// - /// Constructor to allow for creating an indexer at runtime - /// - /// - /// - public UmbracoExamineSearcher(Directory luceneDirectory, Analyzer analyzer) - : base(luceneDirectory, analyzer) - { - _configBased = false; - } - - /// - /// we override name because we need to manually set it if !CanInitialize() - /// since we cannot call base.Initialize in that case. - /// - public override string Name - { - get { return _name; } - } - - /// - /// Method used for initializing based on a configuration based searcher - /// - /// - /// - public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) - { - if (name == null) throw new ArgumentNullException("name"); - - //ensure name is set - _name = name; - - //We need to check if we actually can initialize, if not then don't continue - if (CanInitialize() == false) - { - return; - } - - base.Initialize(name, config); - - if (config != null && config["useTempStorage"] != null) - { - throw new NotImplementedException("Fix how local temp storage works and is synced with Examine v2.0 - since a writer is always open we cannot snapshot it, we need to use the same logic in AzureDirectory"); - - //Use the temp storage directory which will store the index in the local/codegen folder, this is useful - // for websites that are running from a remove file server and file IO latency becomes an issue - var attemptUseTempStorage = config["useTempStorage"].TryConvertTo(); - if (attemptUseTempStorage) - { - //this is the default - ILocalStorageDirectory localStorageDir = new CodeGenLocalStorageDirectory(); - if (config["tempStorageDirectory"] != null) - { - //try to get the type - var dirType = BuildManager.GetType(config["tempStorageDirectory"], false); - if (dirType != null) - { - try - { - localStorageDir = (ILocalStorageDirectory)Activator.CreateInstance(dirType); - } - catch (Exception ex) - { - Current.Logger.Error( - string.Format("Could not create a temp storage location of type {0}, reverting to use the " + typeof(CodeGenLocalStorageDirectory).FullName, dirType), - ex); - } - } - } - var indexSet = IndexSets.Instance.Sets[IndexSetName]; - var configuredPath = indexSet.IndexPath; - var tempPath = localStorageDir.GetLocalStorageDirectory(config, configuredPath); - if (tempPath == null) throw new InvalidOperationException("Could not resolve a temp location from the " + localStorageDir.GetType() + " specified"); - var localTempPath = tempPath.FullName; - _localStorageType = attemptUseTempStorage.Result; - - //initialize the lazy callback - _localTempDirectory = new Lazy(() => - { - switch (_localStorageType) - { - case LocalStorageType.Sync: - var fsDir = base.GetLuceneDirectory() as FSDirectory; - if (fsDir != null) - { - return LocalTempStorageDirectoryTracker.Current.GetDirectory( - new DirectoryInfo(localTempPath), - fsDir); - } - return base.GetLuceneDirectory(); - case LocalStorageType.LocalOnly: - return DirectoryTracker.Current.GetDirectory(new DirectoryInfo(localTempPath)); - default: - throw new ArgumentOutOfRangeException(); - } - }); - } - } - } - - /// - /// Returns true if the Umbraco application is in a state that we can initialize the examine indexes - /// - - protected bool CanInitialize() - { - // only affects indexers that are config file based, if an index was created via code then - // this has no effect, it is assumed the index would not be created if it could not be initialized - return _configBased == false || Current.RuntimeState.Level == RuntimeLevel.Run; - } - - /// - /// Returns a list of fields to search on, this will also exclude the IndexPathFieldName and node type alias - /// - /// - protected internal override string[] GetSearchFields() - { - var fields = base.GetSearchFields(); - return fields - .Where(x => x != BaseUmbracoIndexer.IndexPathFieldName) - .Where(x => x != LuceneIndexer.NodeTypeAliasFieldName) - .ToArray(); - } - - protected override Directory GetLuceneDirectory() - { - //local temp storage is not enabled, just return the default - return _localTempDirectory.IsValueCreated == false - ? base.GetLuceneDirectory() - : _localTempDirectory.Value; - } - } -} +using System; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Security; +using System.Web; +using System.Web.Compilation; +using Examine; +using Examine.LuceneEngine.Config; +using Examine.Providers; +using Examine.SearchCriteria; +using Lucene.Net.Index; +using Lucene.Net.Store; +using Umbraco.Core; +using Examine.LuceneEngine; +using Examine.LuceneEngine.Providers; +using Examine.LuceneEngine.SearchCriteria; +using Lucene.Net.Analysis; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; +using Umbraco.Examine.LocalStorage; +using Directory = Lucene.Net.Store.Directory; + + +namespace Umbraco.Examine +{ + /// + /// An Examine searcher which uses Lucene.Net as the + /// + public class UmbracoExamineSearcher : LuceneSearcher + { + + private Lazy _localTempDirectory; + private LocalStorageType _localStorageType = LocalStorageType.Sync; + private string _name; + private readonly bool _configBased = false; + + /// + /// Default constructor for config based construction + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public UmbracoExamineSearcher() + : base() + { + _configBased = true; + } + + /// + /// Constructor to allow for creating an indexer at runtime + /// + /// + /// + public UmbracoExamineSearcher(DirectoryInfo indexPath, Analyzer analyzer) + : base(indexPath, analyzer) + { + _configBased = false; + } + + /// + /// Constructor to allow for creating an indexer at runtime + /// + /// + /// + public UmbracoExamineSearcher(Directory luceneDirectory, Analyzer analyzer) + : base(luceneDirectory, analyzer) + { + _configBased = false; + } + + /// + /// we override name because we need to manually set it if !CanInitialize() + /// since we cannot call base.Initialize in that case. + /// + public override string Name + { + get { return _name; } + } + + /// + /// Method used for initializing based on a configuration based searcher + /// + /// + /// + public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) + { + if (name == null) throw new ArgumentNullException("name"); + + //ensure name is set + _name = name; + + //We need to check if we actually can initialize, if not then don't continue + if (CanInitialize() == false) + { + return; + } + + base.Initialize(name, config); + + if (config != null && config["useTempStorage"] != null) + { + throw new NotImplementedException("Fix how local temp storage works and is synced with Examine v2.0 - since a writer is always open we cannot snapshot it, we need to use the same logic in AzureDirectory"); + + //Use the temp storage directory which will store the index in the local/codegen folder, this is useful + // for websites that are running from a remove file server and file IO latency becomes an issue + var attemptUseTempStorage = config["useTempStorage"].TryConvertTo(); + if (attemptUseTempStorage) + { + //this is the default + ILocalStorageDirectory localStorageDir = new CodeGenLocalStorageDirectory(); + if (config["tempStorageDirectory"] != null) + { + //try to get the type + var dirType = BuildManager.GetType(config["tempStorageDirectory"], false); + if (dirType != null) + { + try + { + localStorageDir = (ILocalStorageDirectory)Activator.CreateInstance(dirType); + } + catch (Exception ex) + { + Current.Logger.Error( + string.Format("Could not create a temp storage location of type {0}, reverting to use the " + typeof(CodeGenLocalStorageDirectory).FullName, dirType), + ex); + } + } + } + var indexSet = IndexSets.Instance.Sets[IndexSetName]; + var configuredPath = indexSet.IndexPath; + var tempPath = localStorageDir.GetLocalStorageDirectory(config, configuredPath); + if (tempPath == null) throw new InvalidOperationException("Could not resolve a temp location from the " + localStorageDir.GetType() + " specified"); + var localTempPath = tempPath.FullName; + _localStorageType = attemptUseTempStorage.Result; + + //initialize the lazy callback + _localTempDirectory = new Lazy(() => + { + switch (_localStorageType) + { + case LocalStorageType.Sync: + var fsDir = base.GetLuceneDirectory() as FSDirectory; + if (fsDir != null) + { + return LocalTempStorageDirectoryTracker.Current.GetDirectory( + new DirectoryInfo(localTempPath), + fsDir); + } + return base.GetLuceneDirectory(); + case LocalStorageType.LocalOnly: + return DirectoryTracker.Current.GetDirectory(new DirectoryInfo(localTempPath)); + default: + throw new ArgumentOutOfRangeException(); + } + }); + } + } + } + + /// + /// Returns true if the Umbraco application is in a state that we can initialize the examine indexes + /// + + protected bool CanInitialize() + { + // only affects indexers that are config file based, if an index was created via code then + // this has no effect, it is assumed the index would not be created if it could not be initialized + return _configBased == false || Current.RuntimeState.Level == RuntimeLevel.Run; + } + + /// + /// Returns a list of fields to search on, this will also exclude the IndexPathFieldName and node type alias + /// + /// + protected override string[] GetSearchFields() + { + var fields = base.GetSearchFields(); + return fields + .Where(x => x != BaseUmbracoIndexer.IndexPathFieldName) + .Where(x => x != LuceneIndexer.NodeTypeAliasFieldName) + .ToArray(); + } + + protected override Directory GetLuceneDirectory() + { + //local temp storage is not enabled, just return the default + return _localTempDirectory.IsValueCreated == false + ? base.GetLuceneDirectory() + : _localTempDirectory.Value; + } + } +} diff --git a/src/UmbracoExamine/UmbracoMemberIndexer.cs b/src/Umbraco.Examine/UmbracoMemberIndexer.cs similarity index 97% rename from src/UmbracoExamine/UmbracoMemberIndexer.cs rename to src/Umbraco.Examine/UmbracoMemberIndexer.cs index d1f475b2cc..188f3df9ed 100644 --- a/src/UmbracoExamine/UmbracoMemberIndexer.cs +++ b/src/Umbraco.Examine/UmbracoMemberIndexer.cs @@ -1,223 +1,223 @@ -using System; -using System.Collections; -using System.Linq; -using System.Xml.Linq; -using Examine.LuceneEngine.Config; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; -using Umbraco.Core.Services; -using Umbraco.Core.Strings; -using System.Collections.Generic; -using Examine; -using System.IO; -using Examine.LuceneEngine.Providers; -using Lucene.Net.Analysis; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; -using Umbraco.Core.Scoping; -using Directory = Lucene.Net.Store.Directory; - -namespace UmbracoExamine -{ - - /// - /// Custom indexer for members - /// - public class UmbracoMemberIndexer : BaseUmbracoIndexer - { - private readonly IMemberService _memberService; - private readonly IScopeProvider _scopeProvider; - - /// - /// Default constructor - /// - public UmbracoMemberIndexer() - : base() - { - _memberService = Current.Services.MemberService; - _scopeProvider = Current.ScopeProvider; - } - - /// - /// Constructor to allow for creating an indexer at runtime - /// - /// - /// - /// - /// - /// - /// - public UmbracoMemberIndexer( - IEnumerable fieldDefinitions, - Directory luceneDirectory, - Analyzer analyzer, - ProfilingLogger profilingLogger, - IValueSetValidator validator, - IMemberService memberService) : - base(fieldDefinitions, luceneDirectory, analyzer, profilingLogger, validator) - { - if (memberService == null) throw new ArgumentNullException("memberService"); - _memberService = memberService; - } - - /// - /// Ensures that the'_searchEmail' is added to the user fields so that it is indexed - without having to modify the config - /// - /// - /// - [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) - { - //TODO: This is only required for config based index delcaration - We need to change this! - - var indexerData = base.GetIndexerData(indexSet); - - if (CanInitialize()) - { - //If the fields are missing a custom _searchEmail, then add it - - if (indexerData.UserFields.Any(x => x.Name == "_searchEmail") == false) - { - var field = new IndexField { Name = "_searchEmail" }; - var policy = IndexFieldPolicies.FirstOrDefault(x => x.Name == "_searchEmail"); - if (policy != null) - { - field.Type = policy.Type; - field.EnableSorting = policy.EnableSorting; - } - - return new IndexCriteria( - indexerData.StandardFields, - indexerData.UserFields.Concat(new[] { field }), - indexerData.IncludeNodeTypes, - indexerData.ExcludeNodeTypes, - indexerData.ParentNodeId - ); - } - } - - 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[] {IndexTypes.Member}; } - } - - /// - /// Reindex all members - /// - /// - protected override void PerformIndexAll(string type) - { - //This only supports members - if (SupportedTypes.Contains(type) == false) - return; - - const int pageSize = 1000; - var pageIndex = 0; - - IMember[] members; - - using (var scope = _scopeProvider.CreateScope()) - { - if (IndexerData != null && IndexerData.IncludeNodeTypes.Any()) - { - //if there are specific node types then just index those - foreach (var nodeType in IndexerData.IncludeNodeTypes) - { - do - { - long total; - members = _memberService.GetAll(pageIndex, pageSize, out total, "LoginName", Direction.Ascending, true, null, nodeType).ToArray(); - - IndexItems(GetValueSets(members)); - - pageIndex++; - } while (members.Length == pageSize); - } - } - else - { - //no node types specified, do all members - do - { - long total; - members = _memberService.GetAll(pageIndex, pageSize, out total).ToArray(); - - IndexItems(GetValueSets(members)); - - pageIndex++; - } while (members.Length == pageSize); - } - scope.Complete(); - } - } - - private IEnumerable GetValueSets(IEnumerable 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); - - if (e.OriginalValues.ContainsKey("key") && e.IndexItem.ValueSet.Values.ContainsKey("__key") == false) - { - 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("@", " ") }; - } - - } - - } -} +using System; +using System.Collections; +using System.Linq; +using System.Xml.Linq; +using Examine.LuceneEngine.Config; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using System.Collections.Generic; +using Examine; +using System.IO; +using Examine.LuceneEngine.Providers; +using Lucene.Net.Analysis; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Scoping; +using Directory = Lucene.Net.Store.Directory; + +namespace Umbraco.Examine +{ + + /// + /// Custom indexer for members + /// + public class UmbracoMemberIndexer : BaseUmbracoIndexer + { + private readonly IMemberService _memberService; + private readonly IScopeProvider _scopeProvider; + + /// + /// Default constructor + /// + public UmbracoMemberIndexer() + : base() + { + _memberService = Current.Services.MemberService; + _scopeProvider = Current.ScopeProvider; + } + + /// + /// Constructor to allow for creating an indexer at runtime + /// + /// + /// + /// + /// + /// + /// + public UmbracoMemberIndexer( + IEnumerable fieldDefinitions, + Directory luceneDirectory, + Analyzer analyzer, + ProfilingLogger profilingLogger, + IValueSetValidator validator, + IMemberService memberService) : + base(fieldDefinitions, luceneDirectory, analyzer, profilingLogger, validator) + { + if (memberService == null) throw new ArgumentNullException("memberService"); + _memberService = memberService; + } + + /// + /// Ensures that the'_searchEmail' is added to the user fields so that it is indexed - without having to modify the config + /// + /// + /// + [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) + { + //TODO: This is only required for config based index delcaration - We need to change this! + + var indexerData = base.GetIndexerData(indexSet); + + if (CanInitialize()) + { + //If the fields are missing a custom _searchEmail, then add it + + if (indexerData.UserFields.Any(x => x.Name == "_searchEmail") == false) + { + var field = new IndexField { Name = "_searchEmail" }; + var policy = IndexFieldPolicies.FirstOrDefault(x => x.Name == "_searchEmail"); + if (policy != null) + { + field.Type = policy.Type; + field.EnableSorting = policy.EnableSorting; + } + + return new IndexCriteria( + indexerData.StandardFields, + indexerData.UserFields.Concat(new[] { field }), + indexerData.IncludeNodeTypes, + indexerData.ExcludeNodeTypes, + indexerData.ParentNodeId + ); + } + } + + 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[] {IndexTypes.Member}; } + } + + /// + /// Reindex all members + /// + /// + protected override void PerformIndexAll(string type) + { + //This only supports members + if (SupportedTypes.Contains(type) == false) + return; + + const int pageSize = 1000; + var pageIndex = 0; + + IMember[] members; + + using (var scope = _scopeProvider.CreateScope()) + { + if (IndexerData != null && IndexerData.IncludeNodeTypes.Any()) + { + //if there are specific node types then just index those + foreach (var nodeType in IndexerData.IncludeNodeTypes) + { + do + { + long total; + members = _memberService.GetAll(pageIndex, pageSize, out total, "LoginName", Direction.Ascending, true, null, nodeType).ToArray(); + + IndexItems(GetValueSets(members)); + + pageIndex++; + } while (members.Length == pageSize); + } + } + else + { + //no node types specified, do all members + do + { + long total; + members = _memberService.GetAll(pageIndex, pageSize, out total).ToArray(); + + IndexItems(GetValueSets(members)); + + pageIndex++; + } while (members.Length == pageSize); + } + scope.Complete(); + } + } + + private IEnumerable GetValueSets(IEnumerable 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); + + if (e.OriginalValues.ContainsKey("key") && e.IndexItem.ValueSet.Values.ContainsKey("__key") == false) + { + 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("@", " ") }; + } + + } + + } +} diff --git a/src/UmbracoExamine/XsltExtensions.cs b/src/Umbraco.Examine/XsltExtensions.cs similarity index 97% rename from src/UmbracoExamine/XsltExtensions.cs rename to src/Umbraco.Examine/XsltExtensions.cs index f2aa8b950e..63bb0a480a 100644 --- a/src/UmbracoExamine/XsltExtensions.cs +++ b/src/Umbraco.Examine/XsltExtensions.cs @@ -1,255 +1,255 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security; -using System.Xml.Linq; -using System.Xml.XPath; -using Examine; -using Examine.LuceneEngine; -using Examine.LuceneEngine.Providers; -using Examine.LuceneEngine.SearchCriteria; -using Examine.SearchCriteria; -using Examine.Providers; -using Umbraco.Core.Macros; - -namespace UmbracoExamine -{ - /// - /// Methods to support Umbraco XSLT extensions. - /// - /// - /// XSLT extensions will ONLY work for provider that have a base class of BaseUmbracoIndexer - /// - [XsltExtension("Examine")] - - public class XsltExtensions - { - /// - /// Uses the provider specified to search, returning an XPathNodeIterator - /// - /// - /// - /// - /// - /// - internal static XPathNodeIterator Search(string searchText, bool useWildcards, LuceneSearcher provider, string indexType) - { - if (provider == null) throw new ArgumentNullException("provider"); - - var results = provider.Search(searchText, useWildcards, indexType); - return GetResultsAsXml(results); - } - - /// - /// Uses the provider specified to search, returning an XPathNodeIterator - /// - /// The search text. - /// if set to true [use wildcards]. - /// Name of the provider. - /// Type of the index. - /// - public static XPathNodeIterator Search(string searchText, bool useWildcards, string providerName, string indexType) - { - var provider = (LuceneSearcher)ExamineManager.Instance.SearchProviderCollection[providerName]; - - return Search(searchText, useWildcards, provider, indexType); - } - - /// - /// Uses the provider specified to search, returning an XPathNodeIterator - /// - /// - /// - /// - /// - public static XPathNodeIterator Search(string searchText, bool useWildcards, string providerName) - { - return Search(searchText, useWildcards, providerName, string.Empty); - } - - /// - /// Uses the default provider specified to search, returning an XPathNodeIterator - /// - /// The search query - /// Enable a wildcard search query - /// A node-set of the search results - public static XPathNodeIterator Search(string searchText, bool useWildcards) - { - return Search(searchText, useWildcards, ExamineManager.Instance.DefaultSearchProvider.Name); - } - - /// - /// Uses the default provider specified to search, returning an XPathNodeIterator - /// - /// The search query - /// A node-set of the search results - public static XPathNodeIterator Search(string searchText) - { - return Search(searchText, true); - } - - /// - /// Will perform a search against the media index type only - /// - /// - /// - /// - /// - public static XPathNodeIterator SearchMediaOnly(string searchText, bool useWildcards, string providerName) - { - return Search(searchText, useWildcards, providerName, IndexTypes.Media); - } - - /// - /// Will perform a search against the media index type only - /// - /// - /// - /// - public static XPathNodeIterator SearchMediaOnly(string searchText, bool useWildcards) - { - return SearchMediaOnly(searchText, useWildcards, ExamineManager.Instance.DefaultSearchProvider.Name); - } - - /// - /// Will perform a search against the media index type only - /// - /// - /// - public static XPathNodeIterator SearchMediaOnly(string searchText) - { - return SearchMediaOnly(searchText, true); - } - - /// - /// Searches the member only. - /// - /// The search text. - /// if set to true [use wildcards]. - /// Name of the provider. - /// - public static XPathNodeIterator SearchMemberOnly(string searchText, bool useWildcards, string providerName) - { - return Search(searchText, useWildcards, providerName, IndexTypes.Member); - } - - /// - /// Searches the member only. - /// - /// The search text. - /// if set to true [use wildcards]. - /// - public static XPathNodeIterator SearchMemberOnly(string searchText, bool useWildcards) - { - return SearchMemberOnly(searchText, useWildcards, ExamineManager.Instance.DefaultSearchProvider.Name); - } - - /// - /// Searches the member only. - /// - /// The search text. - /// - public static XPathNodeIterator SearchMemberOnly(string searchText) - { - return SearchMemberOnly(searchText, true); - } - - /// - /// Will perform a search against the content index type only - /// - /// - /// - /// - /// - public static XPathNodeIterator SearchContentOnly(string searchText, bool useWildcards, string providerName) - { - return Search(searchText, useWildcards, providerName, IndexTypes.Content); - } - - - /// - /// Will perform a search against the content index type only - /// - /// - /// - /// - public static XPathNodeIterator SearchContentOnly(string searchText, bool useWildcards) - { - return SearchContentOnly(searchText, useWildcards, ExamineManager.Instance.DefaultSearchProvider.Name); - } - - /// - /// Will perform a search against the content index type only - /// - /// - /// - public static XPathNodeIterator SearchContentOnly(string searchText) - { - return SearchContentOnly(searchText, true); - } - - /// - /// Gets the results as XML. - /// - /// The results. - /// - private static XPathNodeIterator GetResultsAsXml(ILuceneSearchResults results) - { - // create the XDocument - XDocument doc = new XDocument(); - - // check there are any search results - if (results.TotalItemCount > 0) - { - // create the root element - XElement root = new XElement("nodes"); - - // iterate through the search results - foreach (SearchResult result in results) - { - // create a new element - XElement node = new XElement("node"); - - // create the @id attribute - XAttribute nodeId = new XAttribute("id", result.Id); - - // create the @score attribute - XAttribute nodeScore = new XAttribute("score", result.Score); - - // add the content - node.Add(nodeId, nodeScore); - - foreach (KeyValuePair field in result.Fields) - { - // create a new element - XElement data = new XElement("data"); - - // create the @alias attribute - XAttribute alias = new XAttribute("alias", field.Key); - - // assign the value to a CDATA section - XCData value = new XCData(field.Value); - - // append the content - data.Add(alias, value); - - // append the element - node.Add(data); - } - - // add the node - root.Add(node); - } - - // add the root node - doc.Add(root); - } - else - { - doc.Add(new XElement("error", "There were no search results.")); - } - - return doc.CreateNavigator().Select("/"); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security; +using System.Xml.Linq; +using System.Xml.XPath; +using Examine; +using Examine.LuceneEngine; +using Examine.LuceneEngine.Providers; +using Examine.LuceneEngine.SearchCriteria; +using Examine.SearchCriteria; +using Examine.Providers; +using Umbraco.Core.Macros; + +namespace Umbraco.Examine +{ + /// + /// Methods to support Umbraco XSLT extensions. + /// + /// + /// XSLT extensions will ONLY work for provider that have a base class of BaseUmbracoIndexer + /// + [XsltExtension("Examine")] + + public class XsltExtensions + { + /// + /// Uses the provider specified to search, returning an XPathNodeIterator + /// + /// + /// + /// + /// + /// + internal static XPathNodeIterator Search(string searchText, bool useWildcards, LuceneSearcher provider, string indexType) + { + if (provider == null) throw new ArgumentNullException("provider"); + + var results = provider.Search(searchText, useWildcards, indexType); + return GetResultsAsXml(results); + } + + /// + /// Uses the provider specified to search, returning an XPathNodeIterator + /// + /// The search text. + /// if set to true [use wildcards]. + /// Name of the provider. + /// Type of the index. + /// + public static XPathNodeIterator Search(string searchText, bool useWildcards, string providerName, string indexType) + { + var provider = (LuceneSearcher)ExamineManager.Instance.SearchProviderCollection[providerName]; + + return Search(searchText, useWildcards, provider, indexType); + } + + /// + /// Uses the provider specified to search, returning an XPathNodeIterator + /// + /// + /// + /// + /// + public static XPathNodeIterator Search(string searchText, bool useWildcards, string providerName) + { + return Search(searchText, useWildcards, providerName, string.Empty); + } + + /// + /// Uses the default provider specified to search, returning an XPathNodeIterator + /// + /// The search query + /// Enable a wildcard search query + /// A node-set of the search results + public static XPathNodeIterator Search(string searchText, bool useWildcards) + { + return Search(searchText, useWildcards, ExamineManager.Instance.DefaultSearchProvider.Name); + } + + /// + /// Uses the default provider specified to search, returning an XPathNodeIterator + /// + /// The search query + /// A node-set of the search results + public static XPathNodeIterator Search(string searchText) + { + return Search(searchText, true); + } + + /// + /// Will perform a search against the media index type only + /// + /// + /// + /// + /// + public static XPathNodeIterator SearchMediaOnly(string searchText, bool useWildcards, string providerName) + { + return Search(searchText, useWildcards, providerName, IndexTypes.Media); + } + + /// + /// Will perform a search against the media index type only + /// + /// + /// + /// + public static XPathNodeIterator SearchMediaOnly(string searchText, bool useWildcards) + { + return SearchMediaOnly(searchText, useWildcards, ExamineManager.Instance.DefaultSearchProvider.Name); + } + + /// + /// Will perform a search against the media index type only + /// + /// + /// + public static XPathNodeIterator SearchMediaOnly(string searchText) + { + return SearchMediaOnly(searchText, true); + } + + /// + /// Searches the member only. + /// + /// The search text. + /// if set to true [use wildcards]. + /// Name of the provider. + /// + public static XPathNodeIterator SearchMemberOnly(string searchText, bool useWildcards, string providerName) + { + return Search(searchText, useWildcards, providerName, IndexTypes.Member); + } + + /// + /// Searches the member only. + /// + /// The search text. + /// if set to true [use wildcards]. + /// + public static XPathNodeIterator SearchMemberOnly(string searchText, bool useWildcards) + { + return SearchMemberOnly(searchText, useWildcards, ExamineManager.Instance.DefaultSearchProvider.Name); + } + + /// + /// Searches the member only. + /// + /// The search text. + /// + public static XPathNodeIterator SearchMemberOnly(string searchText) + { + return SearchMemberOnly(searchText, true); + } + + /// + /// Will perform a search against the content index type only + /// + /// + /// + /// + /// + public static XPathNodeIterator SearchContentOnly(string searchText, bool useWildcards, string providerName) + { + return Search(searchText, useWildcards, providerName, IndexTypes.Content); + } + + + /// + /// Will perform a search against the content index type only + /// + /// + /// + /// + public static XPathNodeIterator SearchContentOnly(string searchText, bool useWildcards) + { + return SearchContentOnly(searchText, useWildcards, ExamineManager.Instance.DefaultSearchProvider.Name); + } + + /// + /// Will perform a search against the content index type only + /// + /// + /// + public static XPathNodeIterator SearchContentOnly(string searchText) + { + return SearchContentOnly(searchText, true); + } + + /// + /// Gets the results as XML. + /// + /// The results. + /// + private static XPathNodeIterator GetResultsAsXml(ILuceneSearchResults results) + { + // create the XDocument + XDocument doc = new XDocument(); + + // check there are any search results + if (results.TotalItemCount > 0) + { + // create the root element + XElement root = new XElement("nodes"); + + // iterate through the search results + foreach (SearchResult result in results) + { + // create a new element + XElement node = new XElement("node"); + + // create the @id attribute + XAttribute nodeId = new XAttribute("id", result.Id); + + // create the @score attribute + XAttribute nodeScore = new XAttribute("score", result.Score); + + // add the content + node.Add(nodeId, nodeScore); + + foreach (KeyValuePair field in result.Fields) + { + // create a new element + XElement data = new XElement("data"); + + // create the @alias attribute + XAttribute alias = new XAttribute("alias", field.Key); + + // assign the value to a CDATA section + XCData value = new XCData(field.Value); + + // append the content + data.Add(alias, value); + + // append the element + node.Add(data); + } + + // add the node + root.Add(node); + } + + // add the root node + doc.Add(root); + } + else + { + doc.Add(new XElement("error", "There were no search results.")); + } + + return doc.CreateNavigator().Select("/"); + } + } +} diff --git a/src/Umbraco.Tests/Plugins/TypeFinderTests.cs b/src/Umbraco.Tests/Plugins/TypeFinderTests.cs index 58ec1d8a1b..a6ac604d6b 100644 --- a/src/Umbraco.Tests/Plugins/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Plugins/TypeFinderTests.cs @@ -49,7 +49,7 @@ namespace Umbraco.Tests.Plugins //typeof(TabPage).Assembly, typeof(System.Web.Mvc.ActionResult).Assembly, typeof(TypeFinder).Assembly, - typeof(global::UmbracoExamine.BaseUmbracoIndexer).Assembly + typeof(global::Umbraco.Examine.BaseUmbracoIndexer).Assembly }; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index 662d77150e..54c2b10fbc 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -16,7 +16,7 @@ using Examine.Session; using Umbraco.Core.Cache; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Strings; -using UmbracoExamine; +using Umbraco.Examine; using Current = Umbraco.Web.Composing.Current; using Umbraco.Tests.Testing; using LightInject; diff --git a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs index 07749a9db7..176ca3bcad 100644 --- a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs +++ b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs @@ -12,7 +12,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; -using UmbracoExamine; +using Umbraco.Examine; namespace Umbraco.Tests.Runtimes { diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestIndexCollectionAccessor.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestIndexCollectionAccessor.cs index ec0ccbd608..550bb6e613 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestIndexCollectionAccessor.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestIndexCollectionAccessor.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using Examine; -using UmbracoExamine; +using Umbraco.Examine; namespace Umbraco.Tests.TestHelpers.Stubs { diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 398afaee5e..ba0cbeaff3 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -29,7 +29,7 @@ using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.DI; using Umbraco.Web.Services; -using UmbracoExamine; +using Umbraco.Examine; using Current = Umbraco.Core.Composing.Current; namespace Umbraco.Tests.Testing diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index c5354fff12..98407a72ec 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -608,9 +608,9 @@ {651E1350-91B6-44B7-BD60-7207006D7003} Umbraco.Web - + {07fbc26b-2927-4a22-8d96-d644c667fecc} - UmbracoExamine + Umbraco.Examine diff --git a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs index 2a71e51a80..d480cd9b1e 100644 --- a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs @@ -6,7 +6,7 @@ using Lucene.Net.Store; using NUnit.Framework; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; -using UmbracoExamine; +using Umbraco.Examine; namespace Umbraco.Tests.UmbracoExamine { diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs index d078f04bc6..e52b972429 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs @@ -9,7 +9,7 @@ using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Profiling; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; -using UmbracoExamine; +using Umbraco.Examine; namespace Umbraco.Tests.UmbracoExamine { diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataContentService.cs b/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataContentService.cs index 07619fd12e..ffbd4f82d0 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataContentService.cs +++ b/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataContentService.cs @@ -5,7 +5,7 @@ using System.Xml.Linq; using System.Xml.XPath; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Repositories; -using UmbracoExamine; +using Umbraco.Examine; namespace Umbraco.Tests.UmbracoExamine { diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index c2b519c922..0a93a0c350 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -21,7 +21,7 @@ using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Strings; -using UmbracoExamine; +using Umbraco.Examine; using IContentService = Umbraco.Core.Services.IContentService; using IMediaService = Umbraco.Core.Services.IMediaService; using Version = Lucene.Net.Util.Version; diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index 8ab60a4a9f..8964a6e15d 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -7,7 +7,7 @@ using Lucene.Net.Store; using NUnit.Framework; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; -using UmbracoExamine; +using Umbraco.Examine; namespace Umbraco.Tests.UmbracoExamine { diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 5f1e5964c7..5b9c7cfff8 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -435,8 +435,9 @@ - - UmbracoExamine + + Umbraco.Examine + {07FBC26B-2927-4A22-8D96-D644C667FECC} Umbraco.Core diff --git a/src/Umbraco.Web.UI/config/ExamineSettings.Release.config b/src/Umbraco.Web.UI/config/ExamineSettings.Release.config index 642785739f..49287b21c3 100644 --- a/src/Umbraco.Web.UI/config/ExamineSettings.Release.config +++ b/src/Umbraco.Web.UI/config/ExamineSettings.Release.config @@ -1,5 +1,5 @@  - - - + + - - - - - + + diff --git a/src/Umbraco.Web.UI/config/ExamineSettings.config b/src/Umbraco.Web.UI/config/ExamineSettings.config index 076fb08493..40586ee854 100644 --- a/src/Umbraco.Web.UI/config/ExamineSettings.config +++ b/src/Umbraco.Web.UI/config/ExamineSettings.config @@ -9,30 +9,30 @@ More information and documentation can be found on GitHub: https://github.com/Sh - - - + - - + - diff --git a/src/Umbraco.Web/IPublishedContentQuery.cs b/src/Umbraco.Web/IPublishedContentQuery.cs index 2358f0cf40..26a332e239 100644 --- a/src/Umbraco.Web/IPublishedContentQuery.cs +++ b/src/Umbraco.Web/IPublishedContentQuery.cs @@ -6,6 +6,8 @@ using Umbraco.Core.Xml; namespace Umbraco.Web { + using Examine = global::Examine; + /// /// Query methods used for accessing strongly typed content in templates /// diff --git a/src/Umbraco.Web/Models/Mapping/EntityProfile.cs b/src/Umbraco.Web/Models/Mapping/EntityProfile.cs index 01579a223c..3a8a9a0e93 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityProfile.cs @@ -9,7 +9,7 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Web.Models.ContentEditing; -using UmbracoExamine; +using Umbraco.Examine; namespace Umbraco.Web.Models.Mapping { diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index 025f0622eb..1dafd8c16d 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -9,10 +9,12 @@ using Newtonsoft.Json.Linq; using Umbraco.Core; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Xml; -using UmbracoExamine; +using Umbraco.Examine; namespace Umbraco.Web.PropertyEditors { + using Examine = global::Examine; + [PropertyEditor(Constants.PropertyEditors.GridAlias, "Grid layout", "grid", HideLabel = true, IsParameterEditor = false, ValueType = PropertyEditorValueTypes.Json, Group="rich content", Icon="icon-layout")] public class GridPropertyEditor : PropertyEditor { diff --git a/src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs b/src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs index 620a5ee46a..ebfbd303bf 100644 --- a/src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs +++ b/src/Umbraco.Web/PropertyEditors/PropertyEditorsComponent.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Components; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; -using UmbracoExamine; +using Umbraco.Examine; namespace Umbraco.Web.PropertyEditors { diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 30f989ffde..880be1d971 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -17,7 +17,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Xml; using Umbraco.Web.Models; -using UmbracoExamine; +using Umbraco.Examine; using umbraco; using Umbraco.Core.Cache; using Umbraco.Core.Services; diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 1b99681be5..e3b5fd3ccb 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -13,6 +13,8 @@ using Umbraco.Web.Composing; namespace Umbraco.Web { + using Examine = global::Examine; + /// /// Provides extension methods for IPublishedContent. /// diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs index 7f8185e6e1..8aea27f675 100644 --- a/src/Umbraco.Web/PublishedContentQuery.cs +++ b/src/Umbraco.Web/PublishedContentQuery.cs @@ -9,6 +9,8 @@ using Umbraco.Web.PublishedCache; namespace Umbraco.Web { + using Examine = global::Examine; + /// /// A class used to query for published content, media items /// diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index ef34f74b80..44710b4ba8 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -20,7 +20,7 @@ using Umbraco.Core.Sync; using Umbraco.Web.Cache; using Umbraco.Web.Composing; using Umbraco.Web.PropertyEditors; -using UmbracoExamine; +using Umbraco.Examine; namespace Umbraco.Web.Search { diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index c573acc40e..9c8ac148cf 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -103,8 +103,9 @@ - - UmbracoExamine + + Umbraco.Examine + {07FBC26B-2927-4A22-8D96-D644C667FECC} Umbraco.Core diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 28aa96ec25..847b1d707b 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -22,6 +22,8 @@ using Umbraco.Web.Composing; namespace Umbraco.Web { + using Examine = global::Examine; + /// /// A helper class that provides many useful methods and functionality for using Umbraco in templates /// diff --git a/src/Umbraco.Web/WebRuntimeComponent.cs b/src/Umbraco.Web/WebRuntimeComponent.cs index 38d6ff1649..cee9ef0a04 100644 --- a/src/Umbraco.Web/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/WebRuntimeComponent.cs @@ -41,7 +41,7 @@ using Umbraco.Web.SignalR; using Umbraco.Web.UI.JavaScript; using Umbraco.Web.WebApi; using Umbraco.Web._Legacy.Actions; -using UmbracoExamine; +using Umbraco.Examine; using Current = Umbraco.Web.Composing.Current; namespace Umbraco.Web diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/search.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/search.aspx.cs index c14e4006de..c75234ad03 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/search.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/search.aspx.cs @@ -5,7 +5,7 @@ using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using Umbraco.Core; -using UmbracoExamine; +using Umbraco.Examine; using System.Xml; using Examine; using Examine.LuceneEngine.SearchCriteria; diff --git a/src/umbraco.sln b/src/umbraco.sln index af3c34a1a6..dd5841345c 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26430.14 +VisualStudioVersion = 15.0.26430.16 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Web.UI", "Umbraco.Web.UI\Umbraco.Web.UI.csproj", "{4C4C194C-B5E4-4991-8F87-4373E24CC19F}" EndProject @@ -62,7 +62,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Core", "Umbraco.Cor EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests", "Umbraco.Tests\Umbraco.Tests.csproj", "{5D3B8245-ADA6-453F-A008-50ED04BFE770}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UmbracoExamine", "UmbracoExamine\UmbracoExamine.csproj", "{07FBC26B-2927-4A22-8D96-D644C667FECC}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Examine", "Umbraco.Examine\Umbraco.Examine.csproj", "{07FBC26B-2927-4A22-8D96-D644C667FECC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{E3F9F378-AFE1-40A5-90BD-82833375DBFE}" ProjectSection(SolutionItems) = preProject