## Scoring ZomboDB provides a function named `zdb.score(tid) RETURNS real` that returns the score for the current matching row. You can use it in the target list of your query and can also sort by it. Without an `ORDER BY` clause, SQL doesn't guarantee any kind of ordering, so it's always important to also order by the score if you want the top-ranked documents first in your results. Using the [tutorial](TUTORIAL.md) database, an example of using scores is: ```sql tutorial=# SELECT zdb.score(ctid), * FROM products WHERE products ==> 'sports box' ORDER BY score desc; score | id | name | keywords | short_summary | long_description | price | ----------+----+----------+--------------------------------------+--------------------------------+-------------------------------------------------------------------------------------+-------+- 1.00079 | 4 | Box | {wooden,box,"negative space",square} | Just an empty box made of wood | A wooden container that will eventually rot away. Put stuff it in (but not a cat). | 17000 | 0.698622 | 2 | Baseball | {baseball,sports,round} | It's a baseball | Throw it at a person with a big wooden stick and hope they don't hit it | 1249 | (2 rows) ``` Note that the argument provided to `zdb.score()` is the hidden Postgres system column called `ctid`. Internally, ZomboDB uses ctids to identify matching rows, and this is how you tell `zdb.score()` the row you want. Also, `zdb.score()` is **not** allowed in the `WHERE` clause of queries. It is only allowed in `ORDER BY` clauses and what Postgres calls the "target list" -- the list of columns the query should return. If you need to limit the results of a query by score you can use ZomboDB's [`dsl.min_score()`](QUERY-BUILDER-API.md) function, or you can use a subselect of some kind, such as: ```sql SELECT * FROM (SELECT zdb.score(ctid), * FROM products WHERE products ==> 'sports box') x WHERE x.score > 1.0; ``` But, this won't work: ```sql # SELECT zdb.score(ctid), * FROM products WHERE products ==> 'sports box' AND zdb.score(ctid) > 1.0; ERROR: zdb.score() can only be used as a target entry or as a sort ``` ## Highlighting Similar to scoring support, ZomboDB can returning highlighted fragments from fields that support it (typically text fields that use an analyzer). The function is called `zdb.highlight(tid, fieldname [, json_highlight_descriptor]) RETURNS text[]`. Using the [tutorial](TUTORIAL.md) database, an example of highlighting is: ```sql tutorial=# SELECT zdb.score(ctid), zdb.highlight(ctid, 'long_description'), long_description FROM products WHERE products ==> 'wooden person' ORDER BY score desc; score | highlight | long_description ----------+--------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------ 0.882384 | {"Throw it at a person with a big wooden stick and hope they don't hit it"} | Throw it at a person with a big wooden stick and hope they don't hit it 0.224636 | {"A wooden container that will eventually rot away. Put stuff it in (but not a cat)."} | A wooden container that will eventually rot away. Put stuff it in (but not a cat). (2 rows) ``` Similarly to `zdb.score()`, the first argument to `zdb.highlight()` is the Postgres hidden system column `ctid` that identifies the row for which you want highlights. As Elasticsearch can return multiple highlight fragments for any given field, `zdb.highlight()` returns a `text[]` which allows you to address each fragment individually. ZomboDB uses Elasticsearch's defaults for highlighting, but if these are not sufficient for your needs, the third argument to `zdb.highlight()` allows you to set a per-field highlight definition as decribed in [Elasticsearch's highlighting documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-highlighting.html). ZomboDB provides a type-checked helper function (also named `zdb.highlight()`) that allows you to build up a highlight definition using SQL. ```sql CREATE TYPE esqdsl_highlight_type AS ENUM ('unified', 'plain', 'fvh'); CREATE TYPE esqdsl_fragmenter_type AS ENUM ('simple', 'span'); CREATE TYPE esqdsl_encoder_type AS ENUM ('default', 'html'); CREATE TYPE esqdsl_boundary_scanner_type AS ENUM ('chars', 'sentence', 'word'); FUNCTION highlight( type zdb.esqdsl_highlight_type DEFAULT NULL, require_field_match boolean DEFAULT false, number_of_fragments int DEFAULT NULL, highlight_query zdbquery DEFAULT NULL, pre_tags text[] DEFAULT NULL, post_tags text[] DEFAULT NULL, tags_schema text DEFAULT NULL, no_match_size int DEFAULT NULL, fragmenter zdb.esqdsl_fragmenter_type DEFAULT NULL, fragment_size int DEFAULT NULL, fragment_offset int DEFAULT NULL, force_source boolean DEFAULT true, encoder zdb.esqdsl_encoder_type DEFAULT NULL, boundary_scanner_locale text DEFAULT NULL, boundary_scan_max int DEFAULT NULL, boundary_chars text DEFAULT NULL, phrase_limit int DEFAULT NULL, matched_fields boolean DEFAULT NULL, "order" text DEFAULT NULL) RETURNS json ``` An example usage of this function, where we change the pre/post highlight tags is: ```sql SELECT zdb.score(ctid), zdb.highlight(ctid, 'long_description', zdb.highlight(pre_tags=>'{}', post_tags=>'{}') ), long_description FROM products WHERE products ==> 'wooden person' ORDER BY score desc; ``` Which results in: ```sql score | highlight | long_description ----------+------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------- 0.882384 | {"Throw it at a person with a big wooden stick and hope they don't hit it"} | Throw it at a person with a big wooden stick and hope they don't hit it 0.224636 | {"A wooden container that will eventually rot away. Put stuff it in (but not a cat)."} | A wooden container that will eventually rot away. Put stuff it in (but not a cat). (2 rows) ``` Akin to the `zdb.highlight()` function there is also `zdb.highlight_all_fields()`, which works the same but takes no "field name" argument. Its definition is: ```sql FUNCTION zdb.highlight_all_fields( "ctid" tid, "_highlight_definition" json DEFAULT zdb.highlight() ) RETURNS json ``` And using a similar example to above, returns the following: ```sql # SELECT zdb.score(ctid), zdb.highlight_all_fields(ctid, zdb.highlight(pre_tags=>'{}', post_tags=>'{}')), long_description FROM products WHERE products ==> 'wooden person or box' ORDER BY score desc; -[ RECORD 1 ]--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- score | 0.5753641724586487 highlight_all_fields | {"long_description":["Throw it at a person with a big wooden stick and hope they don't hit it"],"zdb_all":["Throw it at a person with a big wooden stick and hope they don't hit it"]} long_description | Throw it at a person with a big wooden stick and hope they don't hit it -[ RECORD 2 ]--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- score | 0.4520718455314636 highlight_all_fields | {"long_description":["A wooden container that will eventually rot away. Put stuff it in (but not a cat)."],"zdb_all":["wooden","box","Box","A wooden container that will eventually rot away. Put stuff it in (but not a cat).","Just an empty box made of wood"],"keywords":["wooden","box"],"short_summary":["Just an empty box made of wood"],"name":["Box"]} long_description | A wooden container that will eventually rot away. Put stuff it in (but not a cat). ```