就搜索算法而言,没有万能的解决方案。 不同的算法在不同的场景下效果更好,有时需要算法的组合才能达到最好的效果。 在 Elasticsearch 中,一种流行的组合搜索算法的方法是使用混合搜索,将用于文本搜索的 BM25 算法与用于最近邻搜索的 HNSW 算法相结合。 在这篇博文中,我们将探讨 Elasticsearch 中混合搜索的优势、挑战和用例。

BM25 是一种广泛用于文本搜索的算法,它根据查询中每个词的词频(TF)和逆向文档频率(IDF)计算分数。 正如我们在之前的博文中看到的,HNSW 是一种用于近似最近邻搜索的算法,它构建了一个由互连节点组成的小世界图。 通过结合这两种算法,我们可以执行结合两者优势的混合搜索。

混合搜索的最大挑战之一是平衡两种算法的权重。 换句话说,我们需要决定在组合它们时给 BM25 分数和给 HNSW 分数多少权重。 这可能很棘手,因为最佳权重可能会因数据和特定搜索场景而异。

但是,如果操作得当,混合搜索可以显着提高搜索准确性和效率。 例如,在电子商务应用程序中,混合搜索可用于将文本搜索与视觉搜索相结合,使用户能够找到与其文本和视觉查询相匹配的产品。 在科学应用中,混合搜索可用于将文本搜索与高维数据的相似性搜索相结合,使研究人员能够根据文本内容和数据找到相关文档。

让我们看一个例子,看看如何在 Elasticsearch 中实现混合搜索。 首先,我们需要使用包含 BM25 和 HNSW 相似性算法的映射来索引我们的数据:

PUT byte-image-index
{
  "mappings": {
    "properties": {
      "byte-image-vector": {
        "type": "dense_vector",
        "element_type": "byte",
        "dims": 2,
        "index": true,
        "similarity": "cosine"
      },
      "title": {
        "type": "text"
      },
      "area": {
        "type": "keyword"
      }
    }
  }
}

在这里,我们定义了一个映射,其中包括用于 BM25 文本搜索的 title 字段和用于 HNSW 高维数据相似性搜索的 byte-image-vector 字段。我们索引一下数据:

POST byte-image-index/_bulk?refresh=true
{ "index": { "_id": "1" } }
{ "byte-image-vector": [5, -20], "title": "moose family", "area": "Asia" }
{ "index": { "_id": "2" } }
{ "byte-image-vector": [8, -15], "title": "alpine lake", "area": "Europe" }
{ "index": { "_id": "3" } }
{ "byte-image-vector": [11, 23], "title": "full moon", "area": "Africa" }

接下来,我们可以使用文本和向量字段执行搜索,如下所示:

POST byte-image-index/_search
{
  "query": {
    "match": {
      "title": {
        "query": "mountain lake",
        "boost": 0.5
      }
    }
  },
  "knn": {
    "field": "byte-image-vector",
    "query_vector": [6, 10],
    "k": 5,
    "num_candidates": 50,
    "boost": 0.5,
    "filter": {
      "term": {
        "area": "Asia"
      }
    }
  },
  "size": 10
}

此搜索查询结合了 title 字段上的 match 查询和 image-vector 字段上的 knn 查询,使用了我们之前定义的混合相似性算法。 在此示例中,我们为 BM25 和 HNSW 使用了 0.5 和 0.5 的权重,但你可以尝试使用不同的权重来找到适合你的数据和搜索场景的最佳平衡点。 总之,Elasticsearch 中的混合搜索是一种强大的技术,可以结合不同搜索算法的优势,以实现更好的搜索准确性和效率。 通过将用于文本搜索的 BM25 算法与用于最近邻搜索的 HNSW 算法相结合,用户可以在其搜索和分析应用程序中利用这两种算法的强大功能。 通过仔细调整两种算法的权重,混合搜索可以成为从电子商务到科学研究等广泛应用的强大工具。

上述搜索结果为:

{
  "took": 10,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 3,
      "relation": "eq"
    },
    "max_score": 0.9070214,
    "hits": [
      {
        "_index": "byte-image-index",
        "_id": "2",
        "_score": 0.9070214,
        "_source": {
          "byte-image-vector": [
            8,
            -15
          ],
          "title": "alpine lake"
        }
      },
      {
        "_index": "byte-image-index",
        "_id": "3",
        "_score": 0.09977779,
        "_source": {
          "byte-image-vector": [
            11,
            23
          ],
          "title": "full moon"
        }
      },
      {
        "_index": "byte-image-index",
        "_id": "1",
        "_score": 0.014644662,
        "_source": {
          "byte-image-vector": [
            5,
            -20
          ],
          "title": "moose family"
        }
      }
    ]
  }
}

我们甚至可以针对 knn 搜索设置一下 filter 以限制他的搜索范围,比如:

POST byte-image-index/_search
{
  "query": {
    "match": {
      "title": {
        "query": "mountain lake",
        "boost": 0.5
      }
    }
  },
  "knn": {
    "field": "byte-image-vector",
    "query_vector": [6, 10],
    "k": 5,
    "num_candidates": 50,
    "boost": 0.5,
    "filter": {
      "term": {
        "area": "Asia"
      }
    }
  },
  "size": 10
}

在上面,我们使用 filter 来针对 area 来进行过滤,从而加快搜索的速度。上面返回的结果为:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 0.49041456,
    "hits": [
      {
        "_index": "byte-image-index",
        "_id": "2",
        "_score": 0.49041456,
        "_source": {
          "byte-image-vector": [
            8,
            -15
          ],
          "title": "alpine lake",
          "area": "Europe"
        }
      },
      {
        "_index": "byte-image-index",
        "_id": "1",
        "_score": 0.07322331,
        "_source": {
          "byte-image-vector": [
            5,
            -20
          ],
          "title": "moose family",
          "area": "Asia"
        }
      }
    ]
  }
}