教你快速從SQL過度到Elasticsearch的DSL查詢


前言

Elasticsearch太強大了,強大到跟python一樣,一種查詢能好幾種語法。其實我們用到的可能只是其中的一部分,比如:全文搜索。
我們一般是會將mysql的部分字段導入到es,再查詢出相應的ID,再根據這些ID去數據庫找出來。

問題來了:數據導入到es后,很多人都要面對這個es的json查詢語法,也叫DSL,如下

於是一堆新詞來了,比如:filter、match、multi_match、query、term、range,容易讓沒學過的人抵觸。

如果正常開發業務的程序員,只關心原先怎么用sql查詢出來的數據,在es中查詢出來。
sql查詢定位,一般常用的是:=、!=、>、<、and、or、in、between等等。

舉個例子,原先sql查詢一商品是這樣的

SELECT * FROM goods WHERE spu_id = "wp123" OR ( spu_id = "wp345" AND min_price = 30 ) 

對應到es是

{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "spu_id": "wp123"
          }
        },
        {
          "bool": {
            "must": [
              {
                "term": {
                  "spu_id": "wp345"
                }
              },
              {
                "term": {
                  "min_price": 30
                }
              }
            ]
          }
        }
      ]
    }
  }
}

sql和dsl是有一定對應關系的,下面把一些常用的總結下,讓不熟悉es的童鞋能絲滑能從sql過度

以下內容由chenqionghe傾情提供,祝您es使用愉快

bool-相當於一個括號

用bool包含起來的{},相當用()包含了一個復合查詢語句,如上邊的

{
  "bool": {
    "must": [
      {
        "term": {
          "spu_id": "wp345"
        }
      },
      {
        "term": {
          "min_price": 30
        }
      }
    ]
  }
}

相當於

看到沒有就是這么簡單

should-相當於or

must-相當於and

must_not-相當於 ! and

這個就相當於and取反了,

例如:

SELECT  *  FROM goods WHERE !(shop_id =79)

相當於

{
  "query": {
    "bool": {
      "must_not": [
        {
          "term": {
            "shop_id": "79"
          }
        }
      ]
    }
  }
}

term-相當於=

例如

SELECT * FROM goods WHERE shop_id =79

相當於

{
  "query": {
    "bool": {
      "must": [
        {
          "term": {"shop_id": "79"}
        }
      ]
    }
  }
}

terms-相當於in

例如

SELECT * FROM goods WHERE shop_id in (79,80,81)

相當於

{
  "query": {
    "bool": {
      "must": [
        {
          "terms": {
            "shop_id": [79, 80, 81]
          }
        }
      ]
    }
  }
}

range-相當於between

例如

SELECT * FROM goods WHERE id between 1000 and 10005

相當於

{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "id": {
              "gte": 1000,
              "lte": 10005
            }
          }
        }
      ]
    }
  }
}

exist相當於is not null

例如

SELECT * FROM goods WHERE id is not null

相當於

{
  "query": {
    "bool": {
      "must_not": [
        {
          "exists": {
            "field": "id"
          }
        }
      ]
    }
  }
}

match-類似match...against

這個match就相當於mysql的全文索引,關於mysql的全文索引,可以看一下這篇文章:從零開始學習MySQL全文索引

舉個查詢的例子,我要搜索包含 "海南 2018"的詞,如下
{
	"query": {
		"match": {
			"name": "海南 2018"
		}
	}
}

這相當於把所有的“海南”和“2018”記錄找出來了,他們是一個or的關系。如果想同時匹配怎么辦呢?
可以這樣,指定一個operator,默認是用的"or",可以改成這樣

{
  "query": {
    "match": {
      "name": {
        "query": "海南 2018",
        "operator": "and"
      }
    }
  }
}

includes-相當於select

比如

SELECT id,name FROM goods WHERE id = 1765

相當於

{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "id": 1765
          }
        }
      ]
    }
  },
  "_source":{"includes":["id","name"]}
}

sort-相當於order by

比如

SELECT * FROM goods ORDER BY id DESC 

相當於

{
	"sort": [
	  {
	    "id": {
	      "order": "desc"
	    }
	  }
	]
}



from size-相當於limit

例如

SELECT * FROM goods ORDER BY id DESC LIMIT 5,2;

相當於

{
 "from": 5,
 "size": 2
}

到這里,差不多就已經可以絲滑地從sql過度到es的dsl了

一些常見問題

match和term的區別

match在匹配時會對所查找的關鍵詞進行分詞,然后按分詞匹配查找,而term會直接對關鍵詞進行查找。一般模糊查找的時候,多用match

query和filter的區別

filter:只查詢出搜索條件的數據,不計算相關度分數
query:查詢出搜索條件的數據,並計算相關度分數,按照分數進行倒序排序
filter比query性能好,兩者可以一起使用

filtered和filter區別

filtered是比較老的的版本的語法。現在已經被bool替代,推薦使用bool。
老版本寫法

{
  "query": {
    "filtered": {
      "query": {
        "match": {
          "text": "quick brown fox"
        }
      },
      "filter": {
        "term": {
          "status": "published"
        }
      }
    }
  }
}

新版本寫法

{
  "query": {
    "bool": {
      "must": {
        "match": {
          "text": "quick brown fox"
        }
      },
      "filter": {
        "term": {
          "status": "published"
        }
      }
    }
  }
}

filter兩種用法
嵌套在bool下

{
  "query": {
    "bool": {
      "must": {
        "term": {
          "term": {
            "title": "kitchen3"
          }
        }
      },
      "filter": {
        "term": {
          "price": 1000
        }
      }
    }
  }
}

在根目錄下使用

{
  "query": {
    "term": {
      "title": "kitchen3"
    }
  },
  "filter": {
    "term": {
      "price": 1000
    }
  }
}

term和terms的區別

term相當於where =
terms相當於 where in (xx,xx,xx)
如果想要=於多次,得用多個term,而不是terms

如何高亮關鍵詞

這里需要用到
比如,我們查詢包含“海南”和“面試”的詞,match指定"海南 面試",highlight指定字段和要包含的標簽

GET /goods-search-v20210511/_search
{
  "query": {
    "match": {
      "name": "海南 面試"
    }
  },
  "highlight": {
    "fields": {
      "name": {
        "pre_tags": [
        "<cheniqonghe>"
      ], 
      "post_tags": [
        "</cheniqonghe>"
      ]
      }
    }
  },
  "_source":{"includes":["id","name"]}
} 

查詢結果如下

可以看到es已經將關鍵詞用指定的標簽包起來了

有沒有sql生成dsl的工具

有,找到一個:在線sql轉dsl
但是這種工具只能是作為輔助,不能完全靠它

  1. 傻瓜式的生成不一定是最優的
  2. sql有局限性,比如沒有高亮、嵌套查詢等等。

我們可以生成,再自己優化成最終的json


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM