Spec-Zone.ru › Elasticsearch 8
›Elasticsearch Guide [8.17] ›Query DSL ›Специализированные запросы

Запрос script score

Использует скрипт для вычисления пользовательского рейтинга возвращаемых документов.

Запрос script_score полезен, например, если функция расчёта рейтинга является дорогостоящей, и вам нужно вычислить рейтинг только для отфильтрованного набора документов.

Пример запроса

Следующий запрос script_score присваивает каждому возвращаемому документу рейтинг, равный значению поля my-int, делённому на 10.

resp = client.search(
    query={
        "script_score": {
            "query": {
                "match": {
                    "message": "elasticsearch"
                }
            },
            "script": {
                "source": "doc['my-int'].value / 10 "
            }
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      script_score: {
        query: {
          match: {
            message: 'elasticsearch'
          }
        },
        script: {
          source: "doc['my-int'].value / 10 "
        }
      }
    }
  }
)
puts response
const response = await client.search({
  query: {
    script_score: {
      query: {
        match: {
          message: "elasticsearch",
        },
      },
      script: {
        source: "doc['my-int'].value / 10 ",
      },
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "script_score": {
      "query": {
        "match": { "message": "elasticsearch" }
      },
      "script": {
        "source": "doc['my-int'].value / 10 "
      }
    }
  }
}

Параметры верхнего уровня для script_score

query
(Обязательно, объект запроса) Запрос, используемый для возврата документов.
script

(Обязательно, объект скрипта) Скрипт, используемый для вычисления рейтинга документов, возвращаемых запросом query.

Окончательные рейтинги релевантности из запроса script_score не могут быть отрицательными. Для поддержки определенных оптимизаций поиска, Lucene требует, чтобы рейтинги были положительными или 0.

min_score
(Необязательно, число с плавающей точкой) Документы с рейтингом ниже этого значения исключаются из результатов поиска.
boost
(Необязательно, число с плавающей точкой) Рейтинги документов, созданные запросом script, умножаются на boost для получения окончательных рейтингов документов. По умолчанию 1.0.

Примечания

Использование рейтингов релевантности в скрипте

Внутри скрипта вы можете обратиться к переменной _score, которая представляет текущий рейтинг релевантности документа.

Использование статистических данных терминов в скрипте

Внутри скрипта вы можете обратиться к переменной _termStats, которая предоставляет статистическую информацию о терминах, используемых в дочернем запросе запроса script_score.

Предопределённые функции

Вы можете использовать любые доступные функции painless в вашем скрипте. Вы также можете использовать следующие предопределённые функции для настройки рейтинга:

  • Насыщение
  • Сигмоида
  • Случайная функция рейтинга
  • Функции спадания для числовых полей
  • Функции спадания для гео полей
  • Функции спадания для полей дат
  • Функции для векторизованных полей

Мы рекомендуем использовать эти предопределённые функции вместо написания собственных. Эти функции используют преимущества оптимизаций, встроенных в механизмы Elasticsearch.

Насыщение

saturation(value,k) = value/(k + value)

"script" : {
    "source" : "saturation(doc['my-int'].value, 1)"
}
Сигмоида

sigmoid(value, k, a) = value^a/ (k^a + value^a)

"script" : {
    "source" : "sigmoid(doc['my-int'].value, 2, 1)"
}
Случайная функция рейтинга

Функция random_score генерирует рейтинги, равномерно распределённые от 0 до 1 (не включая 1).

Функция randomScore имеет следующий синтаксис: randomScore(<seed>, <fieldName>). Она имеет обязательный параметр - seed в качестве целого значения и необязательный параметр - fieldName в качестве строкового значения.

"script" : {
    "source" : "randomScore(100, '_seq_no')"
}

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

"script" : {
    "source" : "randomScore(100)"
}

Обратите внимание, что документы в одном фрагменте с одинаковым значением поля получат одинаковый рейтинг, поэтому обычно желательно использовать поле с уникальными значениями для всех документов в фрагменте. Хорошим выбором по умолчанию может быть использование поля _seq_no, единственным недостатком которого является то, что рейтинги изменятся, если документ будет обновлён, так как операции обновления также обновляют значение поля _seq_no.

Функции спадания для числовых полей

Вы можете узнать больше о функциях спадания здесь.

  • double decayNumericLinear(double origin, double scale, double offset, double decay, double docValue)
  • double decayNumericExp(double origin, double scale, double offset, double decay, double docValue)
  • double decayNumericGauss(double origin, double scale, double offset, double decay, double docValue)
"script" : {
    "source" : "decayNumericLinear(params.origin, params.scale, params.offset, params.decay, doc['dval'].value)",
    "params": { 
        "origin": 20,
        "scale": 10,
        "decay" : 0.5,
        "offset" : 0
    }
}

Использование params позволяет скомпилировать скрипт только один раз, даже если параметры изменятся.

Функции спадания для гео полей
  • double decayGeoLinear(String originStr, String scaleStr, String offsetStr, double decay, GeoPoint docValue)
  • double decayGeoExp(String originStr, String scaleStr, String offsetStr, double decay, GeoPoint docValue)
  • double decayGeoGauss(String originStr, String scaleStr, String offsetStr, double decay, GeoPoint docValue)
"script" : {
    "source" : "decayGeoExp(params.origin, params.scale, params.offset, params.decay, doc['location'].value)",
    "params": {
        "origin": "40, -70.12",
        "scale": "200km",
        "offset": "0km",
        "decay" : 0.2
    }
}
Функции спадания для полей дат
  • double decayDateLinear(String originStr, String scaleStr, String offsetStr, double decay, JodaCompatibleZonedDateTime docValueDate)
  • double decayDateExp(String originStr, String scaleStr, String offsetStr, double decay, JodaCompatibleZonedDateTime docValueDate)
  • double decayDateGauss(String originStr, String scaleStr, String offsetStr, double decay, JodaCompatibleZonedDateTime docValueDate)
"script" : {
    "source" : "decayDateGauss(params.origin, params.scale, params.offset, params.decay, doc['date'].value)",
    "params": {
        "origin": "2008-01-01T01:00:00Z",
        "scale": "1h",
        "offset" : "0",
        "decay" : 0.5
    }
}

Функции спадания для дат ограничены датами в формате по умолчанию и часовом поясе по умолчанию. Также не поддерживаются вычисления с now.

Функции для векторизованных полей

Функции для векторизованных полей доступны через запрос script_score.

Разрешение дорогостоящих запросов

Запросы script score не будут выполнены, если search.allow_expensive_queries установлено в значение false.

Более быстрые альтернативы

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

  • Если вам нужно усилить документы на основе некоторых статических полей, используйте запрос rank_feature.
  • Если вам нужно усилить документы, близкие к дате или географической точке, используйте запрос distance_feature.

Переход от запроса функция score

Рекомендуется использовать запрос script_score вместо запроса function_score для простоты запроса script_score.

Вы можете реализовать следующие функции запроса function_score с помощью запроса script_score:

  • script_score
  • weight
  • random_score
  • field_value_factor
  • функции decay
script_score

То, что вы использовали в script_score запроса Function Score, можно скопировать в запрос Script Score. Здесь нет изменений.

weight

Функция weight может быть реализована в запросе Script Score с помощью следующего скрипта:

"script" : {
    "source" : "params.weight * _score",
    "params": {
        "weight": 2
    }
}
random_score

Используйте функцию randomScore, как описано в функции случайного балла.

field_value_factor

Функцию field_value_factor можно легко реализовать с помощью скрипта:

"script" : {
    "source" : "Math.log10(doc['field'].value * params.factor)",
    "params" : {
        "factor" : 5
    }
}

Для проверки, есть ли у документа пропущенное значение, можно использовать doc['field'].size() == 0. Например, этот скрипт будет использовать значение 1, если у документа отсутствует поле field:

"script" : {
    "source" : "Math.log10((doc['field'].size() == 0 ? 1 : doc['field'].value()) * params.factor)",
    "params" : {
        "factor" : 5
    }
}

В этой таблице показано, как можно реализовать модификаторы field_value_factor с помощью скрипта:

Модификатор Реализация в Script Score

none

-

log

Math.log10(doc['f'].value)

log1p

Math.log10(doc['f'].value + 1)

log2p

Math.log10(doc['f'].value + 2)

ln

Math.log(doc['f'].value)

ln1p

Math.log(doc['f'].value + 1)

ln2p

Math.log(doc['f'].value + 2)

square

Math.pow(doc['f'].value, 2)

sqrt

Math.sqrt(doc['f'].value)

reciprocal

1.0 / doc['f'].value

decay функции

Запрос script_score имеет эквивалентные функции распада, которые можно использовать в скриптах.

Функции для векторных полей

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

Это список доступных векторных функций и методов доступа к векторам:

  1. cosineSimilarity – вычисляет косинусную близость
  2. dotProduct – вычисляет скалярное произведение
  3. l1norm – вычисляет расстояние L1
  4. hamming – вычисляет расстояние Хэмминга
  5. l2norm - вычисляет расстояние L2
  6. doc[<field>].vectorValue – возвращает значение вектора как массив чисел с плавающей точкой
  7. doc[<field>].magnitude – возвращает величину вектора

Функция cosineSimilarity не поддерживается для векторов bit.

Рекомендуемый способ доступа к плотным векторам осуществляется через функции cosineSimilarity, dotProduct, l1norm или l2norm. Обратите внимание, что вы должны вызывать эти функции только один раз на скрипт. Например, не используйте эти функции в цикле для вычисления сходства между вектором документа и множеством других векторов. Если вам нужна такая функциональность, перепишите эти функции самостоятельно, получая доступ к значениям векторов напрямую.

Давайте создадим индекс с отображением dense_vector и проиндексируем в нём пару документов.

resp = client.indices.create(
    index="my-index-000001",
    mappings={
        "properties": {
            "my_dense_vector": {
                "type": "dense_vector",
                "index": False,
                "dims": 3
            },
            "my_byte_dense_vector": {
                "type": "dense_vector",
                "index": False,
                "dims": 3,
                "element_type": "byte"
            },
            "status": {
                "type": "keyword"
            }
        }
    },
)
print(resp)

resp1 = client.index(
    index="my-index-000001",
    id="1",
    document={
        "my_dense_vector": [
            0.5,
            10,
            6
        ],
        "my_byte_dense_vector": [
            0,
            10,
            6
        ],
        "status": "published"
    },
)
print(resp1)

resp2 = client.index(
    index="my-index-000001",
    id="2",
    document={
        "my_dense_vector": [
            -0.5,
            10,
            10
        ],
        "my_byte_dense_vector": [
            0,
            10,
            10
        ],
        "status": "published"
    },
)
print(resp2)

resp3 = client.indices.refresh(
    index="my-index-000001",
)
print(resp3)
const response = await client.indices.create({
  index: "my-index-000001",
  mappings: {
    properties: {
      my_dense_vector: {
        type: "dense_vector",
        index: false,
        dims: 3,
      },
      my_byte_dense_vector: {
        type: "dense_vector",
        index: false,
        dims: 3,
        element_type: "byte",
      },
      status: {
        type: "keyword",
      },
    },
  },
});
console.log(response);

const response1 = await client.index({
  index: "my-index-000001",
  id: 1,
  document: {
    my_dense_vector: [0.5, 10, 6],
    my_byte_dense_vector: [0, 10, 6],
    status: "published",
  },
});
console.log(response1);

const response2 = await client.index({
  index: "my-index-000001",
  id: 2,
  document: {
    my_dense_vector: [-0.5, 10, 10],
    my_byte_dense_vector: [0, 10, 10],
    status: "published",
  },
});
console.log(response2);

const response3 = await client.indices.refresh({
  index: "my-index-000001",
});
console.log(response3);
PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_dense_vector": {
        "type": "dense_vector",
        "index": false,
        "dims": 3
      },
      "my_byte_dense_vector": {
        "type": "dense_vector",
        "index": false,
        "dims": 3,
        "element_type": "byte"
      },
      "status" : {
        "type" : "keyword"
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "my_dense_vector": [0.5, 10, 6],
  "my_byte_dense_vector": [0, 10, 6],
  "status" : "published"
}

PUT my-index-000001/_doc/2
{
  "my_dense_vector": [-0.5, 10, 10],
  "my_byte_dense_vector": [0, 10, 10],
  "status" : "published"
}

POST my-index-000001/_refresh
Косинусная близость

Функция cosineSimilarity вычисляет меру косинусной близости между заданным вектором запроса и векторами документов.

resp = client.search(
    index="my-index-000001",
    query={
        "script_score": {
            "query": {
                "bool": {
                    "filter": {
                        "term": {
                            "status": "published"
                        }
                    }
                }
            },
            "script": {
                "source": "cosineSimilarity(params.query_vector, 'my_dense_vector') + 1.0",
                "params": {
                    "query_vector": [
                        4,
                        3.4,
                        -0.2
                    ]
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    query: {
      script_score: {
        query: {
          bool: {
            filter: {
              term: {
                status: 'published'
              }
            }
          }
        },
        script: {
          source: "cosineSimilarity(params.query_vector, 'my_dense_vector') + 1.0",
          params: {
            query_vector: [
              4,
              3.4,
              -0.2
            ]
          }
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  query: {
    script_score: {
      query: {
        bool: {
          filter: {
            term: {
              status: "published",
            },
          },
        },
      },
      script: {
        source:
          "cosineSimilarity(params.query_vector, 'my_dense_vector') + 1.0",
        params: {
          query_vector: [4, 3.4, -0.2],
        },
      },
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "term" : {
              "status" : "published" 
            }
          }
        }
      },
      "script": {
        "source": "cosineSimilarity(params.query_vector, 'my_dense_vector') + 1.0", 
        "params": {
          "query_vector": [4, 3.4, -0.2]  
        }
      }
    }
  }
}

Чтобы ограничить количество документов, к которым применяется вычисление оценки скрипта, предоставьте фильтр.

Скрипт добавляет 1,0 к косинусной близости, чтобы предотвратить отрицательное значение оценки.

Чтобы воспользоваться преимуществами оптимизации скриптов, предоставьте вектор запроса в качестве параметра скрипта.

Если поле плотного вектора документа имеет другое количество измерений, чем вектор запроса, будет выброшено исключение.

Скалярное произведение

Функция dotProduct вычисляет меру скалярного произведения между заданным вектором запроса и векторами документов.

resp = client.search(
    index="my-index-000001",
    query={
        "script_score": {
            "query": {
                "bool": {
                    "filter": {
                        "term": {
                            "status": "published"
                        }
                    }
                }
            },
            "script": {
                "source": "\n          double value = dotProduct(params.query_vector, 'my_dense_vector');\n          return sigmoid(1, Math.E, -value); \n        ",
                "params": {
                    "query_vector": [
                        4,
                        3.4,
                        -0.2
                    ]
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    query: {
      script_score: {
        query: {
          bool: {
            filter: {
              term: {
                status: 'published'
              }
            }
          }
        },
        script: {
          source: "\n          double value = dotProduct(params.query_vector, 'my_dense_vector');\n          return sigmoid(1, Math.E, -value); \n        ",
          params: {
            query_vector: [
              4,
              3.4,
              -0.2
            ]
          }
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  query: {
    script_score: {
      query: {
        bool: {
          filter: {
            term: {
              status: "published",
            },
          },
        },
      },
      script: {
        source:
          "\n          double value = dotProduct(params.query_vector, 'my_dense_vector');\n          return sigmoid(1, Math.E, -value); \n        ",
        params: {
          query_vector: [4, 3.4, -0.2],
        },
      },
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "term" : {
              "status" : "published"
            }
          }
        }
      },
      "script": {
        "source": """
          double value = dotProduct(params.query_vector, 'my_dense_vector');
          return sigmoid(1, Math.E, -value); 
        """,
        "params": {
          "query_vector": [4, 3.4, -0.2]
        }
      }
    }
  }
}

Использование стандартной сигмоидной функции предотвращает отрицательные значения оценок.

Расстояние L1 (расстояние Манхэттена)

Функция l1norm вычисляет расстояние L1 (расстояние Манхэттена) между заданным вектором запроса и векторами документов.

resp = client.search(
    index="my-index-000001",
    query={
        "script_score": {
            "query": {
                "bool": {
                    "filter": {
                        "term": {
                            "status": "published"
                        }
                    }
                }
            },
            "script": {
                "source": "1 / (1 + l1norm(params.queryVector, 'my_dense_vector'))",
                "params": {
                    "queryVector": [
                        4,
                        3.4,
                        -0.2
                    ]
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    query: {
      script_score: {
        query: {
          bool: {
            filter: {
              term: {
                status: 'published'
              }
            }
          }
        },
        script: {
          source: "1 / (1 + l1norm(params.queryVector, 'my_dense_vector'))",
          params: {
            "queryVector": [
              4,
              3.4,
              -0.2
            ]
          }
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  query: {
    script_score: {
      query: {
        bool: {
          filter: {
            term: {
              status: "published",
            },
          },
        },
      },
      script: {
        source: "1 / (1 + l1norm(params.queryVector, 'my_dense_vector'))",
        params: {
          queryVector: [4, 3.4, -0.2],
        },
      },
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "term" : {
              "status" : "published"
            }
          }
        }
      },
      "script": {
        "source": "1 / (1 + l1norm(params.queryVector, 'my_dense_vector'))", 
        "params": {
          "queryVector": [4, 3.4, -0.2]
        }
      }
    }
  }
}

В отличие от cosineSimilarity, которые представляют собой сходство, l1norm и l2norm, показанные ниже, представляют расстояния или различия. Это означает, что чем более похожи векторы, тем ниже будут оценки, которые будут производиться функциями l1norm и l2norm. Таким образом, поскольку нам нужны более похожие векторы для получения более высокой оценки, мы изменили вывод из l1norm и l2norm. Кроме того, чтобы избежать деления на 0, когда вектор документа точно соответствует запросу, мы добавили 1 в знаменатель.

Расстояние Хэмминга

Функция hamming вычисляет расстояние Хэмминга между заданным вектором запроса и векторами документов. Она доступна только для байтовых и битовых векторов.

resp = client.search(
    index="my-index-000001",
    query={
        "script_score": {
            "query": {
                "bool": {
                    "filter": {
                        "term": {
                            "status": "published"
                        }
                    }
                }
            },
            "script": {
                "source": "(24 - hamming(params.queryVector, 'my_byte_dense_vector')) / 24",
                "params": {
                    "queryVector": [
                        4,
                        3,
                        0
                    ]
                }
            }
        }
    },
)
print(resp)
const response = await client.search({
  index: "my-index-000001",
  query: {
    script_score: {
      query: {
        bool: {
          filter: {
            term: {
              status: "published",
            },
          },
        },
      },
      script: {
        source:
          "(24 - hamming(params.queryVector, 'my_byte_dense_vector')) / 24",
        params: {
          queryVector: [4, 3, 0],
        },
      },
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "term" : {
              "status" : "published"
            }
          }
        }
      },
      "script": {
        "source": "(24 - hamming(params.queryVector, 'my_byte_dense_vector')) / 24", 
        "params": {
          "queryVector": [4, 3, 0]
        }
      }
    }
  }
}

Вычислите расстояние Хэмминга и нормализуйте его по битам, чтобы получить оценку от 0 до 1.

Расстояние L2 (евклидово расстояние)

Функция l2norm вычисляет расстояние L2 (евклидово расстояние) между заданным вектором запроса и векторами документов.

resp = client.search(
    index="my-index-000001",
    query={
        "script_score": {
            "query": {
                "bool": {
                    "filter": {
                        "term": {
                            "status": "published"
                        }
                    }
                }
            },
            "script": {
                "source": "1 / (1 + l2norm(params.queryVector, 'my_dense_vector'))",
                "params": {
                    "queryVector": [
                        4,
                        3.4,
                        -0.2
                    ]
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    query: {
      script_score: {
        query: {
          bool: {
            filter: {
              term: {
                status: 'published'
              }
            }
          }
        },
        script: {
          source: "1 / (1 + l2norm(params.queryVector, 'my_dense_vector'))",
          params: {
            "queryVector": [
              4,
              3.4,
              -0.2
            ]
          }
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  query: {
    script_score: {
      query: {
        bool: {
          filter: {
            term: {
              status: "published",
            },
          },
        },
      },
      script: {
        source: "1 / (1 + l2norm(params.queryVector, 'my_dense_vector'))",
        params: {
          queryVector: [4, 3.4, -0.2],
        },
      },
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "term" : {
              "status" : "published"
            }
          }
        }
      },
      "script": {
        "source": "1 / (1 + l2norm(params.queryVector, 'my_dense_vector'))",
        "params": {
          "queryVector": [4, 3.4, -0.2]
        }
      }
    }
  }
}
Проверка на пропущенные значения

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

Вы можете проверить, есть ли у документа значение для поля my_vector с помощью doc['my_vector'].size() == 0. Ваш общий скрипт может выглядеть так:

"source": "doc['my_vector'].size() == 0 ? 0 : cosineSimilarity(params.queryVector, 'my_vector')"
Прямой доступ к векторам

Вы можете напрямую получать доступ к значениям векторов с помощью следующих функций:

  • doc[<field>].vectorValue – возвращает значение вектора как массив чисел с плавающей точкой

Для векторов bit он возвращает float[], где каждый элемент представляет 8 бит.

  • doc[<field>].magnitude – возвращает величину вектора как число с плавающей точкой (для векторов, созданных до версии 7.5, величина не хранится. Поэтому эта функция пересчитывает её каждый раз, когда она вызывается).

Для векторов bit это просто квадратный корень из суммы 1 битов.

Например, приведенный ниже скрипт реализует косинусную близость с использованием этих двух функций:

resp = client.search(
    index="my-index-000001",
    query={
        "script_score": {
            "query": {
                "bool": {
                    "filter": {
                        "term": {
                            "status": "published"
                        }
                    }
                }
            },
            "script": {
                "source": "\n          float[] v = doc['my_dense_vector'].vectorValue;\n          float vm = doc['my_dense_vector'].magnitude;\n          float dotProduct = 0;\n          for (int i = 0; i < v.length; i++) {\n            dotProduct += v[i] * params.queryVector[i];\n          }\n          return dotProduct / (vm * (float) params.queryVectorMag);\n        ",
                "params": {
                    "queryVector": [
                        4,
                        3.4,
                        -0.2
                    ],
                    "queryVectorMag": 5.25357
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    query: {
      script_score: {
        query: {
          bool: {
            filter: {
              term: {
                status: 'published'
              }
            }
          }
        },
        script: {
          source: "\n          float[] v = doc['my_dense_vector'].vectorValue;\n          float vm = doc['my_dense_vector'].magnitude;\n          float dotProduct = 0;\n          for (int i = 0; i < v.length; i++) {\n            dotProduct += v[i] * params.queryVector[i];\n          }\n          return dotProduct / (vm * (float) params.queryVectorMag);\n        ",
          params: {
            "queryVector": [
              4,
              3.4,
              -0.2
            ],
            "queryVectorMag": 5.25357
          }
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index-000001",
  query: {
    script_score: {
      query: {
        bool: {
          filter: {
            term: {
              status: "published",
            },
          },
        },
      },
      script: {
        source:
          "\n          float[] v = doc['my_dense_vector'].vectorValue;\n          float vm = doc['my_dense_vector'].magnitude;\n          float dotProduct = 0;\n          for (int i = 0; i < v.length; i++) {\n            dotProduct += v[i] * params.queryVector[i];\n          }\n          return dotProduct / (vm * (float) params.queryVectorMag);\n        ",
        params: {
          queryVector: [4, 3.4, -0.2],
          queryVectorMag: 5.25357,
        },
      },
    },
  },
});
console.log(response);
GET my-index-000001/_search
{
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "term" : {
              "status" : "published"
            }
          }
        }
      },
      "script": {
        "source": """
          float[] v = doc['my_dense_vector'].vectorValue;
          float vm = doc['my_dense_vector'].magnitude;
          float dotProduct = 0;
          for (int i = 0; i < v.length; i++) {
            dotProduct += v[i] * params.queryVector[i];
          }
          return dotProduct / (vm * (float) params.queryVectorMag);
        """,
        "params": {
          "queryVector": [4, 3.4, -0.2],
          "queryVectorMag": 5.25357
        }
      }
    }
  }
}
Битовые векторы и векторные функции

При использовании векторов bit не все векторные функции доступны. Поддерживаемые функции:

  • Расстояние Хэмминга – вычисляет расстояние Хэмминга, сумму побитового XOR двух векторов
  • Расстояние L1 – вычисляет расстояние L1, это просто расстояние hamming
  • Расстояние L2 - вычисляет расстояние L2, это квадратный корень из расстояния hamming
  • Скалярное произведение – вычисляет скалярное произведение. При сравнении двух векторов bit, это сумма побитового И двух векторов. При предоставлении векторов float[] или byte[], имеющих dims количество элементов, в качестве запроса, скалярное произведение является суммой значений с плавающей точкой, используя хранящийся вектор bit в качестве маски.

При сравнении векторов floats и bytes с векторами bit, вектор bit обрабатывается как маска в порядке big-endian. Например, если вектор bit равен 10100001 (например, значение единственного байта 161) и сравнивается с массивом значений [1, 2, 3, 4, 5, 6, 7, 8], то скалярное произведение будет равно 1 + 3 + 8 = 16.

Вот пример использования скалярного произведения с битовыми векторами.

resp = client.indices.create(
    index="my-index-bit-vectors",
    mappings={
        "properties": {
            "my_dense_vector": {
                "type": "dense_vector",
                "index": False,
                "element_type": "bit",
                "dims": 40
            }
        }
    },
)
print(resp)

resp1 = client.index(
    index="my-index-bit-vectors",
    id="1",
    document={
        "my_dense_vector": [
            8,
            5,
            -15,
            1,
            -7
        ]
    },
)
print(resp1)

resp2 = client.index(
    index="my-index-bit-vectors",
    id="2",
    document={
        "my_dense_vector": [
            -1,
            115,
            -3,
            4,
            -128
        ]
    },
)
print(resp2)

resp3 = client.index(
    index="my-index-bit-vectors",
    id="3",
    document={
        "my_dense_vector": [
            2,
            18,
            -5,
            0,
            -124
        ]
    },
)
print(resp3)

resp4 = client.indices.refresh(
    index="my-index-bit-vectors",
)
print(resp4)
const response = await client.indices.create({
  index: "my-index-bit-vectors",
  mappings: {
    properties: {
      my_dense_vector: {
        type: "dense_vector",
        index: false,
        element_type: "bit",
        dims: 40,
      },
    },
  },
});
console.log(response);

const response1 = await client.index({
  index: "my-index-bit-vectors",
  id: 1,
  document: {
    my_dense_vector: [8, 5, -15, 1, -7],
  },
});
console.log(response1);

const response2 = await client.index({
  index: "my-index-bit-vectors",
  id: 2,
  document: {
    my_dense_vector: [-1, 115, -3, 4, -128],
  },
});
console.log(response2);

const response3 = await client.index({
  index: "my-index-bit-vectors",
  id: 3,
  document: {
    my_dense_vector: [2, 18, -5, 0, -124],
  },
});
console.log(response3);

const response4 = await client.indices.refresh({
  index: "my-index-bit-vectors",
});
console.log(response4);
PUT my-index-bit-vectors
{
  "mappings": {
    "properties": {
      "my_dense_vector": {
        "type": "dense_vector",
        "index": false,
        "element_type": "bit",
        "dims": 40 
      }
    }
  }
}

PUT my-index-bit-vectors/_doc/1
{
  "my_dense_vector": [8, 5, -15, 1, -7] 
}

PUT my-index-bit-vectors/_doc/2
{
  "my_dense_vector": [-1, 115, -3, 4, -128]
}

PUT my-index-bit-vectors/_doc/3
{
  "my_dense_vector": [2, 18, -5, 0, -124]
}

POST my-index-bit-vectors/_refresh

Количество измерений или битов для вектора bit.

Этот вектор представляет 5 байтов или 5 * 8 = 40 бит, что соответствует настроенным измерениям.

resp = client.search(
    index="my-index-bit-vectors",
    query={
        "script_score": {
            "query": {
                "match_all": {}
            },
            "script": {
                "source": "dotProduct(params.query_vector, 'my_dense_vector')",
                "params": {
                    "query_vector": [
                        8,
                        5,
                        -15,
                        1,
                        -7
                    ]
                }
            }
        }
    },
)
print(resp)
const response = await client.search({
  index: "my-index-bit-vectors",
  query: {
    script_score: {
      query: {
        match_all: {},
      },
      script: {
        source: "dotProduct(params.query_vector, 'my_dense_vector')",
        params: {
          query_vector: [8, 5, -15, 1, -7],
        },
      },
    },
  },
});
console.log(response);
GET my-index-bit-vectors/_search
{
  "query": {
    "script_score": {
      "query" : {
        "match_all": {}
      },
      "script": {
        "source": "dotProduct(params.query_vector, 'my_dense_vector')",
        "params": {
          "query_vector": [8, 5, -15, 1, -7] 
        }
      }
    }
  }
}

Этот вектор имеет 40 бит и, следовательно, будет вычислять побитовую операцию & с хранящимися векторами.

resp = client.search(
    index="my-index-bit-vectors",
    query={
        "script_score": {
            "query": {
                "match_all": {}
            },
            "script": {
                "source": "dotProduct(params.query_vector, 'my_dense_vector')",
                "params": {
                    "query_vector": [
                        0.23,
                        1.45,
                        3.67,
                        4.89,
                        -0.56,
                        2.34,
                        3.21,
                        1.78,
                        -2.45,
                        0.98,
                        -0.12,
                        3.45,
                        4.56,
                        2.78,
                        1.23,
                        0.67,
                        3.89,
                        4.12,
                        -2.34,
                        1.56,
                        0.78,
                        3.21,
                        4.12,
                        2.45,
                        -1.67,
                        0.34,
                        -3.45,
                        4.56,
                        -2.78,
                        1.23,
                        -0.67,
                        3.89,
                        -4.34,
                        2.12,
                        -1.56,
                        0.78,
                        -3.21,
                        4.45,
                        2.12,
                        1.67
                    ]
                }
            }
        }
    },
)
print(resp)
const response = await client.search({
  index: "my-index-bit-vectors",
  query: {
    script_score: {
      query: {
        match_all: {},
      },
      script: {
        source: "dotProduct(params.query_vector, 'my_dense_vector')",
        params: {
          query_vector: [
            0.23, 1.45, 3.67, 4.89, -0.56, 2.34, 3.21, 1.78, -2.45, 0.98, -0.12,
            3.45, 4.56, 2.78, 1.23, 0.67, 3.89, 4.12, -2.34, 1.56, 0.78, 3.21,
            4.12, 2.45, -1.67, 0.34, -3.45, 4.56, -2.78, 1.23, -0.67, 3.89,
            -4.34, 2.12, -1.56, 0.78, -3.21, 4.45, 2.12, 1.67,
          ],
        },
      },
    },
  },
});
console.log(response);
GET my-index-bit-vectors/_search
{
  "query": {
    "script_score": {
      "query" : {
        "match_all": {}
      },
      "script": {
        "source": "dotProduct(params.query_vector, 'my_dense_vector')",
        "params": {
          "query_vector": [0.23, 1.45, 3.67, 4.89, -0.56, 2.34, 3.21, 1.78, -2.45, 0.98, -0.12, 3.45, 4.56, 2.78, 1.23, 0.67, 3.89, 4.12, -2.34, 1.56, 0.78, 3.21, 4.12, 2.45, -1.67, 0.34, -3.45, 4.56, -2.78, 1.23, -0.67, 3.89, -4.34, 2.12, -1.56, 0.78, -3.21, 4.45, 2.12, 1.67] 
        }
      }
    }
  }
}

Этот вектор имеет 40 отдельных измерений и, следовательно, будет суммировать значения с плавающей точкой, используя хранящийся вектор bit в качестве маски.

В настоящее время функция cosineSimilarity не поддерживается для векторов bit.

Объяснение запроса

Использование запроса объяснения предоставляет объяснение того, как были вычислены части оценки. Запрос script_score может добавить собственное объяснение, установив параметр explanation:

resp = client.explain(
    index="my-index-000001",
    id="0",
    query={
        "script_score": {
            "query": {
                "match": {
                    "message": "elasticsearch"
                }
            },
            "script": {
                "source": "\n          long count = doc['count'].value;\n          double normalizedCount = count / 10;\n          if (explanation != null) {\n            explanation.set('normalized count = count / 10 = ' + count + ' / 10 = ' + normalizedCount);\n          }\n          return normalizedCount;\n        "
            }
        }
    },
)
print(resp)
response = client.explain(
  index: 'my-index-000001',
  id: 0,
  body: {
    query: {
      script_score: {
        query: {
          match: {
            message: 'elasticsearch'
          }
        },
        script: {
          source: "\n          long count = doc['count'].value;\n          double normalizedCount = count / 10;\n          if (explanation != nil) {\n            explanation.set('normalized count = count / 10 = ' + count + ' / 10 = ' + normalizedCount);\n          }\n          return normalizedCount;\n        "
        }
      }
    }
  }
)
puts response
const response = await client.explain({
  index: "my-index-000001",
  id: 0,
  query: {
    script_score: {
      query: {
        match: {
          message: "elasticsearch",
        },
      },
      script: {
        source:
          "\n          long count = doc['count'].value;\n          double normalizedCount = count / 10;\n          if (explanation != null) {\n            explanation.set('normalized count = count / 10 = ' + count + ' / 10 = ' + normalizedCount);\n          }\n          return normalizedCount;\n        ",
      },
    },
  },
});
console.log(response);
GET /my-index-000001/_explain/0
{
  "query": {
    "script_score": {
      "query": {
        "match": { "message": "elasticsearch" }
      },
      "script": {
        "source": """
          long count = doc['count'].value;
          double normalizedCount = count / 10;
          if (explanation != null) {
            explanation.set('normalized count = count / 10 = ' + count + ' / 10 = ' + normalizedCount);
          }
          return normalizedCount;
        """
      }
    }
  }
}

Обратите внимание, что explanation будет null при использовании в обычном запросе _search, поэтому использование условного блока — лучшая практика.

© 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/query-dsl-script-score-query.html

Spec-Zone.ru

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