Spec-Zone.ru › Elasticsearch 8
›Elasticsearch Guide [8.17] ›Модули индексов

Модуль Similarity

Сходство (модель ранжирования/оценивания) определяет, как оцениваются совпадающие документы. Сходство применяется к каждому полю, что означает, что с помощью отображения можно настроить разное сходство для каждого поля.

Сходство применимо только к полям типа текст и ключевое слово.

Настройка пользовательского сходства считается экспертной функцией, и встроенные сходства, скорее всего, достаточны, как описано в similarity.

Настройка сходства

Большинство существующих или пользовательских сходств имеют параметры настройки, которые можно настроить через параметры индекса, как показано ниже. Параметры индекса можно предоставить при создании индекса или обновлении параметров индекса.

resp = client.indices.create(
    index="index",
    settings={
        "index": {
            "similarity": {
                "my_similarity": {
                    "type": "DFR",
                    "basic_model": "g",
                    "after_effect": "l",
                    "normalization": "h2",
                    "normalization.h2.c": "3.0"
                }
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'index',
  body: {
    settings: {
      index: {
        similarity: {
          my_similarity: {
            type: 'DFR',
            basic_model: 'g',
            after_effect: 'l',
            normalization: 'h2',
            "normalization.h2.c": '3.0'
          }
        }
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "index",
  settings: {
    index: {
      similarity: {
        my_similarity: {
          type: "DFR",
          basic_model: "g",
          after_effect: "l",
          normalization: "h2",
          "normalization.h2.c": "3.0",
        },
      },
    },
  },
});
console.log(response);
PUT /index
{
  "settings": {
    "index": {
      "similarity": {
        "my_similarity": {
          "type": "DFR",
          "basic_model": "g",
          "after_effect": "l",
          "normalization": "h2",
          "normalization.h2.c": "3.0"
        }
      }
    }
  }
}

Здесь мы настраиваем сходство DFR, чтобы его можно было использовать как my_similarity в отображениях, как показано в примере ниже:

resp = client.indices.put_mapping(
    index="index",
    properties={
        "title": {
            "type": "text",
            "similarity": "my_similarity"
        }
    },
)
print(resp)
response = client.indices.put_mapping(
  index: 'index',
  body: {
    properties: {
      title: {
        type: 'text',
        similarity: 'my_similarity'
      }
    }
  }
)
puts response
const response = await client.indices.putMapping({
  index: "index",
  properties: {
    title: {
      type: "text",
      similarity: "my_similarity",
    },
  },
});
console.log(response);
PUT /index/_mapping
{
  "properties" : {
    "title" : { "type" : "text", "similarity" : "my_similarity" }
  }
}

Доступные сходства

Сходство BM25 (по умолчанию)

Сходство, основанное на TF/IDF, с встроенной нормализацией tf и, предположительно, работающее лучше для коротких полей (например, имен). Подробнее см. Okapi_BM25. Это сходство имеет следующие параметры:

k1

Управляет нелинейной нормализацией частоты терминов (насыщение). Значение по умолчанию — 1.2.

b

Управляет степенью нормализации значений tf длиной документа. Значение по умолчанию — 0.75.

discount_overlaps

Определяет, игнорируются ли перекрывающиеся токены (токены с приращением позиции 0) при вычислении нормы. По умолчанию это значение true, что означает, что перекрывающиеся токены не учитываются при вычислении норм.

Имя типа: BM25

Сходство DFR

Сходство, реализующее фреймворк расхождения от случайности. Это сходство имеет следующие параметры:

basic_model

Возможные значения: g, if, in и ine.

after_effect

Возможные значения: b и l.

normalization

Возможные значения: no, h1, h2, h3 и z.

Все параметры, кроме первого, требуют значения нормализации.

Имя типа: DFR

Сходство DFI

Сходство, реализующее модель расхождения от независимости. Это сходство имеет следующие параметры:

independence_measure

Возможные значения standardized, saturated, chisquared.

При использовании этого сходства настоятельно рекомендуется не удалять стоп-слова, чтобы получить хорошую релевантность. Также будьте осторожны, что термины, частота которых меньше ожидаемой частоты, получат оценку, равную 0.

Имя типа: DFI

Сходство IB.

Модель, основанная на информации. Алгоритм основан на концепции того, что информационное содержание в любом символическом распределении последовательности в первую очередь определяется повторяющимся использованием его основных элементов. Для письменных текстов эта задача соответствует сравнению стилей письма разных авторов. Это сходство имеет следующие параметры:

distribution

Возможные значения: ll и spl.

lambda

Возможные значения: df и ttf.

normalization

То же, что и в сходстве DFR.

Имя типа: IB

Сходство LM Dirichlet.

Сходство LM Dirichlet. Это сходство имеет следующие параметры:

mu

По умолчанию 2000.

Формула оценки в статье присваивает отрицательные оценки терминам, частота которых меньше, чем предсказывается языковой моделью, что недопустимо для Lucene, поэтому таким терминам присваивается оценка 0.

Имя типа: LMDirichlet

Сходство LM Jelinek Mercer.

Сходство LM Jelinek Mercer. Алгоритм пытается захватить важные закономерности в тексте, оставив при этом шум. Это сходство имеет следующие параметры:

lambda

Оптимальное значение зависит как от коллекции, так и от запроса. Оптимальное значение составляет примерно 0.1 для запросов к заголовку и 0.7 для длинных запросов. По умолчанию 0.1. При приближении значения к 0 документы, соответствующие большему количеству терминов запроса, будут ранжированы выше, чем те, которые соответствуют меньшему количеству терминов.

Имя типа: LMJelinekMercer

Скриптированное сходство

Сходство, которое позволяет использовать скрипт для задания способа вычисления оценок. Например, приведенный ниже пример показывает, как переопределить TF-IDF:

resp = client.indices.create(
    index="index",
    settings={
        "number_of_shards": 1,
        "similarity": {
            "scripted_tfidf": {
                "type": "scripted",
                "script": {
                    "source": "double tf = Math.sqrt(doc.freq); double idf = Math.log((field.docCount+1.0)/(term.docFreq+1.0)) + 1.0; double norm = 1/Math.sqrt(doc.length); return query.boost * tf * idf * norm;"
                }
            }
        }
    },
    mappings={
        "properties": {
            "field": {
                "type": "text",
                "similarity": "scripted_tfidf"
            }
        }
    },
)
print(resp)

resp1 = client.index(
    index="index",
    id="1",
    document={
        "field": "foo bar foo"
    },
)
print(resp1)

resp2 = client.index(
    index="index",
    id="2",
    document={
        "field": "bar baz"
    },
)
print(resp2)

resp3 = client.indices.refresh(
    index="index",
)
print(resp3)

resp4 = client.search(
    index="index",
    explain=True,
    query={
        "query_string": {
            "query": "foo^1.7",
            "default_field": "field"
        }
    },
)
print(resp4)
response = client.indices.create(
  index: 'index',
  body: {
    settings: {
      number_of_shards: 1,
      similarity: {
        scripted_tfidf: {
          type: 'scripted',
          script: {
            source: 'double tf = Math.sqrt(doc.freq); double idf = Math.log((field.docCount+1.0)/(term.docFreq+1.0)) + 1.0; double norm = 1/Math.sqrt(doc.length); return query.boost * tf * idf * norm;'
          }
        }
      }
    },
    mappings: {
      properties: {
        field: {
          type: 'text',
          similarity: 'scripted_tfidf'
        }
      }
    }
  }
)
puts response

response = client.index(
  index: 'index',
  id: 1,
  body: {
    field: 'foo bar foo'
  }
)
puts response

response = client.index(
  index: 'index',
  id: 2,
  body: {
    field: 'bar baz'
  }
)
puts response

response = client.indices.refresh(
  index: 'index'
)
puts response

response = client.search(
  index: 'index',
  explain: true,
  body: {
    query: {
      query_string: {
        query: 'foo^1.7',
        default_field: 'field'
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "index",
  settings: {
    number_of_shards: 1,
    similarity: {
      scripted_tfidf: {
        type: "scripted",
        script: {
          source:
            "double tf = Math.sqrt(doc.freq); double idf = Math.log((field.docCount+1.0)/(term.docFreq+1.0)) + 1.0; double norm = 1/Math.sqrt(doc.length); return query.boost * tf * idf * norm;",
        },
      },
    },
  },
  mappings: {
    properties: {
      field: {
        type: "text",
        similarity: "scripted_tfidf",
      },
    },
  },
});
console.log(response);

const response1 = await client.index({
  index: "index",
  id: 1,
  document: {
    field: "foo bar foo",
  },
});
console.log(response1);

const response2 = await client.index({
  index: "index",
  id: 2,
  document: {
    field: "bar baz",
  },
});
console.log(response2);

const response3 = await client.indices.refresh({
  index: "index",
});
console.log(response3);

const response4 = await client.search({
  index: "index",
  explain: "true",
  query: {
    query_string: {
      query: "foo^1.7",
      default_field: "field",
    },
  },
});
console.log(response4);
PUT /index
{
  "settings": {
    "number_of_shards": 1,
    "similarity": {
      "scripted_tfidf": {
        "type": "scripted",
        "script": {
          "source": "double tf = Math.sqrt(doc.freq); double idf = Math.log((field.docCount+1.0)/(term.docFreq+1.0)) + 1.0; double norm = 1/Math.sqrt(doc.length); return query.boost * tf * idf * norm;"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "field": {
        "type": "text",
        "similarity": "scripted_tfidf"
      }
    }
  }
}

PUT /index/_doc/1
{
  "field": "foo bar foo"
}

PUT /index/_doc/2
{
  "field": "bar baz"
}

POST /index/_refresh

GET /index/_search?explain=true
{
  "query": {
    "query_string": {
      "query": "foo^1.7",
      "default_field": "field"
    }
  }
}

Что дает:

{
  "took": 12,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
        "value": 1,
        "relation": "eq"
    },
    "max_score": 1.9508477,
    "hits": [
      {
        "_shard": "[index][0]",
        "_node": "OzrdjxNtQGaqs4DmioFw9A",
        "_index": "index",
        "_id": "1",
        "_score": 1.9508477,
        "_source": {
          "field": "foo bar foo"
        },
        "_explanation": {
          "value": 1.9508477,
          "description": "weight(field:foo in 0) [PerFieldSimilarity], result of:",
          "details": [
            {
              "value": 1.9508477,
              "description": "score from ScriptedSimilarity(weightScript=[null], script=[Script{type=inline, lang='painless', idOrCode='double tf = Math.sqrt(doc.freq); double idf = Math.log((field.docCount+1.0)/(term.docFreq+1.0)) + 1.0; double norm = 1/Math.sqrt(doc.length); return query.boost * tf * idf * norm;', options={}, params={}}]) computed from:",
              "details": [
                {
                  "value": 1.0,
                  "description": "weight",
                  "details": []
                },
                {
                  "value": 1.7,
                  "description": "query.boost",
                  "details": []
                },
                {
                  "value": 2,
                  "description": "field.docCount",
                  "details": []
                },
                {
                  "value": 4,
                  "description": "field.sumDocFreq",
                  "details": []
                },
                {
                  "value": 5,
                  "description": "field.sumTotalTermFreq",
                  "details": []
                },
                {
                  "value": 1,
                  "description": "term.docFreq",
                  "details": []
                },
                {
                  "value": 2,
                  "description": "term.totalTermFreq",
                  "details": []
                },
                {
                  "value": 2.0,
                  "description": "doc.freq",
                  "details": []
                },
                {
                  "value": 3,
                  "description": "doc.length",
                  "details": []
                }
              ]
            }
          ]
        }
      }
    ]
  }
}

Хотя скриптированные сходства обеспечивают большую гибкость, они должны удовлетворять ряду правил. Несоблюдение этих правил может привести к тому, что Elasticsearch будет молча возвращать неправильные лучшие совпадения или сбоям во время поиска с внутренними ошибками:

  • Возвращаемые баллы должны быть положительными.
  • При всех остальных равных условиях, баллы не должны уменьшаться при увеличении doc.freq.
  • При всех остальных равных условиях, баллы не должны увеличиваться при увеличении doc.length.

Вы могли заметить, что значительная часть вышеприведенного скрипта зависит от статистических данных, которые одинаковы для каждого документа. Можно сделать вышеуказанный код немного эффективнее, предоставив weight_script, которое будет вычислять независимую от документа часть балла и будет доступно через переменную weight. Если weight_script не предоставлено, weight равно 1. weight_script имеет доступ к тем же переменным, что и script, за исключением doc, поскольку оно должно вычислять независимый вклад в балл, связанный с документом.

Нижеприведенная конфигурация даст те же баллы tf-idf, но будет немного эффективнее:

resp = client.indices.create(
    index="index",
    settings={
        "number_of_shards": 1,
        "similarity": {
            "scripted_tfidf": {
                "type": "scripted",
                "weight_script": {
                    "source": "double idf = Math.log((field.docCount+1.0)/(term.docFreq+1.0)) + 1.0; return query.boost * idf;"
                },
                "script": {
                    "source": "double tf = Math.sqrt(doc.freq); double norm = 1/Math.sqrt(doc.length); return weight * tf * norm;"
                }
            }
        }
    },
    mappings={
        "properties": {
            "field": {
                "type": "text",
                "similarity": "scripted_tfidf"
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'index',
  body: {
    settings: {
      number_of_shards: 1,
      similarity: {
        scripted_tfidf: {
          type: 'scripted',
          weight_script: {
            source: 'double idf = Math.log((field.docCount+1.0)/(term.docFreq+1.0)) + 1.0; return query.boost * idf;'
          },
          script: {
            source: 'double tf = Math.sqrt(doc.freq); double norm = 1/Math.sqrt(doc.length); return weight * tf * norm;'
          }
        }
      }
    },
    mappings: {
      properties: {
        field: {
          type: 'text',
          similarity: 'scripted_tfidf'
        }
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "index",
  settings: {
    number_of_shards: 1,
    similarity: {
      scripted_tfidf: {
        type: "scripted",
        weight_script: {
          source:
            "double idf = Math.log((field.docCount+1.0)/(term.docFreq+1.0)) + 1.0; return query.boost * idf;",
        },
        script: {
          source:
            "double tf = Math.sqrt(doc.freq); double norm = 1/Math.sqrt(doc.length); return weight * tf * norm;",
        },
      },
    },
  },
  mappings: {
    properties: {
      field: {
        type: "text",
        similarity: "scripted_tfidf",
      },
    },
  },
});
console.log(response);
PUT /index
{
  "settings": {
    "number_of_shards": 1,
    "similarity": {
      "scripted_tfidf": {
        "type": "scripted",
        "weight_script": {
          "source": "double idf = Math.log((field.docCount+1.0)/(term.docFreq+1.0)) + 1.0; return query.boost * idf;"
        },
        "script": {
          "source": "double tf = Math.sqrt(doc.freq); double norm = 1/Math.sqrt(doc.length); return weight * tf * norm;"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "field": {
        "type": "text",
        "similarity": "scripted_tfidf"
      }
    }
  }
}

Имя типа: scripted

Стандартное сходство

По умолчанию Elasticsearch будет использовать сходство, настроенное как default.

Вы можете изменить стандартное сходство для всех полей в индексе при его создании:

resp = client.indices.create(
    index="index",
    settings={
        "index": {
            "similarity": {
                "default": {
                    "type": "boolean"
                }
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'index',
  body: {
    settings: {
      index: {
        similarity: {
          default: {
            type: 'boolean'
          }
        }
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "index",
  settings: {
    index: {
      similarity: {
        default: {
          type: "boolean",
        },
      },
    },
  },
});
console.log(response);
PUT /index
{
  "settings": {
    "index": {
      "similarity": {
        "default": {
          "type": "boolean"
        }
      }
    }
  }
}

Если вы хотите изменить стандартное сходство после создания индекса, вы должны закрыть свой индекс, отправить следующий запрос и открыть его снова после этого:

resp = client.indices.close(
    index="index",
)
print(resp)

resp1 = client.indices.put_settings(
    index="index",
    settings={
        "index": {
            "similarity": {
                "default": {
                    "type": "boolean"
                }
            }
        }
    },
)
print(resp1)

resp2 = client.indices.open(
    index="index",
)
print(resp2)
response = client.indices.close(
  index: 'index'
)
puts response

response = client.indices.put_settings(
  index: 'index',
  body: {
    index: {
      similarity: {
        default: {
          type: 'boolean'
        }
      }
    }
  }
)
puts response

response = client.indices.open(
  index: 'index'
)
puts response
const response = await client.indices.close({
  index: "index",
});
console.log(response);

const response1 = await client.indices.putSettings({
  index: "index",
  settings: {
    index: {
      similarity: {
        default: {
          type: "boolean",
        },
      },
    },
  },
});
console.log(response1);

const response2 = await client.indices.open({
  index: "index",
});
console.log(response2);
POST /index/_close

PUT /index/_settings
{
  "index": {
    "similarity": {
      "default": {
        "type": "boolean"
      }
    }
  }
}

POST /index/_open

© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/index-modules-similarity.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API