# xpass-search **Repository Path**: sxran/xpass-search ## Basic Information - **Project Name**: xpass-search - **Description**: 通用的搜索模块 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-10-21 - **Last Updated**: 2025-11-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 芋道搜索模块 (yudao-module-search) ## 📖 项目介绍 芋道搜索模块是一个通用的搜索服务模块,基于芋道微服务架构设计,提供统一的搜索API接口,支持多种搜索引擎的适配。通过适配器模式,可以轻松切换不同的搜索引擎实现,如 ZincSearch、Elasticsearch、OpenSearch、MeiliSearch 等。 ## ✨ 核心特性 - **🔌 多引擎适配**: 支持多种搜索引擎,通过配置即可切换 - **🚀 高性能**: 基于 Spring Boot 3.x + WebFlux 异步架构 - **📊 完整功能**: 支持索引管理、文档管理、搜索查询、批量操作等 - **🏢 多租户隔离**: 租户级别的索引物理隔离,确保数据完全隔离 - **🌍 星球过滤**: 同租户内通过星球ID字段实现灵活的数据过滤 - **🔒 安全可靠**: 集成芋道安全框架,支持租户和星球权限控制 - **📝 API 友好**: 提供 RPC 接口和 HTTP REST API - **🧪 测试完备**: 完整的单元测试覆盖 - **📚 文档齐全**: 详细的 API 文档和使用说明 ## 🏗️ 架构设计 ``` ┌─────────────────────────────────────────────────────────┐ │ HTTP REST API │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 管理后台API │ │ 用户端API │ │ RPC API │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ 权限验证层 │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ TenantPermissionInterceptor │ │ │ │ (租户权限验证 + 星球权限验证) │ │ │ └─────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ Service 业务层 │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ TenantSearchService │ │ │ │ (租户索引隔离 + 星球字段过滤) │ │ │ └─────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ SearchEngine 适配层 │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ ZincSearch │ │Elasticsearch│ │ OpenSearch │ ... │ │ │ Adapter │ │ Adapter │ │ Adapter │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ └─────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────┐ │ 搜索引擎实例 │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ ZincSearch │ │Elasticsearch│ │ OpenSearch │ ... │ │ │ Server │ │ Cluster │ │ Cluster │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ └─────────────────────────────────────────────────────────┘ 多租户索引隔离示例: ┌─────────────────────────────────────────────────────────┐ │ 租户索引隔离 │ │ 租户001: tenant_001_blog, tenant_001_user │ │ 租户002: tenant_002_blog, tenant_002_user │ │ 租户003: tenant_003_blog, tenant_003_user │ └─────────────────────────────────────────────────────────┘ 星球字段过滤示例: ┌─────────────────────────────────────────────────────────┐ │ 星球字段过滤 │ │ tenant_001_blog: │ │ - 文档1: {title: "标题1", planet_id: 1001} │ │ - 文档2: {title: "标题2", planet_id: 1002} │ │ - 文档3: {title: "标题3", planet_id: 1001} │ │ 查询星球1001: 返回文档1、文档3 │ │ 查询星球1002: 返回文档2 │ └─────────────────────────────────────────────────────────┘ ``` ## 🚀 快速开始 ### 1. 环境要求 - JDK 17+ - Spring Boot 3.5.4+ - Maven 3.6+ - ZincSearch 0.4.9+ (默认搜索引擎) ### 2. 启动 ZincSearch 使用 Docker 快速启动 ZincSearch: ```bash docker run -d \ --name zincsearch \ -p 4080:4080 \ -e ZINC_FIRST_ADMIN_USER=admin \ -e ZINC_FIRST_ADMIN_PASSWORD=admin \ -v zinc_data:/data \ public.ecr.aws/zinclabs/zincsearch:latest ``` ### 3. 配置搜索模块 在 `application.yml` 中添加搜索配置: ```yaml yudao: search: enabled: true type: zincsearch zincsearch: url: http://localhost:4080 username: admin password: admin connect-timeout: 5s read-timeout: 30s ``` ### 4. 添加依赖 在需要使用搜索功能的模块中添加依赖: ```xml cn.iocoder.cloud yudao-module-search-api ${revision} ``` ### 5. 使用搜索服务 #### 5.1 通过 RPC 调用 ```java @Resource private SearchApi searchApi; // 创建索引(租户隔离) SearchIndexReqDTO indexReq = new SearchIndexReqDTO(); indexReq.setTenantId(1L); // 租户ID indexReq.setIndexName("user_index"); indexReq.setMapping(Map.of("properties", Map.of( "name", Map.of("type", "text"), "age", Map.of("type", "integer"), "planet_id", Map.of("type", "long") // 星球ID字段 ))); CommonResult result = searchApi.createIndex(indexReq); // 创建文档(租户+星球隔离) SearchDocumentReqDTO docReq = new SearchDocumentReqDTO(); docReq.setTenantId(1L); // 租户ID docReq.setPlanetId(1001L); // 星球ID docReq.setIndexName("user_index"); docReq.setDocument(Map.of("name", "张三", "age", 25)); CommonResult docResult = searchApi.createDocument(docReq); // 搜索查询(支持多星球查询) SearchQueryReqDTO queryReq = new SearchQueryReqDTO(); queryReq.setTenantId(1L); // 租户ID queryReq.setPlanetIds(List.of(1001L, 1002L)); // 星球ID列表 queryReq.setIndexName("user_index"); queryReq.setQuery("张三"); queryReq.setPageNo(1); queryReq.setPageSize(10); CommonResult searchResult = searchApi.search(queryReq); ``` #### 5.2 通过 HTTP API 调用 ```bash # 创建索引(租户隔离) curl -X POST "http://localhost:8080/admin-api/search/index/create" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{ "tenantId": 1, "indexName": "user_index", "mapping": { "properties": { "name": {"type": "text"}, "age": {"type": "integer"}, "planet_id": {"type": "long"} } } }' # 创建文档(租户+星球隔离) curl -X POST "http://localhost:8080/admin-api/search/document/create" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{ "tenantId": 1, "planetId": 1001, "indexName": "user_index", "document": { "name": "张三", "age": 25 } }' # 搜索查询(支持多星球查询) curl -X POST "http://localhost:8080/admin-api/search/query" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{ "tenantId": 1, "planetIds": [1001, 1002], "indexName": "user_index", "query": "张三", "pageNo": 1, "pageSize": 10 }' # 获取租户索引列表 curl -X GET "http://localhost:8080/admin-api/search/index/list?tenantId=1" \ -H "Authorization: Bearer YOUR_TOKEN" # 删除租户索引 curl -X DELETE "http://localhost:8080/admin-api/search/index/delete?tenantId=1&indexName=user_index" \ -H "Authorization: Bearer YOUR_TOKEN" ``` ## 📋 API 接口 ### 索引管理 | 接口 | 方法 | 路径 | 描述 | |------|------|------|------| | 创建索引 | POST | `/search/index/create` | 创建新的搜索索引 | | 删除索引 | DELETE | `/search/index/delete` | 删除指定索引 | | 检查索引 | GET | `/search/index/exists` | 检查索引是否存在 | | 获取索引 | GET | `/search/index/get` | 获取索引详细信息 | | 索引列表 | GET | `/search/index/list` | 获取所有索引列表 | ### 文档管理 | 接口 | 方法 | 路径 | 描述 | |------|------|------|------| | 创建文档 | POST | `/search/document/create` | 创建新文档 | | 更新文档 | PUT | `/search/document/update` | 更新现有文档 | | 删除文档 | DELETE | `/search/document/delete` | 删除指定文档 | | 获取文档 | GET | `/search/document/get` | 获取文档详情 | | 批量操作 | POST | `/search/document/bulk` | 批量操作文档 | ### 搜索查询 | 接口 | 方法 | 路径 | 描述 | |------|------|------|------| | 搜索查询 | POST | `/search/query` | 执行搜索查询 | ## 🏢 多租户和星球隔离 ### 多租户索引隔离 本搜索模块实现了严格的多租户索引物理隔离机制: #### 索引命名策略 - **格式**: `tenant_{tenantId}_{originalIndexName}` - **示例**: - 租户001的blog索引: `tenant_001_blog` - 租户002的user索引: `tenant_002_user` #### 隔离优势 - **物理隔离**: 每个租户的数据存储在完全独立的索引中 - **安全性**: 租户间数据无法相互访问 - **性能**: 避免了查询时的租户过滤开销 - **扩展性**: 可以为不同租户配置不同的索引策略 ### 星球字段过滤 在同一租户内部,通过 `planet_id` 字段实现星球级别的数据过滤: #### 过滤机制 - **字段名**: `planet_id` (Long类型) - **自动添加**: 创建/更新文档时自动添加星球ID字段 - **查询过滤**: 搜索时自动添加星球过滤条件 #### 使用场景 - **权限控制**: 用户只能查看有权限的星球数据 - **数据分组**: 同一租户内按星球组织数据 - **灵活查询**: 支持单星球或多星球查询 ### 权限验证 #### 租户权限 - 用户只能访问自己所属租户的数据 - 通过拦截器在请求层面进行验证 - 支持超级管理员跳过租户验证 #### 星球权限 - 用户只能访问有权限的星球数据 - 支持多星球权限配置 - 查询时自动过滤无权限的星球 #### 权限配置示例 ```java // 获取用户权限(需要根据实际业务实现) Long userTenantId = 1L; // 用户所属租户 Set userPlanetIds = Set.of(1001L, 1002L); // 用户有权限的星球 // 权限验证 boolean hasPermission = TenantPermissionValidator.validateTenantAndPlanetPermissions( requestTenantId, requestPlanetIds, userTenantId, userPlanetIds); ``` ## ⚙️ 配置说明 ### 基础配置 ```yaml yudao: search: enabled: true # 是否启用搜索功能 type: zincsearch # 搜索引擎类型: zincsearch, elasticsearch, opensearch, meilisearch ``` ### ZincSearch 配置 ```yaml yudao: search: zincsearch: url: http://localhost:4080 # ZincSearch 服务地址 username: admin # 用户名 password: admin # 密码 connect-timeout: 5s # 连接超时时间 read-timeout: 30s # 读取超时时间 ``` ### Elasticsearch 配置 ```yaml yudao: search: elasticsearch: url: http://localhost:9200 # Elasticsearch 服务地址 username: elastic # 用户名 (可选) password: password # 密码 (可选) connect-timeout: 5s # 连接超时时间 read-timeout: 30s # 读取超时时间 ``` ## 📝 使用示例 ### 多租户博客系统示例 ```java // 1. 创建租户博客索引 SearchIndexReqDTO blogIndexReq = new SearchIndexReqDTO(); blogIndexReq.setTenantId(1L); blogIndexReq.setIndexName("blog"); blogIndexReq.setMapping(Map.of("properties", Map.of( "title", Map.of("type", "text"), "content", Map.of("type", "text"), "author", Map.of("type", "keyword"), "planet_id", Map.of("type", "long"), "created_time", Map.of("type", "date") ))); searchApi.createIndex(blogIndexReq); // 2. 创建博客文档(星球1001) SearchDocumentReqDTO blogDoc1 = new SearchDocumentReqDTO(); blogDoc1.setTenantId(1L); blogDoc1.setPlanetId(1001L); blogDoc1.setIndexName("blog"); blogDoc1.setDocument(Map.of( "title", "Java开发最佳实践", "content", "本文介绍Java开发的最佳实践...", "author", "张三", "created_time", "2024-01-01T10:00:00Z" )); searchApi.createDocument(blogDoc1); // 3. 创建博客文档(星球1002) SearchDocumentReqDTO blogDoc2 = new SearchDocumentReqDTO(); blogDoc2.setTenantId(1L); blogDoc2.setPlanetId(1002L); blogDoc2.setIndexName("blog"); blogDoc2.setDocument(Map.of( "title", "Spring Boot进阶指南", "content", "深入学习Spring Boot框架...", "author", "李四", "created_time", "2024-01-02T14:30:00Z" )); searchApi.createDocument(blogDoc2); // 4. 搜索特定星球的博客 SearchQueryReqDTO queryReq = new SearchQueryReqDTO(); queryReq.setTenantId(1L); queryReq.setPlanetIds(List.of(1001L)); // 只查询星球1001 queryReq.setIndexName("blog"); queryReq.setQuery("Java"); queryReq.setPageNo(1); queryReq.setPageSize(10); SearchQueryRespDTO result = searchApi.search(queryReq).getData(); // 5. 搜索多个星球的博客 queryReq.setPlanetIds(List.of(1001L, 1002L)); // 查询多个星球 SearchQueryRespDTO multiResult = searchApi.search(queryReq).getData(); ``` ### 最佳实践 #### 1. 索引设计 ```java // 推荐的索引映射设计 Map mapping = Map.of("properties", Map.of( // 业务字段 "title", Map.of("type", "text", "analyzer", "ik_max_word"), "content", Map.of("type", "text", "analyzer", "ik_max_word"), "tags", Map.of("type", "keyword"), // 必需的系统字段 "planet_id", Map.of("type", "long"), // 星球ID(必需) "created_time", Map.of("type", "date"), // 创建时间 "updated_time", Map.of("type", "date"), // 更新时间 "status", Map.of("type", "keyword") // 状态字段 )); ``` #### 2. 权限控制 ```java // 在业务层进行权限验证 @PreAuthorize("@tenantPermissionValidator.validateTenantPermission(#reqDTO.tenantId)") public CommonResult createDocument(SearchDocumentReqDTO reqDTO) { // 业务逻辑 } // 获取用户星球权限 Set userPlanetIds = getCurrentUserPlanetIds(); reqDTO.setPlanetIds(filterAuthorizedPlanetIds(reqDTO.getPlanetIds(), userPlanetIds)); ``` #### 3. 查询优化 ```java // 使用合适的分页大小 queryReq.setPageSize(20); // 推荐10-50 // 添加排序条件 queryReq.setSorts(List.of(Map.of("created_time", "desc"))); // 使用字段过滤减少网络传输 queryReq.setFields(List.of("title", "author", "created_time")); ``` ## 🔧 扩展开发 ### 添加新的搜索引擎适配器 1. 实现 `SearchEngine` 接口: ```java @Component public class MySearchEngineImpl implements SearchEngine { @Override public Boolean createIndex(SearchIndexReqDTO reqDTO) { // 实现创建索引逻辑 return true; } // 实现其他接口方法... } ``` 2. 在 `SearchConfiguration` 中注册适配器: ```java @Bean @ConditionalOnProperty(prefix = "yudao.search", name = "type", havingValue = "mysearch") public SearchEngine mySearchEngine() { return new MySearchEngineImpl(); } ``` 3. 添加配置类: ```java @Data public static class MySearchConfig { private String url = "http://localhost:8080"; private String apiKey; // 其他配置... } ``` ## 🧪 测试 运行单元测试: ```bash mvn test ``` 运行集成测试: ```bash mvn verify ``` ## 📊 监控指标 搜索模块提供以下监控指标: - 搜索请求总数 - 搜索请求耗时 - 索引操作成功率 - 文档操作成功率 - 搜索引擎连接状态 ## 🤝 贡献指南 1. Fork 本仓库 2. 创建特性分支 (`git checkout -b feature/AmazingFeature`) 3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) 4. 推送到分支 (`git push origin feature/AmazingFeature`) 5. 打开 Pull Request ## 📄 许可证 本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。 ## 🙏 致谢 - [ZincSearch](https://github.com/zincsearch/zincsearch) - 轻量级搜索引擎 - [Elasticsearch](https://www.elastic.co/) - 分布式搜索引擎 - [Spring Boot](https://spring.io/projects/spring-boot) - Java 应用框架 - [芋道源码](https://www.iocoder.cn/) - 微服务架构 ## 📞 联系我们 如有问题或建议,请通过以下方式联系: - 提交 [Issue](https://github.com/your-repo/issues) - 发送邮件至:support@iocoder.cn - 加入微信群:YudaoSourceCode