關於 Elasticsearch nested field /script 的一些複雜查詢

quickly3發表於2020-06-09

1.擬相似度百分比評分

nested filed 模擬相似度按照相似度百分比,給予不同評分
關鍵:重複打分機制

minimum_should_match 為輸入文字分詞總數的最小匹配百分比,比如當你輸入的查詢文字的”you are here for whole day”該文字有6個分詞,同時設定minimum_should_match 為50%,即6*50% = 3 這個查詢就只會返回至少有3個分詞匹配的文件

例子:experiences.workSoldDesc 欄位相似度為50% socre 為原始socre,達到70%,socre 為兩倍

GET 1_Talents/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "nested": {
            "path": ["experiences"],
            "query": {
              "query_string": {
                "default_field": "experiences.workSoldDesc", 
                "query": "Saas Information",
                "minimum_should_match": "50%"
              }
            }
          }
        },    
        {
          "nested": {
            "path": ["experiences"],
            "query": {
              "query_string": {
                "default_field": "experiences.workSoldDesc", 
                "query": "Saas Information",
                "minimum_should_match": "70%"
              }
            }
          }
        }
      ]
    }
  },
  "_source": [
    "experiences.roles.id",
    "experiences.workSoldDesc",
    "experiences.industries.id"
    ]
}

2.histogram查詢

histogram:

POST /1_Talents/_search
{
  "query": {
    "nested": {
      "path": "experiences",
      "query": {
        "query_string": {
          "query": "experiences.talentId:*"
        }        
      }
    }
  },
  "size": 0, 
  "_source":["experiences.talentId"],
  "aggs" : {
    "nesting" : {
        "nested": {
          "path": "experiences"
        },
        "aggs": {
          "cate":{
            "histogram": {
              "field" : "experiences.talentId",
              "interval" : 10
            }
          }
        }
    }
  }  
}

3.terms聚合查詢:

注意:
返回查詢結果中的doc_count 並不是一般的doc總數,而是一個doc 的nested 欄位的匹配次數之和,即doc數會因為nested欄位的原因被重複統計。

建議:使用copy_to屬性將nested欄位的中的屬性,自動複製到一個非nested欄位中,或者由程式碼處理

POST /1_Talents/_search?size=0
{
  "aggs" : {
    "nesting" : {
        "nested": {
          "path": "experiences"
        },
        "aggs": {
          "cate":{
            "terms": {
              "field" : "experiences.talentId"
            }
          }
        }
    }
  }
}

4.script fileds (使用nested型別欄位,格式化並求和,計算工作時長 )

painless 是將java 一些物件和函式封裝成painless api 。

例子:
根據experince 的startAt/endAt 計算每個exp的工作時長,當不存在endAt 時候,預設endAt 為now

GET /1_Talents/_search
{
  "query" : {
    "nested": {
      "path": "experiences",
      "query": {
        "query_string": {
          "fields": ["experiences"], 
          "query": "experiences:* && -experiences.endAt:*"
        }
      }
    }
  },
  "script_fields": {
    "exp_work_length": {
      "script": {
        "lang": "painless",
        "source": """
        def resp = [];
        for(exp in params._source.experiences){
          def item = ['exp_id':exp.id];
          if(exp.startAt != null){
            item['startAt'] = exp.startAt;
            item['title'] = exp.title;
          ZonedDateTime zdt1 = ZonedDateTime.parse(exp.startAt);
          ZonedDateTime zdt2;
            if(exp.endAt != null){
              zdt2 = ZonedDateTime.parse(exp.endAt);
              item['current'] = false;
            }else{
              def now_ts = new Date().getTime();
              def now_inst = Instant.ofEpochMilli(now_ts);
              zdt2 = ZonedDateTime.ofInstant(now_inst,ZoneId.of('Z'));
              item['current'] = true;
            }
            def diff = ChronoUnit.MONTHS.between(zdt1, zdt2);
            item['endAt'] = exp.endAt;
            item['wrok_len_of_months'] = diff;
            resp.add(item);      
          }
        }
        return resp
        """
      }
    }
  }
}

5.script aggragation 的子聚合查詢(包含nested的欄位)


注意:
agg script 主要工作原理是通過獲得兄弟agg 結果來進行程式設計。
agg script 拿不到doc欄位,因此無法根據doc來計算
子agg script拿不到父親agg 兄弟的agg結果
agg 不能使用script_fileds 進行計算。(https://discuss.elastic.co/t/can-elasticsearch-do-group-by-and-order-by-count/65365/2)

所以需要先計算再進行統計的欄位,不能在script中實現,建議還是先由程式計算後,直接儲存到indx裡面。

例子:
下面的例子是通過expereices 中的talentId ,來計算文件分佈情況,同時想要獲得不同 talentId 分佈下experience.id的id求和,可以用於實驗上述注意項。
POST /1_Talents/_search
{
  "query": {
    "nested": {
      "path": "experiences",
      "query": {
        "query_string": {
          "query": "experiences.talentId:*"
        }        
      }
    }
  },
  "size": 0, 
  "_source":["experiences.talentId"],
  "aggs" : {
    "nesting" : {
      "nested": {
        "path": "experiences"
      },
      "aggs": {
        "cate":{
          "histogram": {
            "field" : "experiences.talentId",
            "interval" : 10
          },
          "aggs": {
            "total_id":{
                "sum": {
                    "field": "experiences.id"
                }
            },
            "script_aggs": {
              "bucket_script": {
                "buckets_path": {
                  "total_id":"total_id"
                }, 
                "script": "params.total_id"
              }
            }
          }
        }
      }
    },
    "p_id":{
      "sum": {
          "field": "id"
      }      
    }
  }  
}

6.script query

下面的script查詢模擬一般查詢的experiences.startAt:*

注意script query 無法使用params[‘_source’]

POST /1_Talents/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "nested": {
            "path": "experiences",
            "query": {
              "script": {
                "script": """
                  return doc['experiences.startAt'].size()>0
                """
              }
            }
          }
        }
      ]
    }
  }
}

7.function score => scirpt score

根據不同條件和doc 值返回不同的score 權重乘數
無法得到該文件條目1的效果,因為function score 中拿不到欄位的匹配百分比和匹配次數

GET 1_Talents/_search
{
  "query": {
    "function_score": {
      "query": {
        "bool": {
          "should": [
            {
              "nested": {
                "path": ["experiences"],
                "query": {
                  "query_string": {
                    "default_field": "experiences.workSoldDesc", 
                    "query": "Saas Information",
                    "minimum_should_match": "50%"
                  }
                }
              }
            },    
            {
              "nested": {
                "path": ["experiences"],
                "query": {
                  "query_string": {
                    "default_field": "experiences.workSoldDesc", 
                    "query": "Saas Information",
                    "minimum_should_match": "70%"
                  }
                }
              }
            }
          ]
        }
      },
      "functions":[
        {
            "filter": { "match": { "id": 76 } },
            "random_score": {}, 
            "weight": 10
        },
        {
            "filter": { "match": { "id": "91" } },
            "weight": 100
        },
        {
          "script_score": {
            "script": "if(doc['id'].value == 109){return 1000}"
          }
        }
      ]
    }
  },
  "_source": ["_score"],
  "explain": false
}

8.painless 上下文

painless 的程式設計環境,有很多記憶體變數和獲取資料的API,但是在不同的功能裡面,這個能用的api都是不一樣的。

例如:
首先params._source, doc , ctx 概念上屬於painless context(painless上下文,三者都是用於Script 程式設計中獲取doc field 用的,
但是不是所有情況都有這三個物件

詳情可以查閱 painless 上下文列表:
script query 使用的是filter context
www.elastic.co/guide/en/elasticsea...

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章