ES 渗透查询 (Percolate query)

Percolate query [ˈpɜ:kəleɪt] [ˈkwɪəri]

📌 简介

🔄 工作原理

传统搜索是存储文档(JSON 文档),然后发出查询以检索数据的子集.

Percolate 查询则相反:

  1. 存储查询 (存储查询条件) :首先在 ES 中预先注册并存储大量的查询条件。这些查询条件描述了文档特征,可以将这些查询条件想象成预设的"过滤器"或"规则"
  2. 渗透文档:当新文档被索引到 ES 中时,或者当有一个新的文档需要被评估时,不是针对文档索引执行搜索,而是使用渗透查询 将文档"渗透"或"流动"到这些已存储的查询中
  3. 匹配查询 (查询匹配): ES 会快速地检查该文档与哪些预先存储的查询条件相匹配。换句话说,系统会判断这个文档是否"穿透"了哪些预设的"过滤器"
  4. 返回结果 (返回匹配结果): 渗透查询最终会返回与该文档成功匹配的已存储查询的子集。这些返回的查询就是描述该文档特征的"规则"

更形象的比喻:

可以将 Percolate Query(渗透查询)想象成一个筛子系统。

  • 筛子孔洞 (存储的查询): 预先制作了很多不同孔径的筛子(存储的查询条件),每种孔径代表一种筛选规则。
  • 沙子 (渗透的文档): 当新的沙子(文档)倒入筛子系统时,会穿过一些孔洞,而被另一些孔洞阻挡。
  • 穿过的孔洞 (匹配的查询): 最终哪些筛子的孔洞允许沙子穿过,就代表这个沙子符合哪些筛选规则(匹配的查询)。

🛠️ 具体实现步骤

1️⃣ 创建索引并定义 percolator 字段

首先,创建一个索引,并在 mappings 中定义一个 percolator 字段:

json 复制代码
PUT /percolator_index
{
  "mappings": {
    "properties": {
      "query": {
        "type": "percolator"
      },
      "category": {
        "type": "keyword"
      }
    }
  }
}
  • query 字段:类型为 percolator,用于存储查询语句。
  • category 字段:存储查询的类别,方便后续分类筛选。

2️⃣ 存储查询(注册规则)

将查询语句存入 percolator_index 索引

json 复制代码
POST /percolator_index/_doc/1
{
  "query": {
    "match": {
      "content": "Elasticsearch"
    }
  },
  "category": "search_engine"
}
json 复制代码
POST /percolator_index/_doc/2
{
  "query": {
    "match": {
      "content": "machine learning"
    }
  },
  "category": "AI"
}

🔹 这里,存储了两个查询:

  • 查询 1 :匹配包含 "Elasticsearch" 的文档。
  • 查询 2:匹配包含 "machine learning" 的文档。

3️⃣ 运行 Percolate 查询

当有新数据到来时,使用 percolate 查询来检查该数据匹配哪些已存储的查询:

json 复制代码
GET /percolator_index/_search
{
  "query": {
    "percolate": {
      "field": "query",
      "document": {
        "content": "Elasticsearch is a powerful search engine"
      }
    }
  }
}

🔹 查询原理:

  • Elasticsearch 会遍历 percolator_index 中所有的 query 字段,检查哪个查询能匹配 content: "Elasticsearch is a powerful search engine"

🔹 查询结果:

json 复制代码
{
  "hits": {
    "total": 1,
    "hits": [
      {
        "_id": "1",
        "_source": {
          "query": { "match": { "content": "Elasticsearch" } },
          "category": "search_engine"
        }
      }
    ]
  }
}

✅ 匹配到了 _id: 1,因为 "Elasticsearch" 存在于新文档中

📊 Percolate 查询的内部机制

  1. 索引阶段(存储查询):
    • Elasticsearch 会将存储的查询转换为 Lucene 查询,并将其存入 percolator 索引。
    • 查询不会像普通文档一样存储,而是以结构化形式索引,以便后续匹配。
  2. 查询阶段(匹配文档):
    • 当新文档到达时,Elasticsearch 会倒转查询流程:
      • 传统查询: 查询 → 过滤文档。
      • Percolate 查询: 文档 → 过滤查询。
      • 通过反向搜索,系统会返回与该文档匹配的所有查询。

🚀 适用场景

✅ 适用于

  • 实时警报系统 (Real-time Alerting System): 例如,监控网络日志,当出现符合特定安全规则(预定义的 percolate 查询)的日志条目时,立即发送警报。
  • 个性化内容推荐 (Personalized Content Recommendation): 用户可以预设自己的兴趣偏好(存储为 percolate 查询),当有新的文章、商品等内容(文档)发布时,系统可以快速判断哪些内容符合用户的兴趣,并进行推荐。
  • 事件订阅 (Event Subscription):用户订阅特定类型的事件,当系统中出现符合订阅条件的事件文档时,用户会收到通知。例如,订阅"服务器宕机"事件,当系统日志中出现宕机相关的文档时,订阅用户会收到通知。

❌ 不适用于

  • 存储百万级查询:如果查询量太大,性能会下降。
  • 复杂正则匹配:Percolate Query 适合结构化查询,而不是复杂的 regexp

✅ 优点(Advantages)

1️⃣ 适用于实时触发系统

  • 事件驱动匹配:适合实时通知日志监控内容分类等场景。
  • 自动触发:当新数据到达时,能够自动匹配存储的查询,无需手动执行搜索。

2️⃣ 反向查询,提高匹配效率

  • 查询是"索引"而非文档: 查询被存储在索引中,数据到来时仅需执行一次查询,避免遍历大量数据。
  • 减少查询次数:比传统遍历所有文档的方式更高效。

3️⃣ 适用于用户自定义搜索

  • 适合用户订阅系统(例如,用户提前定义感兴趣的关键词,当新内容到来时匹配并通知用户)。
  • 适用于个性化推荐(例如,新文章到来时匹配订阅条件并推送给合适的用户)。

4️⃣ 支持复杂查询

  • 可以存储 matchboolrangegeo 等多种类型的查询,灵活度高。

5️⃣ 高效的查询存储

  • 存储的查询会被索引优化,Elasticsearch 使用 Bitset 进行缓存,可以加快匹配速度。

❌ 缺点(Disadvantages)

1️⃣ 查询存储的扩展性有限

  • 不适用于超大量查询:
    • 如果存储的查询数量达到百万级别 ,会导致 索引膨胀查询性能下降
    • Elasticsearch 需要遍历所有查询,这会影响匹配效率。

2️⃣ 不能处理超复杂查询

  • 正则表达式查询 (regexp) 性能较差:
    • Percolate Query 适合结构化查询,如 matchbool
    • 但如果查询中包含复杂的 regexpscript,会影响查询性能。

3️⃣ 需要预先存储查询

  • 适用于查询相对稳定 的场景,例如:
    • 日志监控(规则不会频繁变动)。
    • 订阅推送(用户订阅的兴趣通常不会每天修改)。
  • 但如果查询规则频繁变更,则维护和存储查询的成本较高。

4️⃣ 查询匹配成本较高

  • 与普通搜索相比,查询成本较高:
    • 普通搜索是查询文档 ,而 Percolate Query 是查询查询 ,需要将新文档与所有存储的查询进行匹配,计算量大。

5️⃣ 依赖 Elasticsearch

  • 需要额外的索引存储计算资源 ,对于数据量较大的场景,可能需要专门的集群优化

💡 使用建议

1️⃣ 限制存储的查询数量

✅ 建议:

  • 尽量减少存储的查询数量,建议控制在 几千到几万级,避免超大规模索引影响查询性能。
  • 如果查询量过大,考虑使用 分片(sharding)或 其他查询优化策略。

🔴 避免:

  • 存储百万级查询,因为每次新文档到来时,都要匹配所有存储的查询,可能会导致性能下降。

2️⃣ 采用 filter 而非 query 进行筛选

✅ 建议:

  • 使用 filter 而不是 query 来减少计算开销,因为 filter 查询不会计算相关性评分,性能更高。

示例:用 filter 优化查询

json 复制代码
GET /percolator_index/_search
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "category": "news" } },
        {
          "percolate": {
            "field": "query",
            "document": { "content": "Breaking news: AI is revolutionizing search" }
          }
        }
      ]
    }
  }
}

🔹 效果:

  • 先筛选 category = "news" 的查询,再执行 percolate,减少不必要的匹配计算。

3️⃣ 结合 routing 进行查询分片

✅ 建议:

  • 使用 routing 让查询分布在不同的分片上,避免所有查询都存放在单个分片,提升查询效率。

示例:使用 routing 存储查询

json 复制代码
PUT /percolator_index/_doc/1?routing=user_123
{
  "query": {
    "match": { "content": "Elasticsearch" }
  },
  "user_id": "user_123"
}

🔹 效果:

  • 这样可以根据 user_id 进行分片存储和查询,提高查询效率。

4️⃣ 使用 Bitset 缓存提高查询速度

✅ 建议:

  • Elasticsearch 自动缓存 filter 查询的 Bitset,但可以手动优化:
    • 频繁使用的查询 应该存入索引,以便被 Bitset 缓存。
    • 结合 bool 查询 先筛选静态条件,再执行 percolate 查询。

5️⃣ 控制 percolate 查询的字段类型

✅ 建议:

  • 仅在 percolator 索引中存储必要的字段,减少索引体积。
  • 避免存储大字段,如 text 类型,改用 keyword,能大幅提升查询速度。

🔴 避免存储超长 text 字段,如:

json 复制代码
{
  "content": "This is a very long paragraph of text, which might affect the performance of the percolate query..."
}

✅ 改进方式(使用 keyword):

json 复制代码
{
  "content_short": "AI news",
  "content_category": "technology"
}

6️⃣ 适当调整索引刷新间隔

✅ 建议:

  • 由于 percolate 查询依赖索引,所以新查询存入索引后,必须等待索引刷新(默认 1 秒)。
  • 如果查询更新较频繁,可以手动调整刷新间隔

示例:减少索引刷新频率(提升批量插入性能)

json 复制代码
PUT /percolator_index/_settings
{
  "index": {
    "refresh_interval": "30s"
  }
}

🔹 效果:

  • 适用于批量导入查询时,减少不必要的刷新,提高索引速度。

7️⃣ 避免使用 regexpscript 查询

🔴 避免:

  • 正则表达式 (regexp) 查询:
    • regexp 查询消耗 CPU 资源大,建议改用 wildcard 查询或 match_phrase
  • Script 计算:
    • script 查询执行时需要动态计算,会大幅降低 percolate 查询速度。

8️⃣ 结合其他方法优化

如果 percolate 查询性能仍然不理想,可以结合其他方法:

优化方案 适用场景
预计算结果 + 缓存 当查询规则变化不频繁时,可提前计算匹配结果存入缓存,提高查询速度
Elasticsearch term 查询 + 关键词匹配 如果 percolate 查询压力大,可以使用 matchbool 直接匹配文档
Kafka + 批量匹配 如果 percolate 查询在高流量场景下存在瓶颈,可以用 Kafka 先存储数据,再批量处理

实践

1️⃣ 删除索引(若索引存在)

  • 请求
json 复制代码
DELETE sensitive_keyword
  • 成功响应
json 复制代码
{
  "acknowledged": true
}
  • 失败响应
json 复制代码
{
  "error": {
    "root_cause": [
      {
        "type": "index_not_found_exception",
        "reason": "no such index [sensitive_keyword]",
        "resource.type": "index_or_alias",
        "resource.id": "sensitive_keyword",
        "index_uuid": "_na_",
        "index": "sensitive_keyword"
      }
    ],
    "type": "index_not_found_exception",
    "reason": "no such index [sensitive_keyword]",
    "resource.type": "index_or_alias",
    "resource.id": "sensitive_keyword",
    "index_uuid": "_na_",
    "index": "sensitive_keyword"
  },
  "status": 404
}

2️⃣ 创建索引

json 复制代码
PUT /sensitive_keyword
{
  "settings"  : {
    "index" : {
      "number_of_replicas":2,
      "number_of_shards" : 5   
    }
  },

  "mappings" : {
    "properties": {
      "name" : {
        "type" : "text"
      },
      "reason" : {
        "type" : "text"
      },
      "trademark" : {
        "type" : "keyword"
      },
      "type_num" : {
        "type" : "keyword"
      },
      "type" : {
        "type" : "keyword"
      },
      "special_mark" : {
        "type" : "keyword"
      },
      "site_name" : {
        "type" : "keyword"
      },
      "query" : {
        "type" : "percolator"
      }

    }
  }
}
  • 成功响应
json 复制代码
{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "sensitive_keyword"
}
  • 索引已存在, 重复创建响应
json 复制代码
{
  "error": {
    "root_cause": [
      {
        "type": "resource_already_exists_exception",
        "reason": "index [sensitive_keyword/EX7SEFAcR-yfwlfFe8FG2Q] already exists",
        "index_uuid": "EX7SEFAcR-yfwlfFe8FG2Q",
        "index": "sensitive_keyword"
      }
    ],
    "type": "resource_already_exists_exception",
    "reason": "index [sensitive_keyword/EX7SEFAcR-yfwlfFe8FG2Q] already exists",
    "index_uuid": "EX7SEFAcR-yfwlfFe8FG2Q",
    "index": "sensitive_keyword"
  },
  "status": 400
}

3️⃣ 写入文档

🌰1️⃣ "operator": "AND"

json 复制代码
POST /sensitive_keyword/_doc/5
{
  "name": "MacBook Pro",
  "reasion": "MacBook Pro是苹果公司(Apple Inc.)注册的商标",
  "trademark": "9",
  "type_num": "0901,0907,0909,0913,0920",
  "type": "3",
  "special_mark": 1,
  "site_name": "US",
  "query": {
    "match": {
      "name": {
        "query": "MacBook Pro",
        "operator": "AND"
      }
    }
  }
}

参数含义:

  • "match" -- 全文匹配查询 (Match Query),用于搜索 name 字段中包含 "MacBook Pro" 这个词的文档
  • "name" -- 需要查询的字段(name
  • "query" -- 需要匹配的内容("MacBook Pro"
  • "operator" : "AND" -- 查询逻辑 ,表示查询 name 字段时,必须完全匹配所有词(AND 逻辑,意味着 MacBook Pro 必须完整出现)
  • "operator" : "OR" -- 意味着 MacBook Pro 仅需其中一个词出现即可命中

🌰2️⃣ "operator": "OR" (默认)

json 复制代码
POST /sensitive_keyword/_doc/6
{
  "name": "MacBook Air",
  "reasion": "MacBook Air是苹果公司(Apple Inc.)注册的商标",
  "trademark": "9",
  "type_num": "0901,0907,0909,0913,0920",
  "type": "3",
  "special_mark": 1,
  "site_name": "US",
  "query": {
    "match": {
      "name": {
        "query": "MacBook Air",
        "operator": "OR"
      }
    }
  }
}
  • 成功响应
json 复制代码
{
  "_index": "sensitive_keyword",
  "_type": "_doc",
  "_id": "5",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 3,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

4️⃣ 渗透查询文档

🙋‍♀️🌰1️⃣ 无命中

json 复制代码
POST /sensitive_keyword/_search
{
  "query": {
    "percolate": {
      "field": "query",
      "document": {
        "name": "new book with m5 chip"
      }
    }
  }
}
json 复制代码
POST /sensitive_keyword/_search
{
  "query": {
    "percolate": {
      "field": "query",
      "document": {
        "name": "new pro with m5 chip"
      }
    }
  }
}
json 复制代码
POST /sensitive_keyword/_search
{
  "query": {
    "percolate": {
      "field": "query",
      "document": {
        "name": "new with m5 mac chip book"
      }
    }
  }
}
json 复制代码
POST /sensitive_keyword/_search
{
  "query": {
    "percolate": {
      "field": "query",
      "document": {
        "name": "new with macbookm5 chip"
      }
    }
  }
}
  • 无命中响应
json 复制代码
{
  "took": 0,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 0,
      "relation": "eq"
    },
    "max_score": null,
    "hits": []
  }
}

🙋‍♀️🌰2️⃣ "operator": "OR" 命中

  • 请求
json 复制代码
POST /sensitive_keyword/_search
{
  "query": {
    "percolate": {
      "field": "query",
      "document": {
        "name": "new macbook with m5 chip"
      }
    }
  }
}
json 复制代码
POST /sensitive_keyword/_search
{
  "query": {
    "percolate": {
      "field": "query",
      "document": {
        "name": "new air with m5 chip"
      }
    }
  }
}
json 复制代码
POST /sensitive_keyword/_search
{
  "query": {
    "percolate": {
      "field": "query",
      "document": {
        "name": "new AIR with m5 Macbook chip"
      }
    }
  }
}
  • 响应
json 复制代码
{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 0.13076457,
    "hits": [
      {
        "_index": "sensitive_keyword",
        "_type": "_doc",
        "_id": "6",
        "_score": 0.13076457,
        "_source": {
          "name": "MacBook Air",
          "reasion": "MacBook Air是苹果公司(Apple Inc.)注册的商标",
          "trademark": "9",
          "type_num": "0901,0907,0909,0913,0920",
          "type": "3",
          "special_mark": 1,
          "site_name": "US",
          "query": {
            "match": {
              "name": {
                "query": "MacBook Air",
                "operator": "OR"
              }
            }
          }
        },
        "fields": {
          "_percolator_document_slot": [
            0
          ]
        }
      }
    ]
  }
}

🙋‍♀️🌰3️⃣ "operator": "AND" 和 "operator": "OR"

  • 请求
json 复制代码
POST /sensitive_keyword/_search
{
  "query": {
    "percolate": {
      "field": "query",
      "document": {
        "name": "new PRo with m5 Macbook chip"
      }
    }
  }
}
  • 响应
json 复制代码
{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 0.26152915,
    "hits": [
      {
        "_index": "sensitive_keyword",
        "_type": "_doc",
        "_id": "5",
        "_score": 0.26152915,
        "_source": {
          "name": "MacBook Pro",
          "reasion": "MacBook Pro是苹果公司(Apple Inc.)注册的商标",
          "trademark": "9",
          "type_num": "0901,0907,0909,0913,0920",
          "type": "3",
          "special_mark": 1,
          "site_name": "US",
          "query": {
            "match": {
              "name": {
                "query": "MacBook Pro",
                "operator": "AND"
              }
            }
          }
        },
        "fields": {
          "_percolator_document_slot": [
            0
          ]
        }
      },
      {
        "_index": "sensitive_keyword",
        "_type": "_doc",
        "_id": "6",
        "_score": 0.13076457,
        "_source": {
          "name": "MacBook Air",
          "reasion": "MacBook Air是苹果公司(Apple Inc.)注册的商标",
          "trademark": "9",
          "type_num": "0901,0907,0909,0913,0920",
          "type": "3",
          "special_mark": 1,
          "site_name": "US",
          "query": {
            "match": {
              "name": {
                "query": "MacBook Air",
                "operator": "OR"
              }
            }
          }
        },
        "fields": {
          "_percolator_document_slot": [
            0
          ]
        }
      }
    ]
  }
}

参考链接

ps:如有错误,欢迎批评指正,谢谢!

相关推荐
Yvonne9781 小时前
创建三个节点
java·大数据
OJAC近屿智能5 小时前
苹果新品今日发布,AI手机市场竞争加剧,近屿智能专注AI人才培养
大数据·人工智能·ai·智能手机·aigc·近屿智能
lucky_syq5 小时前
Spark算子:大数据处理的魔法棒
大数据·分布式·spark
m0_748233648 小时前
【分布式】Hadoop完全分布式的搭建(零基础)
大数据·hadoop·分布式
圣享科技SMARTLIC8 小时前
企业软件合规性管理:构建高效、安全的软件资产生态
大数据·安全·浮动许可证监控·许可证管理·浮动许可证优化·软件资产管理·浮动许可证管理
京东零售技术8 小时前
京东广告基于 Apache Doris 的冷热数据分层实践
大数据
D愿你归来仍是少年8 小时前
解决Python升级导致PySpark任务异常方案
大数据·开发语言·python·spark
光仔December8 小时前
【Elasticsearch入门到落地】8、RestClient操作索引库-基础介绍及导入demo
elasticsearch·搜索引擎·全文检索·索引·映射
risc1234568 小时前
【Elasticsearch】Retrieve inner hits获取嵌套查询的具体的嵌套文档来源,以及父子文档的来源
elasticsearch
薇晶晶9 小时前
如何安装Hadoop
大数据·hadoop·分布式