Highlights
Another feature called Highlights
has been added to RavenDB to enhance the search UX.
Usage
Lets consider a class and index as follows:
public class BlogPost
{
public string Id { get; set; }
public string Title { get; set; }
public string Category { get; set; }
public string Content { get; set; }
public DateTime PublishedAt { get; set; }
public string[] Tags { get; set; }
public BlogComment[] Comments { get; set; }
}
public class BlogComment
{
public string Title { get; set; }
public string Content { get; set; }
}
public class BlogPosts_ByContent : AbstractIndexCreationTask<BlogPost>
{
public class Result
{
public string Content { get; set; }
}
public BlogPosts_ByContent()
{
Map = posts => from post in posts
select new
{
post.Content
};
Index(x => x.Content, FieldIndexing.Analyzed);
Store(x => x.Content, FieldStorage.Yes);
TermVector(x => x.Content, FieldTermVector.WithPositionsAndOffsets);
}
}
Now to use Highlights we just need to use one of the Highlight
query extension methods. The basic usage can be as simple as:
FieldHighlightings highlightings;
BlogPost[] results = session
.Advanced
.DocumentQuery<BlogPost, BlogPosts_ByContent>()
.Highlight("Content", 128, 1, out highlightings)
.Search("Content", "raven")
.ToArray();
StringBuilder builder = new StringBuilder()
.AppendLine("<ul>");
foreach (BlogPost result in results)
{
string[] fragments = highlightings.GetFragments(result.Id);
builder.AppendLine(string.Format("<li>{0}</li>", fragments.First()));
}
string ul = builder
.AppendLine("</ul>")
.ToString();
This will return the list of results and for each result we will be displaying first found fragment with the length up to 128 characters.
Highlights + Projections
Highlights can also be accessed when projections are performed.
FieldHighlightings highlightings;
BlogPosts_ByContent.Result[] results = session
.Advanced
.DocumentQuery<BlogPost, BlogPosts_ByContent>()
.Highlight("Content", 128, 1, out highlightings)
.SetHighlighterTags("**", "**")
.Search("Content", "raven")
.SelectFields<BlogPosts_ByContent.Result>() // projecting
.ToArray();
Highlights + Map-Reduce
Highlights can be accessed when performing queries on map-reduce indexes.
public class BlogPosts_ByCategory_Content : AbstractIndexCreationTask<BlogPost, BlogPosts_ByCategory_Content.Result>
{
public class Result
{
public string Category { get; set; }
public string Content { get; set; }
}
public BlogPosts_ByCategory_Content()
{
Map = posts => from post in posts
select new
{
post.Category,
post.Content
};
Reduce = results => from result in results
group result by result.Category into g
select new
{
Category = g.Key,
Content = string.Join(" ", g.Select(r => r.Content))
};
Index(x => x.Content, FieldIndexing.Analyzed);
Store(x => x.Content, FieldStorage.Yes);
TermVector(x => x.Content, FieldTermVector.WithPositionsAndOffsets);
}
}
FieldHighlightings highlightings;
BlogPosts_ByCategory_Content.Result[] results = session
.Advanced
.DocumentQuery<BlogPosts_ByCategory_Content.Result, BlogPosts_ByCategory_Content>()
.Highlight("Content", "Category", 128, 1, out highlightings) // highlighting 'Content', but marking 'Category' as key
.SetHighlighterTags("**", "**")
.Search("Content", "raven")
.ToArray();
var newsHighlightings = highlightings.GetFragments("News"); // get fragments for 'News' category
Customization
IDocumentQuery<T> Highlight(
string fieldName,
int fragmentLength,
int fragmentCount,
out FieldHighlightings highlightings);
IDocumentQuery<T> Highlight(
string fieldName,
string fieldKeyName,
int fragmentLength,
int fragmentCount,
out FieldHighlightings highlightings);
IDocumentQuery<T> Highlight<TValue>(
Expression<Func<T, TValue>> propertySelector,
int fragmentLength,
int fragmentCount,
out FieldHighlightings highlightings);
IDocumentQuery<T> Highlight<TValue>(
Expression<Func<T, TValue>> propertySelector,
Expression<Func<T, TValue>> keyPropertySelector,
int fragmentLength,
int fragmentCount,
out FieldHighlightings highlightings);
where:
* fieldName or propertySelector is used to mark a field/property for highlight.
* fieldKeyName or keyPropertySelector is used to mark a field/property key for highlight.
* fragmentLength this is the maximum length of text fragments that will be returned.
* fragmentCount this is the maximum number of fragments that will be returned.
* highlightings this will return an instance of a FieldHighlightings
that contains the highlight fragments for each returned result.
By default, the highlighted text is wrapped with <b></b>
tags, to change this behavior the SetHighlighterTags
method was introduced.
IDocumentQuery<T> SetHighlighterTags(string preTag, string postTag);
IDocumentQuery<T> SetHighlighterTags(string[] preTags, string[] postTags);
Example. To wrap highlighted text with **
we just need to execute following query:
FieldHighlightings highlightings;
BlogPost[] results = session
.Advanced
.DocumentQuery<BlogPost, BlogPosts_ByContent>()
.Highlight("Content", 128, 1, out highlightings)
.SetHighlighterTags("**", "**")
.Search("Content", "raven")
.ToArray();
Note
Default <b></b>
tags are coloured and colours are returned in following order:
- yellow,
- lawngreen,
- aquamarine,
- magenta,
- palegreen,
- coral,
- wheat,
- khaki,
- lime,
- deepskyblue,
- deeppink,
- salmon,
- peachpuff,
- violet,
- mediumpurple,
- palegoldenrod,
- darkkhaki,
- springgreen,
- turquoise,
- powderblue