Highlighting allows you to get highlighted text fragments (called snippets) from documents that contain matching keywords.
SQL's HIGHLIGHT()
function, "highlight"
property in json queries via HTTP and highlight()
function in the PHP client all use built-in document storage for retrieving original field contents (enabled by default).
- SQL
- HTTP
- PHP
- Python
- Javascript
- Java
SELECT HIGHLIGHT() FROM books WHERE MATCH('try');
+----------------------------------------------------------+
| highlight() |
+----------------------------------------------------------+
| Don`t <strong>try</strong> to compete in childishness, said Bliss. |
+----------------------------------------------------------+
1 row in set (0.00 sec)
When using SQL to highlight search results, you get different snippets from different fields concatenated as a single string. It is a limitation of mysql protocol. You can fine-tune concatenation separators with field_separator
and snippet_separator
options, see below.
When running json queries via HTTP or using the PHP client, there are no such limitations and the result set contains an array of fields which contains arrays of snippets (without the separators).
Note that snippet generation options such as limit
, limit_words
, limit_snippets
are applied to each field separately (by default). You can change this behavior using the limits_per_field
option, but it may lead to undesirable results. I.e. one of the fields has matching keywords, but no snippets from this field are included in the result set because they didn't rank as high as the snippets from the other fields in the highlighting engine.
Highlighting algorithm currently favors better snippets (with closer phrase matches), and then snippets with keywords not yet included in the result. Generally, it will try to highlight the best match with the query, and it will also try to highlight all the query keywords, as made possible by the limits. If there are no matches in the current field, the beginning of the document trimmed down according to the limits will be return by default. You can also return an empty string instead by setting allow_empty
option to 1.
Highlighting is performed on a so-called post limit
stage, meaning that snippet generation is postponed not just until the entire final result set is ready, but even after the LIMIT clause is applied. For example, with a LIMIT 20,10 clause, HIGHLIGHT()
function will be called at most 10 times.
There are several additional optional highlighting options that can be used to fine-tune snippet generation. Most of them are common to SQL, HTTP and PHP client.
A string to insert before a keyword match. A %SNIPPET_ID%
macro can be used in this string. The first match of the macro is replaced with an incrementing snippet number within a current snippet. Numbering starts at 1 by default but can be overridden with start_snippet_id
option. %SNIPPET_ID% restarts at the start of every new document. Default is <strong>
.
A string to insert after a keyword match. Default is </strong>
.
Maximum snippet size, in symbols (codepoints). Default is 256. Per-field by default, see limits_per_field
.
Limits the maximum number of words that can be included in the result. Note the limit applies to any words, and not just the matched keywords to highlight. For example, if we are highlighting Mary
and a snippet Mary had a little lamb
is selected, then it contributes 5 words to this limit, not just 1. Default is 0 (no limit). Per-field by default, see limits_per_field
.
Limits the maximum number of snippets that can be included in the result. Default is 0 (no limit). Per-field by default, see limits_per_field
.
Selects whether limit
, limit_words
and limit_snippets
work as individual limits in every field of the document being highlighted or as global limits for the whole document. Setting this option to 0 means that all combined highlighting results for one document must be within the specified limits. The downside is that you may get several snippets highlighted in one field and none in another if the highlighting engine decides that they are more relevant. Default is 1 (use per-field limits).
How much words to pick around each matching keywords block. Default is 5.
Whether to additionally break snippets by phrase boundary characters, as configured in index settings with phrase_boundary directive. Default is 0 (don't use boundaries).
Whether to sort the extracted snippets in order of relevance (decreasing weight), or in order of appearance in the document (increasing position). Default is 0 (don't use weight order).
Ignores length limit until the result includes all the keywords. Default is 0 (don't force all keywords).
Specifies the starting value of %SNIPPET_ID%
macro (that gets detected and expanded in before_match
, after_match
strings). Default is 1.
HTML stripping mode setting. Defaults to index
, which means that index settings will be used. The other values are none
and strip
, that forcibly skip or apply stripping irregardless of index settings; and retain
, that retains HTML markup and protects it from highlighting. The retain
mode can only be used when highlighting full documents and thus requires that no snippet size limits are set. String, allowed values are none
, strip
, index
, and retain
.
Allows empty string to be returned as highlighting result when no snippets could be generated in the current field (no keywords match, or no snippets fit the limit). By default, the beginning of original text would be returned instead of an empty string. Default is 0 (don't allow empty result).
Ensures that snippets do not cross a sentence, paragraph, or zone boundary (when used with an index that has the respective indexing settings enabled). String, allowed values are sentence
, paragraph
, and zone
.
Emits an HTML tag with an enclosing zone name before each snippet. Default is 0 (don't emit zone names).
Whether to force snippet generation even if limits allow to highlight whole text. Default is 0 (don't force snippet generation).
- SQL
- HTTP
- PHP
- Python
- Javascript
- Java
SELECT HIGHLIGHT({limit=50}) FROM books WHERE MATCH('try|gets|down|said');
+---------------------------------------------------------------------------+
| highlight({limit=50}) |
+---------------------------------------------------------------------------+
| ... , "It <strong>gets</strong> infantile pleasure ... to knock it <strong>down</strong>." |
| Don`t <strong>try</strong> to compete in childishness, <strong>said</strong> Bliss. |
| ... a small room. Bander <strong>said</strong>, "Come, half-humans, I ... |
+---------------------------------------------------------------------------+
3 rows in set (0.00 sec)
HIGHLIGHT()
function can be used to highlight search results. Here's the syntax:
HIGHLIGHT([options], [field_list], [query] )
By default, it works with no arguments.
- SQL
SELECT HIGHLIGHT() FROM books WHERE MATCH('before');
+-----------------------------------------------------------+
| highlight() |
+-----------------------------------------------------------+
| A door opened <strong>before</strong> them, revealing a small room. |
+-----------------------------------------------------------+
1 row in set (0.00 sec)
HIGHLIGHT()
fetches all available full-text fields from document storage and highlights them against the given query. It supports field syntax in queries. Field text is separated by field_separator
, which can be changed in the options.
- SQL
SELECT HIGHLIGHT() FROM books WHERE MATCH('@title one');
+-----------------+
| highlight() |
+-----------------+
| Book <strong>one</strong> |
+-----------------+
1 row in set (0.00 sec)
Optional first argument in HIGHLIGHT()
is the list of options.
- SQL
SELECT HIGHLIGHT({before_match='[match]',after_match='[/match]'}) FROM books WHERE MATCH('@title one');
+------------------------------------------------------------+
| highlight({before_match='[match]',after_match='[/match]'}) |
+------------------------------------------------------------+
| Book [match]one[/match] |
+------------------------------------------------------------+
1 row in set (0.00 sec)
Optional second argument is a string containing a field or a comma-separated list of fields. If this argument is present, only the specified fields will be fetched from document storage and highlighted. An empty string as a second argument means "fetch all available fields".
- SQL
SELECT HIGHLIGHT({},'title,content') FROM books WHERE MATCH('one|robots');
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| highlight({},'title,content') |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Book <strong>one</strong> | They followed Bander. The <strong>robots</strong> remained at a polite distance, but their presence was a constantly felt threat. |
| Bander ushered all three into the room. <strong>One</strong> of the <strong>robots</strong> followed as well. Bander gestured the other <strong>robots</strong> away and entered itself. The door closed behind it. |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)
Another way to use the second argument is to specify string attribute or field name without quotes. This way the supplied string will be highlighted against the provided query, however, field syntax will be ignored.
- SQL
SELECT HIGHLIGHT({}, title) FROM books WHERE MATCH('one');
+---------------------+
| highlight({},title) |
+---------------------+
| Book <strong>one</strong> |
| Book five |
+---------------------+
2 rows in set (0.00 sec)
Optional third argument is the query. It is used to highlight search results against a query different than the one used for searching.
- SQL
SELECT HIGHLIGHT({},'title', 'five') FROM books WHERE MATCH('one');
+-------------------------------+
| highlight({},'title', 'five') |
+-------------------------------+
| Book one |
| Book <strong>five</strong> |
+-------------------------------+
2 rows in set (0.00 sec)
While HIGHLIGHT()
is designed to work with stored full-text fields and string attributes, it can also be used to highlight arbitrary text. Note that if the query has any field search operators (@title hello @body world
), the field part of them is ignored in this case.
- SQL
SELECT HIGHLIGHT({},TO_STRING('some text to highlight'), 'highlight') FROM books WHERE MATCH('@title one');
+----------------------------------------------------------------+
| highlight({},TO_STRING('some text to highlight'), 'highlight') |
+----------------------------------------------------------------+
| some text to <strong>highlight</strong> |
+----------------------------------------------------------------+
1 row in set (0.00 sec)
Several options make sense only when generating a single string as a result (not an array of snippets). This only applies to SQL's HIGHLIGHT()
function:
A string to insert between snippets. Default is ...
.
A string to insert between fields. Default is |
.
Another way to highlight text is to use the CALL SNIPPETS statement. It mostly duplicates HIGHLIGHT()
functionality, but it can't use built-in document storage. It can, however, load source text from files.
To highlight full-text search results in JSON queries via HTTP, field contents has to be stored in document storage (enabled by default). In the example full-text fields content
and title
are fetched from document storage and highlighted against the query specified in query
clause.
Highlighted snippets are returned in the highlight
property of the hits
array.
- HTTP
- PHP
- Python
- Javascript
- Java
POST /search
{
"index": "books",
"query": { "match": { "*": "one|robots" } },
"highlight":
{
"fields": ["content"]
}
}
{
"took": 0,
"timed_out": false,
"hits": {
"total": 1,
"hits": [
{
"_id": "1",
"_score": 2788,
"_source": {
"title": "Books one",
"content": "They followed Bander. The robots remained at a polite distance, but their presence was a constantly felt threat. Bander ushered all three into the room. One of the robots followed as well. Bander gestured the other robots away and entered itself. The door closed behind it. "
},
"highlight": {
"content": [
"They followed Bander. The <strong>robots</strong> remained at a polite distance, ",
" three into the room. <strong>One</strong> of the <strong>robots</strong> followed as well. Bander",
" gestured the other <strong>robots</strong> away and entered itself. The"
]
}
}
]
}
}
To highlight all possible fields, pass an empty object as highlight
propery.
- HTTP
- PHP
- Python
- Javascript
- Java
POST /search
{
"index": "books",
"query": { "match": { "*": "one|robots" } },
"highlight": {}
}
{
"took": 0,
"timed_out": false,
"hits": {
"total": 1,
"hits": [
{
"_id": "1",
"_score": 2788,
"_source": {
"title": "Books one",
"content": "They followed Bander. The robots remained at a polite distance, but their presence was a constantly felt threat. Bander ushered all three into the room. One of the robots followed as well. Bander gestured the other robots away and entered itself. The door closed behind it. "
},
"highlight": {
"title": [
"Books <strong>one</strong>"
],
"content": [
"They followed Bander. The <strong>robots</strong> remained at a polite distance, ",
" three into the room. <strong>One</strong> of the <strong>robots</strong> followed as well. Bander",
" gestured the other <strong>robots</strong> away and entered itself. The"
]
}
}
]
}
}
In addition to common highlighting options, several synonyms are available for JSON queries via HTTP:
fields
object contains attribute names with options. It can also be an array of field names (without any options).
encoder
can be set to default
or html
. When set to html
, retains html markup when highlighting. Works similar to html_strip_mode=retain
option.
highlight_query
makes it possible to highlight against a query other than our search query. Syntax is the same as in the main query
.
- HTTP
- PHP
- Python
- Javascript
- Java
POST /search
{
"index": "books",
"query": { "match": { "content": "one|robots" } },
"highlight":
{
"fields": [ "content"],
"highlight_query": { "match": { "*":"polite distance" } }
}
}
{'aggregations': None,
'hits': {'hits': [{u'_id': u'1',
u'_score': 1788,
u'_source': {u'content': u'They followed Bander. The robots remained at a polite distance, but their presence was a constantly felt threat. Bander ushered all three into the room. One of the robots followed as well. Bander gestured the other robots away and entered itself. The door closed behind it. ',
u'title': u'Books one'},
u'highlight': {u'content': [u'. The robots remained at a <strong>polite distance</strong>, but their presence was a']}}],
'max_score': None,
'total': 1},
'profile': None,
'timed_out': False,
'took': 0}
pre_tags
and post_tags
set opening and closing tags for highlighted text snippets. They work similar to before_match
and after_match
options. Optional, defaults are <strong>
and </strong>
.
- HTTP
- PHP
- Python
- Javascript
- Java
POST /search
{
"index": "books",
"query": { "match": { "*": "one|robots" } },
"highlight":
{
"fields": [ "content", "title" ],
"pre_tags": "before_",
"post_tags": "_after"
}
}
Document: 1
title : Books one
content : They followed Bander. The robots remained at a polite distance, but their presence was a constantly felt threat. Bander ushered all three into the room. One of the robots followed as well. Bander gestured the other robots away and entered itself. The door closed behind it.
Highlight for content:
- They followed Bander. The before_robots_after remained at a polite distance,
- three into the room. before_One_after of the before_robots_after followed as well. Bander
- gestured the other before_robots_after away and entered itself. The
Highlight for title:
- Books before_one_after
no_match_size
works similar to the allow_empty
option. If set to 0, acts as allow_empty=1
, i.e. allows empty string to be returned as highlighting result when a snippet could not be generated. Otherwise, the beginning of the field will be returned. Optional, default is 1.
- HTTP
- PHP
- Python
- Javascript
- Java
POST /search
{
"index": "books",
"query": { "match": { "*": "one|robots" } },
"highlight":
{
"fields": [ "content", "title" ],
"no_match_size": 0
}
}
Document: 1
title : Books one
content : They followed Bander. The robots remained at a polite distance, but their presence was a constantly felt threat. Bander ushered all three into the room. One of the robots followed as well. Bander gestured the other robots away and entered itself. The door closed behind it.
Highlight for content:
- They followed Bander. The <strong>robots</strong> remained at a polite distance,
- three into the room. <strong>One</strong> of the <strong>robots</strong> followed as well. Bander
- gestured the other <strong>robots</strong> away and entered itself. The
Highlight for title:
- Books <strong>one</strong>
order
sets the sorting order of extracted snippets. If set to "score"
, sorts the extracted snippets in order of relevance. Optional. Works similar to weight_order
option.
- HTTP
- PHP
- Python
- Javascript
- Java
POST /search
{
"index": "books",
"query": { "match": { "*": "one|robots" } },
"highlight":
{
"fields": [ "content", "title" ],
"order": "score"
}
}
Document: 1
title : Books one
content : They followed Bander. The robots remained at a polite distance, but their presence was a constantly felt threat. Bander ushered all three into the room. One of the robots followed as well. Bander gestured the other robots away and entered itself. The door closed behind it.
Highlight for content:
- three into the room. <strong>One</strong> of the <strong>robots</strong> followed as well. Bander
- gestured the other <strong>robots</strong> away and entered itself. The
- They followed Bander. The <strong>robots</strong> remained at a polite distance,
Highlight for title:
- Books <strong>one</strong>
fragment_size
sets maximum snippet size in symbols. Can be global or per-field. Per-field options override global options. Optional, default is 256. Works similar to limit
option.
- HTTP
- PHP
- Python
- Javascript
- Java
POST /search
{
"index": "books",
"query": { "match": { "*": "one|robots" } },
"highlight":
{
"fields": [ "content", "title" ],
"fragment_size": 100
}
}
Document: 1
title : Books one
content : They followed Bander. The robots remained at a polite distance, but their presence was a constantly felt threat. Bander ushered all three into the room. One of the robots followed as well. Bander gestured the other robots away and entered itself. The door closed behind it.
Highlight for content:
- the room. <strong>One</strong> of the <strong>robots</strong> followed as well
- Bander gestured the other <strong>robots</strong> away and entered
Highlight for title:
- Books <strong>one</strong>
number_of_fragments
: Limits the maximum number of snippets in the result. Just as fragment_size
, can be global or per-field. Optional, default is 0 (no limit). Works similar to limit_snippets
option.
- HTTP
- PHP
- Python
- Javascript
- Java
POST /search
{
"index": "books",
"query": { "match": { "*": "one|robots" } },
"highlight":
{
"fields": [ "content", "title" ],
"number_of_fragments": 10
}
}
Document: 1
title : Books one
content : They followed Bander. The robots remained at a polite distance, but their presence was a constantly felt threat. Bander ushered all three into the room. One of the robots followed as well. Bander gestured the other robots away and entered itself. The door closed behind it.
Highlight for content:
- They followed Bander. The <strong>robots</strong> remained at a polite distance,
- three into the room. <strong>One</strong> of the <strong>robots</strong> followed as well. Bander
- gestured the other <strong>robots</strong> away and entered itself. The
Highlight for title:
- Books <strong>one</strong>
Options such as limit
, limit_words
, and limit_snippets
can be set as global or per-field options. Global options are used as per-field limits unless per-field options override them. In the example the title
field is highlighted with default limit settings while the content
field uses a different limit.
- HTTP
- PHP
- Python
- Javascript
- Java
POST /search
{
"index": "books",
"query": { "match": { "*": "one|robots" } },
"highlight":
{
"fields":
{
"title": {},
"content" : { "limit": 50 }
}
}
}
Document: 1
title : Books one
content : They followed Bander. The robots remained at a polite distance, but their presence was a constantly felt threat. Bander ushered all three into the room. One of the robots followed as well. Bander gestured the other robots away and entered itself. The door closed behind it.
Highlight for content:
- into the room. <strong>One</strong> of the <strong>robots</strong> followed as well
Highlight for title:
- Books <strong>one</strong>
Global limits can also be forced by specifying limits_per_field=0
. Setting this option means that all combined highlighting results must be within the specified limits. The downside is that you may get several snippets highlighted in one field and none in another if the highlighting engine decides that they are more relevant.
- HTTP
- PHP
- Python
- Javascript
- Java
POST /search
{
"index": "books",
"query": { "match": { "content": "and first" } },
"highlight":
{
"limits_per_field": false,
"fields":
{
"content" : { "limit": 50 }
}
}
}
Document: 1
title : Books one
content : They followed Bander. The robots remained at a polite distance, but their presence was a constantly felt threat. Bander ushered all three into the room. One of the robots followed as well. Bander gestured the other robots away and entered itself. The door closed behind it.
Highlight for content:
- gestured the other robots away <strong>and</strong> entered itself. The door closed
CALL SNIPPETS
statement builds a snippet from provided data and query using specified index settings. It can't access built-in document storage, that's why it's recommended to use HIGHLIGHT() function instead.
The syntax is:
CALL SNIPPETS(data, index, query[, opt_value AS opt_name[, ...]])
data
is the source data to extract a snippet from. It can be a single string, or the list of the strings enclosed in curly brackets.
index
is the name of the index from which to take the text processing settings.
query
is the full-text query to build snippets for.
opt_value
and opt_name
are snippet generation options
- SQL
CALL SNIPPETS(('this is my document text','this is my another text'), 'forum', 'is text', 5 AS around, 200 AS limit);
+----------------------------------------+
| snippet |
+----------------------------------------+
| this <strong>is</strong> my document <strong>text</strong> |
| this <strong>is</strong> my another <strong>text</strong> |
+----------------------------------------+
2 rows in set (0.02 sec)
Most options are the same as in the HIGHLIGHT() function. There are, however, several options that can only be used with CALL SNIPPETS
. The following options can be used to highlight text stored in separate files:
Whether to handle the first argument as data to extract snippets from (default behavior), or to treat it as file names, and load data from specified files on the server side. Up to max_threads_per_query worker threads per request will be used to parallelize the work when this flag is enabled. Default is 0 (no limit). To distribute snippet generation between remote agents invoke snippets generation in a distributed index, that contains only one(!) local agent and several remotes. The snippets_file_prefix option is used to generate the final file name. E.g. when searchd is configured with snippets_file_prefix = /var/data_
and text.txt
is provided as a file name, snippets will be generated from the content of /var/data_text.txt
.
Works only with distributed snippets generation with remote agents. Source files for snippet generation can be distributed among different agents and the main server will merge all non-erroneous results. E.g. if one agent of the distributed index has file1.txt
, another agent has file2.txt
and you use CALL SNIPPETS
with both of these files, searchd will merge agent results, so you will get results from both file1.txt
and file2.txt
. Default is 0.
If load_files
options is also enabled, request will return an error if any of the files is not available anywhere. Otherwise (if load_files
is not enabled) it will just return empty strings for all absent files. Searchd does not pass this flag to agents, so agents do not generate a critical error if the file does not exist. If you want to be sure that all source files are loaded, set both load_files_scattered
and load_files
to 1. If the absence of some source files on some agent is not critical, set only load_files_scattered
to 1.
- SQL
CALL SNIPPETS(('data/doc1.txt','data/doc2.txt'), 'forum', 'is text', 1 AS load_files);
+----------------------------------------+
| snippet |
+----------------------------------------+
| this <strong>is</strong> my document <strong>text</strong> |
| this <strong>is</strong> my another <strong>text</strong> |
+----------------------------------------+
2 rows in set (0.02 sec)