代码之家  ›  专栏  ›  技术社区  ›  dabljues

带过滤器和出现次数的ElasticSearch查询

  •  0
  • dabljues  · 技术社区  · 1 年前

    我有一个ES实例,我将日志推送到其中。然后使用ES来搜索这些日志。这并不理想,有计划改变它,但它就是这样。很抱歉描述太长,但请耐心等待, 问题很简单 .

    目前的搜索是这样的:

    • 我有一个包含N条日志行的索引
    • 用户输入要搜索的短语
    • 我使用以下内容构造ES查询:
      • 查询中的这个短语
      • size=1 (所以我只找到一行)
      • track_total_hits=true
      • from=0
      • sort=<something>

    因此,这让我第一次看到带有特定查询的行(因为它们是按时间戳排序的)。我还得到了总点击量,因此我可以向用户展示:

    • 找到的行
    • 出现次数(对于初始搜索,它总是 1 )
    • 总点击量

    因此,用户知道这是1/300的情况,并可以提示UI找到下一个。搜索是一样的,但如果用户想搜索下一个事件,我只需通过 from=1 , from=2 等等。这个的性能还可以,因为我只需要从ES下载一行。

    太好了。然而,这一切都在一个向用户显示日志的网站上。我想做的是,当用户进行初始搜索时(在进行下一次/上一次搜索之前),我想向他们显示第一行“在他们的光标位置之后”

    例如,用户看到:

    58 foo
    59 bar
    60 baz
    [...]
    

    所以我想把他向下滚动到第一个匹配的行 58 ,以前没有。

    问题是,我仍然想显示 1/<something> 找到个事件。在这种情况下,可能是初始搜索将返回例如第五次出现,即。 5/300 。并且用户可以转到上一个/下一个。

    因此,解决方案是下载所有匹配的行(没有 from= size= 在查询中)。然后在它们上做一个for循环,找到行号比用户看到的行号高的行(即。 58 ),返回它。通过这样做,我还可以计算出“哪个发生”是那个,所以我知道要显示例如 5/300 在UI上。

    问题是:我必须从ES下载所有的线路才能做到这一点。如果指数有数百万行,这可能会对业绩造成巨大打击。所以我想知道的是:有没有办法告诉Elastic:

    • 获取所有匹配行(匹配短语)
    • 在此处应用另一个筛选器(行号>某物)
    • 获取这一行,但也返回关于“匹配行的哪个出现是那个”的信息(在所有匹配行中,没有“行号”过滤器)

    因此,对于以下线路:

    54 content
    55 content
    56 content
    57 content
    58 foo
    59 bar
    60 baz
    61 content
    [...]
    

    短语 content ,搜索“来自第58行”,我会得到这样的回答:

    {
      "line": {"line_number": 61, "content": "content"},
      "total_hits": 300,
      "occurrence": 5
    }
    
    1 回复  |  直到 1 年前
        1
  •  1
  •   imotov    1 年前

    有几种不同的方法可以实现这一点,所有这些方法都基于相同的原则。您需要执行三次搜索:

    • 一个没有行过滤器来计算出现的总数
    • 其中一个在当前行之前带有筛选器,用于获取当前事件之前的记录计数
    • 一个在当前行后按范围过滤以查找当前事件

    这可以通过多重搜索、filter+top_hit聚合和filter+全局聚合来实现。以下是如何使用过滤器+全局聚合实现这一目标的示例:

    DELETE test
    PUT test
    {
      "mappings": {
        "properties": {
          "line_no": {
            "type": "integer"
          },
          "line": {
            "type": "text"
          }
        }
      }
    }
    
    POST test/_bulk?refresh=true
    { "index": { "_id": "1" } }
    { "line_no": 54, "line": "content"}
    { "index": { "_id": "2" } }
    { "line_no": 55, "line": "content"}
    { "index": { "_id": "3" } }
    { "line_no": 56, "line": "content"}
    { "index": { "_id": "4" } }
    { "line_no": 57, "line": "content"}
    { "index": { "_id": "5" } }
    { "line_no": 58, "line": "foo"}
    { "index": { "_id": "6" } }
    { "line_no": 59, "line": "bar"}
    { "index": { "_id": "7" } }
    { "line_no": 60, "line": "baz"}
    { "index": { "_id": "8" } }
    { "line_no": 61, "line": "content"}
    { "index": { "_id": "9" } }
    { "line_no": 62, "line": "content"}
    { "index": { "_id": "10" } }
    { "line_no": 63, "line": "content"}
    
    
    
    POST test/_search?filter_path=hits.hits,aggregations.all.all_occurrencess.doc_count,aggregations.all.all_occurrences.previous_occurrences.doc_count
    {
      "size": 1,
      "query": {
        "bool": {
          "must": [
            {
              "range": {
                "line_no": {
                  "gt": 59
                }
              }
            },
            {
              "match": {
                "line": "content"
              }
            }
          ]
        }
      },
      "sort": [
        {
          "line_no": {
            "order": "asc"
          }
        }
      ],
      "aggs": {
        "all": {
          "global": {},
          "aggs": {
            "all_occurrences": {
              "filter": {
                "match": {
                  "line": "content"
                }
              },
              "aggs": {
                "previous_occurrences": {
                  "filter": {
                    "range": {
                      "line_no": {
                        "lte": 59
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    

    此查询的结果将是:

    {
      "hits": {
        "hits": [
          {
            "_index": "test",
            "_id": "8",
            "_score": 1.3829923,
            "_source": {
              "line_no": 61,
              "line": "content"
            },
            "sort": [
              61
            ]
          }
        ]
      },
      "aggregations": {
        "all": {
          "all_occurrences": {
            "previous_occurrences": {
              "doc_count": 4
            }
          }
        }
      }
    }
    

    在上面的结果中 hits.hits[0] 将表示第59行之后与您的查询匹配的下一行。这个 aggregations.all.all_occurrences.doc_count 将表示包含“内容”的行数(在您的理论示例中为300,但我将其减少到7,因为示例简洁)。最后 aggregations.all.all_occurrences.previous_occurrences.doc_count 表示在当前行之前发生的事件数。若要获得当前的出现次数,您需要将其添加1。