Querying: Highlighting
Another feature called Highlighting
has been added to RavenDB to enhance the search UX.
Setup
- BlogPost
- BlogComment
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; }
}
- Index
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);
}
}
Each of the fields on which we want to use Highlighting needs to have:
- FieldIndexing set to
Search
- FieldStorage set to
Yes
- FieldTermVector set to
WithPositionsAndOffsets
Usage
To use Highlighting we just need to use one of the Highlight
query 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.
Highlighting + Projections
Highlighting can also be done when projections are performed.
- DocumentQuery_1
- DocumentQuery_2
- Index
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();
BlogPosts_ByContent.Result[] results = session
.Advanced
.DocumentQuery<BlogPost, BlogPosts_ByContent>()
.Highlight("Content", 128, 1, new HighlightingOptions
{
PreTags = new[] { "**" },
PostTags = new[] { "**" }
}, out Highlightings highlightings)
.Search("Content", "raven")
.SelectFields<BlogPosts_ByContent.Result>()
.ToArray();
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);
}
}
Highlighting + Map-Reduce
Highlighting can be performed when executing queries on map-reduce indexes.
- Query
- DocumentQuery
- Index
// 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");
// highlighting 'Content', but marking 'Category' as key
BlogPosts_ByCategory_Content.Result[] results = session
.Advanced
.DocumentQuery<BlogPosts_ByCategory_Content.Result, BlogPosts_ByCategory_Content>()
.Highlight("Content", 128, 1, new HighlightingOptions
{
PreTags = new[] { "**" },
PostTags = new[] { "**" },
GroupKey = "Category"
}, out Highlightings highlightings)
.Search("Content", "raven")
.ToArray();
// get fragments for 'News' category
var newsHighlightings = highlightings.GetFragments("News");
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);
}
}
Remarks
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