{
  "schema_version": 2,
  "id": "develop/ai/search-and-query/advanced-concepts/scoring",
  "title": "Scoring documents",
  "url": "https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/scoring/",
  "summary": "Full-text scoring functions",
  "aliases": [
    "/develop/interact/search-and-query/advanced-concepts/scoring",
    "/interact/search-and-query/advanced-concepts/scoring/"
  ],
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-09-11T15:33:04+01:00",
  "page_type": "content",
  "content_hash": "fe51a836c11d718165601b932e9722768ccec198f8a740a1db4cda882c11f2d4",
  "sections": [
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "When searching, documents are scored based on their relevance to the query. The score is a floating point number between 0.0 and 1.0, where 1.0 is the highest score. The score is returned as part of the search results and can be used to sort the results.\n\nRedis Open Source comes with a few scoring functions to evaluate document relevance. They are all based on document scores and term frequency. This is regardless of the ability to use [sortable fields](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/sorting). Scoring functions are specified by adding the `SCORER {scorer_name}` argument to a search query.\n\nIf you prefer a custom scoring function, it is possible to add more functions using the [extension API](https://redis.io/docs/latest/develop/ai/search-and-query/administration/extensions).\n\nThe following is a list of the pre-bundled scoring functions available in Redis and a short explanation about how they work. Each function is mentioned by registered name, which can be passed as a `SCORER` argument in [`FT.SEARCH`](https://redis.io/docs/latest/commands/ft.search)."
    },
    {
      "id": "tfidf",
      "title": "TFIDF",
      "role": "content",
      "text": "Basic [TF-IDF scoring](https://en.wikipedia.org/wiki/Tf%E2%80%93idf) with a few extra features:\n\n1. For each term in each result, the TF-IDF score of that term is calculated to that document. Frequencies are weighted based on field weights that are pre-determined, and each term's frequency is normalized by the highest term frequency in each document.\n\n2. The total TF-IDF for the query term is multiplied by the presumptive document score given on `FT.CREATE` via `SCORE_FIELD`.\n\n3. A penalty is assigned to each result based on \"slop\" or cumulative distance between the search terms. Exact matches will get no penalty, but matches where the search terms are distant will have their score reduced significantly. For each bigram of consecutive terms, the minimal distance between them is determined. The penalty is the square root of the sum of the distances squared; e.g., `1/sqrt(d(t2-t1)^2 + d(t3-t2)^2 + ...)`.\n\nGiven N terms in document D, `T1...Tn`, the resulting score could be described with this Python function:\n\n[code example]"
    },
    {
      "id": "tfidfdocnorm",
      "title": "TFIDF.DOCNORM",
      "role": "content",
      "text": "Identical to the default `TFIDF` scorer, with one important distinction:\n\nTerm frequencies are normalized by the length of the document, expressed as the total number of terms. The length is weighted, so that if a document contains two terms, one in a field that has a weight 1 and one in a field with a weight of 5, the total frequency is 6, not 2.\n\n[code example]"
    },
    {
      "id": "bm25std-default",
      "title": "BM25STD (default)",
      "role": "content",
      "text": "A variation on the basic `TFIDF` scorer, see [this Wikipedia article for more info](https://en.wikipedia.org/wiki/Okapi_BM25).\n\nThe relevance score for each document is multiplied by the presumptive document score and a penalty is applied based on slop as in `TFIDF`.\n\n> [!NOTE]\n> The `BM25` scorer was renamed `BM25STD` in Redis Open Source 8.4. `BM25` is deprecated.\n\n[code example]"
    },
    {
      "id": "bm25stdnorm",
      "title": "BM25STD.NORM",
      "role": "content",
      "text": "A variation of `BM25STD`, where the scores are normalized by the minimum and maximum scores.\n\n`BM25STD.NORM` uses min–max normalization across the collection, making it more accurate in distinguishing documents when term frequency distributions vary significantly. Because it depends on global statistics, results adapt better to collection-specific characteristics, but this comes at a performance cost: min and max values must be computed and updated whenever the collection changes. This method is recommended when ranking precision is critical and the dataset is relatively stable."
    },
    {
      "id": "bm25stdtanh",
      "title": "BM25STD.TANH",
      "role": "content",
      "text": "A variation of `BM25STD.NORM`, where the scores are normalised by linear function `tanh(x)`. `BMSTDSTD.TANH` can take an optional argument, `BM25STD_TANH_FACTOR Y`, which is used to smooth the function and the score values. The default value for `Y` is 4.\n\n`BM25STD.TANH` applies a smooth transformation using the `tanh(x/factor)` function, which avoids collection-dependent statistics and yields faster, more efficient scoring. While this makes it more scalable and consistent across different datasets, the trade-off is reduced accuracy in cases where min–max normalization provides sharper separation. This method is recommended when performance and throughput are prioritized over fine-grained ranking sensitivity.\n\nFollowing is an example of how to use `BM25STD_TANH_FACTOR Y` in a query.\n\n[code example]"
    },
    {
      "id": "dismax",
      "title": "DISMAX",
      "role": "content",
      "text": "A simple scorer that sums up the frequencies of matched terms. In the case of union clauses, it will give the maximum value of those matches. No other penalties or factors are applied.\n\nIt is not a one-to-one implementation of [Solr's DISMAX algorithm](https://wiki.apache.org/solr/DisMax), but it follows it in broad terms.\n\n[code example]"
    },
    {
      "id": "docscore",
      "title": "DOCSCORE",
      "role": "content",
      "text": "A scoring function that just returns the presumptive score of the document without applying any calculations to it. Since document scores can be updated, this can be useful if you'd like to use an external score and nothing further.\n\n[code example]"
    },
    {
      "id": "hamming",
      "title": "HAMMING",
      "role": "content",
      "text": "Scoring by the inverse Hamming distance between the document's payload and the query payload is performed. Since the nearest neighbors are of interest, the inverse Hamming distance (`1/(1+d)`) is used so that a distance of 0 gives a perfect score of 1 and is the highest rank.\n\nThis only works if:\n\n1. The document has a payload.\n2. The query has a payload.\n3. Both are exactly the same length.\n\nPayloads are binary-safe, and having payloads with a length that is a multiple of 64 bits yields slightly faster results.\n\nExample:\n\n[code example]"
    }
  ],
  "examples": [
    {
      "id": "tfidf-ex0",
      "language": "py",
      "code": "def get_score(terms, doc):\n    # the sum of tf-idf\n    score = 0\n\n    # the distance penalty for all terms\n    dist_penalty = 0\n\n    for i, term in enumerate(terms):\n        # tf normalized by maximum frequency\n        tf = doc.freq(term) / doc.max_freq\n\n        # idf is global for the index, and not calculated each time in real life\n        idf = log2(1 + total_docs / docs_with_term(term))\n\n        score += tf*idf\n\n        # sum up the distance penalty\n        if i > 0:\n            dist_penalty += min_distance(term, terms[i-1])**2\n\n    # multiply the score by the document score\n    score *= doc.score\n\n    # divide the score by the root of the cumulative distance\n    if len(terms) > 1:\n        score /= sqrt(dist_penalty)\n\n    return score",
      "section_id": "tfidf"
    },
    {
      "id": "tfidfdocnorm-ex0",
      "language": "plaintext",
      "code": "FT.SEARCH myIndex \"foo\" SCORER TFIDF.DOCNORM",
      "section_id": "tfidfdocnorm"
    },
    {
      "id": "bm25std-default-ex0",
      "language": "plaintext",
      "code": "FT.SEARCH myIndex \"foo\" SCORER BM25STD",
      "section_id": "bm25std-default"
    },
    {
      "id": "bm25stdtanh-ex0",
      "language": "plaintext",
      "code": "FT.SEARCH idx \"term\" SCORER BM25STD.TANH BM25STD_TANH_FACTOR 12 WITHSCORES",
      "section_id": "bm25stdtanh"
    },
    {
      "id": "dismax-ex0",
      "language": "plaintext",
      "code": "FT.SEARCH myIndex \"foo\" SCORER DISMAX",
      "section_id": "dismax"
    },
    {
      "id": "docscore-ex0",
      "language": "plaintext",
      "code": "FT.SEARCH myIndex \"foo\" SCORER DOCSCORE",
      "section_id": "docscore"
    },
    {
      "id": "hamming-ex0",
      "language": "plaintext",
      "code": "> HSET key:1 foo hello payload aaaabbbb\n(integer) 2\n\n> HSET key:2 foo bar payload aaaacccc \n(integer) 2\n\n> FT.CREATE idx ON HASH PREFIX 1 key: PAYLOAD_FIELD payload SCHEMA foo TEXT\n\"OK\"\n\n> FT.SEARCH idx \"*\" PAYLOAD \"aaaabbbc\" SCORER HAMMING WITHSCORES\n1) \"2\"\n2) \"key:1\"\n3) \"0.5\"\n4) 1) \"foo\"\n   2) \"hello\"\n5) \"key:2\"\n6) \"0.25\"\n7) 1) \"foo\"\n   2) \"bar\"",
      "section_id": "hamming"
    }
  ]
}
