Bboss Elasticsearch 简单用法(ES 7.*)

周云
2023-12-01

一、官方文档

https://esdoc.bbossgroups.com/#/README

二、官方demo

https://github.com/bbossgroups/elasticsearch-example.git

三、maven构建

<dependency>
            <groupId>com.bbossgroups.plugins</groupId>
            <artifactId>bboss-elasticsearch-rest-jdbc</artifactId>
            <version>6.1.3</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-log4j12</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.bbossgroups.plugins</groupId>
            <artifactId>bboss-elasticsearch-spring-boot-starter</artifactId>
            <version>6.1.3</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-log4j12</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>

四、我的代码

1,application.yml

spring:
    elasticsearch:
    bboss:
#      elasticUser: elastic
#      elasticPassword: changeme
      elasticsearch:
        rest:
          hostNames: 127.0.0.1:9200
        dateFormat: yyyy.MM.dd
        timeZone: Asia/Shanghai
        showTemplate: true
        discoverHost: false
      dslfile:
        refreshInterval: -1
      http:
        timeoutConnection: 5000
        timeoutSocket: 5000
        connectionRequestTimeout: 5000
        retryTime: 1
        maxLineLength: -1
        maxHeaderCount: 200
        maxTotal: 400
        defaultMaxPerRoute: 200
        soReuseAddress: false
        soKeepAlive: false
        timeToLive: 3600000
        keepAlive: 3600000
        keystore:
        keyPassword:
        hostnameVerifier:

2,esmapper/search.xml

<properties>

    <!--    构建书籍内容索引-->
    <property name="createBookcontent">
        <![CDATA[{
            "settings": {
                "number_of_shards": 1,
                "index.refresh_interval": "5s"
            },
            "mappings": {
                "properties": {
                    "name":{
                        "type":"text",
                        "fields": {
                            "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                            }
                        }
                    },
                    "hid": {
                        "type": "keyword"
                    },
                    "lastSection": {
                        "type": "keyword"
                    },
                    "nextSection": {
                        "type": "keyword"
                    },
                    "sectionName": {
                        "type": "keyword"
                    },
                    "sectionNote": {
                        "type": "text"
                    }
                }
            }
        }]]>
    </property>

    <!--     关键字检索检索-->
    <property name="searchPagineDatas">
        <![CDATA[{
            "query": {
                #if($keyword)
                    "multi_match": {
                      "query": #[keyword],
                      "type": "most_fields",
                      "fields": [
                        #foreach($column in $columns)
                            #if($velocityCount > 0),#end"$column"
                        #end
                      ]
                    }
                #else
                    "match_all": {}
                #end
            },
            "track_total_hits": true,
            "from":#[from],
            "size":#[size]
        }]]>
    </property>


    <!--    ## 根据hid查找书籍内容-->
    <property name="searchBookContent">
        <![CDATA[{
            "query": {
                "term": {
                  "hid": #[hid]
                }
            },
            "track_total_hits": true,
            "size": 1
        }]]>
    </property>



    <!--  使用父子文档存储书籍及其目录  -->
    <property name="createBookIndex">
        <![CDATA[{
            "settings": {
                "number_of_shards": 1,
                "index.refresh_interval": "5s"
            },
            "mappings": {
                "properties": {
                    "name":{
                        "type":"text",
                        "fields": {
                            "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                            }
                        }
                    },
                    "id0":{
                        "type":"keyword"
                    },
                    "hi0": {
                        "type": "keyword"
                    },
                    "directory0": {
                        "type": "text"
                    },
                    #foreach($i in [1,2,3])
                        #if($velocityCount > 0),#end
                        "id$i":{
                            "type":"keyword"
                        },
                        "hi$i": {
                            "type": "keyword"
                        },
                        "directory$i": {
                            "type": "text"
                        }
                    #end
                    ,
                    "level":{
                        "type":"keyword"
                    },
                    "book_join": {
                      "type": "join",
                      "relations": {
                        "book": ["bookmark"]
                      }
                    }
                }
            }
        }]]>
    </property>

    <!--  根据子文档条件筛选父文档  -->
    <property name="searchBookByBookMark">
        <![CDATA[{
            "query": {
                "has_child": {
                    "type": "bookmark",
                    "query": {
                        #if($keyword)
                            "multi_match": {
                              "query": #[keyword],
                              "type": "most_fields",
                              "fields": ["name","directory0","directory1","directory2","directory3"]
                            }
                        #else
                            "match_all": {}
                        #end
                    }
                }
            },
            "track_total_hits": true,
            "from":#[from],
            "size":#[size]
        }]]>
    </property>

    <!--  根据父文档条件筛选子文档  -->
    <property name="searchBookMarkByBook">
        <![CDATA[{
            "query": {
                "parent_id": {
                    "type": "bookmark",
                    "id": #[keyword]
                }
            },
            "track_total_hits": true,
            "size": 5000
        }]]>
    </property>



    <!--  group by -->
    <property name="searchBook">
        <![CDATA[{
            "query": {
                #if($keyword)
                    "multi_match": {
                      "query": #[keyword],
                      "type": "most_fields",
                      "fields": ["name","directory0","directory1","directory2","directory3"]
                    }
                #else
                    "match_all": {}
                #end
            },
            "track_total_hits": true,
            "sort": [
                {
                  "_score": {
                    "order": "desc"
                  }
                }
            ],
            "collapse":{
                "field":"name.keyword"
            },
            "from":#[from],
            "size":#[size]
        }]]>
    </property>

    <!--  count(*) gruop by  -->
    <property name="countBook">
        <![CDATA[{
            "query": {
                #if($keyword)
                    "multi_match": {
                      "query": #[keyword],
                      "type": "most_fields",
                      "fields": ["name","directory0","directory1","directory2","directory3"]
                    }
                #else
                    "match_all": {}
                #end
            },
            "track_total_hits": true,
            "size" : 0,
            "aggs": {
                "count": {
                  "cardinality": {
                    "field": "name.keyword"
                  }
                }
            }
        }]]>
    </property>

    <!-- scroll -->
    <property name="scrollQuery">
        <![CDATA[
            {
                "size":1000,
                "query": {
                    "match_all": {}
                }
            }
        ]]>
    </property>

</properties>

3,ElasticSearchService.java

@Slf4j
@Service
public class ElasticSearchService {

    private String mappath = "esmapper/search.xml";

    private static final int fromLimit = 10000;  //设置查询上限条数
    public static final String INDEX_BOOKMARK = BookMark.class.getAnnotation(ESIndex.class).name();
    public static final String INDEX_BOOKCONTENT = BookContent.class.getAnnotation(ESIndex.class).name();

    /**
     * 检索
     * @param searchDto
     * @param clz
     * @param <T>
     * @return
     */
    public <T> MyPage<T> search(KnowSearchDto searchDto, Class<T> clz){
        ClientInterface client = ElasticSearchHelper.getConfigRestClientUtil(mappath);

        int page = searchDto.getPage();
        int size = searchDto.getLimit();
        Map<String,Object> params = new HashMap<String,Object>();
        if(StringUtils.isNotBlank(searchDto.getKeyword())){
            params.put("keyword", searchDto.getKeyword());
        }
        params.put("from", Math.min((page-1) * size, fromLimit));
        params.put("size", size);

        Field[] fields = clz.getDeclaredFields();
        List<String> columns = new ArrayList<>(fields.length-1);
        Arrays.stream(fields).forEach(field -> {
            if(!"id".equals(field.getName())) columns.add(field.getName());
        });
        params.put("columns", columns);

        ESDatas<T> esDatas = client.searchList(clz.getAnnotation(ESIndex.class).name() +"/_search", "searchPagineDatas", params, clz);

        List<T> records = null == esDatas.getDatas() ? new ArrayList<>() : esDatas.getDatas();

        return new MyPage<T>(records, esDatas.getTotalSize(), size, page);
    }

    /**
     * 检索索引所有数据,每批次返回默认为5000条数据
     * @param index
     * @param clz
     * @param <T>
     * @return
     */
    public <T> ESDatas<T> searchAll(String index, Class<T> clz){
        ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
        ESDatas<T> esDatas = clientUtil.searchAll(index, clz);
        return esDatas;
    }

    /**
     * 根据父文档id查询其所有子文档(根据书籍查找目录)
     * @param esId
     * @return
     */
    public ESDatas<BookMark> searchBookMarkByBook(String esId){
        ClientInterface client = ElasticSearchHelper.getConfigRestClientUtil(mappath);
        ESDatas<BookMark> esDatas = client.searchList(INDEX_BOOKMARK+"/_search", "searchBookMarkByBook", ImmutableMap.of("keyword", esId), BookMark.class);
        return esDatas;
    }

    /**
     * (分组统计分页)
     * @param searchDto
     * @return
     */
    @Deprecated
    public MyPage<BookVo> searchBook(KnowSearchDto searchDto){
        ClientInterface client = ElasticSearchHelper.getConfigRestClientUtil(mappath);

        int page = searchDto.getPage();
        int size = searchDto.getLimit();
        Map<String,Object> params = new HashMap<String,Object>();
        if(StringUtils.isNotBlank(searchDto.getKeyword())){
            params.put("keyword", searchDto.getKeyword());
        }
        params.put("from", (page-1) * size);
        params.put("size", size);

        MapRestResponse restResponse = client.search(INDEX_BOOKMARK+"/_search", "countBook", params);
        int total = (int) restResponse.getAggregations().get("count").get("value");

        MyPage<BookVo> myPage = new MyPage<>(total, size, page);
        if(total > 0){
            ESDatas<BookMark> esDatas = client.searchList(INDEX_BOOKMARK +"/_search", "searchBook", params, BookMark.class);
            List<BookVo> bookVoList = new ArrayList<>();
            esDatas.getDatas().stream().forEach(bookMark -> {
                bookVoList.add(new BookVo(bookMark.getName(), ""));
            });
            myPage.setRecords(bookVoList);
        }
        return myPage;
    }
    public MyPage<BookMark> searchBookByBookMark(KnowSearchDto searchDto){
        ClientInterface clientInterface = ElasticSearchHelper.getConfigRestClientUtil(mappath);

        int page = searchDto.getPage();
        int size = searchDto.getLimit();
        Map<String,Object> params = new HashMap<String,Object>();
        if(StringUtils.isNotBlank(searchDto.getKeyword())){
            params.put("keyword", searchDto.getKeyword());
        }
        params.put("from", (page-1) * size);
        params.put("size", size);

        ESDatas<BookMark> esDatas = clientInterface.searchList(INDEX_BOOKMARK +"/_search", "searchBookByBookMark", params, BookMark.class);

        List<BookMark> records = null == esDatas.getDatas() ? new ArrayList<>() : esDatas.getDatas();

        return new MyPage(records, esDatas.getTotalSize(), size, page);
    }

    /**
     * 根据hid获取书籍篇章内容
     * @param hid
     * @return
     */
    public BookContent getBookContentByHid(String hid){
        ClientInterface client = ElasticSearchHelper.getConfigRestClientUtil(mappath);
        return client.searchObject(INDEX_BOOKCONTENT +"/_search", "searchBookContent", ImmutableMap.of("hid", hid), BookContent.class);
    }

}

4,entity.BookMark.java

@Slf4j
@Data
@ESIndex(name = "bookmark", type = "_doc")
public class BookMark implements Serializable {

    @ESId
    private String id;

    private String name;

    private String id0;
    private String id1;
    private String id2;
    private String id3;

    private String hi0;
    private String hi1;
    private String hi2;
    private String hi3;

    private String directory0;
    private String directory1;
    private String directory2;
    private String directory3;

    @JsonProperty("book_join")
    private JoinSon bookJoin;

    @ESRouting
    private String routingId;

    private Integer level;

    public BookMark() {
    }

    public BookMark(String name) {
        this.name = name;
    }


}

5,test.ESBook.java(建索引,灌数据)

@RunWith(SpringRunner.class)
@SpringBootTest(classes = CmaiDoctorApplication.class)
@Slf4j
@ActiveProfiles("test")
public class ESBook {

    private String index = "bookmark";

//    @Test
    public void createBookMarkIndex(){
        ClientInterface clientInterface = ElasticSearchHelper.getConfigRestClientUtil("esmapper/search.xml");
        boolean exist = clientInterface.existIndice(index);
        if(exist){
            clientInterface.dropIndice(index);
        }
        String response = clientInterface.createIndiceMapping(index, "createBookIndex");
        System.out.println(response);
        String mapping = clientInterface.getIndexMapping(index);
        System.out.println(mapping);

    }

    @Test
    public void insertBook() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        File dir = new File("C:\\Users\\guang\\Desktop\\中医书籍\\test3");
        File[] files = dir.listFiles();
        for (File file : files){
            String ext = file.getName().substring(file.getName().lastIndexOf("."));
            if(!".txt".equals(ext)){
                continue;
            }

            JSONObject jsonObject = JSON.parseObject(txt2String(file));
            String name = jsonObject.getString("name");
            JSONArray jsonArray = jsonObject.getJSONArray("bookmark");
            BookMark bookMark = new BookMark(name);

            List<BookMark> bookMarkList = new ArrayList<>();
            flatDeal(jsonArray, bookMarkList, bookMark, 0);
            log.info(JSON.toJSONString(bookMarkList));

            //移出书名作者,将其作为父文档
            BookMark bookName = bookMarkList.remove(0);
            JoinSon joinSon = new JoinSon();
            joinSon.setName("book");
            bookName.setBookJoin(joinSon);

            ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
            String response = clientUtil.addDocument(index, bookName ,"refresh");
            JSONObject addResponse = JSONObject.parseObject(response);
//            log.info("addResponse={}", JSON.toJSONString(addResponse));
            String id = addResponse.getString("_id");

            for (BookMark next : bookMarkList) {
                JoinSon js = new JoinSon();
                js.setName("bookmark");
                js.setParent(id);
                next.setBookJoin(js);
                next.setRoutingId(id);
            }

            String result = clientUtil.addDocuments(index, bookMarkList,"refresh");
            log.info("result ={}", result);
        }
    }

    /**
     * 将目录扁平化
     * @param jsonArray
     * @param bookMarkList
     * @param bookMark
     * @param level
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    private void flatDeal(JSONArray jsonArray, List<BookMark> bookMarkList, BookMark bookMark, Integer level) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        for (Object o : jsonArray) {
            JSONObject next = (JSONObject) o;
            setValue(bookMark, level, next);
            if (null != bookMark.getHi0() && bookMarkList.size() == 0) {  //如果没有作者,直接遍历到了目录,则手动生成一个假的作者
                bookMarkList.add(new BookMark(bookMark.getName()));
            }
            bookMarkList.add(MyUtils.CloneObj(bookMark));

            JSONArray childs = next.getJSONArray("bookmarkChildren");
            if (childs != null) {
                flatDeal(childs, bookMarkList, bookMark, level + 1);
            }
        }
    }

    /**
     * 通过反射给指定对象赋值
     * @param bookMark
     * @param level
     * @param value
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private void setValue(BookMark bookMark, int level, JSONObject value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        bookMark.setLevel(level);
        Class clz = bookMark.getClass();
        Method id = clz.getMethod("setId"+level, String.class);
        Method hi = clz.getMethod("setHi"+level, String.class);
        Method directory = clz.getMethod("setDirectory"+level, String.class);
        id.invoke(bookMark, value.getString("id"));
        hi.invoke(bookMark, value.getString("hi"));
        directory.invoke(bookMark, value.getString("directory"));
    }

    /**
     * 读取文件转为字符串
     * @param file
     * @return
     */
    private String txt2String(File file){
        StringBuilder result = new StringBuilder();
        try{
            BufferedReader br = new BufferedReader(new FileReader(file));
            String s = null;
            while((s = br.readLine())!=null){
                result.append(System.lineSeparator()+s);
            }
            br.close();
        }catch(Exception e){
            e.printStackTrace();
        }
        return result.toString();
    }

}

5,scroll用法

@RunWith(SpringRunner.class)
@SpringBootTest(classes = CmaiDoctorApplication.class)
@Slf4j
@ActiveProfiles("test")
public class AddImgToYaocai {
    private String mappath = "esmapper/search.xml";

    @Resource
    private FastDfsUtils fastDfsUtils;

    @Test
    public void testUpload(){
        File dir = new File("C:\\Users\\guang\\Desktop\\医药图片第一次汇总");
        File[] files = dir.listFiles();
        Map<String, String> imgMap = new HashMap<>(files.length);
        Arrays.stream(files).forEach(file -> imgMap.put(file.getName().substring(0, file.getName().lastIndexOf(".")), file.getAbsolutePath()));
        log.info("imgMap.size = {}", imgMap.size());

        ClientInterface client = ElasticSearchHelper.getConfigRestClientUtil(mappath);
        List<String > scrollIds = new ArrayList<>();

        ESDatas<YaoCai> response = client.searchList("yaocai/_search?scroll=1m", "scrollQuery", YaoCai.class);
        List<YaoCai> datas  = response.getDatas();
        String scrollId     = response.getScrollId();
        if(scrollId != null)  scrollIds.add(scrollId);

        if(datas != null && datas.size() > 0) {
            for (Iterator<YaoCai> iterator = datas.iterator(); iterator.hasNext(); ) {
                YaoCai yaoCai = iterator.next();
                String imgFile = imgMap.get(yaoCai.getName());
                if(!StringUtils.isEmpty(imgFile)){
                    yaoCai.setTupian(uploadImg(imgFile));
                }else{
                    iterator.remove();
                }
            }
            log.info("update = {}", client.updateDocuments(datas));

            do {
                response = client.searchScroll("1m", scrollId, YaoCai.class);
                scrollId = response.getScrollId();
                if(scrollId != null) scrollIds.add(scrollId);
                datas.clear();
                datas = response.getDatas();

                if(datas == null || datas.size() == 0){
                    break;
                }else{
                    for (Iterator<YaoCai> iterator = datas.iterator(); iterator.hasNext(); ) {
                        YaoCai yaoCai = iterator.next();
                        String imgFile = imgMap.get(yaoCai.getName());
                        if(!StringUtils.isEmpty(imgFile)){
                            yaoCai.setTupian(uploadImg(imgFile));
                        }else{
                            iterator.remove();
                        }
                    }
                    log.info("update = {}", client.updateDocuments(datas));
                }
            } while (true);
        }
        //查询并打印存在于es服务器上的scroll上下文信息
        String scrolls = client.executeHttp("_nodes/stats/indices/search", ClientUtil.HTTP_GET);
//        System.out.println(scrolls);
        //清除scroll上下文信息,虽然说超过1分钟后,scrollid会自动失效,
        //但是手动删除不用的scrollid,是一个好习惯
        if(scrollIds.size() > 0) {
            scrolls = client.deleteScrolls(scrollIds);
            System.out.println(scrolls);
        }
        //清理完毕后查看scroll上下文信息
        scrolls = client.executeHttp("_nodes/stats/indices/search", ClientUtil.HTTP_GET);
        System.out.println(scrolls);
    }

    private String uploadImg(String fileAbsolutePath) {
        try{
            File file = new File(fileAbsolutePath);
            FileInputStream input = new FileInputStream(file);
            MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), "text/plain", IOUtils.toByteArray(input));
            return fastDfsUtils.uploadImg(multipartFile);
        }catch (Exception e){
            log.error("uploadImg error . file ={}", fileAbsolutePath, e);
            return "";
        }
    }




//    @Test
    public void updateTest(){
        ClientInterface client = ElasticSearchHelper.getConfigRestClientUtil(mappath);
        ESIndex esIndex = YaoCai.class.getAnnotation(ESIndex.class);

        YaoCai yaoCai = client.getDocument(esIndex.name(), esIndex.type(), "wKdpFnIBHBOuI-wQ1k3n", YaoCai.class);

        log.info(JSON.toJSONString(yaoCai));

        yaoCai.setTupian("this is tupian");

        log.info("update = {}", client.updateDocument("wKdpFnIBHBOuI-wQ1k3n", yaoCai));

    }

}

 

 类似资料: