Highlights
Another feature called highlights
has been added to RavenDB to enhance the search UX.
Usage
Lets consider a class and index as follows:
@QueryEntity
public class BlogPost {
private String id;
private String title;
private String category;
private String content;
private Date publishedAt;
private String[] tags;
private List<BlogComment> comments;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getPublishedAt() {
return publishedAt;
}
public void setPublishedAt(Date publishedAt) {
this.publishedAt = publishedAt;
}
public String[] getTags() {
return tags;
}
public void setTags(String[] tags) {
this.tags = tags;
}
public List<BlogComment> getComments() {
return comments;
}
public void setComments(List<BlogComment> comments) {
this.comments = comments;
}
}
public class BlogComment {
private String title;
private String content;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
public static class BlogPosts_ByContent extends AbstractIndexCreationTask {
public BlogPosts_ByContent() {
QBlogPost b = QBlogPost.blogPost;
map =
" from post in posts " +
" select new " +
" { " +
" post.Content " +
" };";
index(b.content, FieldIndexing.ANALYZED);
store(b.content, FieldStorage.YES);
termVector(b.content, FieldTermVector.WITH_POSITIONS_AND_OFFSETS);
}
}
Now to use Highlights we just need to use one of the highlight
query methods. The basic usage can be as simple as:
Reference<FieldHighlightings> highlightingsRef = new Reference<>();
List<BlogPost> results = session
.advanced()
.documentQuery(BlogPost.class, BlogPosts_ByContent.class)
.highlight("Content", 128, 1, highlightingsRef)
.search("Content", "raven")
.toList();
StringBuilder builder = new StringBuilder();
builder.append("<ul>\n");
for (BlogPost result : results) {
String[] fragments = highlightingsRef.value.getFragments(result.getId());
builder.append("<li>");
builder.append(fragments[0]);
builder.append("</li>");
}
String ul = builder.append("</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.
Reference<FieldHighlightings> highlightingsRef = new Reference<>();
List<BlogPosts_ByCategory_Content.Result> results = session
.advanced()
.documentQuery(BlogPost.class, BlogPosts_ByCategory_Content.class)
.highlight("Content", 128, 1, highlightingsRef)
.setHighlighterTags("**", "**")
.search("Content", "raven")
.selectFields(BlogPosts_ByCategory_Content.Result.class)
.toList();
Highlights + Map-Reduce
Highlights can be accessed when performing queries on map-reduce indexes.
public static class BlogPosts_ByCategory_Content extends AbstractIndexCreationTask {
public static class Result {
private String category;
private String content;
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
public BlogPosts_ByCategory_Content() {
QBlogPost b = QBlogPost.blogPost;
map =
" from post in posts " +
" select new " +
" { " +
" post.Category, " +
" post.Content " +
" };";
reduce =
" 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(b.content, FieldIndexing.ANALYZED);
store(b.content, FieldStorage.YES);
termVector(b.content, FieldTermVector.WITH_POSITIONS_AND_OFFSETS);
}
}
Reference<FieldHighlightings> highlightingsRef = new Reference<>();
List<BlogPosts_ByCategory_Content.Result> results = session
.advanced()
.documentQuery(BlogPosts_ByCategory_Content.Result.class, BlogPosts_ByCategory_Content.class)
.highlight("Content", "Category", 128, 1, highlightingsRef) // highlighting 'Content', but marking 'Category' as key
.setHighlighterTags("**", "**")
.search("Content", "raven")
.selectFields(BlogPosts_ByCategory_Content.Result.class)
.toList();
String[] newsHighlightings = highlightingsRef.value.getFragments("News"); // get fragments for 'News' category
Customization
public TSelf highlight(String fieldName, int fragmentLength, int fragmentCount, String fragmentsField);
public TSelf highlight(String fieldName, int fragmentLength, int fragmentCount, Reference<FieldHighlightings> highlightings);
public <TValue> TSelf highlight(Expression<?> propertySelector, int fragmentLength, int fragmentCount, ListPath<?, ?> fragmentsPropertySelector);
public <TValue> TSelf highlight(Expression<?> propertySelector, int fragmentLength, int fragmentCount, Reference<FieldHighlightings> highlightings);
where:
* fieldName or propertySelector is used to mark a field/property 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.
public TSelf setHighlighterTags(String preTag, String postTag);
public TSelf setHighlighterTags(String[] preTags, String[] postTags);
Example. To wrap highlighted text with **
we just need to execute following query:
Reference<FieldHighlightings> highlightingsRef = new Reference<>();
List<BlogPost> results = session
.advanced()
.documentQuery(BlogPost.class, BlogPosts_ByContent.class)
.highlight("Content", 128, 1, highlightingsRef)
.setHighlighterTags("**", "**")
.search("Content", "raven")
.toList();
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