/// <summary> /// Supplied with the result of a Lucene query, this method will yield a constructed LookMatch for each in order /// </summary> /// <param name="searcherName"></param> /// <param name="indexSearcher">The searcher supplied to get the Lucene doc for each id in the Lucene results (topDocs)</param> /// <param name="topDocs">The results of the Lucene query (a collection of ids in an order)</param> /// <param name="requestFields">Enum value specifying which Lucene fields shoudl be returned</param> /// <param name="getHighlight">Function used to get the highlight text for a given result text</param> /// <param name="getDistance">Function used to calculate distance (if a location was supplied in the original query)</param> /// <returns></returns> internal static IEnumerable <LookMatch> GetLookMatches( string searcherName, IndexSearcher indexSearcher, ScoreDoc[] scoreDocs, RequestFields requestFields, Func <string, IHtmlString> getHighlight, Func <int, double?> getDistance) { MapFieldSelector mapFieldSelector = null; // when null, all fields are returned // these fields are always requested var lookFieldNames = new string[] { LookConstants.NodeIdField, LookConstants.NodeKeyField, LookConstants.NodeTypeField, LookConstants.NodeAliasField, LookConstants.HostIdField, LookConstants.NameField, LookConstants.DateField, LookConstants.TextField, LookConstants.AllTagsField, LookConstants.LocationField }; if (requestFields == RequestFields.LookFieldsOnly) { // limit fields to be returned mapFieldSelector = new MapFieldSelector(lookFieldNames); } var getHostId = new Func <string, int?>(x => { if (int.TryParse(x, out int id)) { return(id); } return(null); }); var getItemGuid = new Func <string, Guid?>(x => { if (Guid.TryParse(x, out Guid guid)) { return(guid); } return(null); }); var getCultureInfo = new Func <string, CultureInfo>(x => { if (int.TryParse(x, out int lcid)) { return(new CultureInfo(lcid)); } return(null); }); // there should always be a valid node type value to parse var getNodeType = new Func <string, PublishedItemType>(x => { Enum.TryParse(x, out PublishedItemType type); return(type); }); // helper to simplify call below var getTags = new Func <Field[], LookTag[]>(x => { if (x != null) { return(x.Select(y => new LookTag(y.StringValue())).ToArray()); } return(new LookTag[] { }); }); foreach (var scoreDoc in scoreDocs) { var docId = scoreDoc.doc; var doc = indexSearcher.Doc(docId, mapFieldSelector); var lookMatch = new LookMatch( searcherName, scoreDoc.doc, scoreDoc.score, getHostId(doc.Get(LookConstants.HostIdField)), // could be null Convert.ToInt32(doc.Get(LookConstants.NodeIdField)), getItemGuid(doc.Get(LookConstants.NodeKeyField)), // this should only be null for unit tests (outside umbraco context) doc.Get(LookConstants.NodeAliasField), getCultureInfo(doc.Get(LookConstants.CultureField)), doc.Get(LookConstants.NameField), doc.Get(LookConstants.DateField).LuceneStringToDate(), doc.Get(LookConstants.TextField), getHighlight(doc.Get(LookConstants.TextField)), getTags(doc.GetFields(LookConstants.AllTagsField)), doc.Get(LookConstants.LocationField) != null ? Location.FromString(doc.Get(LookConstants.LocationField)) : null, getDistance(docId), getNodeType(doc.Get(LookConstants.NodeTypeField)), LookService.Instance._umbracoHelper ); // populate the Examine SearchResult.Fields collection if (requestFields == RequestFields.AllFields) { string[] fieldNames = doc .GetFields() .Cast <Field>() .Select(x => x.Name()) .ToArray(); foreach (var fieldName in fieldNames) { var values = doc.GetValues(fieldName); // replicating logic from Examine if (values.Length > 1) { // TODO: reflection to set internal MultiValueFields lookMatch.Fields[fieldName] = values[0]; } else if (values.Length > 0) { lookMatch.Fields[fieldName] = values[0]; } } } else { // look fields only // TODO: map fields } yield return(lookMatch); } }
/// <summary> /// /// </summary> /// <param name="indexSearcher"></param> /// <param name="topDocs"></param> /// <param name="getHighlight"></param> /// <param name="getDistance"></param> /// <returns></returns> private static IEnumerable <LookMatch> GetLookMatches( LookQuery lookQuery, IndexSearcher indexSearcher, TopDocs topDocs, Func <string, IHtmlString> getHighlight, Func <int, double?> getDistance) { bool getText = lookQuery.TextQuery != null && lookQuery.TextQuery.GetText; bool getTags = lookQuery.TagQuery != null && lookQuery.TagQuery.GetTags; var fields = new List <string>(); fields.Add(LuceneIndexer.IndexNodeIdFieldName); // "__NodeId" fields.Add(LookService.DateField); fields.Add(LookService.NameField); fields.Add(LookService.LocationField); /// if a highlight function is supplied, then it'll need the text field to process if (getHighlight != null || getText) { fields.Add(LookService.TextField); } if (getHighlight == null) // if highlight func doens't exist, then create one to always return null { getHighlight = x => null; } if (getTags) { fields.Add(LookService.TagsField); } var mapFieldSelector = new MapFieldSelector(fields.ToArray()); foreach (var scoreDoc in topDocs.ScoreDocs) { var docId = scoreDoc.doc; var doc = indexSearcher.Doc(docId, mapFieldSelector); DateTime?date = null; if (long.TryParse(doc.Get(LookService.DateField), out long ticks)) { date = new DateTime(ticks); } var lookMatch = new LookMatch() { Id = Convert.ToInt32(doc.Get(LuceneIndexer.IndexNodeIdFieldName)), Highlight = getHighlight(doc.Get(LookService.TextField)), Text = getText ? doc.Get(LookService.TextField) : null, Tags = getTags ? doc.Get(LookService.TagsField).Split(' ') : null, Date = date, Name = doc.Get(LookService.NameField), Location = doc.Get(LookService.LocationField) != null ? new Location(doc.Get(LookService.LocationField)) : null, Distance = getDistance(docId), Score = scoreDoc.score }; yield return(lookMatch); } }