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 Result
{
Content = post.Content
};
Index(x => x.Content, FieldIndexing.Search);
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:
BlogPost[] results = session
.Advanced
.DocumentQuery<BlogPost, BlogPosts_ByContent>()
.Highlight("Content", 128, 1, out Highlightings highlightings)
.Search("Content", "raven")
.ToArray();
StringBuilder builder = new StringBuilder()
.AppendLine("<ul>");
foreach (BlogPost result in results)
{
string[] fragments = highlightings.GetFragments(result.Id);
builder.AppendLine($"<li>{fragments.First()}</li>");
}
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.
_1
BlogPosts_ByContent.Result[] results = session
.Query<BlogPosts_ByContent.Result, BlogPosts_ByContent>()
.Highlight("Content", 128, 1, new HighlightingOptions
{
PreTags = new[] { "**" },
PostTags = new[] { "**" }
}, out Highlightings highlightings)
.Search(x => x.Content, "raven")
.ProjectInto<BlogPosts_ByContent.Result>()
.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 Result
{
Category = post.Category,
Content = 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.Search);
Store(x => x.Content, FieldStorage.Yes);
TermVector(x => x.Content, FieldTermVector.WithPositionsAndOffsets);
}
}
_1
// highlighting 'Content', but marking 'Category' as key
BlogPosts_ByCategory_Content.Result[] results = session
.Query<BlogPosts_ByCategory_Content.Result, BlogPosts_ByCategory_Content>()
.Highlight("Content", 128, 1, new HighlightingOptions
{
PreTags = new[] { "**" },
PostTags = new[] { "**" },
GroupKey = "Category"
}, out Highlightings highlightings)
.Search(x => x.Content, "raven")
.ToArray();
// get fragments for 'News' category
var newsHighlightings = highlightings.GetFragments("News");
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();
Default <b></b>
tags are coloured and colours are returned in following order:
- <span style="border-left: 10px solid yellow"> </span>yellow,
- <span style="border-left: 10px solid lawngreen"> </span>lawngreen,
- <span style="border-left: 10px solid aquamarine"> </span>aquamarine,
- <span style="border-left: 10px solid magenta"> </span>magenta,
- <span style="border-left: 10px solid palegreen"> </span>palegreen,
- <span style="border-left: 10px solid coral"> </span>coral,
- <span style="border-left: 10px solid wheat"> </span>wheat,
- <span style="border-left: 10px solid khaki"> </span>khaki,
- <span style="border-left: 10px solid lime"> </span>lime,
- <span style="border-left: 10px solid deepskyblue"> </span>deepskyblue,
- <span style="border-left: 10px solid deeppink"> </span>deeppink,
- <span style="border-left: 10px solid salmon"> </span>salmon,
- <span style="border-left: 10px solid peachpuff"> </span>peachpuff,
- <span style="border-left: 10px solid violet"> </span>violet,
- <span style="border-left: 10px solid mediumpurple"> </span>mediumpurple,
- <span style="border-left: 10px solid palegoldenrod"> </span>palegoldenrod,
- <span style="border-left: 10px solid darkkhaki"> </span>darkkhaki,
- <span style="border-left: 10px solid springgreen"> </span>springgreen,
- <span style="border-left: 10px solid turquoise"> </span>turquoise,
- <span style="border-left: 10px solid powderblue"> </span>powderblue