Elasticsearch对排序的汇总结果进行分页

时间:2019-02-18 19:30:10

标签: elasticsearch

据我所知,在Elasticsearch中无法执行以下操作:

SELECT * FROM myindex
GROUP BY agg_field1, agg_field2, agg_field3 // aggregation
ORDER BY order_field1, order_field2, order_field3 // sort
LIMIT 1000, 5000 // paginate -- get page 6 of size 1000 records

以下是与此相关的一些文件:

在Elasticsearch中是否可以执行上述操作?我们的一个限制是我们记录永远不会超过1000万,因此(希望)我们不应该遇到内存错误。我的想法是这样做:

  • 进行汇总查询
  • 从中获取结果数量
  • 根据我们想要的结果和页面大小将其分成N个细分
  • 使用上述细分重新运行查询

实现此目标的最佳方法是什么?在您的回答/建议中,您能否发布一些示例代码,这些代码与如何在ES中完成上述SQL查询有关?


作为对该问题的更新,下面是一个公共索引,可用于测试:

# 5.6
e=Elasticsearch('https://search-testinges-fekocjpedql2f3rneuagyukvy4.us-west-1.es.amazonaws.com')
e.search('testindex')

# 6.4 (same data as above)
e = Elasticsearch('https://search-testinges6-fycj5kjd7l5uyo6npycuashch4.us-west-1.es.amazonaws.com')
e.search('testindex6')

它有10,000条记录。随时进行测试:

enter image description here

我要执行的查询如下(在sql中):

SELECT * FROM testindex
GROUP BY store_url, status, title
ORDER BY title ASC, status DESC
LIMIT 100 OFFSET 6000

换句话说,我正在寻找一个汇总结果(具有多个汇总)并获得偏移量。

2 个答案:

答案 0 :(得分:2)

composite aggregation在这里可能会有所帮助,因为它允许您按多个字段分组,然后对结果进行分页。唯一不允许您执行的操作是跳转到给定的偏移量,但是您可以通过在必要时从客户端代码进行迭代来做到这一点。

因此,这是一个示例查询:

POST testindex6/_search
{
  "size": 0,
  "aggs": {
    "my_buckets": {
      "composite": {
        "size": 100,
        "sources": [
          {
            "store": {
              "terms": {
                "field": "store_url"
              }
            }
          },
          {
            "status": {
              "terms": {
                "field": "status",
                "order": "desc"
              }
            }
          },
          {
            "title": {
              "terms": {
                "field": "title",
                "order": "asc"
              }
            }
          }
        ]
      },
      "aggs": {
        "hits": {
          "top_hits": {
            "size": 100
          }
        }
      }
    }
  }
}

在响应中,您将看到after_key结构:

  "after_key": {
    "store": "http://google.com1087",
    "status": "OK1087",
    "title": "Titanic1087"
  },

您需要在后续查询中使用某种游标,例如:

{
  "size": 0,
  "aggs": {
    "my_buckets": {
      "composite": {
        "size": 100,
        "sources": [
          {
            "store": {
              "terms": {
                "field": "store_url"
              }
            }
          },
          {
            "status": {
              "terms": {
                "field": "status",
                "order": "desc"
              }
            }
          },
          {
            "title": {
              "terms": {
                "field": "title",
                "order": "asc"
              }
            }
          }
        ],
        "after": {
          "store": "http://google.com1087",
          "status": "OK1087",
          "title": "Titanic1087"
        }
      },
      "aggs": {
        "hits": {
          "top_hits": {
            "size": 100
          }
        }
      }
    }
  }
}

它将为您提供下100个存储桶。希望这会有所帮助。

更新

如果您想知道总共有多少个存储桶,composite聚合将不会为您提供该数字。但是,由于composite聚合只是源中所有字段的笛卡尔积,因此您还可以通过返回[]基数](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-cardinality-aggregation.html)来获得该总数的一个很好的近似值。 composite聚合中使用的每个字段并将它们相乘。

  "aggs": {
    "my_buckets": {
      "composite": {
        ...
      }
    },
    "store_cardinality": {
      "cardinality": {
        "field": "store_url"
      }
    },
    "status_cardinality": {
      "cardinality": {
        "field": "status"
      }
    },
    "title_cardinality": {
      "cardinality": {
        "field": "title"
      }
    }
  }

然后,我们可以将store_cardinalitystatus_cardinalitytitle_cardinality中获得的数字相乘,或者至少乘以good approximation thereof(得出在高基数字段上效果不佳,但在低基数字段上效果很好。

答案 1 :(得分:0)

Field collapsing是答案。

当我们要对特定字段上的匹配分组时使用字段折叠功能(如按agg_field分组)。

在Elastic 6之前,将字段分组的方法是use aggregation。这种方法缺乏进行有效寻呼的能力。

但是现在,由于弹性提供了开箱即用的电场塌陷,这非常容易。

下面是一个示例查询,其中字段折叠来自上方链接。

GET /twitter/_search
{
  "query": {
      "match": {
          "message": "elasticsearch"
      }
  },
  "collapse" : {
      "field" : "user", 
      "inner_hits": {
          "name": "last_tweets", 
          "size": 5, 
          "sort": [{ "date": "asc" }] 
      },
      "max_concurrent_group_searches": 4 
  },
  "sort": ["likes"]

}