1. 程式人生 > >ElasticsearchTemplate的詳細使用,完成多條件查詢、匹配度查詢等

ElasticsearchTemplate的詳細使用,完成多條件查詢、匹配度查詢等

ElasticsearchTemplate是Spring對ES的java api進行的封裝,提供了大量的相關的類來完成各種各樣的查詢。在日常的使用中,應該說最常用的查詢就是queryList方法。

public <T> List<T> queryForList(SearchQuery query, Class<T> clazz) {
        return this.queryForPage(query, clazz).getContent();
    }
而我們的主要工作就是構建SearchQuery查詢條件,包括排序、分頁等條件都包含在SearchQuery中。在之前的一篇文章中已經簡單的講過這幾個類的繼承關係了,這裡我們主要看看使用方法(http://blog.csdn.net/tianyaleixiaowu/article/details/76149547  先閱讀該篇瞭解基本的繼承關係)。

直接上例子,Post物件

package com.example.demo.pojo;

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;

@Document(indexName="projectname",type="post",indexStoreType="fs",shards=5,replicas=1,refreshInterval="-1")
public class Post {
    @Id
    private String id;

    private String title;

    private String content;

    private int userId;

    private int weight;

    @Override
    public String toString() {
        return "Post{" +
                "id='" + id + '\'' +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", userId=" + userId +
                ", weight=" + weight +
                '}';
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }
}
import com.example.demo.pojo.Post;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

public interface PostRepository extends ElasticsearchRepository<Post, String> {
}
初始化資料
package com.example.demo.config;

import com.example.demo.pojo.Post;
import com.example.demo.repository.PostRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

@Component
public class Init {
    @Autowired
    PostRepository postRepository;

    @PostConstruct
    public void init() {
        //只初始化一次
        Iterable<Post> posts = postRepository.findAll();
        if (posts.iterator().hasNext()) {
            return;
        }
        for (int i = 0; i < 40; i++) {
            Post post = new Post();
            post.setTitle(getTitle().get(i));
            post.setContent(getContent().get(i));
            post.setWeight(i);
            post.setUserId(i % 10);
            postRepository.save(post);
        }
    }

    private List<String> getTitle() {
        List<String> list = new ArrayList<>();
        list.add("《如夢令·常記溪亭日暮》");
        list.add("《醉花陰·薄霧濃雲愁永晝》");
        list.add("《聲聲慢·尋尋覓覓》");
        list.add("《永遇樂·落日熔金》");
        list.add("《如夢令·昨夜雨疏風驟》");
        list.add("《漁家傲·雪裡已知春信至》");
        list.add("《點絳脣·蹴[1]罷鞦韆》");
        list.add("《點絳脣·寂寞深閨》");
        list.add("《蝶戀花·淚溼羅衣脂粉滿》");
        list.add("《蝶戀花 離情》");
        list.add("《浣溪沙》");
        list.add("《浣溪沙》");
        list.add("《浣溪沙》");
        list.add("《浣溪沙》");
        list.add("《浣溪沙》");
        list.add("《減字木蘭花·賣花擔上》");
        list.add("《臨江仙·歐陽公作《蝶戀花》");
        list.add("《臨江仙·庭院深深深幾許》");
        list.add("《念奴嬌·蕭條庭院》");
        list.add("《菩薩蠻·風柔日薄春猶早》");
        list.add("《菩薩蠻·歸鴻聲斷殘雲碧》");
        list.add("《武陵春·風住塵香花已盡》");
        list.add("《一剪梅·紅藕香殘玉蕈秋》");
        list.add("《漁家傲·天接雲濤連曉霧》");
        list.add("《鷓鴣天·暗淡輕黃體性柔》");
        list.add("《鷓鴣天·寒日蕭蕭上鎖窗》");
        list.add("《一剪梅·紅藕香殘玉簟秋》");
        list.add("《如夢令·常記溪亭日暮》");
        list.add("《浣溪沙》");
        list.add("《浣溪沙》");
        list.add("《浣溪沙》");
        list.add("《蝶戀花·淚溼羅衣脂粉滿》");
        list.add("《蝶戀花·暖日晴風初破凍》");
        list.add("《鷓鴣天·寒日蕭蕭上鎖窗》");
        list.add("《醉花陰·薄霧濃雲愁永晝》");
        list.add("《鷓鴣天·暗淡輕黃體性柔》");
        list.add("《蝶戀花·永夜懨懨歡意少》");
        list.add("《浣溪沙》");
        list.add("《浣溪沙》");
        list.add("《如夢令·誰伴明窗獨坐》");
        return list;
    }

    private List<String> getContent() {
        List<String> list = new ArrayList<>();
        list.add("初中 宋·李清照 常記溪亭日暮,沉醉不知歸路,興盡晚回舟,誤入藕花深處。爭渡,爭渡");
        list.add("重陽節 宋·李清照 薄霧濃雲愁永晝,瑞腦消金獸。佳節又重陽,玉枕紗廚,半夜涼初透。東");
        list.add("閨怨詩 宋·李清照 尋尋覓覓,冷冷清清,悽悽慘慘慼戚。乍暖還寒時候,最難將息。三杯兩");
        list.add("元宵節 宋·李清照 落日熔金,暮雲合璧,人在何處。染柳煙濃,吹梅笛怨,春意知幾許。元");
        list.add("婉約詩 宋·李清照 昨夜雨疏風驟,濃睡不消殘酒,試問卷簾人,卻道海棠依舊。知否,知否");
        list.add("描寫梅花 宋·李清照 雪裡已知春信至,寒梅點綴瓊枝膩,香臉半開嬌旖旎,當庭際,玉人浴出");
        list.add(" 宋·李清照 蹴罷鞦韆,起來慵整纖纖手。露濃花瘦,薄汗輕衣透。見客入來,襪剗金");
        list.add("閨怨詩 宋·李清照 寂寞深閨,柔腸一寸愁千縷。惜春春去。幾點催花雨。倚遍闌干,只是無");
        list.add("婉約詩 宋·李清照 淚溼羅衣脂粉滿。四疊陽關,唱到千千遍。人道山長水又斷。蕭蕭微雨聞");
        list.add("描寫春天 宋·李清照 暖雨晴風初破凍,柳眼梅腮,已覺春心動。酒意詩情誰與共?淚融殘粉花");
        list.add("寒食節 宋·李清照 淡蕩春光寒食天,玉爐瀋水嫋殘煙,夢迴山枕隱花鈿。海燕未來人鬥草,");
        list.add(" 宋·李清照 髻子傷春慵更梳,晚風庭院落梅初,淡雲來往月疏疏,玉鴨薰爐閒瑞腦,");
        list.add(" 宋·李清照 莫許杯深琥珀濃,未成沉醉意先融。疏鍾已應晚來風。瑞腦香消魂夢斷,");
        list.add("閨怨詩 宋·李清照 小院閒窗春已深,重簾未卷影沉沉。倚樓無語理瑤琴,遠岫出山催薄暮。");
        list.add("愛情詩 宋·李清照 繡幕芙蓉一笑開,斜偎寶鴨親香腮,眼波才動被人猜。一面風情深有韻,");
        list.add("描寫春天 宋·李清照 賣花擔上,買得一枝春欲放。淚染輕勻,猶帶彤霞曉露痕。怕郎猜道,奴");
        list.add("》 宋·李清照 歐陽公作《蝶戀花》,有“深深深幾許”之句,予酷愛之。用其語作“庭");
        list.add("描寫梅花 宋·李清照 庭院深深深幾許,雲窗霧閣春遲,為誰憔悴損芳姿。夜來清夢好,應是發");
        list.add("寒食節 宋·李清照 蕭條庭院,又斜風細雨,重門須閉。寵柳嬌花寒食近,種種惱人天氣。險");
        list.add("思鄉詩 宋·李清照 風柔日薄春猶早,夾衫乍著心情好。睡起覺微寒,梅花鬢上殘。故鄉何處");
        list.add("描寫春天 宋·李清照 歸鴻聲斷殘雲碧,背窗雪落爐煙直。燭底鳳釵明,釵頭人勝輕。角聲催曉");
        list.add("閨怨詩 宋·李清照 風住塵香花已盡,日晚倦梳頭。物是人非事事休,欲語淚先流。聞說雙溪");
        list.add(" 宋·李清照 紅藕香殘玉蕈秋,輕解羅裳,獨上蘭舟。雲中誰寄錦書來?雁字回時,月");
        list.add("豪放詩 宋·李清照 天接雲濤連曉霧。星河欲轉千帆舞。彷彿夢魂歸帝所。聞天語。殷勤問我");
        list.add("描寫花 宋·李清照 暗淡輕黃體性柔。情疏跡遠只香留。何須淺碧深紅色,自是花中第一流。");
        list.add("描寫秋天 宋·李清照 寒日蕭蕭上瑣窗,梧桐應恨夜來霜。酒闌更喜團茶苦,夢斷偏宜瑞腦香。");
        list.add("閨怨詩 宋·李清照 紅藕香殘玉簟秋。輕解羅裳,獨上蘭舟。雲中誰寄錦書來?雁字回時,月");
        list.add(" 宋·李清照 常記溪亭日暮。沈醉不知歸路。興盡晚回舟,誤入藕花深處。爭渡。爭渡");
        list.add(" 宋·李清照 莫許杯深琥珀濃。未成沈醉意先融。已應晚來風。瑞腦香消魂夢斷,");
        list.add(" 宋·李清照 小院閒窗春色深。重簾未卷影沈沈。倚樓無語理瑤琴。遠岫出山催薄暮,");
        list.add(" 宋·李清照 淡蕩春光寒食天。玉爐瀋水嫋殘煙。夢迴山枕隱花鈿。海燕未來人鬥草,");
        list.add(" 宋·李清照 淚溼羅衣脂粉滿。四疊陽關,唱到千千遍。人道山長山又斷。蕭蕭微雨聞");
        list.add(" 宋·李清照 暖日晴風初破凍。柳眼眉腮,已覺春心動。酒意詩情誰與共。淚融殘粉花");
        list.add(" 宋·李清照 寒日蕭蕭上鎖窗。梧桐應恨夜來霜。酒闌更喜團茶苦,夢斷偏宜瑞腦香。");
        list.add(" 宋·李清照 薄霧濃雲愁永晝。瑞腦消金獸。佳節又重陽,玉枕紗廚,半夜涼初透。東");
        list.add(" 宋·李清照 暗淡輕黃體性柔。情疏跡遠只香留。何須淺碧深紅色,自是花中第一流。");
        list.add(" 宋·李清照 永夜懨懨歡意少。空夢長安,認取長安道。為報今年春色好。花光月影宜");
        list.add(" 宋·李清照 髻子傷春慵更梳。晚風庭院落梅初。淡雲來往月疏疏。玉鴨薰爐閒瑞腦,");
        list.add(" 宋·李清照 繡面芙蓉一笑開。斜飛寶鴨襯香腮。眼波才動被人猜。一面風情深有韻,");
        list.add(" 宋·李清照 誰伴明窗獨坐,我共影兒倆個。燈盡欲眠時,影也把人拋躲。無那,無那");
        return list;
    }

}

測試開始

定義出來

@Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

1 單字串全文查詢

/**
     * 單字串模糊查詢,預設排序。將從所有欄位中查詢包含傳來的word分詞後字串的資料集
     */
    @RequestMapping("/singleWord")
    public Object singleTitle(String word, @PageableDefault Pageable pageable) {
        //使用queryStringQuery完成單字串查詢
        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(queryStringQuery(word)).withPageable(pageable).build();
        return elasticsearchTemplate.queryForList(searchQuery, Post.class);
    }
請求 http://localhost:8080/singleWord?word=浣溪沙&size=20

結果:

[
  {
    "id": "AV56c5adcu4Vu652ZQrK",
    "title": "《浣溪沙》",
    "content": " 宋·李清照 莫許杯深琥珀濃,未成沉醉意先融。疏鍾已應晚來風。瑞腦香消魂夢斷,",
    "userId": 2,
    "weight": 12
  },
  {
    "id": "AV56c5bfcu4Vu652ZQrM",
    "title": "《浣溪沙》",
    "content": "愛情詩 宋·李清照 繡幕芙蓉一笑開,斜偎寶鴨親香腮,眼波才動被人猜。一面風情深有韻,",
    "userId": 4,
    "weight": 14
  },
  {
    "id": "AV56c5jdcu4Vu652ZQrb",
    "title": "《浣溪沙》",
    "content": " 宋·李清照 小院閒窗春色深。重簾未卷影沈沈。倚樓無語理瑤琴。遠岫出山催薄暮,",
    "userId": 9,
    "weight": 29
  },
  {
    "id": "AV56c5nfcu4Vu652ZQrj",
    "title": "《浣溪沙》",
    "content": " 宋·李清照 髻子傷春慵更梳。晚風庭院落梅初。淡雲來往月疏疏。玉鴨薰爐閒瑞腦,",
    "userId": 7,
    "weight": 37
  },
  {
    "id": "AV56c5aBcu4Vu652ZQrJ",
    "title": "《浣溪沙》",
    "content": " 宋·李清照 髻子傷春慵更梳,晚風庭院落梅初,淡雲來往月疏疏,玉鴨薰爐閒瑞腦,",
    "userId": 1,
    "weight": 11
  },
  {
    "id": "AV56c5Zucu4Vu652ZQrI",
    "title": "《浣溪沙》",
    "content": "寒食節 宋·李清照 淡蕩春光寒食天,玉爐瀋水嫋殘煙,夢迴山枕隱花鈿。海燕未來人鬥草,",
    "userId": 0,
    "weight": 10
  },
  {
    "id": "AV56c5i-cu4Vu652ZQra",
    "title": "《浣溪沙》",
    "content": " 宋·李清照 莫許杯深琥珀濃。未成沈醉意先融。已應晚來風。瑞腦香消魂夢斷,",
    "userId": 8,
    "weight": 28
  },
  {
    "id": "AV56c5j-cu4Vu652ZQrc",
    "title": "《浣溪沙》",
    "content": " 宋·李清照 淡蕩春光寒食天。玉爐瀋水嫋殘煙。夢迴山枕隱花鈿。海燕未來人鬥草,",
    "userId": 0,
    "weight": 30
  },
  {
    "id": "AV56c5a_cu4Vu652ZQrL",
    "title": "《浣溪沙》",
    "content": "閨怨詩 宋·李清照 小院閒窗春已深,重簾未卷影沉沉。倚樓無語理瑤琴,遠岫出山催薄暮。",
    "userId": 3,
    "weight": 13
  },
  {
    "id": "AV56c5oAcu4Vu652ZQrk",
    "title": "《浣溪沙》",
    "content": " 宋·李清照 繡面芙蓉一笑開。斜飛寶鴨襯香腮。眼波才動被人猜。一面風情深有韻,",
    "userId": 8,
    "weight": 38
  },
  {
    "id": "AV56c5Uwcu4Vu652ZQq-",
    "title": "《如夢令·常記溪亭日暮》",
    "content": "初中 宋·李清照 常記溪亭日暮,沉醉不知歸路,興盡晚回舟,誤入藕花深處。爭渡,爭渡",
    "userId": 0,
    "weight": 0
  },
  {
    "id": "AV56c5ifcu4Vu652ZQrZ",
    "title": "《如夢令·常記溪亭日暮》",
    "content": " 宋·李清照 常記溪亭日暮。沈醉不知歸路。興盡晚回舟,誤入藕花深處。爭渡。爭渡",
    "userId": 7,
    "weight": 27
  },
  {
    "id": "AV56c5e-cu4Vu652ZQrT",
    "title": "《武陵春·風住塵香花已盡》",
    "content": "閨怨詩 宋·李清照 風住塵香花已盡,日晚倦梳頭。物是人非事事休,欲語淚先流。聞說雙溪",
    "userId": 1,
    "weight": 21
  }
]
共13個結果,按照預設的排序方式,即匹配相關度排序,前10個匹配度最高,都是完全帶“浣溪沙”三個字的。第10個、11個都是題目和正文都包含“溪”字而且出現了2次“溪”,最後一個是正文帶一個“溪”。


我們修改一下排序方式,按照weight從大到小排序

/**
     * 單字串模糊查詢,單欄位排序。
     */
    @RequestMapping("/singleWord1")
    public Object singlePost(String word, @PageableDefault(sort = "weight", direction = Sort.Direction.DESC) Pageable pageable) {
        //使用queryStringQuery完成單字串查詢
        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(queryStringQuery(word)).withPageable(pageable).build();
        return elasticsearchTemplate.queryForList(searchQuery, Post.class);
    }
請求:

http://localhost:8080/singleWord1?word=浣溪沙&size=20
結果:

[
  {
    "id": "AV56c5oAcu4Vu652ZQrk",
    "title": "《浣溪沙》",
    "content": " 宋·李清照 繡面芙蓉一笑開。斜飛寶鴨襯香腮。眼波才動被人猜。一面風情深有韻,",
    "userId": 8,
    "weight": 38
  },
  {
    "id": "AV56c5nfcu4Vu652ZQrj",
    "title": "《浣溪沙》",
    "content": " 宋·李清照 髻子傷春慵更梳。晚風庭院落梅初。淡雲來往月疏疏。玉鴨薰爐閒瑞腦,",
    "userId": 7,
    "weight": 37
  },
  {
    "id": "AV56c5j-cu4Vu652ZQrc",
    "title": "《浣溪沙》",
    "content": " 宋·李清照 淡蕩春光寒食天。玉爐瀋水嫋殘煙。夢迴山枕隱花鈿。海燕未來人鬥草,",
    "userId": 0,
    "weight": 30
  },
  {
    "id": "AV56c5jdcu4Vu652ZQrb",
    "title": "《浣溪沙》",
    "content": " 宋·李清照 小院閒窗春色深。重簾未卷影沈沈。倚樓無語理瑤琴。遠岫出山催薄暮,",
    "userId": 9,
    "weight": 29
  },
  {
    "id": "AV56c5i-cu4Vu652ZQra",
    "title": "《浣溪沙》",
    "content": " 宋·李清照 莫許杯深琥珀濃。未成沈醉意先融。已應晚來風。瑞腦香消魂夢斷,",
    "userId": 8,
    "weight": 28
  },
  {
    "id": "AV56c5ifcu4Vu652ZQrZ",
    "title": "《如夢令·常記溪亭日暮》",
    "content": " 宋·李清照 常記溪亭日暮。沈醉不知歸路。興盡晚回舟,誤入藕花深處。爭渡。爭渡",
    "userId": 7,
    "weight": 27
  },
  {
    "id": "AV56c5e-cu4Vu652ZQrT",
    "title": "《武陵春·風住塵香花已盡》",
    "content": "閨怨詩 宋·李清照 風住塵香花已盡,日晚倦梳頭。物是人非事事休,欲語淚先流。聞說雙溪",
    "userId": 1,
    "weight": 21
  },
  {
    "id": "AV56c5bfcu4Vu652ZQrM",
    "title": "《浣溪沙》",
    "content": "愛情詩 宋·李清照 繡幕芙蓉一笑開,斜偎寶鴨親香腮,眼波才動被人猜。一面風情深有韻,",
    "userId": 4,
    "weight": 14
  },
  {
    "id": "AV56c5a_cu4Vu652ZQrL",
    "title": "《浣溪沙》",
    "content": "閨怨詩 宋·李清照 小院閒窗春已深,重簾未卷影沉沉。倚樓無語理瑤琴,遠岫出山催薄暮。",
    "userId": 3,
    "weight": 13
  },
  {
    "id": "AV56c5adcu4Vu652ZQrK",
    "title": "《浣溪沙》",
    "content": " 宋·李清照 莫許杯深琥珀濃,未成沉醉意先融。疏鍾已應晚來風。瑞腦香消魂夢斷,",
    "userId": 2,
    "weight": 12
  },
  {
    "id": "AV56c5aBcu4Vu652ZQrJ",
    "title": "《浣溪沙》",
    "content": " 宋·李清照 髻子傷春慵更梳,晚風庭院落梅初,淡雲來往月疏疏,玉鴨薰爐閒瑞腦,",
    "userId": 1,
    "weight": 11
  },
  {
    "id": "AV56c5Zucu4Vu652ZQrI",
    "title": "《浣溪沙》",
    "content": "寒食節 宋·李清照 淡蕩春光寒食天,玉爐瀋水嫋殘煙,夢迴山枕隱花鈿。海燕未來人鬥草,",
    "userId": 0,
    "weight": 10
  },
  {
    "id": "AV56c5Uwcu4Vu652ZQq-",
    "title": "《如夢令·常記溪亭日暮》",
    "content": "初中 宋·李清照 常記溪亭日暮,沉醉不知歸路,興盡晚回舟,誤入藕花深處。爭渡,爭渡",
    "userId": 0,
    "weight": 0
  }
]
可以看到排序已經按照weight從大到小來排了,比字串匹配度的權重更大。

2 某欄位按字串模糊查詢

查詢某個欄位中模糊包含目標字串,使用matchQuery
/**
     * 單欄位對某字串模糊查詢
     */
    @RequestMapping("/singleMatch")
    public Object singleMatch(String content, Integer userId, @PageableDefault Pageable pageable) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("content", content)).withPageable(pageable).build();
//        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("userId", userId)).withPageable(pageable).build();
        return elasticsearchTemplate.queryForList(searchQuery, Post.class);
    }
請求:

http://localhost:8080/singleMatch?content=落日熔金&size=20

部分結果:

結果集是content欄位包含“落日熔金”相關字樣。

把註釋放開,我們可以嘗試用userId查詢,http://localhost:8080/singleMatch?userId=1&size=20


matchQuery應該是比較常用的功能。

3 PhraseMatch查詢,短語匹配

/**
     * 單欄位對某短語進行匹配查詢,短語分詞的順序會影響結果
     */
    @RequestMapping("/singlePhraseMatch")
    public Object singlePhraseMatch(String content, @PageableDefault Pageable pageable) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchPhraseQuery("content", content)).withPageable(pageable).build();
        return elasticsearchTemplate.queryForList(searchQuery, Post.class);
    }

結果只有一條:
[
  {
    "id": "AV56c5WDcu4Vu652ZQrB",
    "title": "《永遇樂·落日熔金》",
    "content": "元宵節 宋·李清照 落日熔金,暮雲合璧,人在何處。染柳煙濃,吹梅笛怨,春意知幾許。元",
    "userId": 3,
    "weight": 3
  }
]
和match查詢類似,match_phrase查詢首先解析查詢字串來產生一個詞條列表。然後會搜尋所有的詞條,但只保留包含了所有搜尋詞條的文件,並且詞條的位置要鄰接。一個針對短語“中華共和國”的查詢不會匹配“中華人民共和國”,因為沒有含有鄰接在一起的“中華”和“共和國”詞條。
這種完全匹配比較嚴格,類似於資料庫裡的“%落日熔金%”這種,使用場景比較狹窄。如果我們希望能不那麼嚴格,譬如搜尋“中華共和國”,希望帶“我愛中華人民共和國”的也能出來,就是分詞後,中間能間隔幾個位置的也能查出來,可以使用slop引數。我們先來新增一個類似的資料
@RequestMapping("/add")
    public Object add() {
        Post post = new Post();
        post.setTitle("我是");
        post.setContent("我愛中華人民共和國");
        post.setWeight(1);
        post.setUserId(1);
        postRepository.save(post);
        post = new Post();
        post.setTitle("我是");
        post.setContent("中華共和國");
        post.setWeight(2);
        post.setUserId(2);
        return postRepository.save(post);
    }

使用如下方法:
@RequestMapping("/singlePhraseMatch")
    public Object singlePhraseMatch(String content, @PageableDefault Pageable pageable) {
//        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchPhraseQuery("content", content)).withPageable(pageable).build();
        //少匹配一個分詞也OK、
        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchPhraseQuery("content", content).slop(2)).withPageable(pageable).build();
        return elasticsearchTemplate.queryForList(searchQuery, Post.class);
    }

當請求“中華共和國時”結果如下
[
  {
    "id": "AV5-ekRHcu4Vu652ZQrw",
    "title": "我是",
    "content": "中華共和國",
    "userId": 2,
    "weight": 2
  },
  {
    "id": "AV5-ekBvcu4Vu652ZQrv",
    "title": "我是",
    "content": "我愛中華人民共和國",
    "userId": 1,
    "weight": 1
  }
]
關於slop的解釋,可以看看這一篇http://blog.csdn.net/xifeijian/article/details/51090707精確短語(Exact-phrase)匹配也許太過於嚴格了。也許我們希望含有"quick brown fox"的文件也能夠匹配"quick fox"查詢,即使位置並不是完全相等的。
我們可以在短語匹配使用slop引數來引入一些靈活性:
GET /my_index/my_type/_search
{
    "query": {
        "match_phrase": {
            "title": {
                "query": "quick fox",
                "slop":  1
            }
        }
    }
}
slop引數告訴match_phrase查詢詞條能夠相隔多遠時仍然將文件視為匹配。相隔多遠的意思是,你需要移動一個詞條多少次來讓查詢和文件匹配?
我們以一個簡單的例子來闡述這個概念。為了讓查詢quick fox能夠匹配含有quick brown fox的文件,我們需要slop的值為1:

            Pos 1         Pos 2         Pos 3
-----------------------------------------------
Doc:        quick         brown         fox
-----------------------------------------------
Query:      quick         fox
Slop 1:     quick                 ↳     fox
儘管在使用了slop的短語匹配中,所有的單詞都需要出現,但是單詞的出現順序可以不同。如果slop的值足夠大,那麼單詞的順序可以是任意的。

為了讓fox quick查詢能夠匹配我們的文件,需要slop的值為3:

            Pos 1         Pos 2         Pos 3
-----------------------------------------------
Doc:        quick         brown         fox
-----------------------------------------------
Query:      fox           quick
Slop 1:     fox|quick  ↵  
Slop 2:     quick      ↳  fox
Slop 3:     quick                 ↳     fox

4 Term查詢

這個是最嚴格的匹配,屬於低階查詢,不進行分詞的,參考這篇文章http://www.cnblogs.com/muniaofeiyu/p/5616316.html
/**
     * term匹配,即不分詞匹配,你傳來什麼值就會拿你傳的值去做完全匹配
     */
    @RequestMapping("/singleTerm")
    public Object singleTerm(Integer userId, @PageableDefault Pageable pageable) {
        //不對傳來的值分詞,去找完全匹配的
        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(termQuery("userId", userId)).withPageable(pageable).build();
        return elasticsearchTemplate.queryForList(searchQuery, Post.class);
    }
我們可以用這個來做那種需要==查詢的操作,當傳userId=1時,會查詢出來所有userId為1的集合。通常情況下,我們不會使用term查詢,絕大部分情況我們使用ES的目的就是為了使用它的分詞模糊查詢功能。而term一般適用於做過濾器filter的情況,譬如我們去查詢title中包含“浣溪沙”且userId=1時,那麼就可以用termQuery("userId", 1)作為查詢的filter。

5 multi_match多個欄位匹配某字串

如果我們希望title,content兩個欄位去匹配某個字串,只要任何一個欄位包括該字串即可,就可以使用multimatch。
/**
     * 多欄位匹配
     */
    @RequestMapping("/multiMatch")
    public Object singleUserId(String title, @PageableDefault(sort = "weight", direction = Sort.Direction.DESC) Pageable pageable) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(multiMatchQuery(title, "title", "content")).withPageable(pageable).build();
        return elasticsearchTemplate.queryForList(searchQuery, Post.class);
    }
訪問http://localhost:8080/multiMatch?title=我是結果如下
[
  {
    "id": "AV56c5oicu4Vu652ZQrl",
    "title": "《如夢令·誰伴明窗獨坐》",
    "content": " 宋·李清照 誰伴明窗獨坐,我共影兒倆個。燈盡欲眠時,影也把人拋躲。無那,無那",
    "userId": 9,
    "weight": 39
  },
  {
    "id": "AV56c5mecu4Vu652ZQrh",
    "title": "《鷓鴣天·暗淡輕黃體性柔》",
    "content": " 宋·李清照 暗淡輕黃體性柔。情疏跡遠只香留。何須淺碧深紅色,自是花中第一流。",
    "userId": 5,
    "weight": 35
  },
  {
    "id": "AV56c5g0cu4Vu652ZQrW",
    "title": "《鷓鴣天·暗淡輕黃體性柔》",
    "content": "描寫花 宋·李清照 暗淡輕黃體性柔。情疏跡遠只香留。何須淺碧深紅色,自是花中第一流。",
    "userId": 4,
    "weight": 24
  },
  {
    "id": "AV56c5gIcu4Vu652ZQrV",
    "title": "《漁家傲·天接雲濤連曉霧》",
    "content": "豪放詩 宋·李清照 天接雲濤連曉霧。星河欲轉千帆舞。彷彿夢魂歸帝所。聞天語。殷勤問我",
    "userId": 3,
    "weight": 23
  },
  {
    "id": "AV56c5e-cu4Vu652ZQrT",
    "title": "《武陵春·風住塵香花已盡》",
    "content": "閨怨詩 宋·李清照 風住塵香花已盡,日晚倦梳頭。物是人非事事休,欲語淚先流。聞說雙溪",
    "userId": 1,
    "weight": 21
  },
  {
    "id": "AV56c5c7cu4Vu652ZQrP",
    "title": "《臨江仙·庭院深深深幾許》",
    "content": "描寫梅花 宋·李清照 庭院深深深幾許,雲窗霧閣春遲,為誰憔悴損芳姿。夜來清夢好,應是發",
    "userId": 7,
    "weight": 17
  },
  {
    "id": "AV56c5YGcu4Vu652ZQrF",
    "title": "《點絳脣·寂寞深閨》",
    "content": "閨怨詩 宋·李清照 寂寞深閨,柔腸一寸愁千縷。惜春春去。幾點催花雨。倚遍闌干,只是無",
    "userId": 7,
    "weight": 7
  },
  {
    "id": "AV5-ekRHcu4Vu652ZQrw",
    "title": "我是",
    "content": "中華共和國",
    "userId": 2,
    "weight": 2
  },
  {
    "id": "AV5-ekBvcu4Vu652ZQrv",
    "title": "我是",
    "content": "我愛中華人民共和國",
    "userId": 1,
    "weight": 1
  }
]
可以看到,無論是title還是content中,包含“我”“是”字樣的都被查詢了出來。
MultiMatch還有一些其他的屬性,


包括Type、tieBreaker等的解釋看下圖


以上圖片來源於http://www.cnblogs.com/yjf512/p/4897294.html

完全包含查詢

之前的查詢中,當我們輸入“我天”時,ES會把分詞後所有包含“我”和“天”的都查詢出來,如果我們希望必須是包含了兩個字的才能被查詢出來,那麼我們就需要設定一下Operator。
/**
     * 單欄位包含所有輸入
     */
    @RequestMapping("/contain")
    public Object contain(String title) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", title).operator(MatchQueryBuilder.Operator.AND)).build();
        return elasticsearchTemplate.queryForList(searchQuery, Post.class);
    }
無論是matchQuery,multiMatchQuery,queryStringQuery等,都可以設定operator。預設為Or,設定為And後,就會把符合包含所有輸入的才查出來。如果是and的話,譬如使用者輸入了5個詞,但包含了4個,也是顯示不出來的。我們可以通過設定精度來控制。
/**
     * 單欄位包含所有輸入(按比例包含)
     */
    @RequestMapping("/contain")
    public Object contain(String title) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", title).operator(MatchQueryBuilder.Operator.AND).minimumShouldMatch("75%")).build();
        return elasticsearchTemplate.queryForList(searchQuery, Post.class);
    }
minimumShouldMatch可以用在match查詢中,設定最少匹配了多少百分比的能查詢出來。

合併查詢

即boolQuery,可以設定多個條件的查詢方式。它的作用是用來組合多個Query,有四種方式來組合,must,mustnot,filter,should。must代表返回的文件必須滿足must子句的條件,會參與計算分值;filter代表返回的文件必須滿足filter子句的條件,但不會參與計算分值;should代表返回的文件可能滿足should子句的條件,也可能不滿足,有多個should時滿足任何一個就可以,通過minimum_should_match設定至少滿足幾個。mustnot代表必須不滿足子句的條件。譬如我想查詢title包含“XXX”,且userId=“1”,且weight最好小於5的結果。那麼就可以使用boolQuery來組合。
/**
     * 多欄位合併查詢
     */
    @RequestMapping("/bool")
    public Object bool(String title, Integer userId, Integer weight) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(boolQuery().must(termQuery("userId", userId))
                .should(rangeQuery("weight").lt(weight)).must(matchQuery("title", title))).build();
        return elasticsearchTemplate.queryForList(searchQuery, Post.class);
    }
如果某個欄位需要匹配多個值,譬如userId為1,2,3任何一個的,類似於mysql中的in,那麼可以使用termsQuery("userId", ids).如果某欄位是字串,我建議空的就設定為null,不要為""空串,貌似某些版本的ES,使用matchQuery空串會不生效。詳細點的看這篇http://blog.csdn.net/dm_vincent/article/details/41743955
boolQuery使用場景非常廣泛,應該是主要學習的知識之一。

Query和Filter的區別


從程式碼上就能看出來,query和Filter都是QueryBuilder,也就是說在使用時,你把Filter的條件放到withQuery裡也行,反過來也行。那麼它們兩個區別在哪?查詢在Query查詢上下文和Filter過濾器上下文中,執行的操作是不一樣的:
1、查詢:是在使用query進行查詢時的執行環境,比如使用search的時候。
在查詢上下文中,查詢會回答這個問題——“這個文件是否匹配這個查詢,它的相關度高麼?”
ES中索引的資料都會儲存一個_score分值,分值越高就代表越匹配。即使lucene使用倒排索引,對於某個搜尋的分值計算還是需要一定的時間消耗。
2、過濾器:在使用filter引數時候的執行環境,比如在bool查詢中使用Must_not或者filter
在過濾器上下文中,查詢會回答這個問題——“這個文件是否匹配?”
它不會去計算任何分值,也不會關心返回的排序問題,因此效率會高一點。
另外,經常使用過濾器,ES會自動的快取過濾器的內容,這對於查詢來說,會提高很多效能。

總而言之:
1 查詢上下文:查詢操作不僅僅會進行查詢,還會計算分值,用於確定相關度;
2 過濾器上下文:查詢操作僅判斷是否滿足查詢條件,不會計算得分,查詢的結果可以被快取。
所以,根據實際的需求是否需要獲取得分,考慮效能因素,選擇不同的查詢子句。
這篇大概就講這麼多,已經能滿足大部分場景了。