主頁 >  其他 > springboo專案用自定義框架來完成elasticsearch7.14.0最新版的增刪改。同時在mybatis-plus中集成elasticsearch,完成一系列自動化操作。

springboo專案用自定義框架來完成elasticsearch7.14.0最新版的增刪改。同時在mybatis-plus中集成elasticsearch,完成一系列自動化操作。

2021-08-17 07:10:19 其他

elasticsearch7.14.0最新版的操作集合,以及對elasticsearch和mybatis-plus整合,

前言

  • 就算不用這個框架,用這個來魔改一下也挺不錯哦🤪
  • 該框架是在springboot中對elasticsearch的方法進行包裝,同時整合mybatis-plus和elasticsearch(利用mybatis-plus的攔截器來完成自動增刪改)
  • 不用在給每個類配置一個獨一無二的增刪改操作了,且能夠自動的實作elasticsearch的增刪改操作,以及自動創建index,還有es的搜索功能、支持高亮顯示、must匹配和should匹配、list串列分頁獲取資料,
  • 版本使用:
    • elasticsearch 7.14.0 最新版本
    • springboot 2.4.1
  • 自定義的yml配置
smart-es:
  client-config:
    hostname: # 服務器ip
    port: 9200
    scheme: http
    username: # es給你生成的用戶名
    password: # es給你生成的密碼
  service-config:
    pre-tags: <span class="highlight">
    post-tags: </span>
    pkg: cn.omisheep.spook.entity # 物體類包名

1. 自動創建索引

如下,有三個注解,分別是@Index@IndexId@IndexField

@Data
@Index
@TableName("article")
@Accessors(chain = true)
public class Article {

    @TableId(value = "id", type = IdType.AUTO)
    @IndexId
    private Integer id;

    @IndexField(isSearch = false)
    private String userAvatar;

    @IndexField
    private String type; 
    @IndexField
    private String tags; 

    @IndexField
    private String title;
    @IndexField(searchAnalyzer = "ik_smart")
    private String content;
    @IndexField(isSearch = false)
    private String firstPicture;
    @IndexField
    private String description;

    @IndexField
    private Integer views; // 瀏覽量
    @IndexField
    @TableField(value = "`like`")
    private Integer like; // 瀏覽量

    @IndexField
    private Date createTime;

}
  • @Index

該注解用于物體類上面,被注解標記的物體類在springboot運行時會自動判斷你的服務器有沒有對應的索引,如果沒有會創建,有的話會update,稍后說,

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Index {
String value() default ""; // 索引名
}
  • @IndexId

    該注解用于屬性上,被該注解標記的物體類在創建index時或者資料自動增刪改時會將其當作es的id,什么型別無所謂,型別會自動轉成string,

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IndexId {
}
  • @IndxField

    該注解用于屬性上,被該注解標記的物體類顧名思義就是index里面的屬性,可以自定義搜索方式分詞方式指定型別指定名稱,基礎型別可以不寫,其他的,比如String型別可以有text或者keyword,默認text,

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IndexField {
String value() default ""; // 欄位名,默認為屬性名

String type() default ""; // 欄位型別,如text等,可以不設定,會默認根據java型別來轉換

String analyzer() default "ik_max_word"; // 在型別為text時生效

String searchAnalyzer() default "ik_max_word"; // 在型別為text時生效

/**
* 默認為true參與搜索,
* false時不參與搜索,只作為內容傳遞,但在指定should和must搜索下仍然能被搜索
*/
boolean isSearch() default true;
}

2. 增刪改統一方法

? 如下,比如在ElasticSearchService類中,使用

@Autowired
private ArticleMapper articleMapper;

@Autowired
private ElasticSearchService elasticSearchService;

public int releaseArticle(Article article, User user) {
    article.setUserId(user.getId()).setUserAvatar(user.getAvatar());
    int insert = articleMapper.insert(article);
    elasticSearchService.insert(article);
    // elasticSearchService.update(article);
    return insert;
}

眾所周知,因為mybatis的特性,當你插入一個資料時,如果你想這個id由資料庫生成,然后在你插入返還id給你,你得重寫從物件里面取出來,比如:

articleMapper.insert(article);

int id = article.getId();

看上面,只需要一行代碼就能實作插入,使用邏輯和mybatis-plus基本一致

當然,你可以不寫,借助mybatis-plus給你的攔截器,你可以在所有mapper呼叫insert方法之后去攔截她,然后在攔截器中寫上這個方法,因為所有的類使用都是一個模樣elasticSearchService.insert(object);

  • 攔截器如下
@Slf4j
@Intercepts(@Signature(type = Executor.class, method = "update", args = {MappedStatement.class,
        Object.class}))
public class ESAop implements Interceptor {

    @Autowired
    private ElasticSearchService elasticSearchService;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object proceed = invocation.proceed(); // 這個就是mapper的方法執行
        fun(mappedStatement.getId(), invocation.getArgs()[1]);
        return proceed;
    }

    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }

    public void setProperties(Properties properties) {
    }

    @SneakyThrows
    private void fun(String method, Object parameter) {
        int i = method.lastIndexOf(".");
        String className = method.substring(0, i);
        String methodName = method.substring(i + 1);

        ParameterizedType parameterizedType =
                (ParameterizedType) Class.forName(className)
                        .getGenericInterfaces()[0];

        Class<?> clz = (Class<?>) parameterizedType.getActualTypeArguments()[0];
        if (!elasticSearchService.isIndex(clz)) {
            return;
        }

        switch (methodName) { // 在這里添加自定義方法,如果不熟悉,可以打個斷點
            case "insert": {
                if (clz == parameter.getClass()) {
                    elasticSearchService.insert(parameter);
                }
                break;
            }
            case "updateById":
                Object o = ((MapperMethod.ParamMap) parameter).get("param1");
                if (clz == o.getClass()) {
                    elasticSearchService.insert(parameter);
                }
                elasticSearchService.insert(o);
                break;
            case "deleteById": {
                elasticSearchService.delete(clz.getName(), parameter + "");
                break;
            }
        }
    }
}

自定義的mapper方法需要攔截,你需要動動你的小手自己加上去,,,

  • 再來看看insert這個方法,其他方法大同小異
/**
 * 請在save之后呼叫此方法,否則獲得不到id,這樣在插入到es服務器中,id是隨機的,
 */
@SneakyThrows
public <E> int insert(E o) {
    log.info(" elastic service insert {}", o);
    Meta meta = new Meta(o);

    IndexRequest request = new IndexRequest(meta.getIndexName())
            .id(meta.getIndexId())
            .timeout("3s")
            .source(meta.getSource());
    return client.index(request, RequestOptions.DEFAULT).status().getStatus();
}

很明顯,引數是不是固定的,所以可以實作插入任意的物體類,這個service有點長,在最后貼出來,

Meta meta = new Meta(o); // 這一行代碼就是根據之前的一些注解來包裝成一個物件,

還有一些其他的如update delete啥的放在最后了,或者去gitee上下載下來,https://gitee.com/iozxc/smart-es

3. 搜索查詢

搜索就不看service的代碼了,直接看controller如何呼叫的

@RestController
@RequestMapping
@Slf4j
public class SearchController {

    @Autowired
    private ElasticSearchService elasticSearchService;

    @GetMapping(value = {"/search", "/s"}) 
    public Result search(@RequestParam(value = "kw", required = false) String keyword,
                         @RequestParam(required = false) Integer pageNo,
                         @RequestParam(required = false) Integer pageSize,
                         @RequestParam(required = false) String type,
                         @RequestParam(required = false) String index,
                         @RequestParam(required = false) String must,
                         @RequestParam(required = false) String should,
                         @RequestParam(value = "highlight", required = false, defaultValue = "true") Boolean isHighlight) {
        if (keyword == null) {
            return new Result(ResultCode.SUCCESS);
        }
        HashMap<String, List<Object>> search = elasticSearchService.search(keyword, pageNo, pageSize, type, index, must, should, isHighlight);
        return new Result(ResultCode.SUCCESS, search);
    }


    @GetMapping(value = {"/list", "/l"})
    public Result list(@RequestParam(value = "i", required = false) String index,
                       @RequestParam(required = false) Integer pageNo,
                       @RequestParam(required = false) Integer pageSize,
                       @RequestParam(required = false) String sortName,
                       @RequestParam(required = false) String order) {
        if (index == null) {
            return new Result(ResultCode.SUCCESS);
        }
        HashMap<String, List<Object>> list = elasticSearchService.list(pageNo, pageSize, index, sortName, order);
        return new Result(ResultCode.SUCCESS, list);
    }


}
  • search方法,根據關鍵字,在指定的index里面搜索,支持分頁,可以指定搜索的欄位,以及是must還是should,同時是否要高亮顯示,
public HashMap<String, List<Object>> search(String keyword, 
Integer pageNo,
Integer pageSize,
String type,
String index,
String must,
String should,
Boolean isHighlight){...}

index 這個欄位可以寫成如 user,blog,comment 這種格式用逗號隔開,must,should同理,

type 為只有在must和should都為null時才生效,默認把指定index的指定型別的所有內容全部查詢出來,

  • list 方法,得到一個排序的集合,支持分頁,
public HashMap<String, List<Object>> list(Integer pageNo,
Integer pageSize,
String index,
String sortName,
String order) 

index 指定的indx名,用法和search一樣,可以多個,

sortName 排序的欄位名,可以為空,使用默認排序

order 排序方式 ASC 或者 DESC

以上兩個方法的回傳值格式如圖

{
  "article": [
            {
                "createTime": "xx-xx-xx xx:xx:xx",
                "like": 123123,
                "userAvatar": "",
                "description": "nihaoa ",
                "id": "21",
                "content": "zxc",
                "views": 999,
                "tags": "<span class=\"highlight\">love</span>"
            },
            {
                "createTime": "xx-xx-xx xx:xx:xx",
                "like": 123123,
                "userAvatar": "",
                "description": "nihaoa ",
                "id": "29",
                "content": "zxc",
                "views": 999,
                "tags": "<span class=\"highlight\">love</span>"
            },
        ],
  "user": [{
    ...
  }],
  "xxx": [{
    ...
  }]
}
  1. 最后貼上其他所有的代碼,還有一些工具類,就不貼出來了,

ESConfig

@Configuration
@ConfigurationProperties(
        prefix = "smart-es"
)
@Data
public class ESConfig {

    private ClientConfig clientConfig;

    private ServiceConfig serviceConfig;

    @Data
    public static class ClientConfig {
        private String hostname;
        private Integer port;
        private String scheme;
        private String username;
        private String password;
    }

    @Data
    public static class ServiceConfig {
        private String pkg = "cn.omisheep.spook.entity"; // 物體類掃描的包名
        private String preTags = "<span class=\"highlight\">"; // 高亮顯示的前綴
        private String postTags = "</span>"; // 高亮顯示的后綴
        private int defaultPageSize = 10;
    }

}

ElasticSearchClient

@Configuration
@Data
public class ElasticSearchClient {

    private final ESConfig.ClientConfig config;

    public ElasticSearchClient(ESConfig esConfig) {
        this.config = esConfig.getClientConfig();
    }

    @Bean
    public RestHighLevelClient restHighLevelClient() {

        HttpHost httpHost = new HttpHost(config.getHostname(), config.getPort(), config.getScheme());
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(config.getUsername(), config.getPassword()));

        RestClientBuilder builder = RestClient.builder(httpHost)
                .setRequestConfigCallback(requestConfigBuilder -> {
                    requestConfigBuilder.setConnectTimeout(-1);
                    requestConfigBuilder.setSocketTimeout(-1);
                    requestConfigBuilder.setConnectionRequestTimeout(-1);
                    return requestConfigBuilder;
                }).setHttpClientConfigCallback(httpClientBuilder -> {
                    httpClientBuilder.disableAuthCaching();
                    return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                });

        return new RestHighLevelClient(builder);
    }
}

ElasticSearchService

@Service
@Slf4j
public class ElasticSearchService {


    private final ESConfig.ServiceConfig config;
    /**
     * 系統屬性
     */
    private final RestHighLevelClient client;
    /**
     * 時間型別
     */
    private final String dateFormat = "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"; // 搜索時的空欄位
    private SimpleDateFormat simpleDateFormat;
    /**
     * 型別轉換
     */
    private static final String[] javaTypes = {"String", "Integer", "Double", "Float", "Boolean", "Date", "*"};
    private static final String[] esTypes = {"text", "integer", "double", "float", "boolean", "date", "unknown"};
    /**
     * className ==> indexName
     */
    private static final Map<String, String> indexMap = new HashMap<>();
    /**
     * indexName ==> indexId
     */
    private static final Map<String, Field> indexIdMap = new HashMap<>();
    /**
     * indexName ==> filedMap
     */
    private static final Map<String, Map<String, Field>> indexFiledMap = new HashMap<>();
    /**
     * 所有的index
     */
    private static String[] allIndex;
    /**
     * indexName =>   {text=>[]}
     */
    private static final Map<String, Map<String, List<String>>> typeFieldMix = new HashMap<>();

    public ElasticSearchService(@Qualifier("restHighLevelClient") RestHighLevelClient client,
                                ESConfig esConfig) {
        this.client = client;
        this.config = esConfig.getServiceConfig();
        initIndex();
        log.info("init ElasticSearchService successfully");
    }

    private String parseType(Field field) {
        IndexField indexField = field.getAnnotation(IndexField.class);
        if (indexField.type().equals("")) {
            for (int i = 0; i < javaTypes.length; i++) {
                if (javaTypes[i].equals(field.getType().getSimpleName())) {
                    return esTypes[i];
                }
            }
        } else {
            return indexField.type();
        }
        return "unknown";
    }

    private HashMap<String, Object> parseClassToMapping(Class<?> clz) throws Exception {
        Map<String, Field> fieldMap = indexFiledMap.get(indexMap.get(clz.getName()));
        if (fieldMap == null) return null;
        HashMap<String, Object> createIndexMap = new HashMap<>();
        HashMap<String, Object> properties = new HashMap<>();

        for (Map.Entry<String, Field> entry : fieldMap.entrySet()) {
            HashMap<String, String> info = new HashMap<>();
            Field field = entry.getValue();
            IndexField indexField = field.getAnnotation(IndexField.class);
            String type = parseType(field);
            info.put("type", type);
            if (type.equals("unknown")) {
                throw new Exception("elasticsearch型別例外");
            } else if (type.equals("text")) {
                info.put("analyzer", indexField.analyzer());
                info.put("search_analyzer", indexField.searchAnalyzer());
            } else if (type.equals("date")) {
                info.put("format", dateFormat);
            }
            properties.put(entry.getKey(), info);
        }
        createIndexMap.put("properties", properties);
        return createIndexMap;
    }

    public boolean isIndex(Class<?> clz) {
        Index index = clz.getDeclaredAnnotation(Index.class);
        return index != null;
    }

    @SneakyThrows
    public boolean createIndex(Class<?> clz) {
        if (!isIndex(clz)) {
            log.error("create index error , class: {}  . require a annotation @Index", clz.getName());
            return false;
        }
        HashMap<String, Object> createIndexMap = parseClassToMapping(clz);

        String indexName = indexMap.get(clz.getName());
        log.debug("createIndexMap . index: {} class: {} =>  {}", indexName, clz.getName(), createIndexMap);

        CreateIndexRequest request = new CreateIndexRequest(indexName)
                .mapping(createIndexMap);
        boolean acknowledged = client.indices()
                .create(request, RequestOptions.DEFAULT).isAcknowledged();
        if (acknowledged) {
            log.info("create index {} is successfully", indexName);
        } else {
            log.error("create index {} is fail", indexName);
        }
        return acknowledged;
    }

    public boolean isExistIndex(Class<?> clz) throws IOException {
        GetIndexRequest request = new GetIndexRequest(indexMap.get(clz.getName()));
        return client.indices().exists(request, RequestOptions.DEFAULT);
    }

    public boolean isAllExistIndex() throws IOException {
        GetIndexRequest request = new GetIndexRequest(
                indexMap.keySet().toArray(ConstantPool.EMPTY_STRINGS)
        );
        return client.indices().exists(request, RequestOptions.DEFAULT);
    }

    /**
     * 只能增刪mapping,不能修改
     *
     * @param clz 對應的index物體類
     */
    public boolean updateIndex(Class<?> clz) throws Exception {
        if (!isIndex(clz)) {
            throw new Exception("不是index");
        }
        PutMappingRequest request = new PutMappingRequest(indexMap.get(clz.getName()));

        HashMap<String, Object> mapping = parseClassToMapping(clz);
        request.source(mapping);
        log.info("{} try update index => {}", indexMap.get(clz.getName()), mapping);

        try {
            return client.indices().putMapping(request, RequestOptions.DEFAULT).isAcknowledged();
        } catch (IOException e) {
            log.error("不能修改型別,請洗掉索引重新添加資料");
            throw e;
        }
    }

    /**
     * 請在save之后呼叫此方法
     */
    @SneakyThrows
    public <E> int insert(E o) {
        log.info(" elastic service insert {}", o);
        Meta meta = new Meta(o);

        IndexRequest request = new IndexRequest(meta.getIndexName())
                .id(meta.getIndexId())
                .timeout("3s")
                .source(meta.getSource());
        return client.index(request, RequestOptions.DEFAULT).status().getStatus();
    }

    @SneakyThrows
    public <E> boolean insert(List<E> list) {
        log.info(" elastic service insert list: {}", list);
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("10s");
        for (E o : list) {
            Meta meta = new Meta(o);

            bulkRequest.add(
                    new IndexRequest(meta.getIndexName())
                            .id(meta.getIndexId())
                            .source(meta.getSource())
            );
        }
        BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        log.info("insert a list => {}", list);
        return bulk.hasFailures();
    }

    @SneakyThrows
    public <E> int update(E o) {
        log.info(" elastic service update ");
        Meta meta = new Meta(o);

        UpdateRequest request = new UpdateRequest(
                meta.getIndexName(), meta.getIndexId())
                .doc(meta.getSource());

        return client.update(request, RequestOptions.DEFAULT).status().getStatus();
    }

    @SneakyThrows
    public <E> boolean update(List<E> list) {
        log.info(" elastic service update list {}", list);
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("10s");

        for (E o : list) {
            Meta meta = new Meta(o);

            bulkRequest.add(
                    new UpdateRequest(
                            meta.getIndexName(), meta.getIndexId())
                            .doc(meta.getSource())
            );
        }
        BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        return bulk.hasFailures();
    }

    @SneakyThrows
    public int delete(String clzName, String id) {
        log.info(" elastic service delete ");
        DeleteRequest request = new DeleteRequest(
                indexMap.get(clzName), id)
                .timeout("2s");

        return client.delete(request, RequestOptions.DEFAULT).status().getStatus();
    }

    @SneakyThrows
    public boolean delete(String clzName, List<String> list) {
        log.info(" elastic service delete ");
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("10s");
        String indexName = indexMap.get(clzName);
        for (String id : list) {
            bulkRequest.add(
                    new DeleteRequest(indexName, id).timeout("2s")
            );
        }
        BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        return bulk.hasFailures();
    }

    private String[] sp(String str) {
        if (str == null) {
            return ConstantPool.EMPTY_STRINGS;
        } else {
            return str.split(",");
        }
    }

    /**
     * 該搜索搜索
     *
     * @param pageNo   開始頁碼
     * @param pageSize 頁大小
     * @param keyword  不為空  !!!must!!!
     * @param index    索引串列
     * @param must     欄位串列:必須匹配
     * @param should   欄位串列:應匹配
     * @param type     默認為 text
     * @return HashMap<String, List < Object>> indexName -> 該index下的所有的搜索結果
     */
    public HashMap<String, List<Object>> search(String keyword, Integer pageNo, Integer pageSize, String type,
                                                String index, String must, String should, Boolean isHighlight) {
        return searchBuild(
                keyword,
                pageNo == null ? 0 : pageNo,
                pageSize == null ? config.getDefaultPageSize() : pageSize,
                type == null ? "text" : type,
                sp(index),
                sp(must),
                sp(should),
                isHighlight
        );
    }

    @SneakyThrows
    @SuppressWarnings("all")
    private HashMap<String, List<Object>> searchBuild(String keyword, int pageNo, int pageSize, String type,
                                                      String[] indices, String[] must, String[] should, Boolean isHighlight) {
        log.info("pageNo: {} | pageSize: {} | keyword: {} | indices: {} | must: {} | should: {} | type: {}",
                pageNo, pageSize, keyword, indices, must, should, type);
        if (keyword == null || keyword.trim().equals("")) return ConstantPool.EMPTY_HASH_MAP;

        if (indices.length == 0) {
            indices = allIndex;
        } else {
            indices = Arrays.stream(indices)
                    .filter(v1 -> Arrays.asList(allIndex).contains(v1))
                    .toArray(String[]::new);
            if (indices.length == 0) {
                return ConstantPool.EMPTY_HASH_MAP;
            }
        }

        SearchRequest request = new SearchRequest(indices);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        BoolQueryBuilder query = QueryBuilders.boolQuery();


        if (0 == must.length && 0 == should.length) {
            HashSet<String> set = new HashSet<>();
            for (String index : indices) {
                typeFieldMix.get(index).get(type).forEach(name -> {
                    set.add(name);
                });
            }
            set.forEach(name -> {
                query.should(QueryBuilders.matchQuery(name, keyword));
                highlightBuilder.field(name);
            });
        } else {
            for (String name : must) {
                query.must(QueryBuilders.matchQuery(name, keyword));
                highlightBuilder.field(name);
            }
            for (String name : should) {
                query.should(QueryBuilders.matchQuery(name, keyword));
                highlightBuilder.field(name);
            }
        }

        if (isHighlight != null && isHighlight) {
            highlightBuilder.preTags(config.getPreTags());
            highlightBuilder.postTags(config.getPostTags());
            searchSourceBuilder.highlighter(highlightBuilder);
        }

        SearchRequest searchRequest = request.source(
                searchSourceBuilder
                        .query(query)
                        .from(pageNo)
                        .size(pageSize)
                        .timeout(new TimeValue(60, TimeUnit.SECONDS))
        );

        // 并且回傳格式化資料,score排序
        SearchHit[] hits = client.search(searchRequest, RequestOptions.DEFAULT).getHits().getHits();

        HashMap<String, List<Object>> dataMap = new HashMap<>();

        for (String index : indices) {
            dataMap.put(index, new ArrayList<>());
        }

        if (isHighlight != null && isHighlight) {
            for (SearchHit hit : hits) {
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                for (Map.Entry<String, HighlightField> entry : hit.getHighlightFields().entrySet()) {
                    sourceAsMap.put(entry.getKey(), entry.getValue().fragments()[0].toString());
                }
                sourceAsMap.put("id", hit.getId());
                dataMap.get(hit.getIndex()).add(sourceAsMap);
            }
        } else {
            for (SearchHit hit : hits) {
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                sourceAsMap.put("id", hit.getId());
                dataMap.get(hit.getIndex()).add(sourceAsMap);
            }
        }

        dataMap.keySet().removeIf(key -> dataMap.get(key).size() == 0);
        return dataMap;
    }


    public HashMap<String, List<Object>> list(Integer pageNo, Integer pageSize, String index, String sortName, String order) {
        return listBuild(
                pageNo == null ? 0 : pageNo,
                pageSize == null ? config.getDefaultPageSize() : pageSize,
                sp(index),
                sortName,
                "asc".equalsIgnoreCase(order) ? SortOrder.ASC : SortOrder.DESC
        );
    }

    @SneakyThrows
    @SuppressWarnings("all")
    private HashMap<String, List<Object>> listBuild(int pageNo, int pageSize, String[] indices, String sortName, SortOrder order) {
        log.info("list  -> pageNo: {} | pageSize: {} | indices: {}", pageNo, pageSize, indices);

        if (indices.length == 0) {
            indices = allIndex;
        } else {
            indices = Arrays.stream(indices)
                    .filter(v1 -> Arrays.asList(allIndex).contains(v1))
                    .toArray(String[]::new);
            if (indices.length == 0) {
                return ConstantPool.EMPTY_HASH_MAP;
            }
        }

        SearchRequest request = new SearchRequest(indices);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        if (sortName != null) {
            if (order != null) {
                searchSourceBuilder.sort(sortName, order);
            } else {
                searchSourceBuilder.sort(sortName, SortOrder.DESC);
            }
        }

        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();

        SearchRequest searchRequest = request.source(
                searchSourceBuilder
                        .query(matchAllQueryBuilder)
                        .from(pageNo)
                        .size(pageSize)
                        .timeout(new TimeValue(60, TimeUnit.SECONDS))
        );


        // 增加高亮,并且回傳格式化資料,score排序
        SearchHit[] hits = client.search(searchRequest, RequestOptions.DEFAULT).getHits().getHits();

        HashMap<String, List<Object>> dataMap = new HashMap<>();

        for (String index : indices) {
            dataMap.put(index, new ArrayList<>());
        }

        for (SearchHit hit : hits) {
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            sourceAsMap.put("id", hit.getId());
            dataMap.get(hit.getIndex()).add(sourceAsMap);
        }

        dataMap.keySet().removeIf(key -> dataMap.get(key).size() == 0);
        return dataMap;
    }

    @SneakyThrows
    private void initIndex() {
        Set<Class<?>> classSet = ClassUtil.getClassSet(config.getPkg()); // 獲得該包下的所有類

        for (Class<?> aClass : classSet) {
            Index index = aClass.getDeclaredAnnotation(Index.class);
            if (index != null) { // 設定indexName
                String indexName = "";
                if (!index.value().equals("")) {
                    indexName = index.value();
                } else {
                    indexName = humpToLine(aClass.getSimpleName());
                }
                indexMap.put(aClass.getName(), indexName);


                HashMap<String, List<String>> typeListMap = new HashMap<>();
                for (String s : esTypes) {
                    typeListMap.put(s, new ArrayList<>());
                }

                HashMap<String, Field> map = new HashMap<>();

                Field[] declaredFields = aClass.getDeclaredFields();
                for (Field field : declaredFields) {
                    if (field.isAnnotationPresent(IndexId.class)) {
                        field.setAccessible(true);
                        indexIdMap.put(indexName, field);
                        continue;
                    }

                    IndexField indexField = field.getAnnotation(IndexField.class);
                    if (indexField != null) {
                        String fieldName = "";
                        field.setAccessible(true);
                        if (!indexField.value().equals("")) {
                            fieldName = indexField.value();
                        } else {
                            fieldName = humpToLine(field.getName());
                        }
                        map.put(fieldName, field);

                        if (indexField.isSearch()) {
                            typeListMap.get(parseType(field)).add(fieldName);
                        }
                    }
                }

                typeFieldMix.put(indexName, typeListMap);
                indexFiledMap.put(indexName, map);

                // 判斷是否存在
                if (!isExistIndex(aClass)) {
                    createIndex(aClass);
                    log.info("auto createIndex {} in ElasticSearchService", indexName);
                } else {
                    updateIndex(aClass);
                    log.info("auto updateIndex {} in ElasticSearchService", indexName);
                }
            }
        }

        String[] split = dateFormat.split("\\|\\|");
        simpleDateFormat = new SimpleDateFormat(split[0]);
        allIndex = indexFiledMap.keySet().toArray(ConstantPool.EMPTY_STRINGS);

        log.debug("initIndexMap=>{}", indexMap);
        log.debug("initIndexIdMap=>{}", indexIdMap);
        log.debug("initIndexFiledMap=>{}", indexFiledMap);
        log.debug("initTypeFieldMix=>{}", typeFieldMix);
    }

    @Data
    private class Meta {
        private String indexName;
        private String indexId;
        private final Map<String, Object> source = new HashMap<>();

        @SneakyThrows
        public Meta(Object o) {
            String className = o.getClass().getName();
            indexName = indexMap.get(className);
            Map<String, Field> fieldMap = indexFiledMap.get(indexName);
            Field indexIdField = indexIdMap.get(indexName);
            if (indexIdField != null) {
                indexId = indexIdField.get(o) == null ? "" : indexIdField.get(o) + "";
            } else {
                indexId = "";
            }

            for (Map.Entry<String, Field> entry : fieldMap.entrySet()) {
                Field field = entry.getValue();
                Object value = field.get(o);
                if (value == null) continue;

                if (value.getClass().getTypeName().equals("java.util.Date")) {
                    value = simpleDateFormat.format(value);
                }

                IndexField indexField = field.getAnnotation(IndexField.class);
                if (indexField.value().equals("")) {
                    source.put(field.getName(), value);
                } else { // 如果有別名,則設定為別名
                    source.put(indexField.value(), value);
                }
            }
        }
    }
  
  	public static String humpToLine(String str) {
        Matcher matcher = humpPattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        if (sb.charAt(0) == '_') {
            return sb.substring(1);
        }
        return sb.toString();
    }

    /**
     * 用于測驗初始化,初始化時會生成所有的索引
     */
    public void runInit() {
//        initIndex();
    }
}

代碼有點長,你們忍一下,,,

最后附上地址 https://gitee.com/iozxc/smart-es

另外另一個專案,一個輕量級的權限專案,

Au權限框架 是基于Springboot的一款權限認證框架,整合了RSA、JWT,使用了RBAC模式,省去了很多繁瑣的配置、使用非常簡單,

https://blog.csdn.net/qq_42883292/article/details/119686717
https://gitee.com/iozxc/au

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/294156.html

標籤:其他

上一篇:ELK日志分析系統(未完...)

下一篇:史上最全 RabbitMQ入門教程,從零開始帶你深入♂學習(九)——Routing 之訂閱模型-Direct(直連)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more