/// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> /// <param name="indexerName">Name of the indexer for which this DocumentWriting event is being executed on</param> private static void DocumentWriting(object sender, DocumentWritingEventArgs e, string indexerName) { IPublishedContent publishedContent = null; var indexerConfiguration = LookService.GetIndexerConfiguration(indexerName); if (indexerConfiguration.ShouldIndexContent) // attempt to get content { publishedContent = LookService.Instance._umbracoHelper.TypedContent(e.NodeId); } if (publishedContent == null && indexerConfiguration.ShouldIndexMedia) // attempt to get as media { publishedContent = LookService.Instance._umbracoHelper.TypedMedia(e.NodeId); } if (publishedContent == null && indexerConfiguration.ShouldIndexMembers) // attempt to get as member { publishedContent = LookService.Instance._umbracoHelper.SafeTypedMember(e.NodeId); } if (publishedContent != null) { var indexingContext = new IndexingContext(null, publishedContent, indexerName); LookService.EnsureContext(); LookService.Index(indexingContext, e.Document); } }
/// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> /// <param name="indexerName">Name of the indexer for which this DocumentWriting event is being executed on</param> private static void DocumentWriting(object sender, DocumentWritingEventArgs e, string indexerName) { IPublishedContent publishedContent = null; if (LookService.Instance._umbracoHelper == null) { throw new Exception("Unexpected null value for UmbracoHelper - Look not initialized"); } publishedContent = LookService.Instance._umbracoHelper.TypedContent(e.NodeId); if (publishedContent == null) { // attempt to get as media publishedContent = LookService.Instance._umbracoHelper.TypedMedia(e.NodeId); if (publishedContent == null) { // attempt to get as member publishedContent = LookService.Instance._umbracoHelper.SafeTypedMember(e.NodeId); } } if (publishedContent != null) { var indexingContext = new IndexingContext( hostNode: null, node: publishedContent, indexerName: indexerName); LookService.EnsureContext(); LookService.Index(indexingContext, e.Document); } }
private static void IndexDate(IndexingContext indexingContext, Document document) { if (indexingContext.Cancelled) { return; } DateTime?date = null; var dateIndexer = LookService.GetDateIndexer(indexingContext.IndexerName); if (dateIndexer != null) { try { date = dateIndexer(indexingContext); } catch (Exception exception) { LogHelper.WarnWithException(typeof(LookService), "Error in date indexer", exception); } } else if (indexingContext.Item != null) { date = indexingContext.HostItem?.UpdateDate ?? indexingContext.Item.UpdateDate; } if (date != null) { var hasDateField = new Field( LookConstants.HasDateField, "1", Field.Store.NO, Field.Index.NOT_ANALYZED); var dateValue = DateTools.DateToString(date.Value, DateTools.Resolution.SECOND); var dateField = new Field( LookConstants.DateField, dateValue, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.YES); var dateSortedField = new Field( LuceneIndexer.SortedFieldNamePrefix + LookConstants.DateField, dateValue, Field.Store.NO, Field.Index.NOT_ANALYZED, Field.TermVector.NO); document.Add(hasDateField); document.Add(dateField); document.Add(dateSortedField); } }
/// <summary> /// Set all examine indexers (this may be called from the Hook indexing startup event) /// </summary> internal static void SetExamineIndexers() { // get all examine indexer names var examineIndexerNames = ExamineManager .Instance .IndexProviderCollection .Select(x => x as BaseUmbracoIndexer) // UmbracoContentIndexer, UmbracoMemberIndexer .Where(x => x != null) .Select(x => x.Name) .ToArray(); LookService.SetExamineIndexers(examineIndexerNames); }
/// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> /// <param name="indexerName">Name of the indexer for which this DocumentWriting event is being executed on</param> private static void DocumentWriting(object sender, DocumentWritingEventArgs e, string indexerName) { IPublishedContent publishedContent = null; if (LookService.Instance._umbracoHelper == null) { throw new Exception("Unexpected null value for UmbracoHelper - Look not initialized"); } publishedContent = LookService.Instance._umbracoHelper.TypedContent(e.NodeId); if (publishedContent == null) { // attempt to get as media publishedContent = LookService.Instance._umbracoHelper.TypedMedia(e.NodeId); if (publishedContent == null) { // attempt to get as member publishedContent = LookService.Instance._umbracoHelper.SafeTypedMember(e.NodeId); } } if (publishedContent != null) { var dummyHttpContext = new HttpContextWrapper(new HttpContext(new SimpleWorkerRequest("", "", new StringWriter()))); UmbracoContext.EnsureContext( dummyHttpContext, ApplicationContext.Current, new WebSecurity(dummyHttpContext, ApplicationContext.Current), UmbracoConfig.For.UmbracoSettings(), UrlProviderResolver.Current.Providers, true, false); var indexingContext = new IndexingContext( hostNode: null, node: publishedContent, indexerName: indexerName); LookService.Index(indexingContext, e.Document); } }
private static void IndexText(IndexingContext indexingContext, Document document) { if (indexingContext.Cancelled) { return; } var textIndexer = LookService.GetTextIndexer(indexingContext.IndexerName); if (textIndexer != null) { string text = null; try { text = textIndexer(indexingContext); } catch (Exception exception) { LogHelper.WarnWithException(typeof(LookService), "Error in text indexer", exception); } if (text != null) { var hasTextField = new Field( LookConstants.HasTextField, "1", Field.Store.NO, Field.Index.NOT_ANALYZED); var textField = new Field( LookConstants.TextField, text, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.YES); document.Add(hasTextField); document.Add(textField); } } }
private static void IndexTags(IndexingContext indexingContext, Document document) { if (indexingContext.Cancelled) { return; } var tagIndexer = LookService.GetTagIndexer(indexingContext.IndexerName); if (tagIndexer != null) { LookTag[] tags = null; try { tags = tagIndexer(indexingContext); } catch (Exception exception) { LogHelper.WarnWithException(typeof(LookService), "Error in tag indexer", exception); } if (tags != null) { foreach (var tag in tags) { var hasTagsField = new Field( LookConstants.HasTagsField, "1", Field.Store.NO, Field.Index.NOT_ANALYZED); // add all tags to a common field (serialized such that Tag objects can be restored from this) var allTagsField = new Field( LookConstants.AllTagsField, tag.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED); // add the tag value to a specific field - this is used for searching on var tagField = new Field( LookConstants.TagsField + tag.Group, tag.Name, Field.Store.YES, Field.Index.NOT_ANALYZED); document.Add(hasTagsField); document.Add(allTagsField); document.Add(tagField); } var tagGroups = tags.Select(x => x.Group).Distinct(); foreach (var tagGroup in tagGroups) { var tagGroupField = new Field( LookConstants.TagGroupField + tagGroup, "1", Field.Store.NO, Field.Index.NOT_ANALYZED); document.Add(tagGroupField); } } } }
/// <summary> /// Get the BeforeIndexing method for a specifed index /// </summary> /// <param name="indexerName"></param> /// <returns></returns> internal static Action <IndexingContext> GetBeforeIndexing(string indexerName) { return(LookService.GetIndexerConfiguration(indexerName).BeforeIndexing // indexer specific ?? new Action <IndexingContext>(x => { })); // not set }
/// <summary> /// Set the supplied examine indexers (this may be called by the consumer to specify the Examine indexes to hook into) /// </summary> /// <param name="examineIndexers">names of Examine indexers to hook into (null or empty array = none)</param> internal static void SetExamineIndexers(string[] examineIndexerNames) { LookService.Instance._examineIndexersConfigured = true; // set flag so that hook indexing startup event doens't reset any conumser set configuration // all examine indexers that should be hooked into (string key = indexer name) var examineIndexers = new Dictionary <string, BaseUmbracoIndexer>(); // default to empty - ie, no examine indexers to hook into if (examineIndexerNames != null && examineIndexerNames.Any()) { // we (might) have indexers to hook into examineIndexers = ExamineManager .Instance .IndexProviderCollection .Select(x => x as BaseUmbracoIndexer) // UmbracoContentIndexer, UmbracoMemberIndexer .Where(x => x != null) .Where(x => examineIndexerNames.Contains(x.Name)) .ToDictionary(x => x.Name, x => x); } // if there are indexers already registered, remove those that are not in the collection var removeEvents = LookService .Instance ._examineDocumentWritingEvents .Where(x => !examineIndexers.ContainsKey(x.Key)) .ToDictionary(x => x.Key, x => x.Value); foreach (var removeEvent in removeEvents) { var indexer = ExamineManager.Instance.IndexProviderCollection[removeEvent.Key] as BaseUmbracoIndexer; indexer.DocumentWriting -= removeEvent.Value; LookService.Instance._examineDocumentWritingEvents.Remove(removeEvent.Key); } // add events if not already registered foreach (var examineIndexer in examineIndexers) { if (!LookService.Instance._examineDocumentWritingEvents.ContainsKey(examineIndexer.Key)) { EventHandler <DocumentWritingEventArgs> addEvent = (sender, e) => LookService.DocumentWriting(sender, e, examineIndexer.Key); LookService.Instance._examineDocumentWritingEvents[examineIndexer.Key] = addEvent; examineIndexers[examineIndexer.Key].DocumentWriting += addEvent; } LogHelper.Info(typeof(LookService), $"Hooking into Examine indexer '{ examineIndexer.Key }'"); } }
internal static Func <IndexingContext, DateTime?> GetDateIndexer(string indexerName) { return(LookService.GetIndexerConfiguration(indexerName).DateIndexer ?? LookService.Instance._defaultDateIndexer); }
private static void IndexLocation(IndexingContext indexingContext, Document document) { if (indexingContext.Cancelled) { return; } var locationIndexer = LookService.GetLocationIndexer(indexingContext.IndexerName); if (locationIndexer != null) { Location location = null; try { location = locationIndexer(indexingContext); } catch (Exception exception) { LogHelper.WarnWithException(typeof(LookService), "Error in location indexer", exception); } if (location != null) { var hasLocationField = new Field( LookConstants.HasLocationField, "1", Field.Store.NO, Field.Index.NOT_ANALYZED); var locationField = new Field( LookConstants.LocationField, location.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED); var locationLatitudeField = new Field( LookConstants.LocationField + "_Latitude", NumericUtils.DoubleToPrefixCoded(location.Latitude), Field.Store.YES, Field.Index.NOT_ANALYZED); var locationLongitudeField = new Field( LookConstants.LocationField + "_Longitude", NumericUtils.DoubleToPrefixCoded(location.Longitude), Field.Store.YES, Field.Index.NOT_ANALYZED); document.Add(hasLocationField); document.Add(locationField); document.Add(locationLatitudeField); document.Add(locationLongitudeField); foreach (var cartesianTierPlotter in LookService.Instance._cartesianTierPlotters) { var boxId = cartesianTierPlotter.GetTierBoxId(location.Latitude, location.Longitude); var tierField = new Field( cartesianTierPlotter.GetTierFieldName(), NumericUtils.DoubleToPrefixCoded(boxId), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS); document.Add(tierField); } } } }
/// <summary> /// Do the indexing and set the field values onto the Lucene document /// </summary> /// <param name="indexingContext"></param> /// <param name="document"></param> internal static void Index(IndexingContext indexingContext, Document document) { var indexerConfiguration = LookService.GetIndexerConfiguration(indexingContext.IndexerName); if (!indexerConfiguration.ShouldIndexAlias(indexingContext.Item?.DocumentTypeAlias)) { indexingContext.Cancel(); return; } var stopwatch = Stopwatch.StartNew(); try { LookService.GetBeforeIndexing()(indexingContext); } catch (Exception exception) { LogHelper.WarnWithException(typeof(LookService), "Error in global BeforeIndexing", exception); } try { LookService.GetBeforeIndexing(indexingContext.IndexerName)(indexingContext); } catch (Exception exception) { LogHelper.WarnWithException(typeof(LookService), "Error in indexer BeforeIndexing", exception); } LookService.IndexNode(indexingContext, document); LookService.IndexName(indexingContext, document); LookService.IndexDate(indexingContext, document); LookService.IndexText(indexingContext, document); LookService.IndexTags(indexingContext, document); LookService.IndexLocation(indexingContext, document); try { LookService.GetAfterIndexing(indexingContext.IndexerName)(indexingContext); } catch (Exception exception) { LogHelper.WarnWithException(typeof(LookService), "Error in indexer AfterIndexing", exception); } try { LookService.GetAfterIndexing()(indexingContext); } catch (Exception exception) { LogHelper.WarnWithException(typeof(LookService), "Error in global AfterIndexing", exception); } stopwatch.Stop(); if (!indexingContext.Cancelled) { LogHelper.Debug(typeof(LookService), () => $"Building Lucene Document for Id='{indexingContext.Item.Id}', Key='{indexingContext.Item.GetGuidKey()}', Name='{indexingContext.Item.Name}' in Index '{ indexingContext.IndexerName }' Took { stopwatch.ElapsedMilliseconds }ms"); } }
/// <summary> /// Perform a Look search /// </summary> /// <param name="lookQuery">A LookQuery model for the search criteria</param> /// <returns>A LookResult model for the search response</returns> public static LookResult Search(LookQuery lookQuery) { // flag to indicate whether there are any query clauses in the supplied LookQuery bool hasQuery = lookQuery?.Compiled != null ? true : false; if (lookQuery == null) { return(LookResult.Error("LookQuery object was null")); } if (lookQuery.SearchingContext == null) // supplied by unit test to skip examine dependency { // attempt to get searching context from examine searcher name lookQuery.SearchingContext = LookService.GetSearchingContext(lookQuery.SearcherName); if (lookQuery.SearchingContext == null) { return(LookResult.Error("SearchingContext was null")); } } if (lookQuery.Compiled == null) { BooleanQuery query = null; // the lucene query being built Filter filter = null; // used for geospatial queries Sort sort = null; Func <string, IHtmlString> getHighlight = x => null; Func <int, double?> getDistance = x => null; query = new BooleanQuery(); #region RawQuery if (!string.IsNullOrWhiteSpace(lookQuery.RawQuery)) { hasQuery = true; query.Add( new QueryParser(Lucene.Net.Util.Version.LUCENE_29, null, lookQuery.SearchingContext.Analyzer).Parse(lookQuery.RawQuery), BooleanClause.Occur.MUST); } #endregion #region ExamineQuery if (lookQuery.ExamineQuery != null) { var luceneSearchCriteria = lookQuery.ExamineQuery as LuceneSearchCriteria; // will be of type LookSearchCriteria when using the custom Look indexer/searcher if (luceneSearchCriteria != null && luceneSearchCriteria.Query != null) { hasQuery = true; query.Add(luceneSearchCriteria.Query, BooleanClause.Occur.MUST); } } #endregion #region NodeQuery if (lookQuery.NodeQuery != null) { hasQuery = true; query.Add(new TermQuery(new Term(LookConstants.HasNodeField, "1")), BooleanClause.Occur.MUST); // HasType if (lookQuery.NodeQuery.Type != null) { query.Add( new TermQuery( new Term(LookConstants.NodeTypeField, lookQuery.NodeQuery.Type.ToString())), BooleanClause.Occur.MUST); } // HasTypeAny if (lookQuery.NodeQuery.TypeAny != null && lookQuery.NodeQuery.TypeAny.Any()) { var nodeTypeQuery = new BooleanQuery(); foreach (var nodeType in lookQuery.NodeQuery.TypeAny) { nodeTypeQuery.Add( new TermQuery( new Term(LookConstants.NodeTypeField, nodeType.ToString())), BooleanClause.Occur.SHOULD); } query.Add(nodeTypeQuery, BooleanClause.Occur.MUST); } // Detached switch (lookQuery.NodeQuery.DetachedQuery) { case DetachedQuery.ExcludeDetached: query.Add( new TermQuery(new Term(LookConstants.IsDetachedField, "1")), BooleanClause.Occur.MUST_NOT); break; case DetachedQuery.OnlyDetached: query.Add( new TermQuery(new Term(LookConstants.IsDetachedField, "1")), BooleanClause.Occur.MUST); break; } // HasCulture if (lookQuery.NodeQuery.Culture != null) { query.Add( new TermQuery( new Term(LookConstants.CultureField, lookQuery.NodeQuery.Culture.LCID.ToString())), BooleanClause.Occur.MUST); } // HasCultureAny if (lookQuery.NodeQuery.CultureAny != null && lookQuery.NodeQuery.CultureAny.Any()) { var nodeCultureQuery = new BooleanQuery(); foreach (var nodeCulture in lookQuery.NodeQuery.CultureAny) { nodeCultureQuery.Add( new TermQuery( new Term(LookConstants.CultureField, nodeCulture.LCID.ToString())), BooleanClause.Occur.SHOULD); } query.Add(nodeCultureQuery, BooleanClause.Occur.MUST); } // HasAlias if (lookQuery.NodeQuery.Alias != null) { query.Add( new TermQuery( new Term(LookConstants.NodeAliasField, lookQuery.NodeQuery.Alias.ToString())), BooleanClause.Occur.MUST); } // HasAliasAny if (lookQuery.NodeQuery.AliasAny != null && lookQuery.NodeQuery.AliasAny.Any()) { var nodeAliasQuery = new BooleanQuery(); foreach (var typeAlias in lookQuery.NodeQuery.AliasAny) { nodeAliasQuery.Add( new TermQuery( new Term(LookConstants.NodeAliasField, typeAlias)), BooleanClause.Occur.SHOULD); } query.Add(nodeAliasQuery, BooleanClause.Occur.MUST); } // Ids if (lookQuery.NodeQuery.Ids != null && lookQuery.NodeQuery.Ids.Any()) { if (lookQuery.NodeQuery.NotIds != null) { var conflictIds = lookQuery.NodeQuery.Ids.Where(x => lookQuery.NodeQuery.NotIds.Contains(x)); if (conflictIds.Any()) { return(LookResult.Error($"Conflict in NodeQuery, Ids: '{ string.Join(",", conflictIds) }' are in both Ids and NotIds")); } } var idQuery = new BooleanQuery(); foreach (var id in lookQuery.NodeQuery.Ids) { idQuery.Add( new TermQuery(new Term(LookConstants.NodeIdField, id.ToString())), BooleanClause.Occur.SHOULD); } query.Add(idQuery, BooleanClause.Occur.MUST); } // Keys if (lookQuery.NodeQuery.Keys != null && lookQuery.NodeQuery.Keys.Any()) { if (lookQuery.NodeQuery.NotKeys != null) { var conflictKeys = lookQuery.NodeQuery.Keys.Where(x => lookQuery.NodeQuery.NotKeys.Contains(x)); if (conflictKeys.Any()) { return(LookResult.Error($"Conflict in NodeQuery, keys: '{ string.Join(",", conflictKeys) }' are in both Keys and NotKeys")); } } var keyQuery = new BooleanQuery(); foreach (var key in lookQuery.NodeQuery.Keys) { keyQuery.Add( new TermQuery(new Term(LookConstants.NodeKeyField, key.GuidToLuceneString())), BooleanClause.Occur.SHOULD); } query.Add(keyQuery, BooleanClause.Occur.MUST); } // NotId if (lookQuery.NodeQuery.NotId != null) { query.Add( new TermQuery(new Term(LookConstants.NodeIdField, lookQuery.NodeQuery.NotId.ToString())), BooleanClause.Occur.MUST_NOT); } // NotIds if (lookQuery.NodeQuery.NotIds != null && lookQuery.NodeQuery.NotIds.Any()) { foreach (var exculudeId in lookQuery.NodeQuery.NotIds) { query.Add( new TermQuery(new Term(LookConstants.NodeIdField, exculudeId.ToString())), BooleanClause.Occur.MUST_NOT); } } // NotKey if (lookQuery.NodeQuery.NotKey != null) { query.Add( new TermQuery(new Term(LookConstants.NodeKeyField, lookQuery.NodeQuery.NotKey.ToString())), BooleanClause.Occur.MUST_NOT); } // NotKeys if (lookQuery.NodeQuery.NotKeys != null && lookQuery.NodeQuery.NotKeys.Any()) { foreach (var excludeKey in lookQuery.NodeQuery.NotKeys) { query.Add( new TermQuery(new Term(LookConstants.NodeKeyField, excludeKey.GuidToLuceneString())), BooleanClause.Occur.MUST_NOT); } } } #endregion #region NameQuery if (lookQuery.NameQuery != null) { hasQuery = true; query.Add(new TermQuery(new Term(LookConstants.HasNameField, "1")), BooleanClause.Occur.MUST); string wildcard1 = null; string wildcard2 = null; // incase Contains specified with StartsWith and/or EndsWith if (!string.IsNullOrEmpty(lookQuery.NameQuery.StartsWith)) { if (!string.IsNullOrEmpty(lookQuery.NameQuery.Is)) { if (!lookQuery.NameQuery.Is.StartsWith(lookQuery.NameQuery.StartsWith)) { return(LookResult.Error("Conflict in NameQuery between Is and StartsWith")); } } else { wildcard1 = lookQuery.NameQuery.StartsWith + "*"; } } if (!string.IsNullOrEmpty(lookQuery.NameQuery.EndsWith)) { if (!string.IsNullOrEmpty(lookQuery.NameQuery.Is)) { if (!lookQuery.NameQuery.Is.EndsWith(lookQuery.NameQuery.EndsWith)) { return(LookResult.Error("Conflict in NameQuery between Is and EndsWith")); } } else { if (wildcard1 == null) { wildcard1 = "*" + lookQuery.NameQuery.EndsWith; } else { wildcard1 += lookQuery.NameQuery.EndsWith; } } } if (!string.IsNullOrEmpty(lookQuery.NameQuery.Contains)) { if (!string.IsNullOrEmpty(lookQuery.NameQuery.Is)) { if (!lookQuery.NameQuery.Is.Contains(lookQuery.NameQuery.Contains)) { return(LookResult.Error("Conflict in NameQuery between Is and Contains")); } } else { if (wildcard1 == null) { wildcard1 = "*" + lookQuery.NameQuery.Contains + "*"; } else { wildcard2 = "*" + lookQuery.NameQuery.Contains + "*"; } } } var nameField = lookQuery.NameQuery.CaseSensitive ? LookConstants.NameField : LookConstants.NameField + "_Lowered"; if (wildcard1 != null) { var wildcard = lookQuery.NameQuery.CaseSensitive ? wildcard1 : wildcard1.ToLower(); query.Add(new WildcardQuery(new Term(nameField, wildcard)), BooleanClause.Occur.MUST); if (wildcard2 != null) { wildcard = lookQuery.NameQuery.CaseSensitive ? wildcard2 : wildcard2.ToLower(); query.Add(new WildcardQuery(new Term(nameField, wildcard)), BooleanClause.Occur.MUST); } } if (!string.IsNullOrEmpty(lookQuery.NameQuery.Is)) { var isText = lookQuery.NameQuery.CaseSensitive ? lookQuery.NameQuery.Is : lookQuery.NameQuery.Is.ToLower(); query.Add(new TermQuery(new Term(nameField, isText)), BooleanClause.Occur.MUST); } } #endregion #region DateQuery if (lookQuery.DateQuery != null) { hasQuery = true; query.Add(new TermQuery(new Term(LookConstants.HasDateField, "1")), BooleanClause.Occur.MUST); if (lookQuery.DateQuery.After.HasValue || lookQuery.DateQuery.Before.HasValue) { var includeLower = lookQuery.DateQuery.After == null || lookQuery.DateQuery.Boundary == DateBoundary.Inclusive || lookQuery.DateQuery.Boundary == DateBoundary.BeforeExclusiveAfterInclusive; var includeUpper = lookQuery.DateQuery.Before == null || lookQuery.DateQuery.Boundary == DateBoundary.Inclusive || lookQuery.DateQuery.Boundary == DateBoundary.BeforeInclusiveAfterExclusive; query.Add( new TermRangeQuery( LookConstants.DateField, lookQuery.DateQuery.After.DateToLuceneString() ?? DateTime.MinValue.DateToLuceneString(), lookQuery.DateQuery.Before.DateToLuceneString() ?? DateTime.MaxValue.DateToLuceneString(), includeLower, includeUpper), BooleanClause.Occur.MUST); } } #endregion #region TextQuery if (lookQuery.TextQuery != null) { hasQuery = true; query.Add(new TermQuery(new Term(LookConstants.HasTextField, "1")), BooleanClause.Occur.MUST); if (!string.IsNullOrWhiteSpace(lookQuery.TextQuery.SearchText)) { var queryParser = new QueryParser(Lucene.Net.Util.Version.LUCENE_29, LookConstants.TextField, lookQuery.SearchingContext.Analyzer); Query searchTextQuery = null; try { searchTextQuery = queryParser.Parse(lookQuery.TextQuery.SearchText); } catch { return(LookResult.Error($"Unable to parse LookQuery.TextQuery.SearchText: '{ lookQuery.TextQuery.SearchText }' into a Lucene query")); } if (searchTextQuery != null) { query.Add(searchTextQuery, BooleanClause.Occur.MUST); if (lookQuery.TextQuery.GetHighlight) { var queryScorer = new QueryScorer(searchTextQuery.Rewrite(lookQuery.SearchingContext.IndexSearcher.GetIndexReader())); var highlighter = new Highlighter(new SimpleHTMLFormatter("<strong>", "</strong>"), queryScorer); getHighlight = (x) => { var tokenStream = lookQuery.SearchingContext.Analyzer.TokenStream(LookConstants.TextField, new StringReader(x)); var highlight = highlighter.GetBestFragments( tokenStream, x, 1, // max number of fragments "..."); return(new HtmlString(highlight)); }; } } } } #endregion #region TagQuery if (lookQuery.TagQuery != null) { hasQuery = true; query.Add(new TermQuery(new Term(LookConstants.HasTagsField, "1")), BooleanClause.Occur.MUST); // Has if (lookQuery.TagQuery.Has != null) { query.Add( new TermQuery(new Term(LookConstants.TagsField + lookQuery.TagQuery.Has.Group, lookQuery.TagQuery.Has.Name)), BooleanClause.Occur.MUST); } // Not if (lookQuery.TagQuery.Not != null) { query.Add( new TermQuery(new Term(LookConstants.TagsField + lookQuery.TagQuery.Not.Group, lookQuery.TagQuery.Not.Name)), BooleanClause.Occur.MUST_NOT); } // HasAll if (lookQuery.TagQuery.HasAll != null && lookQuery.TagQuery.HasAll.Any()) { foreach (var tag in lookQuery.TagQuery.HasAll) { query.Add( new TermQuery(new Term(LookConstants.TagsField + tag.Group, tag.Name)), BooleanClause.Occur.MUST); } } // HasAllOr if (lookQuery.TagQuery.HasAllOr != null && lookQuery.TagQuery.HasAllOr.Any() && lookQuery.TagQuery.HasAllOr.SelectMany(x => x).Any()) { var orQuery = new BooleanQuery(); foreach (var tagCollection in lookQuery.TagQuery.HasAllOr) { if (tagCollection.Any()) { var allTagQuery = new BooleanQuery(); foreach (var tag in tagCollection) { allTagQuery.Add( new TermQuery(new Term(LookConstants.TagsField + tag.Group, tag.Name)), BooleanClause.Occur.MUST); } orQuery.Add(allTagQuery, BooleanClause.Occur.SHOULD); } } query.Add(orQuery, BooleanClause.Occur.MUST); } // HasAny if (lookQuery.TagQuery.HasAny != null && lookQuery.TagQuery.HasAny.Any()) { var anyTagQuery = new BooleanQuery(); foreach (var tag in lookQuery.TagQuery.HasAny) { anyTagQuery.Add( new TermQuery(new Term(LookConstants.TagsField + tag.Group, tag.Name)), BooleanClause.Occur.SHOULD); } query.Add(anyTagQuery, BooleanClause.Occur.MUST); } // HasAnyAnd if (lookQuery.TagQuery.HasAnyAnd != null && lookQuery.TagQuery.HasAnyAnd.Any()) { foreach (var tagCollection in lookQuery.TagQuery.HasAnyAnd) { if (tagCollection.Any()) { var anyTagQuery = new BooleanQuery(); foreach (var tag in tagCollection) { anyTagQuery.Add( new TermQuery(new Term(LookConstants.TagsField + tag.Group, tag.Name)), BooleanClause.Occur.SHOULD); } query.Add(anyTagQuery, BooleanClause.Occur.MUST); } } } // NotAny if (lookQuery.TagQuery.NotAny != null && lookQuery.TagQuery.NotAny.Any()) { foreach (var tag in lookQuery.TagQuery.NotAny) { query.Add( new TermQuery(new Term(LookConstants.TagsField + tag.Group, tag.Name)), BooleanClause.Occur.MUST_NOT); } } } #endregion #region LocationQuery if (lookQuery.LocationQuery != null) { hasQuery = true; query.Add(new TermQuery(new Term(LookConstants.HasLocationField, "1")), BooleanClause.Occur.MUST); if (lookQuery.LocationQuery.Boundary != null) // limit results within an lat lng fixed view (eg, typical map bounds) { query.Add( new TermRangeQuery( LookConstants.LocationField + "_Latitude", NumericUtils.DoubleToPrefixCoded(lookQuery.LocationQuery.Boundary.LatitudeMin), NumericUtils.DoubleToPrefixCoded(lookQuery.LocationQuery.Boundary.LatitudeMax), true, true), BooleanClause.Occur.MUST); query.Add( new TermRangeQuery( LookConstants.LocationField + "_Longitude", NumericUtils.DoubleToPrefixCoded(lookQuery.LocationQuery.Boundary.LongitudeMin), NumericUtils.DoubleToPrefixCoded(lookQuery.LocationQuery.Boundary.LongitudeMax), true, true), BooleanClause.Occur.MUST); } if (lookQuery.LocationQuery.Location != null) // location set, so can calculate distance { double maxDistance = LookService._maxDistance; if (lookQuery.LocationQuery.MaxDistance != null) { maxDistance = Math.Min(lookQuery.LocationQuery.MaxDistance.GetMiles(), maxDistance); } var distanceQueryBuilder = new DistanceQueryBuilder( lookQuery.LocationQuery.Location.Latitude, lookQuery.LocationQuery.Location.Longitude, maxDistance, LookConstants.LocationField + "_Latitude", LookConstants.LocationField + "_Longitude", LookConstants.LocationTierFieldPrefix, true); filter = distanceQueryBuilder.Filter; if (lookQuery.SortOn == SortOn.Distance) { sort = new Sort( new SortField( LookConstants.DistanceField, new DistanceFieldComparatorSource(distanceQueryBuilder.DistanceFilter))); } getDistance = new Func <int, double?>(x => { if (distanceQueryBuilder.DistanceFilter.Distances.ContainsKey(x)) { return(distanceQueryBuilder.DistanceFilter.Distances[x]); } return(null); }); } } #endregion if (hasQuery) { switch (lookQuery.SortOn) { case SortOn.Name: // a -> z sort = new Sort(new SortField(LuceneIndexer.SortedFieldNamePrefix + LookConstants.NameField, SortField.STRING)); break; case SortOn.DateAscending: // oldest -> newest sort = new Sort(new SortField(LuceneIndexer.SortedFieldNamePrefix + LookConstants.DateField, SortField.LONG, false)); break; case SortOn.DateDescending: // newest -> oldest sort = new Sort(new SortField(LuceneIndexer.SortedFieldNamePrefix + LookConstants.DateField, SortField.LONG, true)); break; // SortOn.Distance already set (if valid) } lookQuery.Compiled = new LookQueryCompiled( lookQuery, query, filter, sort ?? new Sort(SortField.FIELD_SCORE), getHighlight, getDistance); } } if (!hasQuery) { return(LookResult.Error("No query clauses supplied")); // empty failure } TopDocs topDocs = lookQuery .SearchingContext .IndexSearcher .Search( lookQuery.Compiled.Query, lookQuery.Compiled.Filter, LookService._maxLuceneResults, lookQuery.Compiled.Sort); if (topDocs.TotalHits > 0) { List <Facet> facets = null; if (lookQuery.TagQuery != null && lookQuery.TagQuery.FacetOn != null) { facets = new List <Facet>(); Query facetQuery = lookQuery.Compiled.Filter != null ? (Query) new FilteredQuery(lookQuery.Compiled.Query, lookQuery.Compiled.Filter) : lookQuery.Compiled.Query; // do a facet query for each group in the array foreach (var group in lookQuery.TagQuery.FacetOn.TagGroups) { var simpleFacetedSearch = new SimpleFacetedSearch( lookQuery.SearchingContext.IndexSearcher.GetIndexReader(), LookConstants.TagsField + group); var facetResult = simpleFacetedSearch.Search(facetQuery); facets.AddRange( facetResult .HitsPerFacet .Select( x => new Facet() { Tags = new LookTag[] { new LookTag(group, x.Name.ToString()) }, Count = Convert.ToInt32(x.HitCount) } )); } } return(new LookResult( lookQuery, topDocs, facets != null ? facets.ToArray() : new Facet[] { })); } return(LookResult.Empty()); // empty success }
/// <summary> /// Do the indexing and set the field values onto the Lucene document /// </summary> /// <param name="indexingContext"></param> /// <param name="document"></param> internal static void Index(IndexingContext indexingContext, Document document) { #if DEBUG var stopwatch = Stopwatch.StartNew(); #endif #region Node if (indexingContext.Item != null) { var publishedItemType = indexingContext?.HostItem?.ItemType ?? indexingContext.Item.ItemType; var hasNodeField = new Field( LookConstants.HasNodeField, "1", Field.Store.NO, Field.Index.NOT_ANALYZED); var nodeIdField = new Field( LookConstants.NodeIdField, indexingContext.Item.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED); var nodeKeyField = new Field( LookConstants.NodeKeyField, indexingContext.Item.GetGuidKey().GuidToLuceneString(), Field.Store.YES, Field.Index.NOT_ANALYZED); var nodeTypeField = new Field( LookConstants.NodeTypeField, publishedItemType.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO); var nodeAliasField = new Field( LookConstants.NodeAliasField, indexingContext.Item.DocumentTypeAlias, Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO); document.Add(hasNodeField); document.Add(nodeIdField); document.Add(nodeKeyField); document.Add(nodeTypeField); document.Add(nodeAliasField); if (publishedItemType == PublishedItemType.Content) { var culture = indexingContext?.HostItem?.GetCulture() ?? indexingContext.Item.GetCulture(); if (culture != null) { var cultureField = new Field( LookConstants.CultureField, culture.LCID.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.NO); document.Add(cultureField); } } if (indexingContext.HostItem != null) { var isDetachedField = new Field( LookConstants.IsDetachedField, "1", Field.Store.NO, Field.Index.NOT_ANALYZED); // indexing detached item, so store the host context id so we can return the detached item var hostIdField = new Field( LookConstants.HostIdField, indexingContext.HostItem.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED); document.Add(isDetachedField); document.Add(hostIdField); } } #endregion #region Name string name = null; if (LookService.GetNameIndexer() != null) { try { name = LookService.GetNameIndexer()(indexingContext); } catch (Exception exception) { LogHelper.WarnWithException(typeof(LookService), "Error in name indexer", exception); } } else if (indexingContext.Item != null) { name = indexingContext.Item.Name; } if (name != null) { var hasNameField = new Field( LookConstants.HasNameField, "1", Field.Store.NO, Field.Index.NOT_ANALYZED); var nameField = new Field( LookConstants.NameField, name, Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.YES); // field for lower case searching var nameFieldLowered = new Field( LookConstants.NameField + "_Lowered", name.ToLower(), Field.Store.NO, Field.Index.NOT_ANALYZED, Field.TermVector.YES); var nameSortedField = new Field( LuceneIndexer.SortedFieldNamePrefix + LookConstants.NameField, name.ToLower(), // force case insentive sorting Field.Store.NO, Field.Index.NOT_ANALYZED, Field.TermVector.NO); document.Add(hasNameField); document.Add(nameField); document.Add(nameFieldLowered); document.Add(nameSortedField); } #endregion #region Date DateTime?date = null; if (LookService.GetDateIndexer() != null) { try { date = LookService.GetDateIndexer()(indexingContext); } catch (Exception exception) { LogHelper.WarnWithException(typeof(LookService), "Error in date indexer", exception); } } else if (indexingContext.Item != null) { date = indexingContext.HostItem?.UpdateDate ?? indexingContext.Item.UpdateDate; } if (date != null) { var hasDateField = new Field( LookConstants.HasDateField, "1", Field.Store.NO, Field.Index.NOT_ANALYZED); var dateValue = DateTools.DateToString(date.Value, DateTools.Resolution.SECOND); var dateField = new Field( LookConstants.DateField, dateValue, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.YES); var dateSortedField = new Field( LuceneIndexer.SortedFieldNamePrefix + LookConstants.DateField, dateValue, Field.Store.NO, Field.Index.NOT_ANALYZED, Field.TermVector.NO); document.Add(hasDateField); document.Add(dateField); document.Add(dateSortedField); } #endregion #region Text if (LookService.GetTextIndexer() != null) { string text = null; try { text = LookService.GetTextIndexer()(indexingContext); } catch (Exception exception) { LogHelper.WarnWithException(typeof(LookService), "Error in text indexer", exception); } if (text != null) { var hasTextField = new Field( LookConstants.HasTextField, "1", Field.Store.NO, Field.Index.NOT_ANALYZED); var textField = new Field( LookConstants.TextField, text, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.YES); document.Add(hasTextField); document.Add(textField); } } #endregion #region Tag if (LookService.GetTagIndexer() != null) { LookTag[] tags = null; try { tags = LookService.GetTagIndexer()(indexingContext); } catch (Exception exception) { LogHelper.WarnWithException(typeof(LookService), "Error in tag indexer", exception); } if (tags != null) { foreach (var tag in tags) { var hasTagsField = new Field( LookConstants.HasTagsField, "1", Field.Store.NO, Field.Index.NOT_ANALYZED); // add all tags to a common field (serialized such that Tag objects can be restored from this) var allTagsField = new Field( LookConstants.AllTagsField, tag.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED); // add the tag value to a specific field - this is used for searching on var tagField = new Field( LookConstants.TagsField + tag.Group, tag.Name, Field.Store.YES, Field.Index.NOT_ANALYZED); document.Add(hasTagsField); document.Add(allTagsField); document.Add(tagField); } } } #endregion #region Location if (LookService.GetLocationIndexer() != null) { Location location = null; try { location = LookService.GetLocationIndexer()(indexingContext); } catch (Exception exception) { LogHelper.WarnWithException(typeof(LookService), "Error in location indexer", exception); } if (location != null) { var hasLocationField = new Field( LookConstants.HasLocationField, "1", Field.Store.NO, Field.Index.NOT_ANALYZED); var locationField = new Field( LookConstants.LocationField, location.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED); var locationLatitudeField = new Field( LookConstants.LocationField + "_Latitude", NumericUtils.DoubleToPrefixCoded(location.Latitude), Field.Store.YES, Field.Index.NOT_ANALYZED); var locationLongitudeField = new Field( LookConstants.LocationField + "_Longitude", NumericUtils.DoubleToPrefixCoded(location.Longitude), Field.Store.YES, Field.Index.NOT_ANALYZED); document.Add(hasLocationField); document.Add(locationField); document.Add(locationLatitudeField); document.Add(locationLongitudeField); foreach (var cartesianTierPlotter in LookService.Instance._cartesianTierPlotters) { var boxId = cartesianTierPlotter.GetTierBoxId(location.Latitude, location.Longitude); var tierField = new Field( cartesianTierPlotter.GetTierFieldName(), NumericUtils.DoubleToPrefixCoded(boxId), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS); document.Add(tierField); } } } #endregion #if DEBUG stopwatch.Stop(); LogHelper.Debug(typeof(LookService), $"Building Lucene Document For '{ indexingContext.Item.GetGuidKey() }' Took { stopwatch.ElapsedMilliseconds }ms"); #endif }
/// <summary> /// Get indexer specific AfterIndexing /// </summary> /// <param name="indexerName"></param> /// <returns></returns> internal static Action <IndexingContext> GetAfterIndexing(string indexerName) { return(LookService.GetIndexerConfiguration(indexerName).AfterIndexing ?? new Action <IndexingContext>(x => { })); }
private static void IndexName(IndexingContext indexingContext, Document document) { if (indexingContext.Cancelled) { return; } string name = null; var nameIndexer = LookService.GetNameIndexer(indexingContext.IndexerName); if (nameIndexer != null) { try { name = nameIndexer(indexingContext); } catch (Exception exception) { LogHelper.WarnWithException(typeof(LookService), "Error in name indexer", exception); } } else if (indexingContext.Item != null) { name = indexingContext.Item.Name; } if (name != null) { var hasNameField = new Field( LookConstants.HasNameField, "1", Field.Store.NO, Field.Index.NOT_ANALYZED); var nameField = new Field( LookConstants.NameField, name, Field.Store.YES, Field.Index.NOT_ANALYZED, Field.TermVector.YES); // field for lower case searching var nameFieldLowered = new Field( LookConstants.NameField + "_Lowered", name.ToLower(), Field.Store.NO, Field.Index.NOT_ANALYZED, Field.TermVector.YES); var nameSortedField = new Field( LuceneIndexer.SortedFieldNamePrefix + LookConstants.NameField, name.ToLower(), // force case insentive sorting Field.Store.NO, Field.Index.NOT_ANALYZED, Field.TermVector.NO); document.Add(hasNameField); document.Add(nameField); document.Add(nameFieldLowered); document.Add(nameSortedField); } }
/// <summary> /// Perform a Look search /// </summary> /// <param name="lookQuery">A LookQuery model for the search criteria</param> /// <returns>A LookResult model for the search response</returns> internal static LookResult Search(LookQuery lookQuery) { if (lookQuery == null) { return(LookResult.Error("LookQuery object was null")); } if (lookQuery.SearchingContext == null) // supplied by unit test to skip examine dependency { // attempt to get searching context from examine searcher name lookQuery.SearchingContext = LookService.GetSearchingContext(lookQuery.SearcherName); if (lookQuery.SearchingContext == null) { return(LookResult.Error("SearchingContext was null")); } } if (lookQuery.Compiled == null) { var parsingContext = new ParsingContext(); // for building/compiling the query try { LookService.ParseRawQuery(parsingContext, lookQuery); LookService.ParseExamineQuery(parsingContext, lookQuery); LookService.ParseNodeQuery(parsingContext, lookQuery); LookService.ParseNameQuery(parsingContext, lookQuery); LookService.ParseDateQuery(parsingContext, lookQuery); LookService.ParseTextQuery(parsingContext, lookQuery); LookService.ParseTagQuery(parsingContext, lookQuery); LookService.ParseLocationQuery(parsingContext, lookQuery); } catch (ParsingException parsingException) { return(LookResult.Error(parsingException.Message)); } if (parsingContext.HasQuery) { lookQuery.Compiled = new LookQueryCompiled(lookQuery, parsingContext); } else { return(LookResult.Error("Unable to compile query - a query clause is required")); // empty failure } } TopDocs topDocs = lookQuery .SearchingContext .IndexSearcher .Search( lookQuery.Compiled.Query, lookQuery.Compiled.Filter, lookQuery.MaxResults, lookQuery.Compiled.Sort); if (topDocs.TotalHits > 0) { List <Facet> facets = null; if (lookQuery.TagQuery != null && lookQuery.TagQuery.FacetOn != null) { facets = new List <Facet>(); Query facetQuery = lookQuery.Compiled.Filter != null ? (Query) new FilteredQuery(lookQuery.Compiled.Query, lookQuery.Compiled.Filter) : lookQuery.Compiled.Query; // do a facet query for each group in the array foreach (var group in lookQuery.TagQuery.FacetOn.TagGroups) { var simpleFacetedSearch = new SimpleFacetedSearch( lookQuery.SearchingContext.IndexSearcher.GetIndexReader(), LookConstants.TagsField + group); var facetResult = simpleFacetedSearch.Search(facetQuery); facets.AddRange( facetResult .HitsPerFacet .Select( x => new Facet() { Tags = new LookTag[] { new LookTag(group, x.Name.ToString()) }, Count = Convert.ToInt32(x.HitCount) } )); } } return(new LookResult( lookQuery, topDocs, facets != null ? facets.ToArray() : new Facet[] { })); } return(LookResult.Empty()); // empty success }
internal static Func <IndexingContext, string> GetTextIndexer(string indexerName) { return(LookService.GetIndexerConfiguration(indexerName).TextIndexer ?? LookService.Instance._defaultTextIndexer); }