You are currently browsing legacy 3.0 version of documentation. Click here to switch to the newest 4.2 version.

We can help you with migration to the latest RavenDB

Contact Us Now
see on GitHub

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