Bboss 集成

Bboss 集成 #

Bboss 是一款高性能、高兼容性的搜索引擎 Java REST 客户端框架,基于 Apache License 2.0 开源,原生支持 Elasticsearch、Easysearch 和 Opensearch。自带客户端集群节点负载均衡和容灾,多集群多数据源,自动索引托管,多种分页机制,傻瓜级 CRUD,脚本,SQL,JDBC,高亮,权重,聚合,IP,GEO 地理位置,父子嵌套,应有尽有。

Bboss 集成方案


核心优势 #

特性说明
学习成本低无需学习额外 API,只需掌握 Elasticsearch DSL,极简使用方式
原生多引擎支持完美支持 ES 1.x ~ 9.x、Easysearch 1.x ~ 2.x+、Opensearch 1.x ~ 2.x+
开箱即用Spring Boot 自动配置,无需复杂设置
高效异步处理内置 BulkProcessor 异步批处理器,大幅提升写入性能
灵活查询方式支持 DSL、SQL、O/R Mapping 多种查询模式
多数据源支持一个应用可同时操作多个不同版本的搜索引擎集群
完整的结果封装返回结果支持 JSON、PO 对象、List 集合、Map 等多种类型
客户端负载均衡默认启用客户端负载均衡,容灾性更好

快速开始 #

1. 添加依赖 #

Maven #

根据 Spring Boot 版本选择对应的 starter:

Spring Boot 1.x / 2.x:

<dependencies>
    <!-- Bboss 核心依赖 -->
    <dependency>
        <groupId>com.bbossgroups.plugins</groupId>
        <artifactId>bboss-datatran-jdbc</artifactId>
        <version>7.5.5</version>
    </dependency>

    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>com.bbossgroups.plugins</groupId>
        <artifactId>bboss-elasticsearch-spring-boot-starter</artifactId>
        <version>7.5.5</version>
    </dependency>

    <!-- 其他依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

Spring Boot 3.x+:

<dependencies>
    <dependency>
        <groupId>com.bbossgroups.plugins</groupId>
        <artifactId>bboss-datatran-jdbc</artifactId>
        <version>7.5.5</version>
    </dependency>

    <!-- Spring Boot 3.x Starter -->
    <dependency>
        <groupId>com.bbossgroups.plugins</groupId>
        <artifactId>bboss-elasticsearch-spring-boot3-starter</artifactId>
        <version>7.5.5</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

Gradle #

plugins {
    id 'java'
    id 'org.springframework.boot' version '2.7.0'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
}

dependencies {
    implementation 'com.bbossgroups.plugins:bboss-datatran-jdbc:7.5.5'
    implementation 'com.bbossgroups.plugins:bboss-elasticsearch-spring-boot-starter:7.5.5'
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

2. 配置连接 #

application.ymlapplication.properties 中配置 Easysearch 连接信息:

YAML 格式 #

spring:
  elasticsearch:
    bboss:
      elasticsearch:
        rest:
          hostNames: localhost:9200
        # 启用 HTTPS(Easysearch 默认启用)
        useHttps: true
        # restHttp: https://
      # 认证信息
      elasticUser: admin
      elasticPassword: your_password_here

Properties 格式 #

spring.elasticsearch.bboss.elasticsearch.rest.hostNames=localhost:9200
spring.elasticsearch.bboss.elasticsearch.useHttps=true
spring.elasticsearch.bboss.elasticUser=admin
spring.elasticsearch.bboss.elasticPassword=your_password_here

配置说明

参数说明示例
elasticsearch.rest.hostNamesEasysearch 服务地址,多个逗号分隔10.20.30.40:9200,10.20.30.41:9200
elasticsearch.useHttps是否使用 HTTPS(Easysearch 默认 true)true
elasticUser用户名(可选,如启用认证)admin
elasticPassword密码(可选,如启用认证)your_password_here

3. 定义实体类 #

import org.frameworkset.elasticsearch.entity.ESDatas;

public class Document {

    /** 文档 ID,标识唯一性(对应索引中的 _id) */
    private String id;

    /** 文档标题 */
    private String title;

    /** 文档内容 - 分词字段 */
    private String content;

    /** 作者 */
    private String author;

    /** 创建时间 */
    private Long createTime;

    /** 点赞数 */
    private Integer likes;

    // Getters & Setters
    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 String getAuthor() { return author; }
    public void setAuthor(String author) { this.author = author; }

    public Long getCreateTime() { return createTime; }
    public void setCreateTime(Long createTime) { this.createTime = createTime; }

    public Integer getLikes() { return likes; }
    public void setLikes(Integer likes) { this.likes = likes; }
}

4. 创建索引并插入数据 #

import org.frameworkset.elasticsearch.boot.BBossESStarter;
import org.frameworkset.elasticsearch.client.ClientInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;

@Service
public class DocumentService {

    @Autowired
    private BBossESStarter bbossESStarter; // 注入 Bboss 客户端启动器

    /**
     * 插入单个文档
     */
    public void insertDocument() {
        // 获取不加载 DSL 配置文件的客户端(用于基础操作)
        ClientInterface clientUtil = bbossESStarter.getRestClient();

        Document doc = new Document();
        doc.setId("1");
        doc.setTitle("Easysearch 与 Bboss 集成");
        doc.setContent("这是一篇关于 Easysearch 与 Bboss ElasticsearchClient 集成的文章");
        doc.setAuthor("admin");
        doc.setCreateTime(System.currentTimeMillis());
        doc.setLikes(100);

        // 添加文档,如文档 ID 已存在则更新
        String response = clientUtil.addDocument("documents", // 索引名
                                                 doc,          // 文档对象
                                                 "refresh=true"); // 直接刷新
        System.out.println("Insert response: " + response);
    }

    /**
     * 批量插入文档
     */
    public void batchInsertDocuments() {
        ClientInterface clientUtil = bbossESStarter.getRestClient();

        // 构建文档列表
        java.util.List<Document> documents = new java.util.ArrayList<>();
        for (int i = 2; i <= 5; i++) {
            Document doc = new Document();
            doc.setId(String.valueOf(i));
            doc.setTitle("Easysearch 文章 " + i);
            doc.setContent("这是第 " + i + " 篇文章内容");
            doc.setAuthor("author" + i);
            doc.setCreateTime(System.currentTimeMillis());
            doc.setLikes(i * 10);
            documents.add(doc);
        }

        // 批量添加
        String response = clientUtil.addDocuments("documents", documents, "refresh=true");
        System.out.println("Batch insert response: " + response);
    }

    /**
     * 根据 ID 获取文档
     */
    public void getDocumentById() {
        ClientInterface clientUtil = bbossESStarter.getRestClient();

        Document doc = clientUtil.getDocument("documents", // 索引名
                                              "1",          // 文档 ID
                                              Document.class); // 返回类型
        if (doc != null) {
            System.out.println("Title: " + doc.getTitle());
            System.out.println("Content: " + doc.getContent());
        }
    }

    /**
     * 更新文档
     */
    public void updateDocument() {
        ClientInterface clientUtil = bbossESStarter.getRestClient();

        Document doc = new Document();
        doc.setId("1");
        doc.setTitle("更新标题");
        doc.setContent("更新内容");
        doc.setLikes(200);

        String response = clientUtil.addDocument("documents", doc, "refresh=true");
        System.out.println("Update response: " + response);
    }

    /**
     * 删除文档
     */
    public void deleteDocument() {
        ClientInterface clientUtil = bbossESStarter.getRestClient();

        String response = clientUtil.deleteDocument("documents", // 索引名
                                                    "1");         // 文档 ID
        System.out.println("Delete response: " + response);
    }

    /**
     * 批量删除文档
     */
    public void batchDeleteDocuments() {
        ClientInterface clientUtil = bbossESStarter.getRestClient();

        String[] ids = {"2", "3", "4"};
        String response = clientUtil.deleteDocuments("documents", ids);
        System.out.println("Batch delete response: " + response);
    }
}

5. 查询操作 #

基础查询 #

/**
 * 查询所有文档
 */
public void searchAll() {
    ClientInterface clientUtil = bbossESStarter.getRestClient();

    // 查询所有文档,默认返回 5000 条
    ESDatas<Document> esDatas = clientUtil.searchAll("documents", Document.class);

    // 获取查询结果列表
    java.util.List<Document> documents = esDatas.getDatas();
    System.out.println("Total size: " + esDatas.getTotalSize());
    System.out.println("Found documents: " + documents.size());
}

/**
 * 分页查询所有文档
 */
public void searchAllWithPagination() {
    ClientInterface clientUtil = bbossESStarter.getRestClient();

    // 使用 ScrollHandler 分页处理数据(适合大数据量)
    clientUtil.searchAll("documents", 1000, // 每页大小
        new org.frameworkset.elasticsearch.scroll.ScrollHandler<Document>() {
            public void handle(ESDatas<Document> esDatas,
                             org.frameworkset.elasticsearch.scroll.HandlerInfo handlerInfo) throws Exception {
                java.util.List<Document> dataList = esDatas.getDatas();
                System.out.println("Current batch size: " + dataList.size());
                System.out.println("Total matched: " + esDatas.getTotalSize());
                // 处理每一批数据
                for (Document doc : dataList) {
                    System.out.println("ID: " + doc.getId() + ", Title: " + doc.getTitle());
                }
            }
        }, Document.class);
}

/**
 * 并行查询所有文档
 */
public void searchAllParallel() {
    ClientInterface clientUtil = bbossESStarter.getRestClient();

    // 使用 2 个线程并行查询
    ESDatas<Document> esDatas = clientUtil.searchAllParallel("documents", Document.class, 2);
    System.out.println("Total size: " + esDatas.getTotalSize());
    System.out.println("Found documents: " + esDatas.getDatas().size());
}

/**
 * 根据字段值查询
 */
public void searchByField() {
    ClientInterface clientUtil = bbossESStarter.getRestClient();

    // 按 author 字段查询,返回分页结果
    ESDatas<Document> esDatas = clientUtil.searchListByField("documents",
                                                             "author.keyword",  // 字段名
                                                             "admin",           // 字段值
                                                             Document.class,    // 返回类型
                                                             0,                 // 分页起始位置
                                                             10);               // 每页大小

    System.out.println("Total matched: " + esDatas.getTotalSize());
    for (Document doc : esDatas.getDatas()) {
        System.out.println("ID: " + doc.getId() + ", Title: " + doc.getTitle());
    }
}

/**
 * 统计文档数量
 */
public void countDocuments() {
    ClientInterface clientUtil = bbossESStarter.getRestClient();

    long count = clientUtil.countAll("documents");
    System.out.println("Total documents: " + count);
}

DSL 查询(高级) #

首先,在 src/main/resources/esmapper 目录下创建 DSL 配置文件 document.xml

<?xml version="1.0" encoding="UTF-8"?>
<properties>
    <!-- 复杂搜索查询 -->
    <property name="searchByKeyword">
        <![CDATA[
        {
            "query": {
                "bool": {
                    "must": [
                        {"match": {"content": "#[keyword]"}}
                    ],
                    "filter": {
                        "range": {
                            "createTime": {
                                "gte": #[startTime],
                                "lte": #[endTime]
                            }
                        }
                    }
                }
            },
            "sort": [
                {"createTime": {"order": "desc"}},
                {"likes": {"order": "desc"}}
            ],
            "from": #[from:0],
            "size": #[size:20]
        }
        ]]>
    </property>

    <!-- 高亮搜索 -->
    <property name="searchWithHighlight">
        <![CDATA[
        {
            "query": {
                "match": {"content": "#[keyword]"}
            },
            "highlight": {
                "pre_tags": ["<em>"],
                "post_tags": ["</em>"],
                "fields": {
                    "content": {}
                }
            },
            "size": #[size:20]
        }
        ]]>
    </property>
</properties>

使用 DSL 配置进行查询:

/**
 * 使用 DSL 配置查询
 */
public void searchWithDSL() {
    // 创建加载 DSL 配置文件的客户端
    ClientInterface clientUtil = bbossESStarter.getConfigRestClient("esmapper/document.xml");

    // 准备查询参数
    java.util.Map<String, Object> params = new java.util.HashMap<>();
    params.put("keyword", "Easysearch");
    params.put("startTime", System.currentTimeMillis() - 30 * 24 * 3600 * 1000L); // 30 天前
    params.put("endTime", System.currentTimeMillis());
    params.put("from", 0);
    params.put("size", 20);

    // 执行查询
    ESDatas<Document> esDatas = clientUtil.searchList("documents/_search",
                                                      "searchByKeyword",  // DSL 名称
                                                      params,             // 查询参数
                                                      Document.class);    // 返回类型

    System.out.println("Total matched: " + esDatas.getTotalSize());
    for (Document doc : esDatas.getDatas()) {
        System.out.println("ID: " + doc.getId() + ", Title: " + doc.getTitle());
    }
}

/**
 * 高亮搜索
 */
public void highlightSearch() {
    ClientInterface clientUtil = bbossESStarter.getConfigRestClient("esmapper/document.xml");

    java.util.Map<String, Object> params = new java.util.HashMap<>();
    params.put("keyword", "Easysearch");
    params.put("size", 20);

    ESDatas<Document> esDatas = clientUtil.searchList("documents/_search",
                                                      "searchWithHighlight",
                                                      params,
                                                      Document.class);

    System.out.println("Found " + esDatas.getDatas().size() + " documents");
}

6. 异步批处理 #

使用 BulkProcessor 进行高效的异步批处理:

import org.frameworkset.elasticsearch.bulk.BulkProcessor;
import org.frameworkset.elasticsearch.bulk.BulkProcsJob;

/**
 * 异步批量插入文档
 */
public void asyncBatchInsert() throws Exception {
    ClientInterface clientUtil = bbossESStarter.getRestClient();

    // 创建异步批处理器
    BulkProcessor bulkProcessor = new BulkProcessor(clientUtil, 100, 5000L);

    try {
        // 插入 10000 条文档
        for (int i = 0; i < 10000; i++) {
            Document doc = new Document();
            doc.setId(String.valueOf(i));
            doc.setTitle("Document " + i);
            doc.setContent("Content for document " + i);
            doc.setAuthor("author");
            doc.setCreateTime(System.currentTimeMillis());
            doc.setLikes(i);

            // 添加到批处理器
            bulkProcessor.insertDocument("documents", doc);
        }

        // 等待所有请求完成
        bulkProcessor.shutdown();
        System.out.println("Async batch insert completed");

    } catch (Exception e) {
        e.printStackTrace();
        bulkProcessor.shutdown();
    }
}

/**
 * 监听批处理进度
 */
public void monitorBulkProcessing() throws Exception {
    ClientInterface clientUtil = bbossESStarter.getRestClient();

    BulkProcessor bulkProcessor = new BulkProcessor(clientUtil, 100, 5000L) {
        @Override
        public void complete() {
            System.out.println("All documents processed successfully");
        }

        @Override
        public void error(Exception exception) {
            System.err.println("Error during batch processing: " + exception.getMessage());
        }
    };

    try {
        for (int i = 0; i < 1000; i++) {
            Document doc = new Document();
            doc.setId(String.valueOf(i));
            doc.setTitle("Document " + i);
            doc.setContent("Content for document " + i);

            bulkProcessor.insertDocument("documents", doc);
        }

        bulkProcessor.shutdown();

    } catch (Exception e) {
        e.printStackTrace();
    }
}

与其他方案对比 #

对比项BbossEasy-ESRestClient
多引擎原生支持✓(ES/Easysearch/Opensearch)Easysearch 原生分支
学习成本低(DSL 即所学)低(Lambda API)中(需学习 API)
开箱即用✓(Spring Boot 自动配置)✓(Spring Boot 自旋配置)✗(需手动配置)
ORM 框架✗(提供 O/R Mapping)✓(完整 ORM)
异步批处理✓(BulkProcessor)✓(框架内置)✓(需自己实现)
多数据源✓(原生支持)✓(需自己实现)
代码量中等极少较多
社区活跃度中等高(Dromara 社区)官方支持

常见操作示例 #

REST API 直接操作 #

/**
 * 执行原始 HTTP 请求
 */
public void executeRawRequest() {
    ClientInterface clientUtil = bbossESStarter.getRestClient();

    // 获取集群状态
    String response = clientUtil.executeHttp("_cluster/state?pretty",
                                            ClientInterface.HTTP_GET);
    System.out.println("Cluster state: " + response);

    // 获取索引信息
    String indexInfo = clientUtil.executeHttp("documents",
                                              ClientInterface.HTTP_GET);
    System.out.println("Index info: " + indexInfo);
}

/**
 * 检查索引是否存在
 */
public void checkIndexExists() {
    ClientInterface clientUtil = bbossESStarter.getRestClient();

    boolean exists = clientUtil.existIndice("documents");
    System.out.println("Index exists: " + exists);
}

/**
 * 删除索引
 */
public void dropIndex() {
    ClientInterface clientUtil = bbossESStarter.getRestClient();

    String response = clientUtil.dropIndice("documents");
    System.out.println("Drop index response: " + response);
}

注意事项 #

注意项说明
Java 版本建议 JDK 11+,兼容 JDK 8
HTTPS 配置Easysearch 默认启用 HTTPS,配置 useHttps: true
用户认证启用认证时需配置 elasticUserelasticPassword
索引名称建议使用小写字母和数字,避免特殊字符
文档 ID需自定义或让 Easysearch 自动生成,建议自定义便于管理
刷新策略生产环境建议不使用 refresh=true,避免频繁刷新影响性能
DSL 配置DSL 文件放在 resources/esmapper 目录下,Spring Boot 会自动加载
多数据源通过 searchListWithCluster() 等带 Cluster 后缀的方法指定数据源

调试技巧 #

/**
 * 打印执行的 JSON 请求
 */
public void enableDebugMode() {
    // 通过配置启用 DSL 打印
    ClientInterface clientUtil = bbossESStarter.getConfigRestClient("esmapper/document.xml");

    // 也可在代码中直接打印返回的 JSON
    String jsonResponse = clientUtil.executeRequest("documents/_search",
                                                    "searchByKeyword",
                                                    params);
    System.out.println("Request JSON: " + jsonResponse);
}

相关链接 #


延伸阅读 #