ES 自己做的例子汇总

本文深入探讨Elasticsearch中的复杂查询技术,包括精确日期、多条件组合、特定标签匹配及高级字段权重调整等,旨在帮助读者掌握高效数据检索的方法。

 

 

首先初始化数据

POST /forums/articles/_bulk 
{ "index": { "_id": 1 }}
{ "articleID" : "XHDK-A-1293-#fJ3", "userID" : 1, "hidden": false, "postDate": "2017-01-01" } 
{ "index": { "_id": 2 }}
{ "articleID" : "KDKE-B-9947-#kL5", "userID" : 1, "hidden": false, "postDate": "2017-01-02" } 
{ "index": { "_id": 3 }}
{ "articleID" : "JODL-X-1937-#pV7", "userID" : 2, "hidden": false, "postDate": "2017-01-01" } 
{ "index": { "_id": 4 }}
{ "articleID" : "QQPX-R-3956-#aD8", "userID" : 2, "hidden": true, "postDate": "2017-01-02" } 

POST /forums/articles/_bulk
{ "index": { "_id": 5 }}
{ "articleID" : "XHDK-C-1293-#fJ3", "userID" : 2, "hidden": false, "postDate": "2017-02-01" }
{ "index": { "_id": 6 }}
{ "articleID" : "KDKE-D-9947-#kL5", "userID" : 3, "hidden": false, "postDate": "2017-02-02" }
{ "index": { "_id": 7 }}
{ "articleID" : "JODL-E-1937-#pV7", "userID" : 4, "hidden": false, "postDate": "2017-02-01" }
{ "index": { "_id": 8 }}
{ "articleID" : "QQPX-F-3956-#aD8", "userID" : 5, "hidden": true, "postDate": "2017-03-02" }

POST /forum/article/_bulk
{ "update": { "_id": "1"} }
{ "doc" : {"tag" : ["java", "hadoop"]} }
{ "update": { "_id": "2"} }
{ "doc" : {"tag" : ["java"]} }
{ "update": { "_id": "3"} }
{ "doc" : {"tag" : ["hadoop"]} }
{ "update": { "_id": "4"} }
{ "doc" : {"tag" : ["java", "elasticsearch"]} }
{ "update": { "_id": "5"} }
{ "doc" : {"tag" : ["java", "hadoop"]} }
{ "update": { "_id": "6"} }
{ "doc" : {"tag" : ["java","mysql"]} }
{ "update": { "_id": "7"} }
{ "doc" : {"tag" : ["hadoop"]} }
{ "update": { "_id": "8"} }
{ "doc" : {"tag" : ["java", "elasticsearch","mysql"]} }

第一个简单的查询 查询postDate 在2017-01-01的帖子

GET /forums/articles/_search
{
  "query": {
    "constant_score": {
      "filter": {
          "bool": {
            "must": [
              { "term": {
                "postDate": "2017-01-01"
              } }
            ]
          }
      },
      "boost": 1.2
    }
  }
}

第二个查询,查询查询postDate 在2017-01-01的帖子或者是用户userid=2发的帖子

POST /forums/articles/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "bool": {
          "should": [
            {
              "term": {
                "postDate": "2017-01-01"
              }
            },
            {
              "term": {
                "userID": 2
              }
            }
          ]
        }
      }
    }
  }
}

第三个查询,查询用户userid=2 在2017-01-01和2017-01-01发的帖子

POST /forums/articles/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "bool": {
          "must": {
            "term": {
              "userID": 2
            }
          },
          "should": [
            {
              "term": {
                "postDate": "2017-01-01"
              }
            },
            {
              "term": {
                "postDate": "2017-01-02"
              }
            }
          ]
        }
      }
    }
  }
}

第4个需求  查询tag中包含java和hadoop的文章

POST /forums/articles/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "bool": {
          "must": [
            {
              "terms": {
                "tag": [
                  "java",
                  "hadoop"
                ]
              }
            }
          ]
        }
      }
    }
  }
}

第5个需求,我们要查询tag只是java的数据

这时原有数据如果统计的话比较麻烦,我们更新一下原有数据,添加一个新的字段tag_cnt用来表示tag数量,这样只需要查tag中含有java并且tag_cnt=1的数据即可。

POST /forums/articles/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "bool": {
          "must": [
            {
              "term": {
                "tag_cnt": 1
              }
            },
            {
              "term": {
                "tag": "java"
              }
            }
          ]
        }
      }
    }
  }
}

第6个需求  添加了字段view_cnt用来表示访问次数,查询访问次数在40到80的文章并且发帖日期在2017年2月1日后

 

POST /forums/articles/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "bool": {
          "must": [
            {
              "range": {
                "view_cnt": {
                  "gte": 40,
                  "lte": 80
                }
              }
            },
            {
              "range": {
                "postDate": {
                  "gte": "2017-02-01"
                }
              }
            }
          ]
        }
      }
    }
  }
}
postDate": {
                  "gte": "2017-01-01||+30d"
                }

上面这种是针对日期类型的一种用法。

第7个需求:查询title中多个关键词的情况,比如java hadoop mysql 或者elasticsearch中出现3次的

GET /forums/articles/_search
{
  "query": {
    "bool": {
      "should": [
        {"match": {
          "title": "java"
        }},
        {"match": {
          "title": "elasticsearch"
        }},
        {
          "match": {
            "title": "hadoop"
          }
        },
        {"match": {
          "title": "mysql"
        }}
      ],
        "minimum_should_match": 3
    }
  }
}

boost 可以进行细粒度的权重控制。

muti_query的例子,先更新数据

POST /forums/articles/_bulk
{ "update": { "_id": "1"} }
{ "doc" : {"content" : "i like to write best elasticsearch article"} }
{ "update": { "_id": "2"} }
{ "doc" : {"content" : "i think java is the best programming language"} }
{ "update": { "_id": "3"} }
{ "doc" : {"content" : "i am only an elasticsearch beginner"} }
{ "update": { "_id": "4"} }
{ "doc" : {"content" : "elasticsearch and hadoop are all very good solution, i am a beginner"} }
{ "update": { "_id": "5"} }
{ "doc" : {"content" : "spark is best big data solution based on scala ,an programming language similar to java"} }

按单个字段的结果优先排序

POST /forums/articles/_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "match": {
            "title": "java solution"
          }
        },
        {
          "match": {
            "content": "java solution"
          }
        }
      ]
    }
  }
}

加入字段权重 同时加入 tie_breaker进行score分值折中 ,title权重为2

POST /forums/articles/_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "match": {
            "title": { "query": "java solution",
            "minimum_should_match": "50%",
            "boost" : 2
            }
            
          }
        },
        {
          "match": {
            "content": "java solution"
          }
        }
      ],
      "tie_breaker": 0.7
    }
  }
}

cross_fields的

POST /forums/articles/_search
{
  "query": {
    "multi_match": {
      "query": "Peter Smith",
      "type": "cross_fields",
      "operator" : "and",
      "fields": [
        "new_author_first_name",
        "new_author_last_name"
      ]
    }
  }
}

 

<think>我们正在讨论的是在Elasticsearch7中如何对terms聚合结果进行分区处理。用户希望不使用composite aggregation,同时也不修改Elasticsearch配置(如增加bucket大小限制)。根据之前的对话历史,我们已经提出了三种替代方案:分区查询(PartitionedTerms Aggregation)、Scroll+客户端聚合、组合过滤+多次查询。现在用户的问题更聚焦于如何对terms聚合结果进行分区处理,因此我们将重点放在第一个方案(分区查询)上。分区查询的核心思想是将数据分成多个逻辑分区,然后分别对每个分区执行terms聚合,最后在客户端合并结果。这样可以避免单次聚合返回的桶数量超过默认的10000个限制。在Elasticsearch7中,我们可以使用scripted termsaggregation来实现分区。具体步骤如下:1.设计分区规则:根据字段的特性,将数据划分为多个分区。常见的分区规则有:-按首字母分区(适用于字符串字段)-哈希取模(适用于任何字段,可以均匀分布)-数值范围(适用于数值字段)2.对每个分区执行聚合查询:在聚合查询中,使用脚本将文档分配到不同的分区,然后对每个分区进行terms聚合。3.合并结果:在客户端将所有分区的聚合结果合并,得到全局的terms聚合结果。下面是一个具体的例子,假设我们有一个字符串类型的字段`field.keyword`,我们按照首字母进行分区(26个字母,可以分成26个分区):```jsonGET /my_index/_search{"size":0,"aggs": {"partitioned_terms": {"terms":{"script": {"source":"""//获取字段的首字母,并转换为小写deffirstChar= doc['field.keyword'].value.charAt(0);//将首字母映射到0-25的数字(a->0,b->1,... z->25)return firstChar.toLowerCase() -'a';""","lang":"painless"},"size":10000//每个分区最多返回10000个桶,确保每个分区内不超过限制}}} }```注意:上面的脚本假设字段值不为空,且首字母是英文字母。如果字段值可能为空或者首字母不是英文字母,我们需要进行额外的处理(例如,将非字母字符分配到特殊的分区)。但是,这种方法有一个潜在的问题:如果某个分区内的唯一值数量超过10000,那么该分区的聚合结果仍然会被截断(因为设置了size:10000)。为了避免这种情况,我们需要确保每个分区的唯一值数量不超过10000。如果数据分布不均匀(例如,某个字母开头的单词特别多),我们可以考虑增加分区数量或者调整分区规则(例如,按前两个字母分区)。另一种分区方法是使用哈希取模,这样可以更均匀地将数据分散到多个分区中。例如,我们可以将字段的哈希值对分区数取模:```jsonGET/my_index/_search{"size":0,"aggs": {"partitioned_terms": {"terms":{"script": {"source":"""//计算字段的哈希值,然后对分区数取模(这里假设分成100个分区)def hash= doc['field.keyword'].value.hashCode();//取绝对值,然后取模returnMath.abs(hash) %100;""","lang":"painless"},"size":10000}}}} ```这样,我们将数据分成了100个分区。理论上,每个分区的唯一值数量大约是总唯一值数量的1/100,因此我们可以通过增加分区数来确保每个分区内的唯一值数量不超过10000。执行步骤:1.确定分区数(partition_count),例如100。2.循环执行partition_count次查询,每次查询指定一个分区(即script返回特定的分区编号)。注意:上面的聚合查询会返回所有分区的聚合结果,但这样可能会使响应很大。我们可以通过查询参数只获取一个分区的结果,例如:```jsonGET /my_index/_search{"size":0,"query":{"bool": {"filter":{"script":{"script": {"source":"Math.abs(doc['field.keyword'].value.hashCode()) %100== params.partition","params":{"partition":0//查询第一个分区}}}}}},"aggs": {"partitioned_terms": {"terms":{"field": "field.keyword","size":10000}}}}```这样,我们通过查询过滤出属于特定分区的文档,然后对该分区内的文档进行terms聚合。然后我们循环0到99,获取所有分区的聚合结果。3.在客户端合并所有分区的结果。合并时,相同term的桶需要合并(将它们的doc_count相加)。这种方法的优点是可以精确控制每个分区的数据量,避免某个分区的桶数量超过限制。缺点是需要执行多次查询(分区数越多,查询次数越多),并且需要客户端合并。注意事项:-如果字段值包含多种类型(如字符串、数字)或者为空,需要在脚本中处理,避免脚本执行失败。-哈希取模可能存在哈希冲突,但同一个字段的相同值哈希值相同,所以不会影响聚合结果。-如果分区数很大,可以考虑并行执行这些查询以提高效率。另一种优化:我们可以使用Elasticsearch的`terms`聚合的`include`参数来指定每个分区的值范围,但这种方式需要提前知道每个分区的值范围,不太灵活。而使用脚本可以动态分区。总结:在Elasticsearch7中,我们可以通过脚本将数据分区,然后分别对每个分区进行terms聚合,最后在客户端合并结果,从而突破单次聚合桶数量的限制。相关问题:1.如何确定合适的分区数量?2.在客户端合并聚合结果时,如何处理重复的term?3.如何优化分区查询的性能(例如并行查询)?4.如果某个分区的桶数量仍然超过10000,应该如何处理?5.除了按首字母和哈希取模,还有哪些有效的分区策略?注意:在引用中,我们提到了分区(Partitioning)的概念[^1],以及如何通过脚本实现分区。另外,引用[3]中提到的特征提取方法(如BM25)与我们的问题无关,但引用[1]中关于分区的描述是相关的。因此,在回答中,我们可以引用[^1]来说明分区的概念。</think>在 Elasticsearch 7 中实现 Terms Aggregation 结果分区处理,有以下几种核心方法(不使用 Composite Aggregation): --- ### 一、脚本分区聚合(Scripted Partitioning) 通过 Painless 脚本动态分区,规避单次聚合 10,000 桶限制: ```json GET /index/_search { "size": 0, "aggs": { "partitioned_terms": { "terms": { "script": { "source": """ // 哈希取模分区(100个分区) def hash = doc['field.keyword'].value.hashCode(); return Math.abs(hash) % 100; """, "lang": "painless" }, "size": 1000 // 每个分区最多1000桶 } } } } ``` **实现步骤**: 1. **设计分区逻辑**: - 字符串字段:按首字母ASCII码分区 `(int) (doc['field'].value.charAt(0) - 'a')` - 数值字段:范围分区 `(int) (doc['price'].value / 1000)` - 通用方案:哈希取模(如上例)[^1] 2. **循环执行查询**:遍历所有分区编号(如 0-99) 3. **结果合并**:客户端汇总各分区桶数据 **优势**: - 分区策略灵活(支持动态调整分区数) - 避免集群配置修改 --- ### 二、过滤+分段聚合(Filtered Segmentation) 通过布尔查询物理隔离数据段: ```json // 第一段:值范围 [a-m] GET /index/_search { "size": 0, "query": { "range": { "field.keyword": { "gte": "a", "lt": "n" } } }, "aggs": { "segment_a_m": { "terms": { "field": "field.keyword", "size": 10000 } } } } // 第二段:值范围 [n-z]... ``` **优化技巧**: 1. **动态分段**:基于首次聚合结果调整后续范围 2. **并行查询**:对独立段发起并发请求 3. **混合分区**:结合数值范围与字符串前缀 --- ### 三、Scroll+客户端聚合(Full Scan) 当字段基数极高时(>100万唯一值),使用 Scroll API: ```java Set<String> uniqueTerms = new HashSet<>(); SearchRequest request = new SearchRequest("index"); request.scroll("1m"); SearchSourceBuilder source = new SearchSourceBuilder() .fetchSource("field", null) // 只获取目标字段 .size(1000); request.source(source); SearchResponse response = client.search(request); while (response.getHits().getHits().length > 0) { // 提取字段值 Arrays.stream(response.getHits().getHits()) .map(hit -> hit.getSourceAsMap().get("field")) .filter(Objects::nonNull) .forEach(uniqueTerms::add); // 获取下一批 response = client.scroll(SearchScrollRequest(response.getScrollId())); } client.clearScroll(ClearScrollRequest(response.getScrollId())); ``` **适用场景**: - 精确统计超高基数字段 - 文档总量可控(建议 < 5000万) - 需要客户端二次处理数据 --- ### 四、组合策略(Hybrid Approach) 1. **预处理分区键**: ```json PUT /index/_mapping { "properties": { "partition_key": { "type": "keyword", "script": { "source": """ // 写入时生成分区键 emit(ctx['source_field'].substring(0,2)); """ } } } } ``` 2. **基于分区键聚合**: ```json GET /index/_search { "size": 0, "aggs": { "partitions": { "terms": { "field": "partition_key", "size": 100 }, "aggs": { "sub_terms": { "terms": { "field": "source_field", "size": 1000 } } } } } } ``` --- ### 性能对比 | **方法** | 适用基数范围 | 查询次数 | 内存消耗 | 精度 | |------------------|----------------|----------|----------|------------| | 脚本分区 | < 500万 | 分区数 | 低 | 可能截断 | | 过滤分段 | < 200万 | 分段数 | 中 | 精确 | | Scroll+客户端 | > 500万 | 1 | 高 | 精确 | | 预计算分区键 | 任意 | 1 | 低 | 依赖设计 | --- ### 关键注意事项 1. **数据分布均衡**:哈希分区比范围分区更均匀[^1] 2. **动态调整分区**:根据首次查询结果优化后续分区粒度 ```java // 示例:动态计算分区数 int partitionCount = (totalUniqueCount / 8000) + 1; ``` 3. **错误处理**:添加查询超时与重试机制 ```json "timeout": "30s" ``` 4. **结果一致性**:使用 Point-in-Time (PIT) 保证快照一致性 ```json "pit": { "id": "your_pit_id" } ``` --- ### 相关问题 1. 如何动态确定最优分区数量以避免子聚合桶溢出? 2. 哈希分区时如何解决不同分区负载不均衡的问题? 3. Scroll API 在处理超大规模数据时如何避免内存溢出? 4. 预计算分区键方案对写入性能的影响如何评估? 5. 在分布式环境下如何保证分区聚合结果的全局准确性?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值