34 Commits

Author SHA1 Message Date
jerryjzhang
593d26a072 [feature](auth)Add role to user registration.
Some checks failed
supersonic CentOS CI / build (21) (push) Has been cancelled
supersonic mac CI / build (21) (push) Has been cancelled
supersonic ubuntu CI / build (21) (push) Has been cancelled
supersonic windows CI / build (21) (push) Has been cancelled
2025-12-08 21:15:02 +08:00
jerryjzhang
9162b922c4 [opt](chat)ParseTime should span all parsers and processors.
Some checks failed
supersonic CentOS CI / build (21) (push) Has been cancelled
supersonic mac CI / build (21) (push) Has been cancelled
supersonic ubuntu CI / build (21) (push) Has been cancelled
supersonic windows CI / build (21) (push) Has been cancelled
2025-11-26 21:58:36 +08:00
jerryjzhang
1d9324f689 [opt](common)Do not use deprecated method. 2025-11-02 17:33:06 +08:00
jerryjzhang
6c5f8fce40 [opt](headless)Deletion of chat model and database should record user.
Some checks failed
supersonic CentOS CI / build (21) (push) Failing after 19s
supersonic ubuntu CI / build (21) (push) Failing after 24s
supersonic mac CI / build (21) (push) Has been cancelled
supersonic windows CI / build (21) (push) Has been cancelled
2025-10-22 19:17:30 +08:00
guilinlewis
04b1edb2e2 (improvement)(chat|common|headless|webapp) 结果分析,改写伪流式输出,加快响应速度 (#2395) 2025-10-22 15:37:50 +08:00
jerryjzhang
9857256488 [opt](headless)Model update should not delete/update metrics/dimensions, use dedicated metric/dimension management.
Some checks failed
supersonic CentOS CI / build (21) (push) Failing after 13s
supersonic ubuntu CI / build (21) (push) Failing after 16s
supersonic mac CI / build (21) (push) Has been cancelled
supersonic windows CI / build (21) (push) Has been cancelled
2025-10-22 12:03:09 +08:00
jerryjzhang
d695bed75d [feature](headless)Add request user to data events.
[feature](headless)Add request user to data events.
2025-10-22 08:43:33 +08:00
jerryjzhang
7490dabdc3 Merge remote-tracking branch 'origin/master'
Some checks failed
supersonic CentOS CI / build (21) (push) Failing after 1m45s
supersonic ubuntu CI / build (21) (push) Failing after 37s
supersonic mac CI / build (21) (push) Has been cancelled
supersonic windows CI / build (21) (push) Has been cancelled
2025-10-12 21:56:03 +08:00
jerryjzhang
fad28ee5ac [feature](chat)Support delete all memory from the agent management. 2025-10-12 21:55:49 +08:00
jerryjzhang
353c8d8b16 (opt)(headless)Fetching dimension values should use partition time field to do filtering.
Some checks failed
supersonic CentOS CI / build (21) (push) Has been cancelled
supersonic mac CI / build (21) (push) Has been cancelled
supersonic ubuntu CI / build (21) (push) Has been cancelled
supersonic windows CI / build (21) (push) Has been cancelled
2025-09-22 17:02:32 +08:00
jerryjzhang
3dd53bad89 (fix)(headless)Fix concurrent modification exception issue. 2025-09-22 15:59:35 +08:00
jerryjzhang
2d39ebf38b [opt](common)Add VALUE type enum.
Some checks failed
supersonic CentOS CI / build (21) (push) Failing after 1m11s
supersonic ubuntu CI / build (21) (push) Failing after 1h0m47s
supersonic mac CI / build (21) (push) Has been cancelled
supersonic windows CI / build (21) (push) Has been cancelled
2025-09-21 23:31:59 +08:00
superhero
6c472e1c76 【feat】release 1.0.0 CHANGELOG.md (#2356) 2025-09-21 23:24:27 +08:00
lwhy
431aa60e4d (fix)(headless)Resolve field matching failures in mixed usage of raw and semantic names (#2370) 2025-09-21 23:24:04 +08:00
lwhy
25df22758a (fix)(handless)Fix parse date number error (#2369) 2025-09-21 23:23:46 +08:00
lwhy
9af6499491 (fix)(headless)Add dimension datatype in prompt (#2371) 2025-09-21 23:23:22 +08:00
wanglongqiang
c992e57b13 Update DictWordService.java (#2385)
dictWords更新指针引用
2025-09-21 23:17:16 +08:00
Xiong Tenghui
80aaabe58b (improvement) (headless) Optimize the performance of the method BaseMatchStrategy.executeTasks() (#2363) (#2364)
Some checks failed
supersonic CentOS CI / build (21) (push) Has been cancelled
supersonic mac CI / build (21) (push) Has been cancelled
supersonic ubuntu CI / build (21) (push) Has been cancelled
supersonic windows CI / build (21) (push) Has been cancelled
2025-08-20 18:52:55 +08:00
lwhy
5a4fd2b888 (feature|common)Add parameter conversion for the LikeExpression in FieldValueReplaceVisitor (#2367) 2025-08-20 18:50:43 +08:00
ChPi
5df0b87da9 (fix)(headless) correct SQL when WHERE condition contains only column without function (#2360)
Some checks failed
supersonic CentOS CI / build (21) (push) Has been cancelled
supersonic mac CI / build (21) (push) Has been cancelled
supersonic ubuntu CI / build (21) (push) Has been cancelled
supersonic windows CI / build (21) (push) Has been cancelled
2025-08-17 18:10:30 +08:00
ChPi
ab24b1777a (fix)(common) prevent NullPointerException for jsonFormat (#2365) 2025-08-17 16:21:36 +08:00
superhero
ff76f8edbd (fix | improvement)Improve the performance of the chat page retrieve function
Some checks failed
supersonic CentOS CI / build (21) (push) Has been cancelled
supersonic mac CI / build (21) (push) Has been cancelled
supersonic ubuntu CI / build (21) (push) Has been cancelled
supersonic windows CI / build (21) (push) Has been cancelled
Merge pull request #2359 from luowanghaoyun/github_master
2025-08-15 09:22:06 +08:00
superhero
76745f38a4 Merge pull request #2362 from wangyong00/fixed-bug-npe-1
(fix)(headless)修复维度和指标信息不存在时,遍历List<NULL>数据抛出NPE异常
2025-08-15 09:15:49 +08:00
wangyong97
ce4cdb62ab (fix)(headless)修复维度和指标信息不存在时,遍历List<NULL>数据抛出NPE异常 2025-08-14 19:15:07 +08:00
luowanghaoyun
c2ce3a75b7 Improve the performance of the chat page retrieve function 2025-08-12 14:26:18 +08:00
jerryjzhang
1f6d217b26 [fix]Fix unit test cases.
Some checks failed
supersonic CentOS CI / build (21) (push) Has been cancelled
supersonic mac CI / build (21) (push) Has been cancelled
supersonic ubuntu CI / build (21) (push) Has been cancelled
supersonic windows CI / build (21) (push) Has been cancelled
2025-08-05 17:53:58 +08:00
ChPi
af28bc7c2a (improvement)(parser) Add json format to LLM request for performance improvement (#2352) 2025-08-05 17:43:03 +08:00
iridescentpeo
42bf355839 处理重新查询失败问题,对于重新查询的逻辑,避免querySQL的替换 (#2354) 2025-08-05 17:41:54 +08:00
jerryjzhang
91e4b51ef8 [fix]Fix unit test cases. 2025-08-05 17:22:10 +08:00
jerryjzhang
bf3213e8fb [fix]Downgrade djl library to support mac intel machines. 2025-08-05 15:20:11 +08:00
QJ_wonder
c75233e37f (fix)(chat) 修复插件功能,无法调用/插件结果被NL2SQL结果覆盖的问题 (#2277)
Some checks failed
supersonic CentOS CI / build (21) (push) Has been cancelled
supersonic mac CI / build (21) (push) Has been cancelled
supersonic ubuntu CI / build (21) (push) Has been cancelled
supersonic windows CI / build (21) (push) Has been cancelled
2025-07-27 09:03:26 +08:00
feelshana
785bda6cd9 (fix)[chat&headless]in mapping phase,DimValuesAlias do not take effect (#2340) 2025-07-27 09:03:05 +08:00
WDEP
6bd8970849 Fix model field update (#2343) 2025-07-27 09:02:53 +08:00
wangyong
c33a85b583 (fix)(webapp)修复问答对话右侧历史对话模块样式异常问题 (#2346) 2025-07-27 09:02:39 +08:00
93 changed files with 1514 additions and 535 deletions

View File

@@ -3,6 +3,205 @@
- All notable changes to this project will be documented in this file.
- "Breaking Changes" describes any changes that may break existing functionality or cause
compatibility issues with previous versions.
## SuperSonic [1.0.0] - 2025-08-05
### 重大特性变更 / Major Features
#### 多数据库支持扩展 / Multi-Database Support
- **Oracle数据库支持**: 新增Oracle数据库引擎类型及适配器 ([8eeed87ba](https://github.com/tencentmusic/supersonic/commit/8eeed87ba) by supersonicbi)
- **StarRocks支持**: 支持StarRocks和多catalog功能 ([33268bf3d](https://github.com/tencentmusic/supersonic/commit/33268bf3d) by zyclove)
- **SAP HANA支持**: 新增SAP HANA数据库适配支持 ([2e28a4c7a](https://github.com/tencentmusic/supersonic/commit/2e28a4c7a) by wwsheng009)
- **DuckDB支持**: 支持DuckDB数据库 ([a058dc8b6](https://github.com/tencentmusic/supersonic/commit/a058dc8b6) by jerryjzhang)
- **Kyuubi支持**: 支持Kyuubi Presto Trino ([5e3bafb95](https://github.com/tencentmusic/supersonic/commit/5e3bafb95) by zyclove)
- **OpenSearch支持**: 新增OpenSearch支持 ([d942d35c9](https://github.com/tencentmusic/supersonic/commit/d942d35c9) by zyclove)
#### 智能问答增强 / AI-Enhanced Query Processing
- **LLM纠错器**: 新增LLM物理SQL纠错器 ([f899d23b6](https://github.com/tencentmusic/supersonic/commit/f899d23b6) by 柯慕灵)
- **记忆管理**: Agent记忆管理启用few-shot优先机制 ([fae9118c2](https://github.com/tencentmusic/supersonic/commit/fae9118c2) by feelshana)
- **结构化查询**: 支持struct查询中的offset子句 ([d2a43a99c](https://github.com/tencentmusic/supersonic/commit/d2a43a99c) by jerryjzhang)
- **向量召回优化**: 优化嵌入向量召回机制 ([8c6ae6252](https://github.com/tencentmusic/supersonic/commit/8c6ae6252) by lexluo09)
#### 权限管理系统 / Permission Management
- **Agent权限**: 支持agent级别的权限管理 ([b5aa6e046](https://github.com/tencentmusic/supersonic/commit/b5aa6e046) by jerryjzhang)
- **用户管理**: 支持用户删除功能 ([1c9cf788c](https://github.com/tencentmusic/supersonic/commit/1c9cf788c) by supersonicbi)
- **鉴权优化**: 全面优化鉴权与召回机制 ([1faf84e37](https://github.com/tencentmusic/supersonic/commit/1faf84e37), [7e6639df8](https://github.com/tencentmusic/supersonic/commit/7e6639df8) by guilinlewis)
### 架构升级 / Architecture Upgrades
#### 核心框架升级 / Core Framework Upgrades
- **SpringBoot 3升级**: 完成SpringBoot 3.x升级 ([07f6be51c](https://github.com/tencentmusic/supersonic/commit/07f6be51c) by mislayming)
- **依赖升级**: 升级依赖包并修复安全漏洞 ([232a20227](https://github.com/tencentmusic/supersonic/commit/232a20227) by beat4ocean)
- **LangChain4j更新**: 替换已废弃的LangChain4j APIs ([acffc03c7](https://github.com/tencentmusic/supersonic/commit/acffc03c7) by beat4ocean)
- **Swagger升级**: 使用SpringDoc支持Swagger在Spring 3.x ([758d170bb](https://github.com/tencentmusic/supersonic/commit/758d170bb) by jerryjzhang)
#### 许可证变更 / License Changes
- **Apache 2.0**: 从MIT更改为Apache 2.0许可证 ([0aa002882](https://github.com/tencentmusic/supersonic/commit/0aa002882) by jerryjzhang)
### 性能优化 / Performance Improvements
#### 系统性能 / System Performance
- **GC优化**: 实现Generational ZGC ([3fc1ec42b](https://github.com/tencentmusic/supersonic/commit/3fc1ec42b) by beat4ocean)
- **Docker优化**: 减少Docker镜像体积 ([614917ba7](https://github.com/tencentmusic/supersonic/commit/614917ba7) by kino)
- **并行处理**: 嵌入向量并行执行优化 ([8c6ae6252](https://github.com/tencentmusic/supersonic/commit/8c6ae6252) by lexluo09)
- **记忆评估**: 记忆评估性能优化 ([524ec38ed](https://github.com/tencentmusic/supersonic/commit/524ec38ed) by yudong)
- **多平台构建**: 支持Docker多平台构建 ([da6d28c18](https://github.com/tencentmusic/supersonic/commit/da6d28c18) by jerryjzhang)
#### 数据处理优化 / Data Processing Optimization
- **日期格式**: 支持更多日期字符串格式 ([2b13866c0](https://github.com/tencentmusic/supersonic/commit/2b13866c0) by supersonicbi)
- **SQL优化**: 优化SQL生成和执行性能 ([0ab764329](https://github.com/tencentmusic/supersonic/commit/0ab764329) by jerryjzhang)
- **模型关联**: 优化模型关联查询性能 ([47c2595fb](https://github.com/tencentmusic/supersonic/commit/47c2595fb) by Willy-J)
### 功能增强 / Feature Enhancements
#### 前端界面优化 / Frontend Improvements
- **图表导出**: 消息支持导出图表图片 ([ce9ae1c0c](https://github.com/tencentmusic/supersonic/commit/ce9ae1c0c) by pisces)
- **路由重构**: 重构语义建模路由交互 ([82c63a7f2](https://github.com/tencentmusic/supersonic/commit/82c63a7f2) by tristanliu)
- **权限界面**: 统一助理权限设置交互界面 ([46d64d78f](https://github.com/tencentmusic/supersonic/commit/46d64d78f) by tristanliu)
- **图表优化**: 优化ChatMsg图表条件 ([06fb6ba74](https://github.com/tencentmusic/supersonic/commit/06fb6ba74) by FredTsang)
- **数据格式**: 提取formatByDataFormatType()方法 ([9ffdba956](https://github.com/tencentmusic/supersonic/commit/9ffdba956) by FredTsang)
#### 开发体验 / Developer Experience
- **构建脚本**: 优化Web应用构建脚本 ([baae7f74b](https://github.com/tencentmusic/supersonic/commit/baae7f74b) by zyclove)
- **GitHub Actions**: 优化GitHub Actions镜像推送 ([6a4458a57](https://github.com/tencentmusic/supersonic/commit/6a4458a57) by lexluo09)
- **基准测试**: 改进基准测试,增加解析结果分析 ([97710a90c](https://github.com/tencentmusic/supersonic/commit/97710a90c) by Antgeek)
### Bug修复 / Bug Fixes
#### 核心功能修复 / Core Function Fixes
- **插件功能**: 修复插件功能无法调用/结果被NL2SQL覆盖问题 ([c75233e37](https://github.com/tencentmusic/supersonic/commit/c75233e37) by QJ_wonder)
- **维度别名**: 修复映射阶段维度值别名不生效问题 ([785bda6cd](https://github.com/tencentmusic/supersonic/commit/785bda6cd) by feelshana)
- **模型字段**: 修复模型字段更新问题 ([6bd897084](https://github.com/tencentmusic/supersonic/commit/6bd897084) by WDEP)
- **多轮对话**: 修复headless中字段查询及多轮对话使用问题 ([be0447ae1](https://github.com/tencentmusic/supersonic/commit/be0447ae1) by QJ_wonder)
#### NPE异常修复 / NPE Exception Fixes
- **聊天查询**: 修复EmbeddingMatchStrategy.detectByBatch() NPE异常 ([6d907b6ad](https://github.com/tencentmusic/supersonic/commit/6d907b6ad) by wangyong)
- **文件处理**: 修复FileHandlerImpl.convert2Resp() 维度值数据行首字符为空格异常 ([da172a030](https://github.com/tencentmusic/supersonic/commit/da172a030) by wangyong)
- **头部服务**: 修复多处headless NPE问题 ([79a44b27e](https://github.com/tencentmusic/supersonic/commit/79a44b27e) by jerryjzhang)
- **解析信息**: 修复getParseInfo中的NPE ([dce9a8a58](https://github.com/tencentmusic/supersonic/commit/dce9a8a58) by supersonicbi)
#### SQL兼容性修复 / SQL Compatibility Fixes
- **SQL处理**: 修复SQL前后换行符导致的语句结尾";"删除问题 ([55ac3d1aa](https://github.com/tencentmusic/supersonic/commit/55ac3d1aa) by wangyong)
- **查询别名**: DictUtils.constructQuerySqlReq针对sql query增加别名 ([042791762](https://github.com/tencentmusic/supersonic/commit/042791762) by andybj0228)
- **SQL变量**: 支持SQL脚本变量替换 ([0709575cd](https://github.com/tencentmusic/supersonic/commit/0709575cd) by wanglongqiang)
#### 前端Bug修复 / Frontend Bug Fixes
- **UI样式**: 修复问答对话右侧历史对话模块样式异常 ([c33a85b58](https://github.com/tencentmusic/supersonic/commit/c33a85b58) by wangyong)
- **推荐维度**: 修复页面不显示推荐下钻维度问题 ([62b9db679](https://github.com/tencentmusic/supersonic/commit/62b9db679) by WDEP)
- **图表显示**: 修复饼图显示条件问题 ([1b8cd7f0d](https://github.com/tencentmusic/supersonic/commit/1b8cd7f0d) by WDEP)
- **负数支持**: 支持负数显示 ([2552e2ae4](https://github.com/tencentmusic/supersonic/commit/2552e2ae4) by FredTsang)
- **百分比显示**: 支持bar图needMultiply100显示正确百分比值 ([8abfc923a](https://github.com/tencentmusic/supersonic/commit/8abfc923a) by coosir)
- **TypeScript错误**: 修复前端TypeScript错误 ([5585b9e22](https://github.com/tencentmusic/supersonic/commit/5585b9e22) by poncheen)
#### 系统兼容性修复 / System Compatibility Fixes
- **Windows脚本**: 修复Windows daemon.bat路径配置问题 ([e5a41765b](https://github.com/tencentmusic/supersonic/commit/e5a41765b) by 柯慕灵)
- **字符编码**: 将utf8编码修改为utf8mb4,解决字符问题 ([2e81b190a](https://github.com/tencentmusic/supersonic/commit/2e81b190a) by Kun Gu)
- **记忆缓存**: 修复记忆管理中因缓存无法存储的问题 ([81cd60d2d](https://github.com/tencentmusic/supersonic/commit/81cd60d2d) by guilinlewis)
- **Mac兼容**: 降级djl库以支持Mac Intel机器 ([bf3213e8f](https://github.com/tencentmusic/supersonic/commit/bf3213e8f) by jerryjzhang)
### 数据管理优化 / Data Management Improvements
#### 维度指标管理 / Dimension & Metric Management
- **维度检索**: 修复维度和指标检索及百分比显示问题 ([d8fe2ed2b](https://github.com/tencentmusic/supersonic/commit/d8fe2ed2b) by 木鱼和尚)
- **查询导出**: 基于queryColumns导出数据 ([11d1264d3](https://github.com/tencentmusic/supersonic/commit/11d1264d3) by FredTsang)
- **表格排序**: 移除表格defaultSortOrder ([32675387d](https://github.com/tencentmusic/supersonic/commit/32675387d) by FredTsang)
- **维度搜索**: 修复维度搜索带key查询范围超出问题 ([269f146c1](https://github.com/tencentmusic/supersonic/commit/269f146c1) by wangyong)
### 测试和质量保证 / Testing & Quality Assurance
#### 单元测试 / Unit Testing
- **测试修复**: 修复单元测试用例 ([91e4b51ef](https://github.com/tencentmusic/supersonic/commit/91e4b51ef) by jerryjzhang)
- **模型测试**: 修复ModelCreateForm.tsx错误 ([d2aa73b85](https://github.com/tencentmusic/supersonic/commit/d2aa73b85) by Antgeek)
### 重要变更说明 / Breaking Changes
#### 升级注意事项 / Upgrade Notes
1. **SpringBoot 3升级**: 可能需要更新依赖配置和代码适配
2. **许可证变更**: 从MIT变更为Apache 2.0,请注意法律合规
3. **API接口调整**: 部分API接口为支持新功能进行了调整
4. **数据库兼容**: 新增多种数据库支持,配置方式有所变化
### 完整提交统计 / Commit Statistics
- **总提交数**: 419个提交
- **主要贡献者**:
- jerryjzhang: 158次提交
- supersonicbi: 22次提交
- zyclove: 20次提交
- beat4ocean: 15次提交
- guilinlewis: 11次提交
- wangyong: 11次提交
- 其他贡献者: 182次提交
- **涉及模块**: headless, chat, auth, common, webapp, launcher, docker
- **时间跨度**: 2024年11月1日 - 2025年8月5日
### 致谢 / Acknowledgments
感谢所有为SuperSonic 1.0.0版本贡献代码、文档、测试和建议的开发者们!🎉
#### 核心贡献者 / Core Contributors
- **jerryjzhang** - 项目维护者,核心架构设计与实现
- **supersonicbi** - 核心功能开发,多数据库支持
- **beat4ocean** - 架构升级,依赖管理,安全优化
- **zyclove** - 数据库适配,构建优化
- **guilinlewis** - 鉴权系统,召回优化
- **wangyong** - Bug修复NPE异常处理
#### 活跃贡献者 / Active Contributors
- **WDEP** - 前端优化,图表功能
- **FredTsang** - Chat SDK优化数据导出
- **feelshana** - 记忆管理,向量召回
- **QJ_wonder** - 插件功能,多轮对话
- **Willy-J** - 模型关联,数据库兼容
- **iridescentpeo** - 查询优化,模型管理
- **tristanliu** - 前端路由,权限界面
- **mislayming** - SpringBoot 3升级
- **Antgeek** - 基准测试,模型修复
- **柯慕灵** - LLM纠错器Windows脚本
- **superhero** - 项目管理,代码审查
#### 其他重要贡献者 / Other Important Contributors
- **木鱼和尚** - 维度指标检索优化
- **pisces** - 图表导出功能
- **lexluo09** - 并行处理GitHub Actions
- **andybj0228** - SQL查询优化
- **wanglongqiang** - SQL变量支持
- **Hyman_bz** - StarRocks支持
- **wwsheng009** - SAP HANA适配
- **poncheen** - TypeScript错误修复
- **kino** - Docker镜像优化
- **coosir** - 前端百分比显示
- **Kun Gu** - 字符编码优化
- **chixiaopao** - NPE异常修复
- **naimehao** - 核心功能修复
- **yudong** - 记忆评估优化
- **mroldx** - 数据库脚本更新
- **ChPi** - 解析器性能优化
- **Hwting** - Docker配置优化
#### 特别感谢 / Special Thanks
感谢所有提交Issue、参与讨论、提供反馈的社区用户你们的每一个建议都让SuperSonic变得更好
#### 社区支持 / Community Support
SuperSonic是一个开源项目我们欢迎更多开发者加入
- 🔗 **GitHub**: https://github.com/tencentmusic/supersonic
- 📖 **文档**: 详见项目README和Wiki
- 🐛 **Issue报告**: 欢迎提交Bug和功能请求
- 🚀 **贡献代码**: 欢迎提交Pull Request
- 💬 **社区讨论**: 加入我们的技术交流群
#### 未来展望 / Future Vision
SuperSonic 1.0.0是一个重要的里程碑,但这只是开始。我们将继续:
- 🌟 **持续优化性能和稳定性**
- 🔧 **扩展更多数据库和AI模型支持**
- 🎨 **改善用户体验和界面设计**
- 📚 **完善文档和最佳实践**
- 🤝 **建设更活跃的开源社区**
**让我们一起把SuperSonic做得更好**
---
*如果您在使用过程中遇到问题或有改进建议欢迎随时与我们交流。每一份贡献都让SuperSonic更加强大*
## SuperSonic [0.9.8] - 2024-11-01
- Add LLM management module to reuse connection across agents.

View File

@@ -15,4 +15,6 @@ public class UserReq {
@NotBlank(message = "password can not be null")
private String newPassword;
private String role;
}

View File

@@ -18,4 +18,5 @@ public class ChatExecuteReq {
private int parseId;
private String queryText;
private boolean saveAnswer;
private boolean streamingResult;
}

View File

@@ -0,0 +1,19 @@
package com.tencent.supersonic.chat.api.pojo.request;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ChatMemoryDeleteReq {
private List<Long> ids;
private Integer agentId;
}

View File

@@ -32,6 +32,7 @@ import dev.langchain4j.model.input.PromptTemplate;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.provider.ModelProvider;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -171,10 +172,6 @@ public class NL2SQLParser implements ChatQueryParser {
return;
}
// derive mapping result of current question and parsing result of last question.
ChatLayerService chatLayerService = ContextUtils.getBean(ChatLayerService.class);
MapResp currentMapResult = chatLayerService.map(queryNLReq);
List<QueryResp> historyQueries =
getHistoryQueries(parseContext.getRequest().getChatId(), 1);
if (historyQueries.isEmpty()) {
@@ -182,12 +179,18 @@ public class NL2SQLParser implements ChatQueryParser {
}
QueryResp lastQuery = historyQueries.get(0);
SemanticParseInfo lastParseInfo = lastQuery.getParseInfos().get(0);
Long dataId = lastParseInfo.getDataSetId();
String histSQL = lastParseInfo.getSqlInfo().getCorrectedS2SQL();
if (StringUtils.isBlank(histSQL)) // 优化性能,如果问答不是chat bi 则无需重写,因为数据都不全
return;
// derive mapping result of current question and parsing result of last question.
ChatLayerService chatLayerService = ContextUtils.getBean(ChatLayerService.class);
MapResp currentMapResult = chatLayerService.map(queryNLReq); // 优化性能 ,只有满足条件才mapping
Long dataId = lastParseInfo.getDataSetId();
String curtMapStr =
generateSchemaPrompt(currentMapResult.getMapInfo().getMatchedElements(dataId));
String histMapStr = generateSchemaPrompt(lastParseInfo.getElementMatches());
String histSQL = lastParseInfo.getSqlInfo().getCorrectedS2SQL();
Map<String, Object> variables = new HashMap<>();
variables.put("current_question", currentMapResult.getQueryText());

View File

@@ -35,9 +35,7 @@ public class ChatMemoryRepositoryImpl implements ChatMemoryRepository {
if (CollectionUtils.isEmpty(ids)) {
return;
}
for (Long id : ids) {
chatMemoryMapper.deleteById(id);
}
chatMemoryMapper.deleteByIds(ids);
}
@Override

View File

@@ -88,10 +88,10 @@ public class WebServiceQuery extends PluginSemanticQuery {
restTemplate = ContextUtils.getBean(RestTemplate.class);
try {
responseEntity =
restTemplate.exchange(requestUrl, HttpMethod.POST, entity, Object.class);
restTemplate.exchange(requestUrl, HttpMethod.POST, entity, String.class);
objectResponse = responseEntity.getBody();
log.info("objectResponse:{}", objectResponse);
Map<String, Object> response = JsonUtil.objectToMap(objectResponse);
Map<String, Object> response = JSON.parseObject(objectResponse.toString());
webServiceResponse.setResult(response);
} catch (Exception e) {
log.info("Exception:{}", e.getMessage());

View File

@@ -19,7 +19,8 @@ public class ParseContext {
}
public boolean enableNL2SQL() {
return Objects.nonNull(agent) && agent.containsDatasetTool();
return Objects.nonNull(agent) && agent.containsDatasetTool()
&& response.getSelectedParses().size() == 0;
}
public boolean enableLLM() {

View File

@@ -1,13 +1,20 @@
package com.tencent.supersonic.chat.server.processor.execute;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tencent.supersonic.chat.api.pojo.response.QueryResult;
import com.tencent.supersonic.chat.server.agent.Agent;
import com.tencent.supersonic.chat.server.persistence.dataobject.ChatQueryDO;
import com.tencent.supersonic.chat.server.persistence.repository.ChatQueryRepository;
import com.tencent.supersonic.chat.server.pojo.ExecuteContext;
import com.tencent.supersonic.common.pojo.ChatApp;
import com.tencent.supersonic.common.pojo.enums.AppModule;
import com.tencent.supersonic.common.util.ChatAppManager;
import com.tencent.supersonic.common.util.ContextUtils;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.StreamingResponseHandler;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.input.Prompt;
import dev.langchain4j.model.input.PromptTemplate;
import dev.langchain4j.model.output.Response;
@@ -24,9 +31,11 @@ import java.util.Objects;
* DataInterpretProcessor interprets query result to make it more readable to the users.
*/
public class DataInterpretProcessor implements ExecuteResultProcessor {
public static String tip = "AI 回答中...\r\n";
private static final Logger keyPipelineLog = LoggerFactory.getLogger("keyPipeline");
private static Map<Long, StringBuffer> resultCache = new HashMap<>();
public static final String APP_KEY = "DATA_INTERPRETER";
private static final String INSTRUCTION = ""
+ "#Role: You are a data expert who communicates with business users everyday."
@@ -41,6 +50,16 @@ public class DataInterpretProcessor implements ExecuteResultProcessor {
.appModule(AppModule.CHAT).description("通过大模型对结果数据做提炼总结").enable(false).build());
}
public static String getTextSummary(Long queryId) {
if (resultCache.get(queryId) != null) {
return resultCache.get(queryId).toString();
}
return "";
}
public static Map<Long, StringBuffer> getResultCache() {
return resultCache;
}
@Override
public boolean accept(ExecuteContext executeContext) {
@@ -71,14 +90,49 @@ public class DataInterpretProcessor implements ExecuteResultProcessor {
variable.put("data", queryResult.getTextResult());
Prompt prompt = PromptTemplate.from(chatApp.getPrompt()).apply(variable);
if (executeContext.getRequest().isStreamingResult()) {
StreamingChatLanguageModel chatLanguageModel =
ModelProvider.getChatStreamingModel(chatApp.getChatModelConfig());
final Long queryId = executeContext.getRequest().getQueryId();
resultCache.put(queryId, new StringBuffer(tip));
chatLanguageModel.generate(prompt.toUserMessage(),
new StreamingResponseHandler<AiMessage>() {
@Override
public void onNext(String token) {
resultCache.get(queryId).append(token);
}
@Override
public void onComplete(Response<AiMessage> response) {
ChatQueryRepository chatQueryRepository =
ContextUtils.getBean(ChatQueryRepository.class);
ChatQueryDO chatQueryDO = chatQueryRepository.getChatQueryDO(queryId);
JSONObject queryResult = JSON.parseObject(chatQueryDO.getQueryResult());
queryResult.put("textSummary",
resultCache.get(queryId).toString().substring(tip.length()));
chatQueryDO.setQueryResult(queryResult.toJSONString());
chatQueryRepository.updateChatQuery(chatQueryDO);
resultCache.remove(queryId);
}
@Override
public void onError(Throwable error) {
error.printStackTrace();
resultCache.remove(queryId);
}
});
} else {
ChatLanguageModel chatLanguageModel =
ModelProvider.getChatModel(chatApp.getChatModelConfig());
Response<AiMessage> response = chatLanguageModel.generate(prompt.toUserMessage());
String anwser = response.content().text();
keyPipelineLog.info("DataInterpretProcessor modelReq:\n{} \nmodelResp:\n{}", prompt.text(),
anwser);
keyPipelineLog.info("DataInterpretProcessor modelReq:\n{} \nmodelResp:\n{}",
prompt.text(), anwser);
if (StringUtils.isNotBlank(anwser)) {
queryResult.setTextSummary(anwser);
}
}
}
}

View File

@@ -5,6 +5,7 @@ import com.tencent.supersonic.chat.api.pojo.request.ChatExecuteReq;
import com.tencent.supersonic.chat.api.pojo.request.ChatParseReq;
import com.tencent.supersonic.chat.api.pojo.request.ChatQueryDataReq;
import com.tencent.supersonic.chat.api.pojo.response.ChatParseResp;
import com.tencent.supersonic.chat.api.pojo.response.QueryResult;
import com.tencent.supersonic.chat.server.service.ChatQueryService;
import com.tencent.supersonic.common.pojo.User;
import com.tencent.supersonic.common.pojo.exception.InvalidArgumentException;
@@ -50,6 +51,14 @@ public class ChatQueryController {
return chatQueryService.execute(chatExecuteReq);
}
@PostMapping("getExecuteSummary")
public Object getExecuteSummary(@RequestBody ChatExecuteReq chatExecuteReq,
HttpServletRequest request, HttpServletResponse response) {
chatExecuteReq.setUser(UserHolder.findUser(request, response));
QueryResult res = chatQueryService.getTextSummary(chatExecuteReq);
return res;
}
@PostMapping("/")
public Object query(@RequestBody ChatParseReq chatParseReq, HttpServletRequest request,
HttpServletResponse response) throws Exception {

View File

@@ -4,12 +4,12 @@ import com.github.pagehelper.PageInfo;
import com.tencent.supersonic.auth.api.authentication.utils.UserHolder;
import com.tencent.supersonic.chat.api.pojo.enums.MemoryReviewResult;
import com.tencent.supersonic.chat.api.pojo.request.ChatMemoryCreateReq;
import com.tencent.supersonic.chat.api.pojo.request.ChatMemoryDeleteReq;
import com.tencent.supersonic.chat.api.pojo.request.ChatMemoryUpdateReq;
import com.tencent.supersonic.chat.api.pojo.request.PageMemoryReq;
import com.tencent.supersonic.chat.server.pojo.ChatMemory;
import com.tencent.supersonic.chat.server.service.MemoryService;
import com.tencent.supersonic.common.pojo.User;
import com.tencent.supersonic.headless.api.pojo.request.MetaBatchReq;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
@@ -53,8 +53,10 @@ public class MemoryController {
}
@PostMapping("batchDelete")
public Boolean batchDelete(@RequestBody MetaBatchReq metaBatchReq) {
memoryService.batchDelete(metaBatchReq.getIds());
public Boolean deleteMemory(@RequestBody ChatMemoryDeleteReq chatMemoryDeleteReq,
HttpServletRequest request, HttpServletResponse response) {
User user = UserHolder.findUser(request, response);
memoryService.batchDelete(chatMemoryDeleteReq, user);
return true;
}
}

View File

@@ -35,6 +35,8 @@ public interface ChatManageService {
QueryResp getChatQuery(Long queryId);
ChatQueryDO getChatQueryDO(Long queryId);
List<QueryResp> getChatQueries(Integer chatId);
ShowCaseResp queryShowCase(PageQueryInfoReq pageQueryInfoReq, int agentId);

View File

@@ -19,6 +19,8 @@ public interface ChatQueryService {
QueryResult execute(ChatExecuteReq chatExecuteReq) throws Exception;
QueryResult getTextSummary(ChatExecuteReq chatExecuteReq);
QueryResult parseAndExecute(ChatParseReq chatParseReq);
Object queryData(ChatQueryDataReq chatQueryDataReq, User user) throws Exception;

View File

@@ -1,6 +1,7 @@
package com.tencent.supersonic.chat.server.service;
import com.github.pagehelper.PageInfo;
import com.tencent.supersonic.chat.api.pojo.request.ChatMemoryDeleteReq;
import com.tencent.supersonic.chat.api.pojo.request.ChatMemoryFilter;
import com.tencent.supersonic.chat.api.pojo.request.ChatMemoryUpdateReq;
import com.tencent.supersonic.chat.api.pojo.request.PageMemoryReq;
@@ -14,7 +15,7 @@ public interface MemoryService {
void updateMemory(ChatMemoryUpdateReq chatMemoryUpdateReq, User user);
void batchDelete(List<Long> ids);
void batchDelete(ChatMemoryDeleteReq chatMemoryDeleteReq, User user);
PageInfo<ChatMemory> pageMemories(PageMemoryReq pageMemoryReq);

View File

@@ -123,6 +123,11 @@ public class ChatManageServiceImpl implements ChatManageService {
return chatQueryRepository.getChatQuery(queryId);
}
@Override
public ChatQueryDO getChatQueryDO(Long queryId) {
return chatQueryRepository.getChatQueryDO(queryId);
}
@Override
public List<QueryResp> getChatQueries(Integer chatId) {
List<QueryResp> queries = chatQueryRepository.getChatQueries(chatId);

View File

@@ -1,5 +1,6 @@
package com.tencent.supersonic.chat.server.service.impl;
import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Lists;
import com.tencent.supersonic.chat.api.pojo.request.ChatExecuteReq;
import com.tencent.supersonic.chat.api.pojo.request.ChatParseReq;
@@ -9,8 +10,10 @@ import com.tencent.supersonic.chat.api.pojo.response.QueryResult;
import com.tencent.supersonic.chat.server.agent.Agent;
import com.tencent.supersonic.chat.server.executor.ChatQueryExecutor;
import com.tencent.supersonic.chat.server.parser.ChatQueryParser;
import com.tencent.supersonic.chat.server.persistence.dataobject.ChatQueryDO;
import com.tencent.supersonic.chat.server.pojo.ExecuteContext;
import com.tencent.supersonic.chat.server.pojo.ParseContext;
import com.tencent.supersonic.chat.server.processor.execute.DataInterpretProcessor;
import com.tencent.supersonic.chat.server.processor.execute.ExecuteResultProcessor;
import com.tencent.supersonic.chat.server.processor.parse.ParseResultProcessor;
import com.tencent.supersonic.chat.server.service.AgentService;
@@ -18,7 +21,11 @@ import com.tencent.supersonic.chat.server.service.ChatManageService;
import com.tencent.supersonic.chat.server.service.ChatQueryService;
import com.tencent.supersonic.chat.server.util.ComponentFactory;
import com.tencent.supersonic.chat.server.util.QueryReqConverter;
import com.tencent.supersonic.common.jsqlparser.*;
import com.tencent.supersonic.common.jsqlparser.FieldExpression;
import com.tencent.supersonic.common.jsqlparser.SqlAddHelper;
import com.tencent.supersonic.common.jsqlparser.SqlRemoveHelper;
import com.tencent.supersonic.common.jsqlparser.SqlReplaceHelper;
import com.tencent.supersonic.common.jsqlparser.SqlSelectHelper;
import com.tencent.supersonic.common.pojo.User;
import com.tencent.supersonic.common.pojo.enums.FilterOperatorEnum;
import com.tencent.supersonic.common.util.DateUtils;
@@ -44,7 +51,11 @@ import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.expression.operators.relational.ComparisonOperator;
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals;
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
import net.sf.jsqlparser.schema.Column;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
@@ -53,7 +64,14 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@@ -110,6 +128,8 @@ public class ChatQueryServiceImpl implements ChatQueryService {
}
if (!parseContext.needFeedback()) {
parseContext.getResponse().getParseTimeCost().setParseTime(System.currentTimeMillis()
- parseContext.getResponse().getParseTimeCost().getParseStartTime());
chatManageService.batchAddParse(chatParseReq, parseContext.getResponse());
chatManageService.updateParseCostTime(parseContext.getResponse());
}
@@ -143,6 +163,21 @@ public class ChatQueryServiceImpl implements ChatQueryService {
return queryResult;
}
@Override
public QueryResult getTextSummary(ChatExecuteReq chatExecuteReq) {
String text = DataInterpretProcessor.getTextSummary(chatExecuteReq.getQueryId());
if (StringUtils.isNotBlank(text)) {
QueryResult res = new QueryResult();
res.setTextSummary(text);
res.setQueryId(chatExecuteReq.getQueryId());
return res;
} else {
ChatQueryDO chatQueryDo = chatManageService.getChatQueryDO(chatExecuteReq.getQueryId());
QueryResult res = JSON.parseObject(chatQueryDo.getQueryResult(), QueryResult.class);
return res;
}
}
@Override
public QueryResult parseAndExecute(ChatParseReq chatParseReq) {
ChatParseResp parseResp = parse(chatParseReq);

View File

@@ -6,6 +6,7 @@ import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tencent.supersonic.chat.api.pojo.enums.MemoryReviewResult;
import com.tencent.supersonic.chat.api.pojo.enums.MemoryStatus;
import com.tencent.supersonic.chat.api.pojo.request.ChatMemoryDeleteReq;
import com.tencent.supersonic.chat.api.pojo.request.ChatMemoryFilter;
import com.tencent.supersonic.chat.api.pojo.request.ChatMemoryUpdateReq;
import com.tencent.supersonic.chat.api.pojo.request.PageMemoryReq;
@@ -26,6 +27,7 @@ import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@@ -108,14 +110,21 @@ public class MemoryServiceImpl implements MemoryService, CommandLineRunner {
}
@Override
public void batchDelete(List<Long> ids) {
public void batchDelete(ChatMemoryDeleteReq chatMemoryDeleteReq, User user) {
QueryWrapper<ChatMemoryDO> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().in(ChatMemoryDO::getId, ids);
if (!CollectionUtils.isEmpty(chatMemoryDeleteReq.getIds())) {
queryWrapper.lambda().in(ChatMemoryDO::getId, chatMemoryDeleteReq.getIds());
}
if (chatMemoryDeleteReq.getAgentId() != null) {
queryWrapper.lambda().eq(ChatMemoryDO::getAgentId, chatMemoryDeleteReq.getAgentId());
}
List<ChatMemoryDO> chatMemoryDOS = chatMemoryRepository.getMemories(queryWrapper);
List<Long> ids = new ArrayList<>();
chatMemoryDOS.forEach(chatMemoryDO -> {
if (MemoryStatus.ENABLED.toString().equals(chatMemoryDO.getStatus().trim())) {
disableMemory(chatMemoryDO);
}
ids.add(chatMemoryDO.getId());
});
chatMemoryRepository.batchDelete(ids);
}

View File

@@ -108,6 +108,7 @@ public class PluginServiceImpl implements PluginService {
if (StringUtils.isNotBlank(pluginQueryReq.getCreatedBy())) {
queryWrapper.lambda().eq(PluginDO::getCreatedBy, pluginQueryReq.getCreatedBy());
}
queryWrapper.orderByAsc("name");
List<PluginDO> pluginDOS = pluginRepository.query(queryWrapper);
if (StringUtils.isNotBlank(pluginQueryReq.getPattern())) {
pluginDOS = pluginDOS.stream()

View File

@@ -46,6 +46,62 @@ public class FieldValueReplaceVisitor extends ExpressionVisitorAdapter {
replaceComparisonExpression(expr);
}
public void visit(LikeExpression expr) {
Expression leftExpression = expr.getLeftExpression();
Expression rightExpression = expr.getRightExpression();
if (!(leftExpression instanceof Column)) {
return;
}
if (CollectionUtils.isEmpty(filedNameToValueMap)) {
return;
}
if (Objects.isNull(rightExpression) || Objects.isNull(leftExpression)) {
return;
}
Column column = (Column) leftExpression;
String columnName = column.getColumnName();
if (StringUtils.isEmpty(columnName)) {
return;
}
Map<String, String> valueMap = filedNameToValueMap.get(columnName);
if (Objects.isNull(valueMap) || valueMap.isEmpty()) {
return;
}
if (rightExpression instanceof StringValue) {
StringValue rightStringValue = (StringValue) rightExpression;
String value = rightStringValue.getValue();
// 使用split处理方式按通配符分割字符串对每个片段进行转换
String[] parts = value.split("%", -1);
boolean changed = false;
// 处理每个部分
for (int i = 0; i < parts.length; i++) {
if (!parts[i].isEmpty()) {
String replaceValue = getReplaceValue(valueMap, parts[i]);
if (StringUtils.isNotEmpty(replaceValue) && !parts[i].equals(replaceValue)) {
parts[i] = replaceValue;
changed = true;
}
}
}
// 如果有任何部分发生变化,则重新构建字符串
if (changed) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < parts.length; i++) {
sb.append(parts[i]);
// 除了最后一个部分,其他部分后面都需要加上"%"
if (i < parts.length - 1) {
sb.append("%");
}
}
rightStringValue.setValue(sb.toString());
}
}
}
public void visit(InExpression inExpression) {
if (!(inExpression.getLeftExpression() instanceof Column)) {
return;

View File

@@ -16,16 +16,21 @@ import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import org.apache.commons.collections.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.*;
@Slf4j
public class FiledFilterReplaceVisitor extends ExpressionVisitorAdapter {
private List<Expression> waitingForAdds = new ArrayList<>();
private Set<String> fieldNames;
private Map<String, String> fieldNameMap = new HashMap<>();
private static Set<String> HAVING_AGG_TYPES = Set.of("SUM", "AVG", "MAX", "MIN", "COUNT");
public FiledFilterReplaceVisitor(Map<String, String> fieldNameMap) {
this.fieldNameMap = fieldNameMap;
this.fieldNames = fieldNameMap.keySet();
}
public FiledFilterReplaceVisitor(Set<String> fieldNames) {
this.fieldNames = fieldNames;
@@ -82,7 +87,22 @@ public class FiledFilterReplaceVisitor extends ExpressionVisitorAdapter {
Expression leftExpression = comparisonOperator.getLeftExpression();
if (!(leftExpression instanceof Function)) {
if (leftExpression instanceof Column) {
Column leftColumn = (Column) leftExpression;
String agg = fieldNameMap.get(leftColumn.getColumnName());
if (agg != null && HAVING_AGG_TYPES.contains(agg.toUpperCase())) {
Expression expression = parseCondExpression(comparisonOperator, condExpr);
if (Objects.nonNull(expression)) {
result.add(expression);
return result;
} else {
return null;
}
}
return result;
} else {
return result;
}
}
Function leftFunction = (Function) leftExpression;
@@ -102,14 +122,24 @@ public class FiledFilterReplaceVisitor extends ExpressionVisitorAdapter {
return null;
}
Expression expression = parseCondExpression(comparisonOperator, condExpr);
if (Objects.nonNull(expression)) {
result.add(expression);
return result;
} else {
return null;
}
}
private Expression parseCondExpression(ComparisonOperator comparisonOperator, String condExpr) {
try {
String comparisonOperatorStr = comparisonOperator.toString();
ComparisonOperator parsedExpression =
(ComparisonOperator) CCJSqlParserUtil.parseCondExpression(condExpr);
comparisonOperator.setLeftExpression(parsedExpression.getLeftExpression());
comparisonOperator.setRightExpression(parsedExpression.getRightExpression());
comparisonOperator.setASTNode(parsedExpression.getASTNode());
result.add(CCJSqlParserUtil.parseCondExpression(comparisonOperatorStr));
return result;
return CCJSqlParserUtil.parseCondExpression(comparisonOperatorStr);
} catch (JSQLParserException e) {
log.error("JSQLParserException", e);
}

View File

@@ -309,7 +309,7 @@ public class SqlAddHelper {
}
}
public static String addHaving(String sql, Set<String> fieldNames) {
public static String addHaving(String sql, Map<String, String> fieldNames) {
Select selectStatement = SqlSelectHelper.getSelect(sql);
if (!(selectStatement instanceof PlainSelect)) {

View File

@@ -727,7 +727,7 @@ public class SqlReplaceHelper {
List<PlainSelect> plainSelects = SqlSelectHelper.getPlainSelects(plainSelectList);
for (PlainSelect plainSelect : plainSelects) {
if (Objects.nonNull(plainSelect.getFromItem())) {
Table table = (Table) plainSelect.getFromItem();
Table table = SqlSelectHelper.getTable(plainSelect.getFromItem());
if (table.getName().equals(tableName)) {
replacePlainSelectByExpr(plainSelect, replace);
if (SqlSelectHelper.hasAggregateFunction(plainSelect)) {

View File

@@ -723,6 +723,44 @@ public class SqlSelectHelper {
return null;
}
public static Table getTable(FromItem fromItem) {
Table table = null;
if (fromItem instanceof Table) {
table = (Table) fromItem;
} else if (fromItem instanceof ParenthesedSelect) {
ParenthesedSelect parenthesedSelect = (ParenthesedSelect) fromItem;
if (parenthesedSelect.getSelect() instanceof PlainSelect) {
PlainSelect subSelect = (PlainSelect) parenthesedSelect.getSelect();
table = getTable(subSelect.getSelectBody());
} else if (parenthesedSelect.getSelect() instanceof SetOperationList) {
table = getTable(parenthesedSelect.getSelect());
}
}
return table;
}
public static Table getTable(Select select) {
if (select == null) {
return null;
}
List<PlainSelect> plainSelectList = getWithItem(select);
if (!CollectionUtils.isEmpty(plainSelectList)) {
List<PlainSelect> selectList = new ArrayList<>(plainSelectList);
Table table = getTable(selectList.get(0));
return table;
}
if (select instanceof PlainSelect) {
PlainSelect plainSelect = (PlainSelect) select;
return getTable(plainSelect.getFromItem());
} else if (select instanceof SetOperationList) {
SetOperationList setOperationList = (SetOperationList) select;
if (!CollectionUtils.isEmpty(setOperationList.getSelects())) {
return getTable(setOperationList.getSelects().get(0));
}
}
return null;
}
public static String getDbTableName(String sql) {
Table table = getTable(sql);
return table.getFullyQualifiedName();

View File

@@ -28,6 +28,8 @@ public class ChatModelConfig implements Serializable {
private Boolean logRequests = false;
private Boolean logResponses = false;
private Boolean enableSearch = false;
private Boolean jsonFormat = false;
private String jsonFormatType = "json_schema";
public String keyDecrypt() {
return AESEncryptionUtil.aesDecryptECB(getApiKey());

View File

@@ -1,27 +1,26 @@
package com.tencent.supersonic.common.pojo;
import com.tencent.supersonic.common.pojo.enums.EventType;
import lombok.Getter;
import org.springframework.context.ApplicationEvent;
import java.util.List;
@Getter
public class DataEvent extends ApplicationEvent {
private List<DataItem> dataItems;
private final List<DataItem> dataItems;
private EventType eventType;
private final EventType eventType;
public DataEvent(Object source, List<DataItem> dataItems, EventType eventType) {
private final String userName;
public DataEvent(Object source, List<DataItem> dataItems, EventType eventType,
String userName) {
super(source);
this.dataItems = dataItems;
this.eventType = eventType;
this.userName = userName;
}
public List<DataItem> getDataItems() {
return dataItems;
}
public EventType getEventType() {
return eventType;
}
}

View File

@@ -5,4 +5,6 @@ public class DimensionConstants {
public static final String DIMENSION_TIME_FORMAT = "time_format";
public static final String DIMENSION_TYPE = "dimension_type";
public static final String DIMENSION_DATA_TYPE = "dimension_data_type";
}

View File

@@ -1,5 +1,5 @@
package com.tencent.supersonic.common.pojo.enums;
public enum TypeEnums {
METRIC, DIMENSION, TAG, DOMAIN, DATASET, MODEL, UNKNOWN
METRIC, DIMENSION, VALUE, TAG, DOMAIN, DATASET, MODEL, UNKNOWN
}

View File

@@ -15,5 +15,5 @@ public interface ChatModelService {
ChatModel updateChatModel(ChatModel chatModel, User user);
void deleteChatModel(Integer id);
void deleteChatModel(Integer id, User user);
}

View File

@@ -79,7 +79,12 @@ public class ChatModelServiceImpl extends ServiceImpl<ChatModelMapper, ChatModel
}
@Override
public void deleteChatModel(Integer id) {
public void deleteChatModel(Integer id, User user) {
ChatModel chatModel = getChatModel(id);
if (!checkAdminPermission(user, chatModel)) {
throw new RuntimeException("没有权限删除该大模型");
}
removeById(id);
}
@@ -103,4 +108,13 @@ public class ChatModelServiceImpl extends ServiceImpl<ChatModelMapper, ChatModel
chatModelDO.setConfig(JsonUtil.toString(chatModel.getConfig()));
return chatModelDO;
}
private boolean checkAdminPermission(User user, ChatModel chatModel) {
String admin = chatModel.getAdmin();
if (user.isSuperAdmin()) {
return true;
}
return admin != null && admin.equals(user.getName())
|| chatModel.getCreatedBy().equals(user.getName());
}
}

View File

@@ -16,6 +16,7 @@ import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@Service
public class SystemConfigServiceImpl extends ServiceImpl<SystemConfigMapper, SystemConfigDO>
@@ -38,8 +39,8 @@ public class SystemConfigServiceImpl extends ServiceImpl<SystemConfigMapper, Sys
return systemConfigDb;
}
private SystemConfig getSystemConfigFromDB() {
List<SystemConfigDO> list = list();
private SystemConfig getSystemConfigFromDB() { // 加上id ,如果有多条记录,会出错
List<SystemConfigDO> list = this.lambdaQuery().eq(SystemConfigDO::getId, 1).list();
if (CollectionUtils.isEmpty(list)) {
SystemConfig systemConfig = new SystemConfig();
systemConfig.setId(1);

View File

@@ -7,6 +7,7 @@ import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.dify.DifyAiChatModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.openai.OpenAiEmbeddingModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
@@ -25,6 +26,11 @@ public class DifyModelFactory implements ModelFactory, InitializingBean {
.modelName(modelConfig.getModelName()).timeOut(modelConfig.getTimeOut()).build();
}
@Override
public OpenAiStreamingChatModel createChatStreamingModel(ChatModelConfig modelConfig) {
throw new RuntimeException("待开发");
}
@Override
public EmbeddingModel createEmbeddingModel(EmbeddingModelConfig embeddingModelConfig) {
return OpenAiEmbeddingModel.builder().baseUrl(embeddingModelConfig.getBaseUrl())

View File

@@ -5,6 +5,7 @@ import com.tencent.supersonic.common.pojo.EmbeddingModelConfig;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.embedding.S2OnnxEmbeddingModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
@@ -35,6 +36,11 @@ public class InMemoryModelFactory implements ModelFactory, InitializingBean {
return EmbeddingModelConstant.BGE_SMALL_ZH_MODEL;
}
@Override
public OpenAiStreamingChatModel createChatStreamingModel(ChatModelConfig modelConfig) {
throw new RuntimeException("待开发");
}
@Override
public void afterPropertiesSet() {
ModelProvider.add(PROVIDER, this);

View File

@@ -6,6 +6,7 @@ import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.localai.LocalAiChatModel;
import dev.langchain4j.model.localai.LocalAiEmbeddingModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
@@ -27,6 +28,11 @@ public class LocalAiModelFactory implements ModelFactory, InitializingBean {
.build();
}
@Override
public OpenAiStreamingChatModel createChatStreamingModel(ChatModelConfig modelConfig) {
throw new RuntimeException("待开发");
}
@Override
public EmbeddingModel createEmbeddingModel(EmbeddingModelConfig embeddingModel) {
return LocalAiEmbeddingModel.builder().baseUrl(embeddingModel.getBaseUrl())

View File

@@ -4,9 +4,12 @@ import com.tencent.supersonic.common.pojo.ChatModelConfig;
import com.tencent.supersonic.common.pojo.EmbeddingModelConfig;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
public interface ModelFactory {
ChatLanguageModel createChatModel(ChatModelConfig modelConfig);
OpenAiStreamingChatModel createChatStreamingModel(ChatModelConfig modelConfig);
EmbeddingModel createEmbeddingModel(EmbeddingModelConfig embeddingModel);
}

View File

@@ -5,7 +5,9 @@ import com.tencent.supersonic.common.pojo.ChatModelConfig;
import com.tencent.supersonic.common.pojo.EmbeddingModelConfig;
import com.tencent.supersonic.common.util.ContextUtils;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
@@ -41,6 +43,20 @@ public class ModelProvider {
"Unsupported ChatLanguageModel provider: " + modelConfig.getProvider());
}
public static StreamingChatLanguageModel getChatStreamingModel(ChatModelConfig modelConfig) {
if (modelConfig == null || StringUtils.isBlank(modelConfig.getProvider())
|| StringUtils.isBlank(modelConfig.getBaseUrl())) {
modelConfig = DEMO_CHAT_MODEL;
}
ModelFactory modelFactory = factories.get(modelConfig.getProvider().toUpperCase());
if (modelFactory != null) {
return modelFactory.createChatStreamingModel(modelConfig);
}
throw new RuntimeException(
"Unsupported ChatLanguageModel provider: " + modelConfig.getProvider());
}
public static EmbeddingModel getEmbeddingModel() {
return getEmbeddingModel(null);
}

View File

@@ -6,6 +6,7 @@ import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.ollama.OllamaChatModel;
import dev.langchain4j.model.ollama.OllamaEmbeddingModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
@@ -28,6 +29,11 @@ public class OllamaModelFactory implements ModelFactory, InitializingBean {
.logResponses(modelConfig.getLogResponses()).build();
}
@Override
public OpenAiStreamingChatModel createChatStreamingModel(ChatModelConfig modelConfig) {
throw new RuntimeException("待开发");
}
@Override
public EmbeddingModel createEmbeddingModel(EmbeddingModelConfig embeddingModelConfig) {
return OllamaEmbeddingModel.builder().baseUrl(embeddingModelConfig.getBaseUrl())

View File

@@ -6,6 +6,7 @@ import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiEmbeddingModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
@@ -22,10 +23,26 @@ public class OpenAiModelFactory implements ModelFactory, InitializingBean {
@Override
public ChatLanguageModel createChatModel(ChatModelConfig modelConfig) {
return OpenAiChatModel.builder().baseUrl(modelConfig.getBaseUrl())
OpenAiChatModel.OpenAiChatModelBuilder openAiChatModelBuilder = OpenAiChatModel.builder()
.baseUrl(modelConfig.getBaseUrl()).modelName(modelConfig.getModelName())
.apiKey(modelConfig.keyDecrypt()).apiVersion(modelConfig.getApiVersion())
.temperature(modelConfig.getTemperature()).topP(modelConfig.getTopP())
.maxRetries(modelConfig.getMaxRetries())
.timeout(Duration.ofSeconds(modelConfig.getTimeOut()))
.logRequests(modelConfig.getLogRequests())
.logResponses(modelConfig.getLogResponses());
if (modelConfig.getJsonFormat() != null && modelConfig.getJsonFormat()) {
openAiChatModelBuilder.strictJsonSchema(true)
.responseFormat(modelConfig.getJsonFormatType());
}
return openAiChatModelBuilder.build();
}
@Override
public OpenAiStreamingChatModel createChatStreamingModel(ChatModelConfig modelConfig) {
return OpenAiStreamingChatModel.builder().baseUrl(modelConfig.getBaseUrl())
.modelName(modelConfig.getModelName()).apiKey(modelConfig.keyDecrypt())
.apiVersion(modelConfig.getApiVersion()).temperature(modelConfig.getTemperature())
.topP(modelConfig.getTopP()).maxRetries(modelConfig.getMaxRetries())
.temperature(modelConfig.getTemperature()).topP(modelConfig.getTopP())
.timeout(Duration.ofSeconds(modelConfig.getTimeOut()))
.logRequests(modelConfig.getLogRequests())
.logResponses(modelConfig.getLogResponses()).build();

View File

@@ -42,6 +42,6 @@ public class TextSegmentConvert {
if (Objects.isNull(textSegment) || Objects.isNull(textSegment.metadata())) {
return null;
}
return textSegment.metadata().get(QUERY_ID);
return textSegment.metadata().getString(QUERY_ID);
}
}

View File

@@ -338,8 +338,8 @@ class SqlAddHelperTest {
List<String> groupByFields = new ArrayList<>();
groupByFields.add("department");
Set<String> fieldNames = new HashSet<>();
fieldNames.add("pv");
Map<String, String> fieldNames = new HashMap<>();
fieldNames.put("pv", "sum");
String replaceSql = SqlAddHelper.addHaving(sql, fieldNames);
@@ -355,6 +355,14 @@ class SqlAddHelperTest {
Assert.assertEquals("SELECT department, sum(pv) FROM t_1 WHERE sys_imp_date = '2023-09-11' "
+ "GROUP BY department HAVING sum(pv) > 2000 ORDER BY sum(pv) DESC LIMIT 10",
replaceSql);
sql = "SELECT 数据日期,访问用户数 FROM 超音数数据集 WHERE 访问次数 > 10 GROUP BY 数据日期";
fieldNames.put("访问次数", "sum");
replaceSql = SqlAddHelper.addHaving(sql, fieldNames);
Assert.assertEquals("SELECT 数据日期, 访问用户数 FROM 超音数数据集 GROUP BY 数据日期 HAVING 访问次数 > 10",
replaceSql);
}
@Test

View File

@@ -3,7 +3,11 @@ package com.tencent.supersonic.headless.api.pojo.request;
import com.google.common.collect.Lists;
import com.tencent.supersonic.common.jsqlparser.SqlAddHelper;
import com.tencent.supersonic.common.jsqlparser.SqlReplaceHelper;
import com.tencent.supersonic.common.pojo.*;
import com.tencent.supersonic.common.pojo.Aggregator;
import com.tencent.supersonic.common.pojo.Constants;
import com.tencent.supersonic.common.pojo.DateConf;
import com.tencent.supersonic.common.pojo.Filter;
import com.tencent.supersonic.common.pojo.Order;
import com.tencent.supersonic.common.pojo.enums.AggOperatorEnum;
import com.tencent.supersonic.common.pojo.enums.QueryType;
import com.tencent.supersonic.common.util.ContextUtils;
@@ -21,14 +25,22 @@ import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.statement.select.GroupByElement;
import net.sf.jsqlparser.statement.select.Limit;
import net.sf.jsqlparser.statement.select.Offset;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.SelectItem;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@Data
@@ -176,7 +188,7 @@ public class QueryStructReq extends SemanticQueryReq {
private List<SelectItem<?>> buildSelectItems(QueryStructReq queryStructReq) {
List<SelectItem<?>> selectItems = new ArrayList<>();
List<String> groups = queryStructReq.getGroups();
Set<String> groups = new HashSet<>(queryStructReq.getGroups());
if (!CollectionUtils.isEmpty(groups)) {
for (String group : groups) {
@@ -236,7 +248,7 @@ public class QueryStructReq extends SemanticQueryReq {
}
private GroupByElement buildGroupByElement(QueryStructReq queryStructReq) {
List<String> groups = queryStructReq.getGroups();
Set<String> groups = new HashSet<>(queryStructReq.getGroups());
if ((!CollectionUtils.isEmpty(groups) && !queryStructReq.getAggregators().isEmpty())
|| !queryStructReq.getMetricFilters().isEmpty()) {
GroupByElement groupByElement = new GroupByElement();

View File

@@ -3,6 +3,7 @@ package com.tencent.supersonic.headless.chat.corrector;
import com.tencent.supersonic.common.jsqlparser.SqlAddHelper;
import com.tencent.supersonic.common.jsqlparser.SqlSelectFunctionHelper;
import com.tencent.supersonic.common.jsqlparser.SqlSelectHelper;
import com.tencent.supersonic.headless.api.pojo.SchemaElement;
import com.tencent.supersonic.headless.api.pojo.SemanticParseInfo;
import com.tencent.supersonic.headless.api.pojo.SemanticSchema;
import com.tencent.supersonic.headless.chat.ChatQueryContext;
@@ -11,7 +12,8 @@ import net.sf.jsqlparser.expression.Expression;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
/** Perform SQL corrections on the "Having" section in S2SQL. */
@@ -29,8 +31,9 @@ public class HavingCorrector extends BaseSemanticCorrector {
SemanticSchema semanticSchema = chatQueryContext.getSemanticSchema();
Set<String> metrics = semanticSchema.getMetrics(dataSet).stream()
.map(schemaElement -> schemaElement.getName()).collect(Collectors.toSet());
Map<String, String> metrics = semanticSchema.getMetrics(dataSet).stream()
.collect(Collectors.toMap(SchemaElement::getName,
e -> Optional.ofNullable(e.getDefaultAgg()).orElse("")));
if (CollectionUtils.isEmpty(metrics)) {
return;

View File

@@ -33,6 +33,7 @@ import java.util.Objects;
import java.util.PriorityQueue;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.PriorityBlockingQueue;
import static com.hankcs.hanlp.utility.Predefine.logger;
@@ -40,7 +41,7 @@ public class MultiCustomDictionary extends DynamicCustomDictionary {
public static int MAX_SIZE = 10;
public static Boolean removeDuplicates = true;
public static ConcurrentHashMap<String, PriorityQueue<Term>> NATURE_TO_VALUES =
public static ConcurrentHashMap<String, PriorityBlockingQueue<Term>> NATURE_TO_VALUES =
new ConcurrentHashMap<>();
private static boolean addToSuggesterTrie = true;
@@ -116,9 +117,17 @@ public class MultiCustomDictionary extends DynamicCustomDictionary {
dictWord.setAlias(word.toLowerCase());
String[] split = nature.split(DictWordType.NATURE_SPILT);
if (split.length >= 2) {
try {
Long dimId = Long.parseLong(
nature.split(DictWordType.NATURE_SPILT)[split.length - 1]);
KnowledgeBaseService.addDimValueAlias(dimId, Arrays.asList(dictWord));
KnowledgeBaseService.addDimValueAlias(dimId,
Arrays.asList(dictWord));
} catch (NumberFormatException e) {
logger.warning(path + " : 非标准文件不存入KnowledgeBaseService");
return true;
}
}
}
}
@@ -146,9 +155,10 @@ public class MultiCustomDictionary extends DynamicCustomDictionary {
}
for (int i = 0; i < attribute.nature.length; i++) {
Nature nature = attribute.nature[i];
PriorityQueue<Term> priorityQueue = NATURE_TO_VALUES.get(nature.toString());
PriorityBlockingQueue<Term> priorityQueue =
NATURE_TO_VALUES.get(nature.toString());
if (Objects.isNull(priorityQueue)) {
priorityQueue = new PriorityQueue<>(MAX_SIZE,
priorityQueue = new PriorityBlockingQueue<>(MAX_SIZE,
Comparator.comparingInt(Term::getFrequency).reversed());
NATURE_TO_VALUES.put(nature.toString(), priorityQueue);
}

View File

@@ -24,6 +24,7 @@ import java.util.PriorityQueue;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.stream.Collectors;
@Slf4j
@@ -200,7 +201,7 @@ public class SearchService {
public static List<String> getDimensionValue(DimensionValueReq dimensionValueReq) {
String nature = DictWordType.NATURE_SPILT + dimensionValueReq.getModelId()
+ DictWordType.NATURE_SPILT + dimensionValueReq.getElementID();
PriorityQueue<Term> terms = MultiCustomDictionary.NATURE_TO_VALUES.get(nature);
PriorityBlockingQueue<Term> terms = MultiCustomDictionary.NATURE_TO_VALUES.get(nature);
if (CollectionUtils.isEmpty(terms)) {
return new ArrayList<>();
}

View File

@@ -12,12 +12,17 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@Service
@Slf4j
@@ -72,18 +77,39 @@ public abstract class BaseMatchStrategy<T extends MapResult> implements MatchStr
}
}
protected void executeTasks(List<Callable<Void>> tasks) {
try {
executor.invokeAll(tasks);
for (Callable<Void> future : tasks) {
future.call();
protected Set<T> executeTasks(List<Supplier<List<T>>> tasks) {
Function<Supplier<List<T>>, Supplier<List<T>>> decorator = taskDecorator();
List<CompletableFuture<List<T>>> futures;
if (decorator == null) {
futures = tasks.stream().map(t -> CompletableFuture.supplyAsync(t, executor)).toList();
} else {
futures = tasks.stream()
.map(t -> CompletableFuture.supplyAsync(decorator.apply(t), executor)).toList();
}
} catch (Exception e) {
CompletableFuture<List<T>> listCompletableFuture =
CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[0]))
.thenApply(v -> futures.stream()
.flatMap(listFuture -> listFuture.join().stream())
.collect(Collectors.toList()));
try {
List<T> ts = listCompletableFuture.get();
Set<T> results = new HashSet<>();
selectResultInOneRound(results, ts);
return results;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Task execution interrupted", e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
public Function<Supplier<List<T>>, Supplier<List<T>>> taskDecorator() {
return null;
}
public double getThreshold(Double threshold, Double minThreshold, MapModeEnum mapModeEnum) {
if (MapModeEnum.STRICT.equals(mapModeEnum)) {
return 1.0d;

View File

@@ -17,6 +17,8 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
@@ -76,6 +78,22 @@ public class DatabaseMatchStrategy extends SingleMatchStrategy<DatabaseMapResult
return allElements;
}
@Override
public Function<Supplier<List<DatabaseMapResult>>, Supplier<List<DatabaseMapResult>>> taskDecorator() {
List<SchemaElement> schemaElements = allElements.get();
if (CollectionUtils.isEmpty(schemaElements)) {
return null;
}
return (t) -> (Supplier<List<DatabaseMapResult>>) () -> {
try {
allElements.set(schemaElements);
return t.get();
} finally {
allElements.remove();
}
};
}
private Double getThreshold(ChatQueryContext chatQueryContext) {
Double threshold =
Double.valueOf(mapperConfig.getParameterValue(MapperConfig.MAPPER_NAME_THRESHOLD));

View File

@@ -24,8 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import static com.tencent.supersonic.headless.chat.mapper.MapperConfig.*;
@@ -141,7 +140,6 @@ public class EmbeddingMatchStrategy extends BatchMatchStrategy<EmbeddingResult>
*/
public List<EmbeddingResult> detectByBatch(ChatQueryContext chatQueryContext,
Set<Long> detectDataSetIds, Set<String> detectSegments, boolean useLlm) {
Set<EmbeddingResult> results = ConcurrentHashMap.newKeySet();
int embeddingMapperBatch = Integer
.valueOf(mapperConfig.getParameterValue(MapperConfig.EMBEDDING_MAPPER_BATCH));
@@ -154,12 +152,11 @@ public class EmbeddingMatchStrategy extends BatchMatchStrategy<EmbeddingResult>
Lists.partition(queryTextsList, embeddingMapperBatch);
// Create and execute tasks for each batch
List<Callable<Void>> tasks = new ArrayList<>();
List<Supplier<List<EmbeddingResult>>> tasks = new ArrayList<>();
for (List<String> queryTextsSub : queryTextsSubList) {
tasks.add(
createTask(chatQueryContext, detectDataSetIds, queryTextsSub, results, useLlm));
tasks.add(createTask(chatQueryContext, detectDataSetIds, queryTextsSub, useLlm));
}
executeTasks(tasks);
Set<EmbeddingResult> results = executeTasks(tasks);
// Apply LLM filtering if enabled
if (useLlm) {
@@ -168,11 +165,11 @@ public class EmbeddingMatchStrategy extends BatchMatchStrategy<EmbeddingResult>
variable.put("retrievedInfo", JSONObject.toJSONString(results));
Prompt prompt = PromptTemplate.from(LLM_FILTER_PROMPT).apply(variable);
ChatModelConfig chatModelConfig = null;
if(chatQueryContext.getRequest().getChatAppConfig()!=null
&& chatQueryContext.getRequest().getChatAppConfig().containsKey("REWRITE_MULTI_TURN")){
chatModelConfig=chatQueryContext.getRequest().getChatAppConfig().get("REWRITE_MULTI_TURN").getChatModelConfig();
if (chatQueryContext.getRequest().getChatAppConfig() != null && chatQueryContext
.getRequest().getChatAppConfig().containsKey("REWRITE_MULTI_TURN")) {
chatModelConfig = chatQueryContext.getRequest().getChatAppConfig()
.get("REWRITE_MULTI_TURN").getChatModelConfig();
}
ChatLanguageModel chatLanguageModel = ModelProvider.getChatModel(chatModelConfig);
String response = chatLanguageModel.generate(prompt.toUserMessage().singleText());
@@ -196,20 +193,13 @@ public class EmbeddingMatchStrategy extends BatchMatchStrategy<EmbeddingResult>
* @param chatQueryContext The context of the chat query
* @param detectDataSetIds Target dataset IDs
* @param queryTextsSub Sub-list of query texts to process
* @param results Shared result set for collecting results
* @param useLlm Whether to use LLM
* @return Callable task
* @return Supplier task
*/
private Callable<Void> createTask(ChatQueryContext chatQueryContext, Set<Long> detectDataSetIds,
List<String> queryTextsSub, Set<EmbeddingResult> results, boolean useLlm) {
return () -> {
List<EmbeddingResult> oneRoundResults = detectByQueryTextsSub(detectDataSetIds,
queryTextsSub, chatQueryContext, useLlm);
synchronized (results) {
selectResultInOneRound(results, oneRoundResults);
}
return null;
};
private Supplier<List<EmbeddingResult>> createTask(ChatQueryContext chatQueryContext,
Set<Long> detectDataSetIds, List<String> queryTextsSub, boolean useLlm) {
return () -> detectByQueryTextsSub(detectDataSetIds, queryTextsSub, chatQueryContext,
useLlm);
}
/**

View File

@@ -1,10 +1,7 @@
package com.tencent.supersonic.headless.chat.mapper;
import com.tencent.supersonic.common.util.ContextUtils;
import com.tencent.supersonic.headless.api.pojo.SchemaElement;
import com.tencent.supersonic.headless.api.pojo.SchemaElementMatch;
import com.tencent.supersonic.headless.api.pojo.SchemaElementType;
import com.tencent.supersonic.headless.api.pojo.SchemaMapInfo;
import com.tencent.supersonic.headless.api.pojo.*;
import com.tencent.supersonic.headless.api.pojo.response.S2Term;
import com.tencent.supersonic.headless.chat.ChatQueryContext;
import com.tencent.supersonic.headless.chat.knowledge.DatabaseMapResult;
@@ -90,13 +87,15 @@ public class KeywordMapper extends BaseMapper {
.similarity(hanlpMapResult.getSimilarity())
.detectWord(hanlpMapResult.getDetectWord()).build();
// doDimValueAliasLogic 将维度值别名进行替换成真实维度值
doDimValueAliasLogic(schemaElementMatch);
doDimValueAliasLogic(schemaElementMatch,
chatQueryContext.getSemanticSchema().getDimensionValues());
addToSchemaMap(chatQueryContext.getMapInfo(), dataSetId, schemaElementMatch);
}
}
}
private void doDimValueAliasLogic(SchemaElementMatch schemaElementMatch) {
private void doDimValueAliasLogic(SchemaElementMatch schemaElementMatch,
List<SchemaElement> dimensionValues) {
SchemaElement element = schemaElementMatch.getElement();
if (SchemaElementType.VALUE.equals(element.getType())) {
Long dimId = element.getId();
@@ -112,6 +111,18 @@ public class KeywordMapper extends BaseMapper {
schemaElementMatch.setWord(wordTech);
}
}
SchemaElement dimensionValue = dimensionValues.stream()
.filter(dimValue -> dimId.equals(dimValue.getId())).findFirst().orElse(null);
if (dimensionValue != null) {
SchemaValueMap dimValue =
dimensionValue.getSchemaValueMaps().stream().filter(schemaValueMap -> {
return StringUtils.equals(schemaValueMap.getBizName(), word)
|| schemaValueMap.getAlias().contains(word);
}).findFirst().orElse(null);
if (dimValue != null) {
schemaElementMatch.setWord(dimValue.getTechName());
}
}
}
}

View File

@@ -11,8 +11,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
@Service
@Slf4j
@@ -26,8 +25,7 @@ public abstract class SingleMatchStrategy<T extends MapResult> extends BaseMatch
Set<Long> detectDataSetIds) {
Map<Integer, Integer> regOffsetToLength = mapperHelper.getRegOffsetToLength(terms);
String text = chatQueryContext.getRequest().getQueryText();
Set<T> results = ConcurrentHashMap.newKeySet();
List<Callable<Void>> tasks = new ArrayList<>();
List<Supplier<List<T>>> tasks = new ArrayList<>();
for (int startIndex = 0; startIndex <= text.length() - 1;) {
for (int index = startIndex; index <= text.length();) {
@@ -35,27 +33,20 @@ public abstract class SingleMatchStrategy<T extends MapResult> extends BaseMatch
index = mapperHelper.getStepIndex(regOffsetToLength, index);
if (index <= text.length()) {
String detectSegment = text.substring(startIndex, index).trim();
Callable<Void> task = createTask(chatQueryContext, detectDataSetIds,
detectSegment, offset, results);
Supplier<List<T>> task =
createTask(chatQueryContext, detectDataSetIds, detectSegment, offset);
tasks.add(task);
}
}
startIndex = mapperHelper.getStepIndex(regOffsetToLength, startIndex);
}
executeTasks(tasks);
Set<T> results = executeTasks(tasks);
return new ArrayList<>(results);
}
private Callable<Void> createTask(ChatQueryContext chatQueryContext, Set<Long> detectDataSetIds,
String detectSegment, int offset, Set<T> results) {
return () -> {
List<T> oneRoundResults =
detectByStep(chatQueryContext, detectDataSetIds, detectSegment, offset);
synchronized (results) {
selectResultInOneRound(results, oneRoundResults);
}
return null;
};
private Supplier<List<T>> createTask(ChatQueryContext chatQueryContext,
Set<Long> detectDataSetIds, String detectSegment, int offset) {
return () -> detectByStep(chatQueryContext, detectDataSetIds, detectSegment, offset);
}
public abstract List<T> detectByStep(ChatQueryContext chatQueryContext,

View File

@@ -57,6 +57,10 @@ public class ParserConfig extends ParameterConfig {
new Parameter("s2.parser.field.count.threshold", "0", "语义字段个数阈值",
"如果映射字段小于该阈值则将数据集所有字段输入LLM", "number", "语义解析配置");
public static final Parameter PARSER_FORMAT_JSON_TYPE =
new Parameter("s2.parser.format.json-type", "", "请求llm返回json格式,默认不设置json格式",
"选项json_schema或者json_object", "string", "语义解析配置");
@Override
public List<Parameter> getSysParameters() {
return Lists.newArrayList(PARSER_LINKING_VALUE_ENABLE, PARSER_RULE_CORRECTOR_ENABLE,

View File

@@ -2,9 +2,11 @@ package com.tencent.supersonic.headless.chat.parser.llm;
import com.google.common.collect.Lists;
import com.tencent.supersonic.common.pojo.ChatApp;
import com.tencent.supersonic.common.pojo.ChatModelConfig;
import com.tencent.supersonic.common.pojo.Text2SQLExemplar;
import com.tencent.supersonic.common.pojo.enums.AppModule;
import com.tencent.supersonic.common.util.ChatAppManager;
import com.tencent.supersonic.headless.chat.parser.ParserConfig;
import com.tencent.supersonic.headless.chat.query.llm.s2sql.LLMReq;
import com.tencent.supersonic.headless.chat.query.llm.s2sql.LLMResp;
import dev.langchain4j.model.chat.ChatLanguageModel;
@@ -14,9 +16,11 @@ import dev.langchain4j.model.output.structured.Description;
import dev.langchain4j.service.AiServices;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
@@ -24,6 +28,8 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.tencent.supersonic.headless.chat.parser.ParserConfig.PARSER_FORMAT_JSON_TYPE;
@Service
@Slf4j
public class OnePassSCSqlGenStrategy extends SqlGenStrategy {
@@ -31,6 +37,10 @@ public class OnePassSCSqlGenStrategy extends SqlGenStrategy {
private static final Logger keyPipelineLog = LoggerFactory.getLogger("keyPipeline");
public static final String APP_KEY = "S2SQL_PARSER";
@Autowired
private ParserConfig parserConfig;
public static final String INSTRUCTION =
"#Role: You are a data analyst experienced in SQL languages."
+ "\n#Task: You will be provided with a natural language question asked by users,"
@@ -74,7 +84,13 @@ public class OnePassSCSqlGenStrategy extends SqlGenStrategy {
// 2.generate sql generation prompt for each self-consistency inference
ChatApp chatApp = llmReq.getChatAppConfig().get(APP_KEY);
ChatLanguageModel chatLanguageModel = getChatLanguageModel(chatApp.getChatModelConfig());
ChatModelConfig chatModelConfig = chatApp.getChatModelConfig();
if (!StringUtils.isBlank(parserConfig.getParameterValue(PARSER_FORMAT_JSON_TYPE))) {
chatModelConfig.setJsonFormat(true);
chatModelConfig
.setJsonFormatType(parserConfig.getParameterValue(PARSER_FORMAT_JSON_TYPE));
}
ChatLanguageModel chatLanguageModel = getChatLanguageModel(chatModelConfig);
SemanticSqlExtractor extractor =
AiServices.create(SemanticSqlExtractor.class, chatLanguageModel);

View File

@@ -17,6 +17,7 @@ import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.stream.Collectors;
import static com.tencent.supersonic.common.pojo.DimensionConstants.*;
import static com.tencent.supersonic.headless.chat.parser.ParserConfig.*;
@Component
@@ -142,6 +143,10 @@ public class PromptHelper {
dimension.getAlias().forEach(a -> alias.append(a).append(";"));
dimensionStr.append(" ALIAS '").append(alias).append("'");
}
if (Objects.nonNull(dimension.getExtInfo().get(DIMENSION_DATA_TYPE))) {
dimensionStr.append(" DATATYPE '")
.append(dimension.getExtInfo().get(DIMENSION_DATA_TYPE)).append("'");
}
if (StringUtils.isNotEmpty(dimension.getTimeFormat())) {
dimensionStr.append(" FORMAT '").append(dimension.getTimeFormat()).append("'");
}

View File

@@ -32,7 +32,7 @@ public class TimeRangeParser implements SemanticParser {
private static final Pattern RECENT_PATTERN_CN = Pattern.compile(
".*(?<periodStr>(近|过去)((?<enNum>\\d+)|(?<zhNum>[一二三四五六七八九十百千万亿]+))个?(?<zhPeriod>[天周月年])).*");
private static final Pattern DATE_PATTERN_NUMBER = Pattern.compile("(\\d{8})");
private static final Pattern DATE_PATTERN_NUMBER = Pattern.compile("\\b(\\d{8})\\b");
private static final DateFormat DATE_FORMAT_NUMBER = new SimpleDateFormat("yyyyMMdd");
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");

View File

@@ -47,24 +47,20 @@ public class SqlQueryParser implements QueryParser {
SqlQuery sqlQuery = queryStatement.getSqlQuery();
List<String> queryFields = SqlSelectHelper.getAllSelectFields(sqlQuery.getSql());
Set<String> queryAliases = SqlSelectHelper.getAliasFields(sqlQuery.getSql());
Set<String> ontologyMetricsDimensions = Collections.synchronizedSet(new HashSet<String>());
Set<String> ontologyBizNameMetricsDimensions = Collections.synchronizedSet(new HashSet<>());
List<Pair<String, String>> ontologyMetricsDimensionsAndBizName =
Collections.synchronizedList(new ArrayList<>());
queryFields.removeAll(queryAliases);
Ontology ontology = queryStatement.getOntology();
OntologyQuery ontologyQuery = buildOntologyQuery(ontology, queryFields);
Set<String> queryFieldsSet = new HashSet<>(queryFields);
ontologyQuery.getMetrics().forEach(m -> {
ontologyMetricsDimensions.add(m.getName());
ontologyBizNameMetricsDimensions.add(m.getBizName());
ontologyMetricsDimensionsAndBizName.add(Pair.of(m.getName(), m.getBizName()));
});
ontologyQuery.getDimensions().forEach(d -> {
ontologyMetricsDimensions.add(d.getName());
ontologyBizNameMetricsDimensions.add(d.getBizName());
ontologyMetricsDimensionsAndBizName.add(Pair.of(d.getName(), d.getBizName()));
});
// check if there are fields not matched with any metric or dimension
if (!(queryFieldsSet.containsAll(ontologyMetricsDimensions)
|| queryFieldsSet.containsAll(ontologyBizNameMetricsDimensions))) {
if (!allFieldMatched(queryFieldsSet, ontologyMetricsDimensionsAndBizName)) {
List<String> semanticFields = Lists.newArrayList();
ontologyQuery.getMetrics().forEach(m -> semanticFields.add(m.getName()));
ontologyQuery.getDimensions().forEach(d -> semanticFields.add(d.getName()));
@@ -103,6 +99,16 @@ public class SqlQueryParser implements QueryParser {
log.info("parse sqlQuery [{}] ", sqlQuery);
}
private boolean allFieldMatched(Set<String> queryFields,
List<Pair<String, String>> ontologyMetricsDimensionsAndBizName) {
for (Pair<String, String> pair : ontologyMetricsDimensionsAndBizName) {
if (!(queryFields.contains(pair.getLeft()) || queryFields.contains(pair.getRight()))) {
return false;
}
}
return true;
}
private void aliasesWithBackticks(QueryStatement queryStatement) {
String sql = queryStatement.getSqlQuery().getSql();
sql = SqlReplaceHelper.replaceAliasWithBackticks(sql);

View File

@@ -124,7 +124,10 @@ public class DimValueAspect {
sql = SqlReplaceHelper.replaceValue(sql, filedNameToValueMap);
log.debug("correctorSql after replacing:{}", sql);
querySqlReq.setSql(sql);
if (StringUtils.isEmpty(querySqlReq.getSqlInfo().getParsedS2SQL())
&& StringUtils.isEmpty(querySqlReq.getSqlInfo().getCorrectedS2SQL())) {
querySqlReq.getSqlInfo().setQuerySQL(sql);
}
Map<String, Map<String, String>> techNameToBizName = getTechNameToBizName(dimensions);
SemanticQueryResp queryResultWithColumns = (SemanticQueryResp) joinPoint.proceed();

View File

@@ -13,5 +13,7 @@ public interface DimensionDOCustomMapper {
void batchUpdateStatus(List<DimensionDO> dimensionDOS);
void batchUpdate(List<DimensionDO> dimensionDOS);
List<DimensionDO> queryDimensions(DimensionsFilter dimensionsFilter);
}

View File

@@ -13,6 +13,8 @@ public interface MetricDOCustomMapper {
void batchUpdateStatus(List<MetricDO> metricDOS);
void batchUpdate(List<MetricDO> metricDOS);
void batchPublish(List<MetricDO> metricDOS);
void batchUnPublish(List<MetricDO> metricDOS);

View File

@@ -16,6 +16,8 @@ public interface DimensionRepository {
void batchUpdateStatus(List<DimensionDO> dimensionDOS);
void batchUpdate(List<DimensionDO> dimensionDOS);
DimensionDO getDimensionById(Long id);
List<DimensionDO> getDimension(DimensionFilter dimensionFilter);

View File

@@ -17,6 +17,8 @@ public interface MetricRepository {
void batchUpdateStatus(List<MetricDO> metricDOS);
void batchUpdateMetric(List<MetricDO> metricDOS);
void batchPublish(List<MetricDO> metricDOS);
void batchUnPublish(List<MetricDO> metricDOS);

View File

@@ -46,6 +46,11 @@ public class DimensionRepositoryImpl implements DimensionRepository {
dimensionDOCustomMapper.batchUpdateStatus(dimensionDOS);
}
@Override
public void batchUpdate(List<DimensionDO> dimensionDOS) {
dimensionDOCustomMapper.batchUpdate(dimensionDOS);
}
@Override
public DimensionDO getDimensionById(Long id) {
return dimensionDOMapper.selectById(id);

View File

@@ -53,6 +53,11 @@ public class MetricRepositoryImpl implements MetricRepository {
metricDOCustomMapper.batchUpdateStatus(metricDOS);
}
@Override
public void batchUpdateMetric(List<MetricDO> metricDOS) {
metricDOCustomMapper.batchUpdate(metricDOS);
}
@Override
public void batchPublish(List<MetricDO> metricDOS) {
metricDOCustomMapper.batchPublish(metricDOS);
@@ -109,14 +114,9 @@ public class MetricRepositoryImpl implements MetricRepository {
}
if (StringUtils.isNotBlank(metricFilter.getKey())) {
String key = metricFilter.getKey();
queryWrapper.lambda()
.and(wrapper -> wrapper
.like(MetricDO::getName, key)
.or().like(MetricDO::getBizName, key)
.or().like(MetricDO::getDescription, key)
.or().like(MetricDO::getAlias, key)
.or().like(MetricDO::getCreatedBy, key)
);
queryWrapper.lambda().and(wrapper -> wrapper.like(MetricDO::getName, key).or()
.like(MetricDO::getBizName, key).or().like(MetricDO::getDescription, key).or()
.like(MetricDO::getAlias, key).or().like(MetricDO::getCreatedBy, key));
}
return metricDOMapper.selectList(queryWrapper);

View File

@@ -46,8 +46,10 @@ public class ChatModelController {
}
@DeleteMapping("/{id}")
public boolean deleteModel(@PathVariable("id") Integer id) {
chatModelService.deleteChatModel(id);
public boolean deleteModel(@PathVariable("id") Integer id,
HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
User user = UserHolder.findUser(httpServletRequest, httpServletResponse);
chatModelService.deleteChatModel(id, user);
return true;
}

View File

@@ -51,7 +51,8 @@ public class DataSetController {
@GetMapping("/getDataSetList")
public List<DataSetResp> getDataSetList(@RequestParam("domainId") Long domainId) {
List<Integer> statuCodeList = Arrays.asList(StatusEnum.ONLINE.getCode(),StatusEnum.OFFLINE.getCode());
List<Integer> statuCodeList =
Arrays.asList(StatusEnum.ONLINE.getCode(), StatusEnum.OFFLINE.getCode());
return dataSetService.getDataSetList(domainId, statuCodeList);
}

View File

@@ -64,8 +64,10 @@ public class DatabaseController {
}
@DeleteMapping("/{id}")
public boolean deleteDatabase(@PathVariable("id") Long id) {
databaseService.deleteDatabase(id);
public boolean deleteDatabase(@PathVariable("id") Long id, HttpServletRequest request,
HttpServletResponse response) {
User user = UserHolder.findUser(request, response);
databaseService.deleteDatabase(id, user);
return true;
}

View File

@@ -34,7 +34,7 @@ public interface DatabaseService {
List<DatabaseResp> getDatabaseList(User user);
void deleteDatabase(Long databaseId);
void deleteDatabase(Long databaseId, User user);
List<String> getCatalogs(Long id) throws SQLException;

View File

@@ -27,10 +27,15 @@ public interface DimensionService {
DimensionResp createDimension(DimensionReq dimensionReq, User user) throws Exception;
void alterDimensionBatch(List<DimensionReq> dimensionReqs, Long modelId, User user)
throws Exception;
void createDimensionBatch(List<DimensionReq> dimensionReqs, User user) throws Exception;
void updateDimension(DimensionReq dimensionReq, User user) throws Exception;
void updateDimensionBatch(List<DimensionReq> dimensionReqs, User user) throws Exception;
PageInfo<DimensionResp> queryDimension(PageDimensionReq pageDimensionReq);
List<DimensionResp> queryDimensions(DimensionsFilter dimensionsFilter);
@@ -39,13 +44,15 @@ public interface DimensionService {
void deleteDimension(Long id, User user);
void deleteDimensionBatch(List<Long> idList, User user);
List<DimensionResp> getDimensionInModelCluster(Long modelId);
List<String> mockAlias(DimensionReq dimensionReq, String mockType, User user);
List<DimValueMap> mockDimensionValueAlias(DimensionReq dimensionReq, User user);
void sendDimensionEventBatch(List<Long> modelIds, EventType eventType);
void sendDimensionEventBatch(List<Long> modelIds, EventType eventType, User user);
DataEvent getAllDataEvents();

View File

@@ -26,8 +26,12 @@ public interface MetricService {
void createMetricBatch(List<MetricReq> metricReqs, User user) throws Exception;
void alterMetricBatch(List<MetricReq> metricReqs, Long modelId, User user) throws Exception;
MetricResp updateMetric(MetricReq metricReq, User user) throws Exception;
void updateMetricBatch(List<MetricReq> metricReqs, User user) throws Exception;
void batchUpdateStatus(MetaBatchReq metaBatchReq, User user);
void batchPublish(List<Long> metricIds, User user);
@@ -40,6 +44,8 @@ public interface MetricService {
void deleteMetric(Long id, User user) throws Exception;
void deleteMetricBatch(List<Long> idList, User user);
PageInfo<MetricResp> queryMetricMarket(PageMetricReq pageMetricReq, User user);
PageInfo<MetricResp> queryMetric(PageMetricReq pageMetricReq, User user);
@@ -64,7 +70,7 @@ public interface MetricService {
MetricQueryDefaultConfig getMetricQueryDefaultConfig(Long metricId, User user);
void sendMetricEventBatch(List<Long> modelIds, EventType eventType);
void sendMetricEventBatch(List<Long> modelIds, EventType eventType, User user);
List<MetricResp> queryMetrics(MetricsFilter metricsFilter);

View File

@@ -11,6 +11,8 @@ import com.tencent.supersonic.headless.api.pojo.request.*;
import com.tencent.supersonic.headless.api.pojo.response.DatabaseResp;
import com.tencent.supersonic.headless.api.pojo.response.ModelResp;
import com.tencent.supersonic.headless.api.pojo.response.UnAvailableItemResp;
import com.tencent.supersonic.headless.server.persistence.dataobject.DimensionDO;
import com.tencent.supersonic.headless.server.persistence.dataobject.MetricDO;
import com.tencent.supersonic.headless.server.pojo.ModelFilter;
import java.sql.SQLException;
@@ -53,5 +55,9 @@ public interface ModelService {
void batchUpdateStatus(MetaBatchReq metaBatchReq, User user);
Dimension updateDimension(DimensionReq dimensionReq, User user);
void updateModelByDimAndMetric(Long modelId, List<DimensionReq> dimensionReqList,
List<MetricReq> metricReqList, User user);
void deleteModelDetailByDimAndMetric(Long modelId, List<DimensionDO> dimensionReqList,
List<MetricDO> metricReqList);
}

View File

@@ -138,7 +138,12 @@ public class DatabaseServiceImpl extends ServiceImpl<DatabaseDOMapper, DatabaseD
}
@Override
public void deleteDatabase(Long databaseId) {
public void deleteDatabase(Long databaseId, User user) {
DatabaseResp databaseResp = getDatabase(databaseId);
if (!checkAdminPermission(user, databaseResp)) {
throw new RuntimeException("没有权限删除该数据库");
}
ModelFilter modelFilter = new ModelFilter();
modelFilter.setDatabaseId(databaseId);
modelFilter.setIncludesDetail(false);

View File

@@ -48,7 +48,7 @@ public class DictWordService {
return;
}
setPreDictWords(dictWords);
knowledgeBaseService.reloadAllData(getAllDictWords());
knowledgeBaseService.reloadAllData(dictWords);
long duration = System.currentTimeMillis() - startTime;
log.info("Dictionary has been regularly reloaded in {} milliseconds", duration);
}

View File

@@ -8,7 +8,10 @@ import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.tencent.supersonic.common.pojo.*;
import com.tencent.supersonic.common.pojo.DataEvent;
import com.tencent.supersonic.common.pojo.DataItem;
import com.tencent.supersonic.common.pojo.ModelRela;
import com.tencent.supersonic.common.pojo.User;
import com.tencent.supersonic.common.pojo.enums.EventType;
import com.tencent.supersonic.common.pojo.enums.StatusEnum;
import com.tencent.supersonic.common.pojo.enums.TypeEnums;
@@ -22,14 +25,22 @@ import com.tencent.supersonic.headless.api.pojo.request.DimValueAliasReq;
import com.tencent.supersonic.headless.api.pojo.request.DimensionReq;
import com.tencent.supersonic.headless.api.pojo.request.MetaBatchReq;
import com.tencent.supersonic.headless.api.pojo.request.PageDimensionReq;
import com.tencent.supersonic.headless.api.pojo.response.*;
import com.tencent.supersonic.headless.api.pojo.response.DataSetResp;
import com.tencent.supersonic.headless.api.pojo.response.DatabaseResp;
import com.tencent.supersonic.headless.api.pojo.response.DimensionResp;
import com.tencent.supersonic.headless.api.pojo.response.ModelResp;
import com.tencent.supersonic.headless.api.pojo.response.SemanticQueryResp;
import com.tencent.supersonic.headless.server.persistence.dataobject.DimensionDO;
import com.tencent.supersonic.headless.server.persistence.mapper.DimensionDOMapper;
import com.tencent.supersonic.headless.server.persistence.repository.DimensionRepository;
import com.tencent.supersonic.headless.server.pojo.DimensionFilter;
import com.tencent.supersonic.headless.server.pojo.DimensionsFilter;
import com.tencent.supersonic.headless.server.pojo.ModelFilter;
import com.tencent.supersonic.headless.server.service.*;
import com.tencent.supersonic.headless.server.service.DataSetService;
import com.tencent.supersonic.headless.server.service.DatabaseService;
import com.tencent.supersonic.headless.server.service.DimensionService;
import com.tencent.supersonic.headless.server.service.ModelRelaService;
import com.tencent.supersonic.headless.server.service.ModelService;
import com.tencent.supersonic.headless.server.utils.AliasGenerateHelper;
import com.tencent.supersonic.headless.server.utils.DimensionConverter;
import com.tencent.supersonic.headless.server.utils.NameCheckUtils;
@@ -37,12 +48,16 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@Service
@@ -50,31 +65,31 @@ import java.util.stream.Collectors;
public class DimensionServiceImpl extends ServiceImpl<DimensionDOMapper, DimensionDO>
implements DimensionService {
private DimensionRepository dimensionRepository;
private final DimensionRepository dimensionRepository;
private ModelService modelService;
private final ModelService modelService;
private AliasGenerateHelper aliasGenerateHelper;
private final AliasGenerateHelper aliasGenerateHelper;
private DatabaseService databaseService;
private final DatabaseService databaseService;
private ModelRelaService modelRelaService;
private final ModelRelaService modelRelaService;
private DataSetService dataSetService;
private final DataSetService dataSetService;
@Autowired
private ApplicationEventPublisher eventPublisher;
private final ApplicationEventPublisher eventPublisher;
public DimensionServiceImpl(DimensionRepository dimensionRepository, ModelService modelService,
AliasGenerateHelper aliasGenerateHelper, DatabaseService databaseService,
ModelRelaService modelRelaService, DataSetService dataSetService) {
ModelRelaService modelRelaService, DataSetService dataSetService,
ApplicationEventPublisher eventPublisher) {
this.modelService = modelService;
this.dimensionRepository = dimensionRepository;
this.aliasGenerateHelper = aliasGenerateHelper;
this.databaseService = databaseService;
this.modelRelaService = modelRelaService;
this.dataSetService = dataSetService;
this.eventPublisher = eventPublisher;
}
@Override
@@ -83,53 +98,50 @@ public class DimensionServiceImpl extends ServiceImpl<DimensionDOMapper, Dimensi
dimensionReq.createdBy(user.getName());
DimensionDO dimensionDO = DimensionConverter.convert2DimensionDO(dimensionReq);
dimensionRepository.createDimension(dimensionDO);
sendEventBatch(Lists.newArrayList(dimensionDO), EventType.ADD);
sendEventBatch(Lists.newArrayList(dimensionDO), EventType.ADD, user);
// should update modelDetail
modelService.updateDimension(dimensionReq, user);
modelService.updateModelByDimAndMetric(dimensionReq.getModelId(),
Lists.newArrayList(dimensionReq), null, user);
return DimensionConverter.convert2DimensionResp(dimensionDO);
}
@Override
public void createDimensionBatch(List<DimensionReq> dimensionReqs, User user) {
if (CollectionUtils.isEmpty(dimensionReqs)) {
return;
}
Long modelId = dimensionReqs.get(0).getModelId();
public void alterDimensionBatch(List<DimensionReq> dimensionReqs, Long modelId, User user)
throws Exception {
List<DimensionResp> dimensionResps = getDimensions(modelId);
// get all dimension in model, only use bizname, because name can be changed to everything
Map<String, DimensionResp> bizNameMap = dimensionResps.stream()
.collect(Collectors.toMap(DimensionResp::getBizName, a -> a, (k1, k2) -> k1));
Map<String, DimensionResp> nameMap = dimensionResps.stream()
.collect(Collectors.toMap(DimensionResp::getName, a -> a, (k1, k2) -> k1));
List<DimensionReq> dimensionToInsert = Lists.newArrayList();
dimensionReqs.stream().forEach(dimension -> {
if (!bizNameMap.containsKey(dimension.getBizName())
&& !nameMap.containsKey(dimension.getName())) {
// look for which dimension need to insert, update, delete
dimensionReqs.forEach(dimension -> {
if (!bizNameMap.containsKey(dimension.getBizName())) {
dimensionToInsert.add(dimension);
} else {
DimensionResp dimensionRespByBizName = bizNameMap.get(dimension.getBizName());
DimensionResp dimensionRespByName = nameMap.get(dimension.getName());
if (null != dimensionRespByBizName && isChange(dimension, dimensionRespByBizName)) {
dimension.setId(dimensionRespByBizName.getId());
this.updateDimension(dimension, user);
} else {
if (null != dimensionRespByName && isChange(dimension, dimensionRespByName)) {
dimension.setId(dimensionRespByName.getId());
this.updateDimension(dimension, user);
}
}
}
});
if (CollectionUtils.isEmpty(dimensionToInsert)) {
return;
// insert
if (!CollectionUtils.isEmpty(dimensionToInsert)) {
createDimensionBatch(dimensionToInsert, user);
}
}
@Override
public void createDimensionBatch(List<DimensionReq> dimensionReqs, User user) {
List<DimensionDO> dimensionDOS =
dimensionToInsert.stream().peek(dimension -> dimension.createdBy(user.getName()))
dimensionReqs.stream().peek(dimension -> dimension.createdBy(user.getName()))
.map(DimensionConverter::convert2DimensionDO).collect(Collectors.toList());
dimensionRepository.createDimensionBatch(dimensionDOS);
sendEventBatch(dimensionDOS, EventType.ADD);
// should update modelDetail as well
modelService.updateModelByDimAndMetric(dimensionReqs.get(0).getModelId(), dimensionReqs,
null, user);
sendEventBatch(dimensionDOS, EventType.ADD, user);
}
@Override
@@ -141,13 +153,26 @@ public class DimensionServiceImpl extends ServiceImpl<DimensionDOMapper, Dimensi
DimensionConverter.convert(dimensionDO, dimensionReq);
dimensionRepository.updateDimension(dimensionDO);
// should update modelDetail as well
modelService.updateDimension(dimensionReq, user);
modelService.updateModelByDimAndMetric(dimensionReq.getModelId(),
Lists.newArrayList(dimensionReq), null, user);
if (!oldName.equals(dimensionDO.getName())) {
sendEvent(getDataItem(dimensionDO), EventType.UPDATE);
sendEvent(getDataItem(dimensionDO), EventType.UPDATE, user.getName());
}
}
@Override
public void updateDimensionBatch(List<DimensionReq> dimensionReqList, User user) {
checkExist(dimensionReqList);
List<DimensionDO> dimensionDOS = dimensionReqList.stream()
.map(DimensionConverter::convert2DimensionDO).collect(Collectors.toList());
dimensionRepository.batchUpdate(dimensionDOS);
// should update modelDetail as well
modelService.updateModelByDimAndMetric(dimensionReqList.get(0).getModelId(),
dimensionReqList, null, user);
sendEventBatch(dimensionDOS, EventType.UPDATE, user);
}
@Override
public void batchUpdateStatus(MetaBatchReq metaBatchReq, User user) {
if (CollectionUtils.isEmpty(metaBatchReq.getIds())) {
@@ -167,9 +192,9 @@ public class DimensionServiceImpl extends ServiceImpl<DimensionDOMapper, Dimensi
dimensionRepository.batchUpdateStatus(dimensionDOS);
if (StatusEnum.OFFLINE.getCode().equals(metaBatchReq.getStatus())
|| StatusEnum.DELETED.getCode().equals(metaBatchReq.getStatus())) {
sendEventBatch(dimensionDOS, EventType.DELETE);
sendEventBatch(dimensionDOS, EventType.DELETE, user);
} else if (StatusEnum.ONLINE.getCode().equals(metaBatchReq.getStatus())) {
sendEventBatch(dimensionDOS, EventType.ADD);
sendEventBatch(dimensionDOS, EventType.ADD, user);
}
}
@@ -199,7 +224,31 @@ public class DimensionServiceImpl extends ServiceImpl<DimensionDOMapper, Dimensi
dimensionDO.setUpdatedAt(new Date());
dimensionDO.setUpdatedBy(user.getName());
dimensionRepository.updateDimension(dimensionDO);
sendEventBatch(Lists.newArrayList(dimensionDO), EventType.DELETE);
// should update modelDetail
modelService.deleteModelDetailByDimAndMetric(dimensionDO.getModelId(),
Lists.newArrayList(dimensionDO), null);
sendEventBatch(Lists.newArrayList(dimensionDO), EventType.DELETE, user);
}
@Override
public void deleteDimensionBatch(List<Long> idList, User user) {
DimensionsFilter dimensionFilter = new DimensionsFilter();
dimensionFilter.setDimensionIds(idList);
List<DimensionDO> dimensionDOList = dimensionRepository.getDimensions(dimensionFilter);
if (CollectionUtils.isEmpty(dimensionDOList)) {
throw new RuntimeException(
String.format("the dimension %s not exist", StringUtils.join(",", idList)));
}
dimensionDOList.forEach(dimensionDO -> {
dimensionDO.setStatus(StatusEnum.DELETED.getCode());
dimensionDO.setUpdatedAt(new Date());
dimensionDO.setUpdatedBy(user.getName());
});
dimensionRepository.batchUpdateStatus(dimensionDOList);
// should update modelDetail
modelService.deleteModelDetailByDimAndMetric(dimensionDOList.get(0).getModelId(),
dimensionDOList, null);
sendEventBatch(dimensionDOList, EventType.DELETE, user);
}
@Override
@@ -400,22 +449,22 @@ public class DimensionServiceImpl extends ServiceImpl<DimensionDOMapper, Dimensi
}
@Override
public void sendDimensionEventBatch(List<Long> modelIds, EventType eventType) {
public void sendDimensionEventBatch(List<Long> modelIds, EventType eventType, User user) {
DimensionFilter dimensionFilter = new DimensionFilter();
dimensionFilter.setModelIds(modelIds);
List<DimensionDO> dimensionDOS = queryDimension(dimensionFilter);
sendEventBatch(dimensionDOS, eventType);
sendEventBatch(dimensionDOS, eventType, user);
}
private void sendEventBatch(List<DimensionDO> dimensionDOS, EventType eventType) {
DataEvent dataEvent = getDataEvent(dimensionDOS, eventType);
private void sendEventBatch(List<DimensionDO> dimensionDOS, EventType eventType, User user) {
DataEvent dataEvent = getDataEvent(dimensionDOS, eventType, user.getName());
eventPublisher.publishEvent(dataEvent);
}
public DataEvent getAllDataEvents() {
DimensionFilter dimensionFilter = new DimensionFilter();
List<DimensionDO> dimensionDOS = queryDimension(dimensionFilter);
return getDataEvent(dimensionDOS, EventType.ADD);
return getDataEvent(dimensionDOS, EventType.ADD, "");
}
@Override
@@ -457,6 +506,9 @@ public class DimensionServiceImpl extends ServiceImpl<DimensionDOMapper, Dimensi
private DataItem getDataItem(DimensionDO dimensionDO) {
ModelResp modelResp = modelService.getModel(dimensionDO.getModelId());
if (modelResp == null) {
return null;
}
DimensionResp dimensionResp = DimensionConverter.convert2DimensionResp(dimensionDO,
ImmutableMap.of(modelResp.getId(), modelResp));
return DataItem.builder().id(dimensionResp.getId().toString()).name(dimensionResp.getName())
@@ -464,14 +516,16 @@ public class DimensionServiceImpl extends ServiceImpl<DimensionDOMapper, Dimensi
.domainId(dimensionResp.getDomainId().toString()).type(TypeEnums.DIMENSION).build();
}
private DataEvent getDataEvent(List<DimensionDO> dimensionDOS, EventType eventType) {
List<DataItem> dataItems =
dimensionDOS.stream().map(this::getDataItem).collect(Collectors.toList());
return new DataEvent(this, dataItems, eventType);
private DataEvent getDataEvent(List<DimensionDO> dimensionDOS, EventType eventType,
String userName) {
List<DataItem> dataItems = dimensionDOS.stream().map(this::getDataItem)
.filter(Objects::nonNull).collect(Collectors.toList());
return new DataEvent(this, dataItems, eventType, userName);
}
private void sendEvent(DataItem dataItem, EventType eventType) {
eventPublisher.publishEvent(new DataEvent(this, Lists.newArrayList(dataItem), eventType));
private void sendEvent(DataItem dataItem, EventType eventType, String userName) {
eventPublisher.publishEvent(
new DataEvent(this, Lists.newArrayList(dataItem), eventType, userName));
}
private boolean isChange(DimensionReq dimensionReq, DimensionResp dimensionResp) {

View File

@@ -8,22 +8,57 @@ import com.github.pagehelper.PageInfo;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.tencent.supersonic.common.pojo.*;
import com.tencent.supersonic.common.pojo.enums.*;
import com.tencent.supersonic.common.pojo.Aggregator;
import com.tencent.supersonic.common.pojo.DataEvent;
import com.tencent.supersonic.common.pojo.DataItem;
import com.tencent.supersonic.common.pojo.DateConf;
import com.tencent.supersonic.common.pojo.Filter;
import com.tencent.supersonic.common.pojo.User;
import com.tencent.supersonic.common.pojo.enums.AuthType;
import com.tencent.supersonic.common.pojo.enums.EventType;
import com.tencent.supersonic.common.pojo.enums.QueryType;
import com.tencent.supersonic.common.pojo.enums.StatusEnum;
import com.tencent.supersonic.common.pojo.enums.TypeEnums;
import com.tencent.supersonic.common.util.BeanMapper;
import com.tencent.supersonic.headless.api.pojo.*;
import com.tencent.supersonic.headless.api.pojo.DrillDownDimension;
import com.tencent.supersonic.headless.api.pojo.Measure;
import com.tencent.supersonic.headless.api.pojo.MetaFilter;
import com.tencent.supersonic.headless.api.pojo.MetricParam;
import com.tencent.supersonic.headless.api.pojo.MetricQueryDefaultConfig;
import com.tencent.supersonic.headless.api.pojo.SchemaElementMatch;
import com.tencent.supersonic.headless.api.pojo.SchemaElementType;
import com.tencent.supersonic.headless.api.pojo.SchemaItem;
import com.tencent.supersonic.headless.api.pojo.enums.MapModeEnum;
import com.tencent.supersonic.headless.api.pojo.enums.MetricDefineType;
import com.tencent.supersonic.headless.api.pojo.request.*;
import com.tencent.supersonic.headless.api.pojo.response.*;
import com.tencent.supersonic.headless.api.pojo.request.MetaBatchReq;
import com.tencent.supersonic.headless.api.pojo.request.MetricBaseReq;
import com.tencent.supersonic.headless.api.pojo.request.MetricReq;
import com.tencent.supersonic.headless.api.pojo.request.PageMetricReq;
import com.tencent.supersonic.headless.api.pojo.request.QueryMapReq;
import com.tencent.supersonic.headless.api.pojo.request.QueryMetricReq;
import com.tencent.supersonic.headless.api.pojo.request.QueryStructReq;
import com.tencent.supersonic.headless.api.pojo.response.DataSetMapInfo;
import com.tencent.supersonic.headless.api.pojo.response.DataSetResp;
import com.tencent.supersonic.headless.api.pojo.response.DimensionResp;
import com.tencent.supersonic.headless.api.pojo.response.MapInfoResp;
import com.tencent.supersonic.headless.api.pojo.response.MetricResp;
import com.tencent.supersonic.headless.api.pojo.response.ModelResp;
import com.tencent.supersonic.headless.server.facade.service.ChatLayerService;
import com.tencent.supersonic.headless.server.persistence.dataobject.CollectDO;
import com.tencent.supersonic.headless.server.persistence.dataobject.MetricDO;
import com.tencent.supersonic.headless.server.persistence.dataobject.MetricQueryDefaultConfigDO;
import com.tencent.supersonic.headless.server.persistence.mapper.MetricDOMapper;
import com.tencent.supersonic.headless.server.persistence.repository.MetricRepository;
import com.tencent.supersonic.headless.server.pojo.*;
import com.tencent.supersonic.headless.server.service.*;
import com.tencent.supersonic.headless.server.pojo.DimensionsFilter;
import com.tencent.supersonic.headless.server.pojo.MetricFilter;
import com.tencent.supersonic.headless.server.pojo.MetricsFilter;
import com.tencent.supersonic.headless.server.pojo.ModelCluster;
import com.tencent.supersonic.headless.server.pojo.ModelFilter;
import com.tencent.supersonic.headless.server.service.CollectService;
import com.tencent.supersonic.headless.server.service.DataSetService;
import com.tencent.supersonic.headless.server.service.DimensionService;
import com.tencent.supersonic.headless.server.service.MetricService;
import com.tencent.supersonic.headless.server.service.ModelService;
import com.tencent.supersonic.headless.server.utils.AliasGenerateHelper;
import com.tencent.supersonic.headless.server.utils.MetricCheckUtils;
import com.tencent.supersonic.headless.server.utils.MetricConverter;
@@ -36,7 +71,18 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@@ -44,21 +90,21 @@ import java.util.stream.Collectors;
public class MetricServiceImpl extends ServiceImpl<MetricDOMapper, MetricDO>
implements MetricService {
private MetricRepository metricRepository;
private final MetricRepository metricRepository;
private ModelService modelService;
private final ModelService modelService;
private DimensionService dimensionService;
private final DimensionService dimensionService;
private AliasGenerateHelper aliasGenerateHelper;
private final AliasGenerateHelper aliasGenerateHelper;
private CollectService collectService;
private final CollectService collectService;
private DataSetService dataSetService;
private final DataSetService dataSetService;
private ApplicationEventPublisher eventPublisher;
private final ApplicationEventPublisher eventPublisher;
private ChatLayerService chatLayerService;
private final ChatLayerService chatLayerService;
public MetricServiceImpl(MetricRepository metricRepository, ModelService modelService,
AliasGenerateHelper aliasGenerateHelper, CollectService collectService,
@@ -77,57 +123,56 @@ public class MetricServiceImpl extends ServiceImpl<MetricDOMapper, MetricDO>
@Override
public MetricResp createMetric(MetricReq metricReq, User user) {
checkExist(Lists.newArrayList(metricReq));
MetricCheckUtils.checkParam(metricReq);
MetricCheckUtils.checkParam(Lists.newArrayList(metricReq));
metricReq.createdBy(user.getName());
MetricDO metricDO = MetricConverter.convert2MetricDO(metricReq);
metricRepository.createMetric(metricDO);
sendEventBatch(Lists.newArrayList(metricDO), EventType.ADD);
sendEventBatch(Lists.newArrayList(metricDO), EventType.ADD, user);
// should update modelDetail as well
modelService.updateModelByDimAndMetric(metricReq.getModelId(), null,
Lists.newArrayList(metricReq), user);
return MetricConverter.convert2MetricResp(metricDO);
}
@Override
public void createMetricBatch(List<MetricReq> metricReqs, User user) {
if (CollectionUtils.isEmpty(metricReqs)) {
return;
}
Long modelId = metricReqs.get(0).getModelId();
List<MetricResp> metricResps = getMetrics(new MetaFilter(Lists.newArrayList(modelId)));
Map<String, MetricResp> bizNameMap = metricResps.stream()
.collect(Collectors.toMap(MetricResp::getBizName, a -> a, (k1, k2) -> k1));
Map<String, MetricResp> nameMap = metricResps.stream()
.collect(Collectors.toMap(MetricResp::getName, a -> a, (k1, k2) -> k1));
List<MetricReq> metricToInsert = Lists.newArrayList();
metricReqs.stream().forEach(metric -> {
if (!bizNameMap.containsKey(metric.getBizName())
&& !nameMap.containsKey(metric.getName())) {
metricToInsert.add(metric);
} else {
MetricResp metricRespByBizName = bizNameMap.get(metric.getBizName());
MetricResp metricRespByName = nameMap.get(metric.getName());
if (null != metricRespByBizName) {
metric.setId(metricRespByBizName.getId());
this.updateMetric(metric, user);
} else {
if (null != metricRespByName) {
metric.setId(metricRespByName.getId());
this.updateMetric(metric, user);
}
}
}
});
if (CollectionUtils.isEmpty(metricToInsert)) {
return;
}
List<MetricDO> metricDOS =
metricToInsert.stream().peek(metric -> metric.createdBy(user.getName()))
metricReqs.stream().peek(metric -> metric.createdBy(user.getName()))
.map(MetricConverter::convert2MetricDO).collect(Collectors.toList());
metricRepository.createMetricBatch(metricDOS);
sendEventBatch(metricDOS, EventType.ADD);
// should update modelDetail as well
modelService.updateModelByDimAndMetric(metricReqs.get(0).getModelId(), null, metricReqs,
user);
sendEventBatch(metricDOS, EventType.ADD, user);
}
@Override
public void alterMetricBatch(List<MetricReq> metricReqs, Long modelId, User user) {
List<MetricResp> metricResps = getMetrics(new MetaFilter(Lists.newArrayList(modelId)));
// get all metric in model, only use bizname, because name can be changed to everything
Map<String, MetricResp> bizNameMap = metricResps.stream()
.collect(Collectors.toMap(MetricResp::getBizName, a -> a, (k1, k2) -> k1));
List<MetricReq> metricToInsert = Lists.newArrayList();
metricReqs.forEach(metric -> {
if (!bizNameMap.containsKey(metric.getBizName())) {
metricToInsert.add(metric);
}
});
// insert
if (!CollectionUtils.isEmpty(metricToInsert)) {
createMetricBatch(metricToInsert, user);
}
}
@Override
public MetricResp updateMetric(MetricReq metricReq, User user) {
MetricCheckUtils.checkParam(metricReq);
MetricCheckUtils.checkParam(Lists.newArrayList(metricReq));
checkExist(Lists.newArrayList(metricReq));
metricReq.updatedBy(user.getName());
MetricDO metricDO = metricRepository.getMetricById(metricReq.getId());
@@ -138,11 +183,28 @@ public class MetricServiceImpl extends ServiceImpl<MetricDOMapper, MetricDO>
DataItem dataItem = getDataItem(metricDO);
dataItem.setName(oldName);
dataItem.setNewName(metricDO.getName());
sendEvent(dataItem, EventType.UPDATE);
sendEvent(dataItem, EventType.UPDATE, user);
}
// should update modelDetail as well
modelService.updateModelByDimAndMetric(metricReq.getModelId(), null,
Lists.newArrayList(metricReq), user);
return MetricConverter.convert2MetricResp(metricDO);
}
@Override
public void updateMetricBatch(List<MetricReq> metricReqs, User user) {
MetricCheckUtils.checkParam(metricReqs);
checkExist(metricReqs);
List<MetricDO> metricDOS = metricReqs.stream().map(MetricConverter::convert2MetricDO)
.collect(Collectors.toList());
metricRepository.batchUpdateMetric(metricDOS);
// should update modelDetail as well
modelService.updateModelByDimAndMetric(metricReqs.get(0).getModelId(), null, metricReqs,
user);
sendEventBatch(metricDOS, EventType.UPDATE, user);
}
@Override
public void batchUpdateStatus(MetaBatchReq metaBatchReq, User user) {
if (CollectionUtils.isEmpty(metaBatchReq.getIds())) {
@@ -160,9 +222,9 @@ public class MetricServiceImpl extends ServiceImpl<MetricDOMapper, MetricDO>
metricRepository.batchUpdateStatus(metricDOS);
if (StatusEnum.OFFLINE.getCode().equals(metaBatchReq.getStatus())
|| StatusEnum.DELETED.getCode().equals(metaBatchReq.getStatus())) {
sendEventBatch(metricDOS, EventType.DELETE);
sendEventBatch(metricDOS, EventType.DELETE, user);
} else if (StatusEnum.ONLINE.getCode().equals(metaBatchReq.getStatus())) {
sendEventBatch(metricDOS, EventType.ADD);
sendEventBatch(metricDOS, EventType.ADD, user);
}
}
@@ -240,7 +302,31 @@ public class MetricServiceImpl extends ServiceImpl<MetricDOMapper, MetricDO>
metricDO.setUpdatedAt(new Date());
metricDO.setUpdatedBy(user.getName());
metricRepository.updateMetric(metricDO);
sendEventBatch(Lists.newArrayList(metricDO), EventType.DELETE);
// should update modelDetail
modelService.deleteModelDetailByDimAndMetric(metricDO.getModelId(), null,
Lists.newArrayList(metricDO));
sendEventBatch(Lists.newArrayList(metricDO), EventType.DELETE, user);
}
@Override
public void deleteMetricBatch(List<Long> idList, User user) {
MetricsFilter metricsFilter = new MetricsFilter();
metricsFilter.setMetricIds(idList);
List<MetricDO> metricDOList = metricRepository.getMetrics(metricsFilter);
if (CollectionUtils.isEmpty(metricDOList)) {
throw new RuntimeException(
String.format("the metrics %s not exist", StringUtils.join(",", idList)));
}
metricDOList.forEach(metricDO -> {
metricDO.setStatus(StatusEnum.DELETED.getCode());
metricDO.setUpdatedAt(new Date());
metricDO.setUpdatedBy(user.getName());
});
metricRepository.batchUpdateStatus(metricDOList);
// should update modelDetail
modelService.deleteModelDetailByDimAndMetric(metricDOList.get(0).getModelId(), null,
metricDOList);
sendEventBatch(metricDOList, EventType.DELETE, user);
}
@Override
@@ -549,7 +635,7 @@ public class MetricServiceImpl extends ServiceImpl<MetricDOMapper, MetricDO>
return metricQueryDefaultConfig;
}
private void checkExist(List<MetricBaseReq> metricReqs) {
private void checkExist(List<MetricReq> metricReqs) {
Long modelId = metricReqs.get(0).getModelId();
MetaFilter metaFilter = new MetaFilter();
metaFilter.setModelIds(Lists.newArrayList(modelId));
@@ -595,11 +681,11 @@ public class MetricServiceImpl extends ServiceImpl<MetricDOMapper, MetricDO>
}
@Override
public void sendMetricEventBatch(List<Long> modelIds, EventType eventType) {
public void sendMetricEventBatch(List<Long> modelIds, EventType eventType, User user) {
MetricFilter metricFilter = new MetricFilter();
metricFilter.setModelIds(modelIds);
List<MetricDO> metricDOS = queryMetric(metricFilter);
sendEventBatch(metricDOS, eventType);
sendEventBatch(metricDOS, eventType, user);
}
@Override
@@ -612,22 +698,23 @@ public class MetricServiceImpl extends ServiceImpl<MetricDOMapper, MetricDO>
public DataEvent getDataEvent() {
MetricsFilter metricsFilter = new MetricsFilter();
List<MetricDO> metricDOS = metricRepository.getMetrics(metricsFilter);
return getDataEvent(metricDOS, EventType.ADD);
return getDataEvent(metricDOS, EventType.ADD, User.getDefaultUser());
}
private DataEvent getDataEvent(List<MetricDO> metricDOS, EventType eventType) {
List<DataItem> dataItems =
metricDOS.stream().map(this::getDataItem).collect(Collectors.toList());
return new DataEvent(this, dataItems, eventType);
private DataEvent getDataEvent(List<MetricDO> metricDOS, EventType eventType, User user) {
List<DataItem> dataItems = metricDOS.stream().map(this::getDataItem)
.filter(Objects::nonNull).collect(Collectors.toList());
return new DataEvent(this, dataItems, eventType, user.getName());
}
private void sendEventBatch(List<MetricDO> metricDOS, EventType eventType) {
DataEvent dataEvent = getDataEvent(metricDOS, eventType);
private void sendEventBatch(List<MetricDO> metricDOS, EventType eventType, User user) {
DataEvent dataEvent = getDataEvent(metricDOS, eventType, user);
eventPublisher.publishEvent(dataEvent);
}
private void sendEvent(DataItem dataItem, EventType eventType) {
eventPublisher.publishEvent(new DataEvent(this, Lists.newArrayList(dataItem), eventType));
private void sendEvent(DataItem dataItem, EventType eventType, User user) {
eventPublisher.publishEvent(
new DataEvent(this, Lists.newArrayList(dataItem), eventType, user.getName()));
}
private DataItem getDataItem(MetricDO metricDO) {
@@ -830,8 +917,10 @@ public class MetricServiceImpl extends ServiceImpl<MetricDOMapper, MetricDO>
return modelResps.stream().map(ModelResp::getId).collect(Collectors.toSet());
}
private boolean isNameChange(MetricReq metricReq, MetricResp metricResp) {
private boolean isChange(MetricReq metricReq, MetricResp metricResp) {
boolean isNameChange = !metricReq.getName().equals(metricResp.getName());
return isNameChange;
boolean isBizNameChange = !Objects.equals(metricReq.getMetricDefineByMeasureParams(),
metricResp.getMetricDefineByMeasureParams());
return isNameChange || isBizNameChange;
}
}

View File

@@ -15,6 +15,8 @@ import com.tencent.supersonic.headless.api.pojo.request.*;
import com.tencent.supersonic.headless.api.pojo.response.*;
import com.tencent.supersonic.headless.server.modeller.SemanticModeller;
import com.tencent.supersonic.headless.server.persistence.dataobject.DateInfoDO;
import com.tencent.supersonic.headless.server.persistence.dataobject.DimensionDO;
import com.tencent.supersonic.headless.server.persistence.dataobject.MetricDO;
import com.tencent.supersonic.headless.server.persistence.dataobject.ModelDO;
import com.tencent.supersonic.headless.server.persistence.repository.DateInfoRepository;
import com.tencent.supersonic.headless.server.persistence.repository.ModelRepository;
@@ -87,9 +89,13 @@ public class ModelServiceImpl implements ModelService {
// checkParams(modelReq);
ModelDO modelDO = ModelConverter.convert(modelReq, user);
modelRepository.createModel(modelDO);
batchCreateDimension(modelDO, user);
batchCreateMetric(modelDO, user);
sendEvent(modelDO, EventType.ADD);
// create or update dimension
List<DimensionReq> dimensionReqs = ModelConverter.convertDimensionList(modelDO);
dimensionService.alterDimensionBatch(dimensionReqs, modelDO.getId(), user);
// create or update metric
List<MetricReq> metricReqs = ModelConverter.convertMetricList(modelDO);
metricService.alterMetricBatch(metricReqs, modelDO.getId(), user);
sendEvent(modelDO, EventType.ADD, user);
return ModelConverter.convert(modelDO);
}
@@ -117,9 +123,13 @@ public class ModelServiceImpl implements ModelService {
ModelDO modelDO = modelRepository.getModelById(modelReq.getId());
ModelConverter.convert(modelDO, modelReq, user);
modelRepository.updateModel(modelDO);
batchCreateDimension(modelDO, user);
batchCreateMetric(modelDO, user);
sendEvent(modelDO, EventType.UPDATE);
// create or update dimension
List<DimensionReq> dimensionReqs = ModelConverter.convertDimensionList(modelDO);
dimensionService.alterDimensionBatch(dimensionReqs, modelDO.getId(), user);
// create or update metric
List<MetricReq> metricReqs = ModelConverter.convertMetricList(modelDO);
metricService.alterMetricBatch(metricReqs, modelDO.getId(), user);
sendEvent(modelDO, EventType.UPDATE, user);
return ModelConverter.convert(modelDO);
}
@@ -239,32 +249,6 @@ public class ModelServiceImpl implements ModelService {
return dbSchema;
}
private void batchCreateDimension(ModelDO modelDO, User user) throws Exception {
List<DimensionReq> dimensionReqs = ModelConverter.convertDimensionList(modelDO);
List<DimensionReq> dimensionToCreate = new ArrayList<>();
for (DimensionReq dimensionReq : dimensionReqs) {
DimensionResp dimensionResp =
dimensionService.getDimension(dimensionReq.getBizName(), modelDO.getId());
if (dimensionResp == null) {
dimensionToCreate.add(dimensionReq);
}
}
dimensionService.createDimensionBatch(dimensionToCreate, user);
}
private void batchCreateMetric(ModelDO modelDO, User user) throws Exception {
List<MetricReq> metricReqs = ModelConverter.convertMetricList(modelDO);
List<MetricReq> metricToCreate = new ArrayList<>();
for (MetricReq metricReq : metricReqs) {
MetricResp metricResp =
metricService.getMetric(modelDO.getId(), metricReq.getBizName());
if (metricResp == null) {
metricToCreate.add(metricReq);
}
}
metricService.createMetricBatch(metricToCreate, user);
}
private void checkParams(ModelReq modelReq) {
String forbiddenCharacters = NameCheckUtils.findForbiddenCharacters(modelReq.getName());
if (StringUtils.isNotBlank(forbiddenCharacters)) {
@@ -523,46 +507,102 @@ public class ModelServiceImpl implements ModelService {
if (StatusEnum.OFFLINE.getCode().equals(metaBatchReq.getStatus())
|| StatusEnum.DELETED.getCode().equals(metaBatchReq.getStatus())) {
metricService.sendMetricEventBatch(Lists.newArrayList(modelDO.getId()),
EventType.DELETE);
EventType.DELETE, user);
dimensionService.sendDimensionEventBatch(Lists.newArrayList(modelDO.getId()),
EventType.DELETE);
EventType.DELETE, user);
} else if (StatusEnum.ONLINE.getCode().equals(metaBatchReq.getStatus())) {
metricService.sendMetricEventBatch(Lists.newArrayList(modelDO.getId()),
EventType.ADD);
EventType.ADD, user);
dimensionService.sendDimensionEventBatch(Lists.newArrayList(modelDO.getId()),
EventType.ADD);
EventType.ADD, user);
}
}).collect(Collectors.toList());
modelRepository.batchUpdate(modelDOS);
}
@Override
public Dimension updateDimension(DimensionReq dimensionReq, User user) {
ModelDO modelDO = getModelDO(dimensionReq.getModelId());
public void updateModelByDimAndMetric(Long modelId, List<DimensionReq> dimensionReqList,
List<MetricReq> metricReqList, User user) {
ModelDO modelDO = getModelDO(modelId);
ModelDetail modelDetail = JsonUtil.toObject(modelDO.getModelDetail(), ModelDetail.class);
Optional<Dimension> dimOptional = modelDetail.getDimensions().stream()
.filter(dimension -> dimension.getBizName().equals(dimensionReq.getBizName()))
if (!CollectionUtils.isEmpty(dimensionReqList)) {
dimensionReqList.forEach(dimensionReq -> {
Optional<Dimension> dimOptional = modelDetail.getDimensions().stream().filter(
dimension -> dimension.getBizName().equals(dimensionReq.getBizName()))
.findFirst();
Dimension result;
if (dimOptional.isPresent()) {
Dimension dimension = dimOptional.get();
dimension.setExpr(dimensionReq.getExpr());
dimension.setName(dimensionReq.getName());
dimension.setType(DimensionType.valueOf(dimensionReq.getType()));
dimension.setDescription(dimensionReq.getDescription());
result = dimension;
} else {
Dimension dimension = Dimension.builder().name(dimensionReq.getName())
.bizName(dimensionReq.getBizName()).expr(dimensionReq.getExpr())
.type(DimensionType.valueOf(dimensionReq.getType()))
.description(dimensionReq.getDescription()).build();
modelDetail.getDimensions().add(dimension);
result = dimension;
}
});
}
if (!CollectionUtils.isEmpty(metricReqList)) {
// 目前modeltail中的measure
Map<String, Measure> mesureMap = modelDetail.getMeasures().stream()
.collect(Collectors.toMap(Measure::getBizName, a -> a, (k1, k2) -> k1));
metricReqList.forEach(metricReq -> {
if (null != metricReq.getMetricDefineByMeasureParams() && !CollectionUtils
.isEmpty(metricReq.getMetricDefineByMeasureParams().getMeasures())) {
for (Measure alterMeasure : metricReq.getMetricDefineByMeasureParams()
.getMeasures()) {
if (mesureMap.containsKey(alterMeasure.getBizName())) {
Measure measure = mesureMap.get(alterMeasure.getBizName());
BeanUtils.copyProperties(alterMeasure, measure);
} else {
modelDetail.getMeasures().add(alterMeasure);
}
}
} else {
modelDetail.getMeasures().clear();
}
});
}
modelDO.setModelDetail(JsonUtil.toString(modelDetail));
modelRepository.updateModel(modelDO);
}
@Override
public void deleteModelDetailByDimAndMetric(Long modelId, List<DimensionDO> dimensionList,
List<MetricDO> metricReqList) {
ModelDO modelDO = getModelDO(modelId);
ModelDetail modelDetail = JsonUtil.toObject(modelDO.getModelDetail(), ModelDetail.class);
if (!CollectionUtils.isEmpty(dimensionList)) {
dimensionList.forEach(dimensionReq -> {
Optional<Dimension> dimOptional = modelDetail.getDimensions().stream().filter(
dimension -> dimension.getBizName().equals(dimensionReq.getBizName()))
.findFirst();
if (dimOptional.isPresent()) {
Dimension dimension = dimOptional.get();
modelDetail.getDimensions().remove(dimension);
}
});
}
if (!CollectionUtils.isEmpty(metricReqList)) {
metricReqList.forEach(metricReq -> {
Optional<Measure> metricOptional = modelDetail.getMeasures().stream()
.filter(measure -> measure.getBizName().equals(metricReq.getBizName()))
.findFirst();
if (metricOptional.isPresent()) {
Measure measure = metricOptional.get();
modelDetail.getMeasures().remove(measure);
}
});
}
modelDO.setModelDetail(JsonUtil.toString(modelDetail));
modelRepository.updateModel(modelDO);
return result;
}
protected ModelDO getModelDO(Long id) {
@@ -630,9 +670,10 @@ public class ModelServiceImpl implements ModelService {
return false;
}
private void sendEvent(ModelDO modelDO, EventType eventType) {
private void sendEvent(ModelDO modelDO, EventType eventType, User user) {
DataItem dataItem = getDataItem(modelDO);
eventPublisher.publishEvent(new DataEvent(this, Lists.newArrayList(dataItem), eventType));
eventPublisher.publishEvent(
new DataEvent(this, Lists.newArrayList(dataItem), eventType, user.getName()));
}
private DataItem getDataItem(ModelDO modelDO) {

View File

@@ -79,7 +79,7 @@ public class RetrieveServiceImpl implements RetrieveService {
Set<Long> dataSetIds = queryNLReq.getDataSetIds();
ChatQueryContext chatQueryContext = new ChatQueryContext(queryNLReq);
chatQueryContext.setModelIdToDataSetIds(dataSetService.getModelIdToDataSetIds());
chatQueryContext.setModelIdToDataSetIds(modelIdToDataSetIds);
Map<MatchText, List<HanlpMapResult>> regTextMap =
searchMatchStrategy.match(chatQueryContext, originals, dataSetIds);

View File

@@ -1,11 +1,13 @@
package com.tencent.supersonic.headless.server.utils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.tencent.supersonic.common.pojo.DimensionConstants;
import com.tencent.supersonic.headless.api.pojo.*;
import com.tencent.supersonic.headless.api.pojo.response.DataSetSchemaResp;
import com.tencent.supersonic.headless.api.pojo.response.DimSchemaResp;
import com.tencent.supersonic.headless.api.pojo.response.MetricSchemaResp;
import com.tencent.supersonic.headless.api.pojo.response.ModelResp;
import com.tencent.supersonic.headless.api.pojo.response.TermResp;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
@@ -92,6 +94,13 @@ public class DataSetSchemaBuilder {
private static Set<SchemaElement> getDimensions(DataSetSchemaResp resp) {
Set<SchemaElement> dimensions = new HashSet<>();
Map<Long, Map<String, String>> dataTypeMap = Maps.newHashMap();
for (ModelResp modelResp : resp.getModelResps()) {
dataTypeMap.put(modelResp.getId(),
modelResp.getModelDetail().getFields().stream().collect(Collectors
.toMap(Field::getFieldName, Field::getDataType, (k1, k2) -> k2)));
}
for (DimSchemaResp dim : resp.getDimensions()) {
List<String> alias = SchemaItem.getAliasList(dim.getAlias());
List<DimValueMap> dimValueMaps = dim.getDimValueMaps();
@@ -109,11 +118,18 @@ public class DataSetSchemaBuilder {
.alias(alias).schemaValueMaps(schemaValueMaps).isTag(dim.getIsTag())
.description(dim.getDescription()).type(SchemaElementType.DIMENSION).build();
dimToAdd.getExtInfo().put(DimensionConstants.DIMENSION_TYPE, dim.getType());
// data type
if (dim.getDataType() != null) {
dimToAdd.getExtInfo().put(DimensionConstants.DIMENSION_DATA_TYPE,
dim.getDataType());
} else {
dimToAdd.getExtInfo().put(DimensionConstants.DIMENSION_DATA_TYPE,
dataTypeMap.get(dim.getModelId()).get(dim.getBizName()));
}
if (dim.isTimeDimension()) {
String timeFormat =
String.valueOf(dim.getExt().get(DimensionConstants.DIMENSION_TIME_FORMAT));
setDefaultTimeFormat(dimToAdd, dim.getTypeParams(), timeFormat);
setDefaultTimeFormat(dimToAdd, timeFormat);
}
dimensions.add(dimToAdd);
}
@@ -125,7 +141,13 @@ public class DataSetSchemaBuilder {
for (DimSchemaResp dim : resp.getDimensions()) {
Set<String> dimValueAlias = new HashSet<>();
List<DimValueMap> dimValueMaps = dim.getDimValueMaps();
List<SchemaValueMap> schemaValueMaps = new ArrayList<>();
if (!CollectionUtils.isEmpty(dimValueMaps)) {
for (DimValueMap dimValueMap : dimValueMaps) {
SchemaValueMap schemaValueMap = new SchemaValueMap();
BeanUtils.copyProperties(dimValueMap, schemaValueMap);
schemaValueMaps.add(schemaValueMap);
}
for (DimValueMap dimValueMap : dimValueMaps) {
if (StringUtils.isNotEmpty(dimValueMap.getBizName())) {
dimValueAlias.add(dimValueMap.getBizName());
@@ -138,7 +160,7 @@ public class DataSetSchemaBuilder {
SchemaElement dimValueToAdd = SchemaElement.builder().dataSetId(resp.getId())
.dataSetName(resp.getName()).model(dim.getModelId()).id(dim.getId())
.name(dim.getName()).bizName(dim.getBizName()).type(SchemaElementType.VALUE)
.useCnt(dim.getUseCnt())
.schemaValueMaps(schemaValueMaps).useCnt(dim.getUseCnt())
.alias(new ArrayList<>(Arrays.asList(dimValueAlias.toArray(new String[0]))))
.isTag(dim.getIsTag()).description(dim.getDescription()).build();
dimensionValues.add(dimValueToAdd);
@@ -192,8 +214,7 @@ public class DataSetSchemaBuilder {
}).collect(Collectors.toList());
}
private static void setDefaultTimeFormat(SchemaElement dimToAdd,
DimensionTimeTypeParams dimensionTimeTypeParams, String timeFormat) {
private static void setDefaultTimeFormat(SchemaElement dimToAdd, String timeFormat) {
dimToAdd.getExtInfo().put(DimensionConstants.DIMENSION_TIME_FORMAT, timeFormat);
}
}

View File

@@ -422,33 +422,25 @@ public class DictUtils {
return joiner.toString();
}
public String defaultDateFilter(DateConf dateConf) {
String format = itemValueDateFormat;
String start = LocalDate.now().minusDays(itemValueDateStart)
.format(DateTimeFormatter.ofPattern(format));
String end = LocalDate.now().minusDays(itemValueDateEnd)
.format(DateTimeFormatter.ofPattern(format));
if (Objects.nonNull(dateConf)) {
return String.format("( %s >= '%s' and %s <= '%s' )", dateConf.getDateField(), start,
dateConf.getDateField(), end);
} else {
return String.format("( %s >= '%s' and %s <= '%s' )", "dt", start, "dt", end);
}
}
private String generateDictDateFilter(DictItemResp dictItemResp) {
ItemValueConfig config = dictItemResp.getConfig();
if (config == null) {
Dimension partitionTimeDimension = getPartitionTimeDimension(dictItemResp.getModelId());
// 如果没有设置数据时间维度,则无法做时间分区过滤
if (partitionTimeDimension == null) {
return "";
}
if (!partitionedModel(dictItemResp.getModelId())) {
return "";
}
// 未进行设置
ItemValueConfig config = dictItemResp.getConfig();
// 默认使用数据时间维度进行时间分区过滤
if (Objects.isNull(config) || Objects.isNull(config.getDateConf())) {
return defaultDateFilter(null);
String startDate = LocalDate.now().minusDays(itemValueDateStart)
.format(DateTimeFormatter.ofPattern(partitionTimeDimension.getDateFormat()));
String endDate = LocalDate.now().minusDays(itemValueDateEnd)
.format(DateTimeFormatter.ofPattern(partitionTimeDimension.getDateFormat()));
return String.format("( %s >= '%s' and %s <= '%s' )",
partitionTimeDimension.getBizName(), startDate,
partitionTimeDimension.getBizName(), endDate);
}
// 全表扫描
if (DateConf.DateMode.ALL.equals(config.getDateConf().getDateMode())) {
return "";
@@ -467,15 +459,15 @@ public class DictUtils {
return "";
}
private boolean partitionedModel(Long modelId) {
private Dimension getPartitionTimeDimension(Long modelId) {
ModelResp model = modelService.getModel(modelId);
if (Objects.nonNull(model)) {
List<Dimension> timeDims = model.getTimeDimension();
if (!CollectionUtils.isEmpty(timeDims)) {
return true;
return timeDims.get(0);
}
}
return false;
return null;
}
private String generateDictDateFilterRecent(DictItemResp dictItemResp) {

View File

@@ -70,6 +70,9 @@ public class DimensionConverter {
if (dimensionReq.getExt() != null) {
dimensionDO.setExt(JSONObject.toJSONString(dimensionReq.getExt()));
}
if (dimensionReq.getTypeParams() != null) {
dimensionDO.setTypeParams(JSONObject.toJSONString(dimensionReq.getTypeParams()));
}
dimensionDO.setStatus(StatusEnum.ONLINE.getCode());
return dimensionDO;
}

View File

@@ -10,9 +10,12 @@ import com.tencent.supersonic.headless.api.pojo.request.MetricReq;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import java.util.List;
public class MetricCheckUtils {
public static void checkParam(MetricReq metricReq) {
public static void checkParam(List<MetricReq> metricReqList) {
metricReqList.forEach(metricReq -> {
String expr = "";
if (MetricDefineType.METRIC.equals(metricReq.getMetricDefineType())) {
MetricDefineByMetricParams typeParams = metricReq.getMetricDefineByMetricParams();
@@ -53,11 +56,13 @@ public class MetricCheckUtils {
if (StringUtils.isBlank(expr)) {
throw new InvalidArgumentException("表达式不可为空");
}
String forbiddenCharacters = NameCheckUtils.findForbiddenCharacters(metricReq.getName());
String forbiddenCharacters =
NameCheckUtils.findForbiddenCharacters(metricReq.getName());
if (StringUtils.isNotBlank(forbiddenCharacters)) {
throw new InvalidArgumentException(
String.format("名称包含特殊字符%s, 请修改", forbiddenCharacters));
}
});
}
private static boolean hasAggregateFunction(String expr) {

View File

@@ -28,7 +28,7 @@ public class ModelConverter {
public static ModelDO convert(ModelReq modelReq, User user) {
ModelDO modelDO = new ModelDO();
ModelDetail modelDetail = createModelDetail(modelReq);
ModelDetail modelDetail = convert(modelReq);
modelReq.createdBy(user.getName());
BeanMapper.mapper(modelReq, modelDO);
modelDO.setStatus(StatusEnum.ONLINE.getCode());
@@ -68,7 +68,7 @@ public class ModelConverter {
}
public static ModelDO convert(ModelDO modelDO, ModelReq modelReq, User user) {
ModelDetail modelDetail = updateModelDetail(modelReq);
ModelDetail modelDetail = convert(modelReq);
BeanMapper.mapper(modelReq, modelDO);
if (modelReq.getDrillDownDimensions() != null) {
modelDO.setDrillDownDimensions(
@@ -288,18 +288,23 @@ public class ModelConverter {
.collect(Collectors.toList());
}
private static ModelDetail createModelDetail(ModelReq modelReq) {
private static ModelDetail convert(ModelReq modelReq) {
ModelDetail modelDetail = new ModelDetail();
List<Measure> measures = modelReq.getModelDetail().getMeasures();
List<Dimension> dimensions = modelReq.getModelDetail().getDimensions();
List<Identify> identifiers = modelReq.getModelDetail().getIdentifiers();
List<Field> fields = modelReq.getModelDetail().getFields();
List<String> fieldNames =
fields.stream().map(Field::getFieldName).collect(Collectors.toList());
if (measures != null) {
for (Measure measure : measures) {
if (StringUtils.isNotBlank(measure.getBizName())
&& StringUtils.isBlank(measure.getExpr())) {
measure.setExpr(measure.getBizName());
}
if (StringUtils.isNotBlank(measure.getBizName())
&& !fieldNames.contains(measure.getBizName())) {
fields.add(new Field(measure.getBizName(), ""));
}
}
@@ -309,6 +314,9 @@ public class ModelConverter {
if (StringUtils.isNotBlank(dimension.getBizName())
&& StringUtils.isBlank(dimension.getExpr())) {
dimension.setExpr(dimension.getBizName());
}
if (StringUtils.isNotBlank(dimension.getBizName())
&& !fieldNames.contains(dimension.getBizName())) {
fields.add(new Field(dimension.getBizName(), ""));
}
}
@@ -320,43 +328,14 @@ public class ModelConverter {
identify.setName(identify.getBizName());
}
identify.setIsCreateDimension(1);
if (StringUtils.isNotBlank(identify.getBizName())
&& !fieldNames.contains(identify.getBizName())) {
fields.add(new Field(identify.getBizName(), ""));
}
}
}
BeanMapper.mapper(modelReq.getModelDetail(), modelDetail);
return modelDetail;
}
private static ModelDetail updateModelDetail(ModelReq modelReq) {
ModelDetail modelDetail = new ModelDetail();
List<Measure> measures = modelReq.getModelDetail().getMeasures();
List<Dimension> dimensions = modelReq.getModelDetail().getDimensions();
if (!CollectionUtils.isEmpty(dimensions)) {
for (Dimension dimension : dimensions) {
if (StringUtils.isNotBlank(dimension.getBizName())
&& StringUtils.isBlank(dimension.getExpr())) {
dimension.setExpr(dimension.getBizName());
}
}
}
if (measures == null) {
measures = Lists.newArrayList();
}
for (Measure measure : measures) {
if (StringUtils.isBlank(measure.getBizName())) {
continue;
}
// Compatible with front-end tmp
String oriFieldName =
measure.getBizName().replaceFirst(modelReq.getBizName() + "_", "");
measure.setExpr(oriFieldName);
if (!measure.getBizName().startsWith(modelReq.getBizName())) {
measure.setBizName(String.format("%s_%s", modelReq.getBizName(), oriFieldName));
}
}
BeanMapper.mapper(modelReq.getModelDetail(), modelDetail);
return modelDetail;
}
}

View File

@@ -20,7 +20,8 @@
<result column="default_values" jdbcType="VARCHAR" property="defaultValues"/>
<result column="dim_value_maps" jdbcType="VARCHAR" property="dimValueMaps"/>
</resultMap>
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="com.tencent.supersonic.headless.server.persistence.dataobject.DimensionDO">
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs"
type="com.tencent.supersonic.headless.server.persistence.dataobject.DimensionDO">
<result column="type_params" jdbcType="LONGVARCHAR" property="typeParams"/>
<result column="expr" jdbcType="LONGVARCHAR" property="expr"/>
</resultMap>
@@ -59,7 +60,7 @@
</sql>
<sql id="Base_Column_List">
id
, name, biz_name, description, status, model_id, type, type_params, expr, datasource_id,
, name, biz_name, description, status, model_id, type, type_params, expr,
created_at, created_by, updated_by, updated_at, semantic_type
</sql>
@@ -86,21 +87,36 @@
<update id="batchUpdate" parameterType="java.util.List">
<foreach collection="list" item="dimension" separator=";">
update s2_dimension
set name = #{dimension.name,jdbcType=VARCHAR},
biz_name = #{dimension.bizName,jdbcType=VARCHAR},
description = #{dimension.description,jdbcType=VARCHAR},
status = #{dimension.status,jdbcType=INTEGER},
model_id = #{dimension.modelId,jdbcType=BIGINT},
type = #{dimension.type,jdbcType=VARCHAR},
type_params = #{dimension.typeParams,jdbcType=VARCHAR},
datasource_id = #{dimension.datasourceId,jdbcType=BIGINT},
created_at = #{dimension.createdAt,jdbcType=TIMESTAMP},
created_by = #{dimension.createdBy,jdbcType=VARCHAR},
updated_by = #{dimension.updatedBy,jdbcType=VARCHAR},
updated_at = #{dimension.updatedAt,jdbcType=TIMESTAMP},
semantic_type = #{dimension.semanticType,jdbcType=VARCHAR},
sensitive_level = #{dimension.sensitiveLevel,jdbcType=INTEGER},
expr = #{dimension.expr,jdbcType=LONGVARCHAR}
set
<if test="dimension.name != null and dimension.name !=''">name = #{dimension.name,jdbcType=VARCHAR},</if>
<if test="dimension.bizName != null and dimension.bizName !=''">biz_name =
#{dimension.bizName,jdbcType=VARCHAR},
</if>
<if test="dimension.description != null and dimension.description !=''">description =
#{dimension.description,jdbcType=VARCHAR},
</if>
<if test="dimension.status != null">status = #{dimension.status,jdbcType=INTEGER},</if>
<if test="dimension.modelId != null">model_id = #{dimension.modelId,jdbcType=BIGINT},</if>
<if test="dimension.type != null and dimension.type !=''">type = #{dimension.type,jdbcType=VARCHAR},</if>
<if test="dimension.typeParams != null and dimension.typeParams !=''">type_params =
#{dimension.typeParams,jdbcType=VARCHAR},
</if>
<if test="dimension.createdAt != null">created_at = #{dimension.createdAt,jdbcType=TIMESTAMP},</if>
<if test="dimension.createdBy != null and dimension.createdBy !=''">created_by =
#{dimension.createdBy,jdbcType=VARCHAR},
</if>
<if test="dimension.semanticType != null and dimension.semanticType !=''">semantic_type =
#{dimension.semanticType,jdbcType=VARCHAR},
</if>
<if test="dimension.sensitiveLevel != null and dimension.sensitiveLevel !=''">sensitive_level =
#{dimension.sensitiveLevel,jdbcType=INTEGER},
</if>
<if test="dimension.expr != null and dimension.expr !=''">expr = #{dimension.expr,jdbcType=LONGVARCHAR},
</if>
<if test="dimension.updatedBy != null and dimension.updatedBy !=''">updated_by =
#{dimension.updatedBy,jdbcType=VARCHAR},
</if>
<if test="dimension.updatedAt != null">updated_at = #{dimension.updatedAt,jdbcType=TIMESTAMP}</if>
where id = #{dimension.id,jdbcType=BIGINT}
</foreach>
</update>

View File

@@ -97,6 +97,35 @@
</foreach>
</update>
<update id="batchUpdate" parameterType="java.util.List">
<foreach collection="list" item="metric" separator=";">
update s2_metric
set
<if test="metric.name != null and metric.name !=''">name = #{metric.name,jdbcType=VARCHAR},</if>
<if test="metric.bizName != null and metric.bizName !=''">biz_name = #{metric.bizName,jdbcType=VARCHAR},
</if>
<if test="metric.description != null and metric.description !=''">description =
#{metric.description,jdbcType=VARCHAR},
</if>
<if test="metric.status != null">status = #{metric.status,jdbcType=INTEGER},</if>
<if test="metric.modelId != null">model_id = #{metric.modelId,jdbcType=BIGINT},</if>
<if test="metric.type != null and metric.type !=''">type = #{metric.type,jdbcType=VARCHAR},</if>
<if test="metric.typeParams != null and metric.typeParams !=''">type_params =
#{metric.typeParams,jdbcType=VARCHAR},
</if>
<if test="metric.createdAt != null">created_at = #{metric.createdAt,jdbcType=TIMESTAMP},</if>
<if test="metric.createdBy != null and dimension.createdBy !=''">created_by =
#{metric.createdBy,jdbcType=VARCHAR},
</if>
<if test="metric.sensitiveLevel != null">sensitive_level = #{metric.sensitiveLevel,jdbcType=INTEGER},</if>
<if test="metric.updatedBy != null and metric.updatedBy !=''">updated_by =
#{metric.updatedBy,jdbcType=VARCHAR},
</if>
<if test="metric.updatedAt != null">updated_at = #{metric.updatedAt,jdbcType=TIMESTAMP}</if>
where id = #{metric.id,jdbcType=BIGINT}
</foreach>
</update>
<update id="batchPublish" parameterType="java.util.List">
<foreach collection="list" item="metric" separator=";">
update s2_metric

View File

@@ -82,6 +82,7 @@ public class BaseTest extends BaseApplication {
protected SemanticQueryReq buildQuerySqlReq(String sql) {
QuerySqlReq querySqlCmd = new QuerySqlReq();
querySqlCmd.setSql(sql);
querySqlCmd.getSqlInfo().setCorrectedS2SQL(sql);
querySqlCmd.setModelIds(DataUtils.getMetricAgentIModelIds());
return querySqlCmd;
}

View File

@@ -33,8 +33,7 @@ public class QueryBySqlTest extends BaseTest {
@Test
@SetSystemProperty(key = "s2.test", value = "true")
public void testSumQuery() throws Exception {
SemanticQueryResp semanticQueryResp =
queryBySql("SELECT SUM(访问次数) AS 总访问次数 FROM 超音数PVUV统计 ");
SemanticQueryResp semanticQueryResp = queryBySql("SELECT SUM(访问次数) AS 总访问次数 FROM 超音数数据集 ");
assertEquals(1, semanticQueryResp.getColumns().size());
QueryColumn queryColumn = semanticQueryResp.getColumns().get(0);
@@ -45,7 +44,7 @@ public class QueryBySqlTest extends BaseTest {
@Test
public void testGroupByQuery() throws Exception {
SemanticQueryResp result =
queryBySql("SELECT 部门, SUM(访问次数) AS 总访问次数 FROM 超音数PVUV统计 GROUP BY 部门 ");
queryBySql("SELECT 部门, SUM(访问次数) AS 总访问次数 FROM 超音数数据集 GROUP BY 部门 ");
assertEquals(2, result.getColumns().size());
QueryColumn firstColumn = result.getColumns().get(0);
QueryColumn secondColumn = result.getColumns().get(1);
@@ -56,8 +55,8 @@ public class QueryBySqlTest extends BaseTest {
@Test
public void testFilterQuery() throws Exception {
SemanticQueryResp result = queryBySql(
"SELECT 部门, SUM(访问次数) AS 总访问次数 FROM 超音数PVUV统计 WHERE 部门 ='HR' GROUP BY 部门 ");
SemanticQueryResp result =
queryBySql("SELECT 部门, SUM(访问次数) AS 总访问次数 FROM 超音数数据集 WHERE 部门 ='HR' GROUP BY 部门 ");
assertEquals(2, result.getColumns().size());
QueryColumn firstColumn = result.getColumns().get(0);
QueryColumn secondColumn = result.getColumns().get(1);
@@ -71,8 +70,7 @@ public class QueryBySqlTest extends BaseTest {
public void testDateSumQuery() throws Exception {
String startDate = now().plusDays(-365).toString();
String endDate = now().plusDays(0).toString();
String sql =
"SELECT SUM(访问次数) AS 总访问次数 FROM 超音数PVUV统计 WHERE 数据日期 >= '%s' AND 数据日期 <= '%s' ";
String sql = "SELECT SUM(访问次数) AS 总访问次数 FROM 超音数数据集 WHERE 数据日期 >= '%s' AND 数据日期 <= '%s' ";
SemanticQueryResp semanticQueryResp = queryBySql(String.format(sql, startDate, endDate));
assertEquals(1, semanticQueryResp.getColumns().size());
QueryColumn queryColumn = semanticQueryResp.getColumns().get(0);
@@ -82,9 +80,9 @@ public class QueryBySqlTest extends BaseTest {
@Test
public void testCacheQuery() throws Exception {
queryBySql("SELECT 部门, SUM(访问次数) AS 访问次数 FROM 超音数PVUV统计 GROUP BY 部门 ");
queryBySql("SELECT 部门, SUM(访问次数) AS 访问次数 FROM 超音数数据集 GROUP BY 部门 ");
SemanticQueryResp result2 =
queryBySql("SELECT 部门, SUM(访问次数) AS 访问次数 FROM 超音数PVUV统计 GROUP BY 部门 ");
queryBySql("SELECT 部门, SUM(访问次数) AS 访问次数 FROM 超音数数据集 GROUP BY 部门 ");
assertTrue(result2.isUseCache());
}

View File

@@ -172,6 +172,11 @@
<artifactId>langchain4j-ollama</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<dependency>
<groupId>ai.djl.huggingface</groupId>
<artifactId>tokenizers</artifactId>
<version>0.28.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>

View File

@@ -1,8 +1,11 @@
.conversation {
position: relative;
width: 0;
width: 248px;
height: 100%;
background: #fff;
background: #f9f9f9;
padding: 10px 10px;
border-right: 1px solid #f1f1f1;
.rightSection {
width: 100%;

View File

@@ -10,7 +10,14 @@ import {
SimilarQuestionType,
} from '../../common/type';
import { createContext, useEffect, useRef, useState } from 'react';
import { chatExecute, chatParse, queryData, deleteQuery, switchEntity } from '../../service';
import {
chatExecute,
chatParse,
queryData,
deleteQuery,
switchEntity,
getExecuteSummary,
} from '../../service';
import { PARSE_ERROR_TIP, PREFIX_CLS, SEARCH_EXCEPTION_TIP } from '../../common/constants';
import { message, Spin } from 'antd';
import IconFont from '../IconFont';
@@ -169,7 +176,7 @@ const ChatItem: React.FC<Props> = ({
setExecuteLoading(true);
}
try {
const res: any = await chatExecute(msg, conversationId!, parseInfoValue, agentId);
const res: any = await chatExecute(msg, conversationId!, parseInfoValue, agentId, true);
const valid = updateData(res);
onMsgDataLoaded?.(
{
@@ -180,6 +187,20 @@ const ChatItem: React.FC<Props> = ({
valid,
isRefresh
);
const queryId = parseInfoValue.queryId; // 伪流式 大模型输出
if (queryId != undefined && res.data.queryState != 'INVALID') {
const getSummary = async (data: any, queryId: number) => {
const res2: any = await getExecuteSummary(queryId);
if (res2.data.queryMode == null) {
res2.data = { ...data, textSummary: res2.data.textSummary };
setData(res2.data);
setTimeout(() => getSummary(data, queryId), 500);
} else {
setData(res2.data);
}
};
setTimeout(() => getSummary(res.data, queryId), 500);
}
} catch (e) {
const tip = SEARCH_EXCEPTION_TIP;
setExecuteTip(SEARCH_EXCEPTION_TIP);
@@ -423,6 +444,10 @@ const ChatItem: React.FC<Props> = ({
return result;
}, {});
});
if (exportData.length === 0) {
message.error('该条消息暂不支持该操作');
return;
}
exportCsvFile(exportData);
}
};

View File

@@ -79,7 +79,8 @@ export function chatExecute(
queryText: string,
chatId: number,
parseInfo: ChatContextType,
agentId?: number
agentId?: number,
streamingResult?:boolean
) {
return axios.post<MsgDataType>(`${prefix}/chat/query/execute`, {
queryText,
@@ -87,6 +88,15 @@ export function chatExecute(
chatId: chatId || DEFAULT_CHAT_ID,
queryId: parseInfo.queryId,
parseId: parseInfo.id,
streamingResult:streamingResult
});
}
export function getExecuteSummary(
queryId: number
) {
return axios.post<MsgDataType>(`${prefix}/chat/query/getExecuteSummary`, {
queryId: queryId,
});
}

View File

@@ -435,17 +435,17 @@ const ClassDimensionTable: React.FC<Props> = ({}) => {
queryDimensionList({ ...filterParams, ...currentPagin });
}}
toolBarRender={() => [
<Button
key="create"
type="primary"
onClick={() => {
toDimensionEditPage(domainId, modelId!, 0);
// setDimensionItem(undefined);
// setCreateModalVisible(true);
}}
>
</Button>,
// <Button
// key="create"
// type="primary"
// onClick={() => {
// toDimensionEditPage(domainId, modelId!, 0);
// // setDimensionItem(undefined);
// // setCreateModalVisible(true);
// }}
// >
// 创建维度
// </Button>,
<BatchCtrlDropDownButton
key="ctrlBtnList"
extenderList={['batchSensitiveLevel', 'exportTagButton']}

View File

@@ -367,6 +367,15 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
message.error('请输入度量表达式');
return;
}
if (defineType === METRIC_DEFINE_TYPE.MEASURE) {
const { bizName, name, metricDefineByMeasureParams } = queryParams;
queryParams[queryParamsTypeParamsKey[METRIC_DEFINE_TYPE.MEASURE]].measures =
metricDefineByMeasureParams.measures.map((item: ISemantic.IMeasure) => {
return item.bizName === bizName && name ? { ...item, name } : item;
});
}
if (!dataFormatType) {
delete queryParams.dataFormat;
}

View File

@@ -75,6 +75,14 @@ const MetricMeasuresFormTable: React.FC<Props> = ({
return tableData;
};
useEffect(() => {
if (measuresParams?.measures) {
setSelectedKeys(measuresParams.measures.map((item: ISemantic.IMeasure) => item.bizName));
}
typeParams?.expr && setExprString(typeParams.expr);
}, [measuresParams, typeParams?.expr]);
useEffect(() => {
setTableData(getTableData());
}, [measuresList, measuresParams]);