32 Commits

Author SHA1 Message Date
zyclove
0eb8897d08 Merge b1dadb4a1a into 21e213fb19 2025-03-12 22:35:57 +08:00
guilinlewis
21e213fb19 (improvement)(headless | chat ) 向量数据被重置后,记忆不会再次添加到向量数据库 (#2164)
Some checks are pending
supersonic CentOS CI / build (21) (push) Waiting to run
supersonic mac CI / build (21) (push) Waiting to run
supersonic ubuntu CI / build (21) (push) Waiting to run
supersonic windows CI / build (21) (push) Waiting to run
2025-03-12 22:19:51 +08:00
jerryjzhang
f67bf3eeac (fix)(chat)Fix bug in creating chat model. 2025-03-12 16:47:40 +08:00
jerryjzhang
9d13038599 (fix)(headless)Fix schema corrector in that aliases should not be replaced. 2025-03-12 16:31:43 +08:00
beat4ocean
0c8c2d4804 [fix][headless] Fix issue filterSql is not working. (#2157)
Some checks are pending
supersonic CentOS CI / build (21) (push) Waiting to run
supersonic mac CI / build (21) (push) Waiting to run
supersonic ubuntu CI / build (21) (push) Waiting to run
supersonic windows CI / build (21) (push) Waiting to run
2025-03-12 13:53:08 +08:00
williamhliu
f05a4b523c (fix)(supersonic-fe) show tip when register failed and replace icon when login failed and fix metric style issue (#2160) 2025-03-12 08:22:46 +08:00
jerryjzhang
b7369abcca [improvement][docker]Facilitate database configuration when running with docker.
Some checks are pending
supersonic CentOS CI / build (21) (push) Waiting to run
supersonic mac CI / build (21) (push) Waiting to run
supersonic ubuntu CI / build (21) (push) Waiting to run
supersonic windows CI / build (21) (push) Waiting to run
2025-03-11 23:14:40 +08:00
jerryjzhang
b40cb13740 (fix)(chat)Should check permission when returning data set tree.
Some checks are pending
supersonic CentOS CI / build (21) (push) Waiting to run
supersonic mac CI / build (21) (push) Waiting to run
supersonic ubuntu CI / build (21) (push) Waiting to run
supersonic windows CI / build (21) (push) Waiting to run
2025-03-11 15:32:12 +08:00
williamhliu
6f8cf9853b (fix)(supersonic-fe) fix the issue where there is no prompt when saving the dataset (#2154)
Some checks are pending
supersonic CentOS CI / build (21) (push) Waiting to run
supersonic mac CI / build (21) (push) Waiting to run
supersonic ubuntu CI / build (21) (push) Waiting to run
supersonic windows CI / build (21) (push) Waiting to run
2025-03-11 10:36:03 +08:00
williamhliu
75906037ac (fix)(supersonic-fe) fix the issue where there is no prompt when saving the dataset (#2152) 2025-03-11 09:19:22 +08:00
jerryjzhang
b58e041e8d [improvement][chat]Adopt accept pattern to parsers and executors.
Some checks are pending
supersonic CentOS CI / build (21) (push) Waiting to run
supersonic mac CI / build (21) (push) Waiting to run
supersonic ubuntu CI / build (21) (push) Waiting to run
supersonic windows CI / build (21) (push) Waiting to run
2025-03-11 00:27:06 +08:00
jerryjzhang
93d585c0d5 [feature][common]Add permission management to chat model. 2025-03-10 23:58:19 +08:00
jerryjzhang
0dbf56d357 [fix][chat]Fix query time cost bug. 2025-03-10 23:21:07 +08:00
zyclove
a3293e6788 fix:java.lang.NullPointerException: Cannot invoke "com.tencent.supersonic.common.pojo.DateConf.getDateField()" because "dateConf" is null (#2142)
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-03-09 13:44:07 +08:00
jerryjzhang
a99f5985f5 (improvement)(headless)Set fields of model detail when restoring model.
Some checks are pending
supersonic CentOS CI / build (21) (push) Waiting to run
supersonic mac CI / build (21) (push) Waiting to run
supersonic ubuntu CI / build (21) (push) Waiting to run
supersonic windows CI / build (21) (push) Waiting to run
2025-03-09 10:58:49 +08:00
jerryjzhang
91243005bc (fix)(launcher)Change join_condition to type text to avoid "value too long for column". 2025-03-09 09:18:29 +08:00
jerryjzhang
a76b5a4300 (improvement)(headless)Add unit to measure. 2025-03-09 09:15:13 +08:00
jerryjzhang
c1f9df963c (improvement)(headless)Add expr to semantic column. 2025-03-09 08:31:48 +08:00
Shaofeng Shi
954aa4eea5 fix: https://github.com/tencentmusic/supersonic/issues/2132 for Trino syntax (#2144)
Some checks are pending
supersonic CentOS CI / build (21) (push) Waiting to run
supersonic mac CI / build (21) (push) Waiting to run
supersonic ubuntu CI / build (21) (push) Waiting to run
supersonic windows CI / build (21) (push) Waiting to run
2025-03-08 21:41:35 +08:00
jerryjzhang
33bd0de604 (improvement)(headless)Set fields of model detail when creating model. 2025-03-08 21:16:10 +08:00
jerryjzhang
881d891d70 (license)Update communication email address.
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-03-06 09:31:51 +08:00
jerryjzhang
d9db455dab (improvement)(headless)Optimize compatibility and robustness in ontology query translation.
Some checks are pending
supersonic CentOS CI / build (21) (push) Waiting to run
supersonic mac CI / build (21) (push) Waiting to run
supersonic ubuntu CI / build (21) (push) Waiting to run
supersonic windows CI / build (21) (push) Waiting to run
2025-03-05 20:47:57 +08:00
jerryjzhang
e0dc3fbf1a (improvement)(headless)Optimize compatibility and robustness in ontology query translation.
(improvement)(headless)Optimize compatibility and robustness in ontology query translation.
2025-03-05 17:11:59 +08:00
zyclove
efddf4cacf fix: https://github.com/tencentmusic/supersonic/issues/2132 (#2137) 2025-03-05 14:54:16 +08:00
jerryjzhang
732222ab98 (fix)(headless)Fix database permission check.
(fix)(headless)Fix database permission check.
2025-03-05 14:39:53 +08:00
jerryjzhang
5b994c4f8f (fix)(github)Upgrade actions/cache from 2 to 4. 2025-03-05 12:51:15 +08:00
Dack Wang
5d2ebdf680 (fix)(README) 404 docs link (#2133) 2025-03-05 12:36:56 +08:00
zhaoyingchao
b1dadb4a1a Merge remote-tracking branch 'origin/master' into hanlp-upgrade 2025-03-05 10:01:32 +08:00
coosir
f1bc18ef65 Update docker-compose.yml (#2127)
Some checks are pending
supersonic CentOS CI / build (21) (push) Waiting to run
supersonic mac CI / build (21) (push) Waiting to run
supersonic ubuntu CI / build (21) (push) Waiting to run
supersonic windows CI / build (21) (push) Waiting to run
fix data persistence issue
2025-03-05 08:36:04 +08:00
zhaoyingchao
158a0a802a feat:upgrade 1.8.4 2025-03-04 18:55:31 +08:00
jerryjzhang
8f361f9932 (improvement)(auth)Use interface in place of impl class.
Some checks are pending
supersonic CentOS CI / build (21) (push) Waiting to run
supersonic mac CI / build (21) (push) Waiting to run
supersonic ubuntu CI / build (21) (push) Waiting to run
supersonic windows CI / build (21) (push) Waiting to run
2025-03-04 17:56:41 +08:00
jerryjzhang
f532088e38 (feature)(releaes)Start 1.0.0-SNAPSHOT release.
Some checks are pending
supersonic CentOS CI / build (21) (push) Waiting to run
supersonic mac CI / build (21) (push) Waiting to run
supersonic ubuntu CI / build (21) (push) Waiting to run
supersonic windows CI / build (21) (push) Waiting to run
2025-03-04 10:11:00 +08:00
88 changed files with 838 additions and 318 deletions

View File

@@ -47,7 +47,7 @@ jobs:
mvn -version mvn -version
- name: Cache Maven packages - name: Cache Maven packages
uses: actions/cache@v2 uses: actions/cache@v4
with: with:
path: ~/.m2 path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}

View File

@@ -26,7 +26,7 @@ jobs:
distribution: 'adopt' distribution: 'adopt'
- name: Cache Maven packages - name: Cache Maven packages
uses: actions/cache@v2 uses: actions/cache@v4
with: with:
path: ~/Library/Caches/Maven # macOS Maven cache path path: ~/Library/Caches/Maven # macOS Maven cache path
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}

View File

@@ -26,7 +26,7 @@ jobs:
distribution: 'adopt' distribution: 'adopt'
- name: Cache Maven packages - name: Cache Maven packages
uses: actions/cache@v2 uses: actions/cache@v4
with: with:
path: ~/.m2 path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}

View File

@@ -26,7 +26,7 @@ jobs:
distribution: 'adopt' # You might need to change this if 'adopt' doesn't support JDK 21 distribution: 'adopt' # You might need to change this if 'adopt' doesn't support JDK 21
- name: Cache Maven packages - name: Cache Maven packages
uses: actions/cache@v2 uses: actions/cache@v4
with: with:
path: ~\.m2 # Windows uses a backslash for paths path: ~\.m2 # Windows uses a backslash for paths
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}

View File

@@ -14,7 +14,7 @@ code and logo.
b. a commercial license must be obtained from the author if you want to develop and distribute a derivative work based b. a commercial license must be obtained from the author if you want to develop and distribute a derivative work based
on SuperSonic. on SuperSonic.
Please contact zhangjun2915@163.com by email to inquire about licensing matters. Please contact supersonicbi@qq.com by email to inquire about licensing matters.
2. As a contributor, you should agree that: 2. As a contributor, you should agree that:

View File

@@ -75,7 +75,7 @@ SuperSonic comes with sample semantic models as well as chat conversations that
## Build and Development ## Build and Development
Please refer to project [Docs](https://supersonicbi.github.io/docs/%E7%B3%BB%E7%BB%9F%E9%83%A8%E7%BD%B2/%E7%BC%96%E8%AF%91%E6%9E%84%E5%BB%BA/). Please refer to project [Docs](https://supersonicbi.github.io/docs/%E7%B3%BB%E7%BB%9F%E9%83%A8%E7%BD%B2/%E6%BA%90%E7%A0%81%E7%BC%96%E8%AF%91%E9%83%A8%E7%BD%B2/).
## WeChat Contact ## WeChat Contact

View File

@@ -75,7 +75,7 @@ SuperSonic自带样例的语义模型和问答对话只需以下三步即可
## 如何构建和部署 ## 如何构建和部署
请参考项目[文档](https://supersonicbi.github.io/docs/%E7%B3%BB%E7%BB%9F%E9%83%A8%E7%BD%B2/%E7%BC%96%E8%AF%91%E6%9E%84%E5%BB%BA/)。 请参考项目[文档](https://supersonicbi.github.io/docs/%E7%B3%BB%E7%BB%9F%E9%83%A8%E7%BD%B2/%E6%BA%90%E7%A0%81%E7%BC%96%E8%AF%91%E9%83%A8%E7%BD%B2/)。
## 微信联系方式 ## 微信联系方式

View File

@@ -71,7 +71,7 @@ SuperSonicには、サンプルのセマンティックモデルとチャット
## ビルドと開発 ## ビルドと開発
プロジェクト[ドキュメント](https://supersonicbi.github.io/docs/%E7%B3%BB%E7%BB%9F%E9%83%A8%E7%BD%B2/%E7%BC%96%E8%AF%91%E6%9E%84%E5%BB%BA/)を参照してください。 プロジェクト[ドキュメント](https://supersonicbi.github.io/docs/%E7%B3%BB%E7%BB%9F%E9%83%A8%E7%BD%B2/%E6%BA%90%E7%A0%81%E7%BC%96%E8%AF%91%E9%83%A8%E7%BD%B2/)を参照してください。
## WeChat連絡先 ## WeChat連絡先

View File

@@ -0,0 +1,5 @@
#!/usr/bin/env sh
export SUPERSONIC_VERSION=latest
docker-compose -f docker-compose.yml -p supersonic up

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env sh
export SUPERSONIC_VERSION=latest
#### Set below DB configs to connect to your own database
# Supported DB_TYPE: h2, mysql, postgres
export S2_DB_TYPE=h2
export S2_DB_HOST=
export S2_DB_PORT=
export S2_DB_USER=
export S2_DB_PASSWORD=
export S2_DB_DATABASE=
docker run --rm -it -d \
--name supersonic_standalone \
-p 9080:9080 \
-e S2_DB_TYPE=${S2_DB_TYPE} \
-e S2_DB_HOST=${S2_DB_HOST} \
-e S2_DB_PORT=${S2_DB_PORT} \
-e S2_DB_USER=${S2_DB_USER} \
-e S2_DB_PASSWORD=${S2_DB_PASSWORD} \
-e S2_DB_DATABASE=${S2_DB_DATABASE} \
supersonicbi/supersonic:${SUPERSONIC_VERSION}

View File

@@ -1,10 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
#### Set below DB configs to connect to your own database #### Set below DB configs to connect to your own database
# Comment out below exports to config your DB connection
# Supported DB_TYPE: h2, mysql, postgres # Supported DB_TYPE: h2, mysql, postgres
export S2_DB_TYPE=h2 #export S2_DB_TYPE=h2
export S2_DB_HOST= #export S2_DB_HOST=
export S2_DB_PORT= #export S2_DB_PORT=
export S2_DB_USER= #export S2_DB_USER=
export S2_DB_PASSWORD= #export S2_DB_PASSWORD=
export S2_DB_DATABASE= #export S2_DB_DATABASE=

View File

@@ -1,7 +1,7 @@
package com.tencent.supersonic.auth.authentication.interceptor; package com.tencent.supersonic.auth.authentication.interceptor;
import com.tencent.supersonic.auth.api.authentication.config.AuthenticationConfig; import com.tencent.supersonic.auth.api.authentication.config.AuthenticationConfig;
import com.tencent.supersonic.auth.authentication.service.UserServiceImpl; import com.tencent.supersonic.auth.api.authentication.service.UserService;
import com.tencent.supersonic.auth.authentication.utils.TokenService; import com.tencent.supersonic.auth.authentication.utils.TokenService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -16,7 +16,7 @@ public abstract class AuthenticationInterceptor implements HandlerInterceptor {
protected AuthenticationConfig authenticationConfig; protected AuthenticationConfig authenticationConfig;
protected UserServiceImpl userServiceImpl; protected UserService userService;
protected TokenService tokenService; protected TokenService tokenService;

View File

@@ -3,7 +3,7 @@ package com.tencent.supersonic.auth.authentication.interceptor;
import com.tencent.supersonic.auth.api.authentication.annotation.AuthenticationIgnore; import com.tencent.supersonic.auth.api.authentication.annotation.AuthenticationIgnore;
import com.tencent.supersonic.auth.api.authentication.config.AuthenticationConfig; import com.tencent.supersonic.auth.api.authentication.config.AuthenticationConfig;
import com.tencent.supersonic.auth.api.authentication.pojo.UserWithPassword; import com.tencent.supersonic.auth.api.authentication.pojo.UserWithPassword;
import com.tencent.supersonic.auth.authentication.service.UserServiceImpl; import com.tencent.supersonic.auth.api.authentication.service.UserService;
import com.tencent.supersonic.auth.authentication.utils.TokenService; import com.tencent.supersonic.auth.authentication.utils.TokenService;
import com.tencent.supersonic.common.pojo.exception.AccessException; import com.tencent.supersonic.common.pojo.exception.AccessException;
import com.tencent.supersonic.common.util.ContextUtils; import com.tencent.supersonic.common.util.ContextUtils;
@@ -16,12 +16,7 @@ import org.springframework.web.method.HandlerMethod;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Optional; import java.util.Optional;
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_IS_ADMIN; import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.*;
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_DISPLAY_NAME;
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_EMAIL;
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_ID;
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_NAME;
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_PASSWORD;
@Slf4j @Slf4j
public class DefaultAuthenticationInterceptor extends AuthenticationInterceptor { public class DefaultAuthenticationInterceptor extends AuthenticationInterceptor {
@@ -30,7 +25,7 @@ public class DefaultAuthenticationInterceptor extends AuthenticationInterceptor
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws AccessException { Object handler) throws AccessException {
authenticationConfig = ContextUtils.getBean(AuthenticationConfig.class); authenticationConfig = ContextUtils.getBean(AuthenticationConfig.class);
userServiceImpl = ContextUtils.getBean(UserServiceImpl.class); userService = ContextUtils.getBean(UserService.class);
tokenService = ContextUtils.getBean(TokenService.class); tokenService = ContextUtils.getBean(TokenService.class);
if (!authenticationConfig.isEnabled()) { if (!authenticationConfig.isEnabled()) {
return true; return true;

View File

@@ -5,5 +5,7 @@ import com.tencent.supersonic.chat.server.pojo.ExecuteContext;
public interface ChatQueryExecutor { public interface ChatQueryExecutor {
boolean accept(ExecuteContext executeContext);
QueryResult execute(ExecuteContext executeContext); QueryResult execute(ExecuteContext executeContext);
} }

View File

@@ -37,11 +37,12 @@ public class PlainTextExecutor implements ChatQueryExecutor {
} }
@Override @Override
public QueryResult execute(ExecuteContext executeContext) { public boolean accept(ExecuteContext executeContext) {
if (!"PLAIN_TEXT".equals(executeContext.getParseInfo().getQueryMode())) { return "PLAIN_TEXT".equals(executeContext.getParseInfo().getQueryMode());
return null; }
}
@Override
public QueryResult execute(ExecuteContext executeContext) {
AgentService agentService = ContextUtils.getBean(AgentService.class); AgentService agentService = ContextUtils.getBean(AgentService.class);
Agent chatAgent = agentService.getAgent(executeContext.getAgent().getId()); Agent chatAgent = agentService.getAgent(executeContext.getAgent().getId());
ChatApp chatApp = chatAgent.getChatAppConfig().get(APP_KEY); ChatApp chatApp = chatAgent.getChatAppConfig().get(APP_KEY);

View File

@@ -8,6 +8,11 @@ import com.tencent.supersonic.headless.api.pojo.SemanticParseInfo;
public class PluginExecutor implements ChatQueryExecutor { public class PluginExecutor implements ChatQueryExecutor {
@Override
public boolean accept(ExecuteContext executeContext) {
return PluginQueryManager.isPluginQuery(executeContext.getParseInfo().getQueryMode());
}
@Override @Override
public QueryResult execute(ExecuteContext executeContext) { public QueryResult execute(ExecuteContext executeContext) {
SemanticParseInfo parseInfo = executeContext.getParseInfo(); SemanticParseInfo parseInfo = executeContext.getParseInfo();

View File

@@ -25,6 +25,11 @@ import java.util.Objects;
public class SqlExecutor implements ChatQueryExecutor { public class SqlExecutor implements ChatQueryExecutor {
@Override
public boolean accept(ExecuteContext executeContext) {
return true;
}
@SneakyThrows @SneakyThrows
@Override @Override
public QueryResult execute(ExecuteContext executeContext) { public QueryResult execute(ExecuteContext executeContext) {
@@ -80,9 +85,9 @@ public class SqlExecutor implements ChatQueryExecutor {
queryResult.setQueryId(executeContext.getRequest().getQueryId()); queryResult.setQueryId(executeContext.getRequest().getQueryId());
queryResult.setChatContext(parseInfo); queryResult.setChatContext(parseInfo);
queryResult.setQueryMode(parseInfo.getQueryMode()); queryResult.setQueryMode(parseInfo.getQueryMode());
queryResult.setQueryTimeCost(System.currentTimeMillis() - startTime);
SemanticQueryResp queryResp = SemanticQueryResp queryResp =
semanticLayer.queryByReq(sqlReq, executeContext.getRequest().getUser()); semanticLayer.queryByReq(sqlReq, executeContext.getRequest().getUser());
queryResult.setQueryTimeCost(System.currentTimeMillis() - startTime);
if (queryResp != null) { if (queryResp != null) {
queryResult.setQueryAuthorization(queryResp.getQueryAuthorization()); queryResult.setQueryAuthorization(queryResp.getQueryAuthorization());
queryResult.setQuerySql(queryResp.getSql()); queryResult.setQuerySql(queryResp.getSql());

View File

@@ -4,5 +4,7 @@ import com.tencent.supersonic.chat.server.pojo.ParseContext;
public interface ChatQueryParser { public interface ChatQueryParser {
boolean accept(ParseContext parseContext);
void parse(ParseContext parseContext); void parse(ParseContext parseContext);
} }

View File

@@ -14,12 +14,12 @@ public class NL2PluginParser implements ChatQueryParser {
private final List<PluginRecognizer> pluginRecognizers = private final List<PluginRecognizer> pluginRecognizers =
ComponentFactory.getPluginRecognizers(); ComponentFactory.getPluginRecognizers();
public boolean accept(ParseContext parseContext) {
return parseContext.getAgent().containsPluginTool();
}
@Override @Override
public void parse(ParseContext parseContext) { public void parse(ParseContext parseContext) {
if (!parseContext.getAgent().containsPluginTool()) {
return;
}
pluginRecognizers.forEach(pluginRecognizer -> { pluginRecognizers.forEach(pluginRecognizer -> {
pluginRecognizer.recognize(parseContext); pluginRecognizer.recognize(parseContext);
log.info("{} recallResult:{}", pluginRecognizer.getClass().getSimpleName(), log.info("{} recallResult:{}", pluginRecognizer.getClass().getSimpleName(),

View File

@@ -73,12 +73,12 @@ public class NL2SQLParser implements ChatQueryParser {
.build()); .build());
} }
public boolean accept(ParseContext parseContext) {
return parseContext.enableNL2SQL();
}
@Override @Override
public void parse(ParseContext parseContext) { public void parse(ParseContext parseContext) {
if (!parseContext.enableNL2SQL()) {
return;
}
// first go with rule-based parsers unless the user has already selected one parse. // first go with rule-based parsers unless the user has already selected one parse.
if (Objects.isNull(parseContext.getRequest().getSelectedParse())) { if (Objects.isNull(parseContext.getRequest().getSelectedParse())) {
QueryNLReq queryNLReq = QueryReqConverter.buildQueryNLReq(parseContext); QueryNLReq queryNLReq = QueryReqConverter.buildQueryNLReq(parseContext);

View File

@@ -6,12 +6,12 @@ import com.tencent.supersonic.headless.api.pojo.response.ParseResp;
public class PlainTextParser implements ChatQueryParser { public class PlainTextParser implements ChatQueryParser {
public boolean accept(ParseContext parseContext) {
return !parseContext.getAgent().containsAnyTool();
}
@Override @Override
public void parse(ParseContext parseContext) { public void parse(ParseContext parseContext) {
if (parseContext.getAgent().containsAnyTool()) {
return;
}
SemanticParseInfo parseInfo = new SemanticParseInfo(); SemanticParseInfo parseInfo = new SemanticParseInfo();
parseInfo.setQueryMode("PLAIN_TEXT"); parseInfo.setQueryMode("PLAIN_TEXT");
parseInfo.setId(1); parseInfo.setId(1);

View File

@@ -66,8 +66,10 @@ public class ChatConfigController {
} }
@GetMapping("/getDomainDataSetTree") @GetMapping("/getDomainDataSetTree")
public List<ItemResp> getDomainDataSetTree() { public List<ItemResp> getDomainDataSetTree(HttpServletRequest request,
return semanticLayerService.getDomainDataSetTree(); HttpServletResponse response) {
User user = UserHolder.findUser(request, response);
return semanticLayerService.getDomainDataSetTree(user);
} }
@GetMapping("/getDataSetSchema/{id}") @GetMapping("/getDataSetSchema/{id}")

View File

@@ -161,9 +161,11 @@ public class AgentServiceImpl extends ServiceImpl<AgentDOMapper, AgentDO> implem
JsonUtil.toMap(agentDO.getChatModelConfig(), String.class, ChatApp.class)); JsonUtil.toMap(agentDO.getChatModelConfig(), String.class, ChatApp.class));
agent.setVisualConfig(JsonUtil.toObject(agentDO.getVisualConfig(), VisualConfig.class)); agent.setVisualConfig(JsonUtil.toObject(agentDO.getVisualConfig(), VisualConfig.class));
agent.getChatAppConfig().values().forEach(c -> { agent.getChatAppConfig().values().forEach(c -> {
ChatModel chatModel = chatModelService.getChatModel(c.getChatModelId()); if (c.isEnable()) {// 优化,减少访问数据库的次数
if (Objects.nonNull(chatModel)) { ChatModel chatModel = chatModelService.getChatModel(c.getChatModelId());
c.setChatModelConfig(chatModelService.getChatModel(c.getChatModelId()).getConfig()); if (Objects.nonNull(chatModel)) {
c.setChatModelConfig(chatModel.getConfig());
}
} }
}); });
agent.setAdmins(JsonUtil.toList(agentDO.getAdmin(), String.class)); agent.setAdmins(JsonUtil.toList(agentDO.getAdmin(), String.class));

View File

@@ -95,7 +95,11 @@ public class ChatQueryServiceImpl implements ChatQueryService {
} }
ParseContext parseContext = buildParseContext(chatParseReq, new ChatParseResp(queryId)); ParseContext parseContext = buildParseContext(chatParseReq, new ChatParseResp(queryId));
chatQueryParsers.forEach(p -> p.parse(parseContext)); for (ChatQueryParser parser : chatQueryParsers) {
if (parser.accept(parseContext)) {
parser.parse(parseContext);
}
}
for (ParseResultProcessor processor : parseResultProcessors) { for (ParseResultProcessor processor : parseResultProcessors) {
if (processor.accept(parseContext)) { if (processor.accept(parseContext)) {
@@ -116,9 +120,11 @@ public class ChatQueryServiceImpl implements ChatQueryService {
QueryResult queryResult = new QueryResult(); QueryResult queryResult = new QueryResult();
ExecuteContext executeContext = buildExecuteContext(chatExecuteReq); ExecuteContext executeContext = buildExecuteContext(chatExecuteReq);
for (ChatQueryExecutor chatQueryExecutor : chatQueryExecutors) { for (ChatQueryExecutor chatQueryExecutor : chatQueryExecutors) {
queryResult = chatQueryExecutor.execute(executeContext); if (chatQueryExecutor.accept(executeContext)) {
if (queryResult != null) { queryResult = chatQueryExecutor.execute(executeContext);
break; if (queryResult != null) {
break;
}
} }
} }

View File

@@ -18,19 +18,23 @@ import com.tencent.supersonic.common.config.EmbeddingConfig;
import com.tencent.supersonic.common.pojo.Text2SQLExemplar; import com.tencent.supersonic.common.pojo.Text2SQLExemplar;
import com.tencent.supersonic.common.pojo.User; import com.tencent.supersonic.common.pojo.User;
import com.tencent.supersonic.common.service.ExemplarService; import com.tencent.supersonic.common.service.ExemplarService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
public class MemoryServiceImpl implements MemoryService { @Slf4j
public class MemoryServiceImpl implements MemoryService , CommandLineRunner {
@Autowired @Autowired
private ChatMemoryRepository chatMemoryRepository; private ChatMemoryRepository chatMemoryRepository;
@@ -187,4 +191,23 @@ public class MemoryServiceImpl implements MemoryService {
return memory; return memory;
} }
@Override
public void run(String... args) { // 优化,启动时检查,向量数据,将记忆放到向量数据库
loadSysExemplars();
}
public void loadSysExemplars() {
try {
List<ChatMemory> memories =
this.getMemories(ChatMemoryFilter.builder().status(MemoryStatus.ENABLED).build());
for(ChatMemory memory:memories){
exemplarService.storeExemplar(embeddingConfig.getMemoryCollectionName(memory.getAgentId()),
Text2SQLExemplar.builder().question(memory.getQuestion())
.sideInfo(memory.getSideInfo()).dbSchema(memory.getDbSchema())
.sql(memory.getS2sql()).build());
}
} catch (Exception e) {
log.error("Failed to load system exemplars", e);
}
}
} }

View File

@@ -26,6 +26,16 @@ public class SqlDialectFactory {
.withLiteralQuoteString("'").withIdentifierQuoteString("\"") .withLiteralQuoteString("'").withIdentifierQuoteString("\"")
.withLiteralEscapedQuoteString("''").withUnquotedCasing(Casing.UNCHANGED) .withLiteralEscapedQuoteString("''").withUnquotedCasing(Casing.UNCHANGED)
.withQuotedCasing(Casing.UNCHANGED).withCaseSensitive(true); .withQuotedCasing(Casing.UNCHANGED).withCaseSensitive(true);
public static final Context PRESTO_CONTEXT =
SqlDialect.EMPTY_CONTEXT.withDatabaseProduct(DatabaseProduct.PRESTO)
.withLiteralQuoteString("'").withIdentifierQuoteString("\"")
.withLiteralEscapedQuoteString("''").withUnquotedCasing(Casing.UNCHANGED)
.withQuotedCasing(Casing.UNCHANGED).withCaseSensitive(true);
public static final Context KYUUBI_CONTEXT =
SqlDialect.EMPTY_CONTEXT.withDatabaseProduct(DatabaseProduct.BIG_QUERY)
.withLiteralQuoteString("'").withIdentifierQuoteString("`")
.withLiteralEscapedQuoteString("''").withUnquotedCasing(Casing.UNCHANGED)
.withQuotedCasing(Casing.UNCHANGED).withCaseSensitive(false);
private static Map<EngineType, SemanticSqlDialect> sqlDialectMap; private static Map<EngineType, SemanticSqlDialect> sqlDialectMap;
static { static {
@@ -35,6 +45,10 @@ public class SqlDialectFactory {
sqlDialectMap.put(EngineType.H2, new SemanticSqlDialect(DEFAULT_CONTEXT)); sqlDialectMap.put(EngineType.H2, new SemanticSqlDialect(DEFAULT_CONTEXT));
sqlDialectMap.put(EngineType.POSTGRESQL, new SemanticSqlDialect(POSTGRESQL_CONTEXT)); sqlDialectMap.put(EngineType.POSTGRESQL, new SemanticSqlDialect(POSTGRESQL_CONTEXT));
sqlDialectMap.put(EngineType.HANADB, new SemanticSqlDialect(HANADB_CONTEXT)); sqlDialectMap.put(EngineType.HANADB, new SemanticSqlDialect(HANADB_CONTEXT));
sqlDialectMap.put(EngineType.STARROCKS, new SemanticSqlDialect(DEFAULT_CONTEXT));
sqlDialectMap.put(EngineType.KYUUBI, new SemanticSqlDialect(KYUUBI_CONTEXT));
sqlDialectMap.put(EngineType.PRESTO, new SemanticSqlDialect(PRESTO_CONTEXT));
sqlDialectMap.put(EngineType.TRINO, new SemanticSqlDialect(PRESTO_CONTEXT));
} }
public static SemanticSqlDialect getSqlDialect(EngineType engineType) { public static SemanticSqlDialect getSqlDialect(EngineType engineType) {

View File

@@ -2,19 +2,11 @@ package com.tencent.supersonic.common.calcite;
import com.tencent.supersonic.common.pojo.enums.EngineType; import com.tencent.supersonic.common.pojo.enums.EngineType;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.calcite.sql.SqlIdentifier; import net.sf.jsqlparser.expression.Alias;
import org.apache.calcite.sql.SqlLiteral; import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import org.apache.calcite.sql.SqlNode; import net.sf.jsqlparser.statement.select.ParenthesedSelect;
import org.apache.calcite.sql.SqlNodeList; import net.sf.jsqlparser.statement.select.Select;
import org.apache.calcite.sql.SqlOrderBy; import net.sf.jsqlparser.statement.select.WithItem;
import org.apache.calcite.sql.SqlSelect;
import org.apache.calcite.sql.SqlWith;
import org.apache.calcite.sql.SqlWithItem;
import org.apache.calcite.sql.SqlWriterConfig;
import org.apache.calcite.sql.parser.SqlParseException;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.pretty.SqlPrettyWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -22,85 +14,37 @@ import java.util.List;
@Slf4j @Slf4j
public class SqlMergeWithUtils { public class SqlMergeWithUtils {
public static String mergeWith(EngineType engineType, String sql, List<String> parentSqlList, public static String mergeWith(EngineType engineType, String sql, List<String> parentSqlList,
List<String> parentWithNameList) throws SqlParseException { List<String> parentWithNameList) throws Exception {
SqlParser.Config parserConfig = Configuration.getParserConfig(engineType);
// Parse the main SQL statement Select selectStatement = (Select) CCJSqlParserUtil.parse(sql);
SqlParser parser = SqlParser.create(sql, parserConfig); List<WithItem> withItemList = new ArrayList<>();
SqlNode sqlNode1 = parser.parseQuery();
// List to hold all WITH items
List<SqlNode> withItemList = new ArrayList<>();
// Iterate over each parentSql and parentWithName pair
for (int i = 0; i < parentSqlList.size(); i++) { for (int i = 0; i < parentSqlList.size(); i++) {
String parentSql = parentSqlList.get(i); String parentSql = parentSqlList.get(i);
String parentWithName = parentWithNameList.get(i); String parentWithName = parentWithNameList.get(i);
// Parse the parent SQL statement Select parentSelect = (Select) CCJSqlParserUtil.parse(parentSql);
parser = SqlParser.create(parentSql, parserConfig); ParenthesedSelect select = new ParenthesedSelect();
SqlNode sqlNode2 = parser.parseQuery(); select.setSelect(parentSelect);
// Create a new WITH item for parentWithName without quotes // Create a new WITH item for parentWithName without quotes
SqlWithItem withItem = new SqlWithItem(SqlParserPos.ZERO, WithItem withItem = new WithItem();
new SqlIdentifier(parentWithName, SqlParserPos.ZERO), null, sqlNode2, withItem.setAlias(new Alias(parentWithName));
SqlLiteral.createBoolean(false, SqlParserPos.ZERO)); withItem.setSelect(select);
// Add the new WITH item to the list // Add the new WITH item to the list
withItemList.add(withItem); withItemList.add(withItem);
} }
// Check if the main SQL node contains an ORDER BY or LIMIT clause // Extract existing WITH items from mainSelectBody if it has any
SqlNode limitNode = null; if (selectStatement.getWithItemsList() != null) {
SqlNodeList orderByList = null; withItemList.addAll(selectStatement.getWithItemsList());
if (sqlNode1 instanceof SqlOrderBy) {
SqlOrderBy sqlOrderBy = (SqlOrderBy) sqlNode1;
limitNode = sqlOrderBy.fetch;
orderByList = sqlOrderBy.orderList;
sqlNode1 = sqlOrderBy.query;
} else if (sqlNode1 instanceof SqlSelect) {
SqlSelect sqlSelect = (SqlSelect) sqlNode1;
limitNode = sqlSelect.getFetch();
sqlSelect.setFetch(null);
sqlNode1 = sqlSelect;
} }
// Extract existing WITH items from sqlNode1 if it is a SqlWith // Set the new WITH items list to the main select body
if (sqlNode1 instanceof SqlWith) { selectStatement.setWithItemsList(withItemList);
SqlWith sqlWith = (SqlWith) sqlNode1;
withItemList.addAll(sqlWith.withList.getList());
sqlNode1 = sqlWith.body;
}
// Create a new SqlWith node
SqlWith finalSqlNode = new SqlWith(SqlParserPos.ZERO,
new SqlNodeList(withItemList, SqlParserPos.ZERO), sqlNode1);
// If there was an ORDER BY or LIMIT clause, wrap the finalSqlNode in a SqlOrderBy
SqlNode resultNode = finalSqlNode;
if (orderByList != null || limitNode != null) {
resultNode = new SqlOrderBy(SqlParserPos.ZERO, finalSqlNode,
orderByList != null ? orderByList : SqlNodeList.EMPTY, null, limitNode);
}
// Custom SqlPrettyWriter configuration to avoid quoting identifiers
SqlWriterConfig config = Configuration.getSqlWriterConfig(engineType);
// Pretty print the final SQL // Pretty print the final SQL
SqlPrettyWriter writer = new SqlPrettyWriter(config); return selectStatement.toString();
return writer.format(resultNode);
}
public static boolean hasWith(EngineType engineType, String sql) throws SqlParseException {
SqlParser.Config parserConfig = Configuration.getParserConfig(engineType);
SqlParser parser = SqlParser.create(sql, parserConfig);
SqlNode sqlNode = parser.parseQuery();
SqlNode sqlSelect = sqlNode;
if (sqlNode instanceof SqlOrderBy) {
SqlOrderBy sqlOrderBy = (SqlOrderBy) sqlNode;
sqlSelect = sqlOrderBy.query;
} else if (sqlNode instanceof SqlSelect) {
sqlSelect = (SqlSelect) sqlNode;
}
return sqlSelect instanceof SqlWith;
} }
} }

View File

@@ -1,9 +1,11 @@
package com.tencent.supersonic.common.config; package com.tencent.supersonic.common.config;
import com.google.common.collect.Lists;
import com.tencent.supersonic.common.pojo.ChatModelConfig; import com.tencent.supersonic.common.pojo.ChatModelConfig;
import lombok.Data; import lombok.Data;
import java.util.Date; import java.util.Date;
import java.util.List;
@Data @Data
public class ChatModel { public class ChatModel {
@@ -25,5 +27,11 @@ public class ChatModel {
private String admin; private String admin;
private String viewer; private List<String> viewers = Lists.newArrayList();
private Integer isOpen = 0;
public boolean isPublic() {
return isOpen != null && isOpen == 1;
}
} }

View File

@@ -91,7 +91,8 @@ public class FiledFilterReplaceVisitor extends ExpressionVisitorAdapter {
} }
ExpressionList<?> leftFunctionParams = leftFunction.getParameters(); ExpressionList<?> leftFunctionParams = leftFunction.getParameters();
if (CollectionUtils.isEmpty(leftFunctionParams)) { if (CollectionUtils.isEmpty(leftFunctionParams)
|| !(leftFunctionParams.get(0) instanceof Column)) {
return result; return result;
} }

View File

@@ -146,6 +146,10 @@ public class SqlReplaceHelper {
public static String replaceFields(String sql, Map<String, String> fieldNameMap, public static String replaceFields(String sql, Map<String, String> fieldNameMap,
boolean exactReplace) { boolean exactReplace) {
Select selectStatement = SqlSelectHelper.getSelect(sql); Select selectStatement = SqlSelectHelper.getSelect(sql);
// alias field should not be replaced
Set<String> aliases = SqlSelectHelper.getAliasFields(sql);
aliases.forEach(alias -> fieldNameMap.put(alias, alias));
Set<Select> plainSelectList = SqlSelectHelper.getAllSelect(selectStatement); Set<Select> plainSelectList = SqlSelectHelper.getAllSelect(selectStatement);
for (Select plainSelect : plainSelectList) { for (Select plainSelect : plainSelectList) {
if (plainSelect instanceof PlainSelect) { if (plainSelect instanceof PlainSelect) {

View File

@@ -989,6 +989,15 @@ public class SqlSelectHelper {
for (SelectItem selectItem : selectItems) { for (SelectItem selectItem : selectItems) {
selectItem.accept(visitor); selectItem.accept(visitor);
} }
if (plainSelect.getHaving() != null) {
plainSelect.getHaving().accept(visitor);
}
if (!CollectionUtils.isEmpty(plainSelect.getOrderByElements())) {
for (OrderByElement orderByElement : plainSelect.getOrderByElements()) {
orderByElement.getExpression().accept(visitor);
}
}
return !visitor.getFunctionNames().isEmpty(); return !visitor.getFunctionNames().isEmpty();
} }

View File

@@ -30,4 +30,6 @@ public class ChatModelDO {
private String admin; private String admin;
private String viewer; private String viewer;
private Integer isOpen;
} }

View File

@@ -3,6 +3,7 @@ package com.tencent.supersonic.common.pojo;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.Data; import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
import static com.tencent.supersonic.common.pojo.Constants.ASC_UPPER; import static com.tencent.supersonic.common.pojo.Constants.ASC_UPPER;

View File

@@ -7,7 +7,7 @@ import com.tencent.supersonic.common.pojo.User;
import java.util.List; import java.util.List;
public interface ChatModelService { public interface ChatModelService {
List<ChatModel> getChatModels(); List<ChatModel> getChatModels(User user);
ChatModel getChatModel(Integer id); ChatModel getChatModel(Integer id);

View File

@@ -23,8 +23,15 @@ import java.util.stream.Collectors;
public class ChatModelServiceImpl extends ServiceImpl<ChatModelMapper, ChatModelDO> public class ChatModelServiceImpl extends ServiceImpl<ChatModelMapper, ChatModelDO>
implements ChatModelService { implements ChatModelService {
@Override @Override
public List<ChatModel> getChatModels() { public List<ChatModel> getChatModels(User user) {
return list().stream().map(this::convert).collect(Collectors.toList()); return list().stream().map(this::convert).filter(chatModel -> {
if (chatModel.isPublic() || user.isSuperAdmin()
|| chatModel.getCreatedBy().equals(user.getName())
|| chatModel.getViewers().contains(user.getName())) {
return true;
}
return false;
}).collect(Collectors.toList());
} }
@Override @Override
@@ -41,10 +48,14 @@ public class ChatModelServiceImpl extends ServiceImpl<ChatModelMapper, ChatModel
chatModelDO.setCreatedBy(user.getName()); chatModelDO.setCreatedBy(user.getName());
chatModelDO.setCreatedAt(new Date()); chatModelDO.setCreatedAt(new Date());
chatModelDO.setUpdatedBy(user.getName()); chatModelDO.setUpdatedBy(user.getName());
chatModelDO.setUpdatedAt(new Date()); chatModelDO.setUpdatedAt(chatModelDO.getCreatedAt());
chatModelDO.setIsOpen(chatModel.getIsOpen());
if (StringUtils.isBlank(chatModel.getAdmin())) { if (StringUtils.isBlank(chatModel.getAdmin())) {
chatModelDO.setAdmin(user.getName()); chatModelDO.setAdmin(user.getName());
} }
if (!chatModel.getViewers().isEmpty()) {
chatModelDO.setViewer(JsonUtil.toString(chatModel.getViewers()));
}
save(chatModelDO); save(chatModelDO);
chatModel.setId(chatModelDO.getId()); chatModel.setId(chatModelDO.getId());
return chatModel; return chatModel;
@@ -55,9 +66,13 @@ public class ChatModelServiceImpl extends ServiceImpl<ChatModelMapper, ChatModel
ChatModelDO chatModelDO = convert(chatModel); ChatModelDO chatModelDO = convert(chatModel);
chatModelDO.setUpdatedBy(user.getName()); chatModelDO.setUpdatedBy(user.getName());
chatModelDO.setUpdatedAt(new Date()); chatModelDO.setUpdatedAt(new Date());
chatModelDO.setIsOpen(chatModel.getIsOpen());
if (StringUtils.isBlank(chatModel.getAdmin())) { if (StringUtils.isBlank(chatModel.getAdmin())) {
chatModel.setAdmin(user.getName()); chatModel.setAdmin(user.getName());
} }
if (!chatModel.getViewers().isEmpty()) {
chatModelDO.setViewer(JsonUtil.toString(chatModel.getViewers()));
}
updateById(chatModelDO); updateById(chatModelDO);
return chatModel; return chatModel;
} }
@@ -74,6 +89,7 @@ public class ChatModelServiceImpl extends ServiceImpl<ChatModelMapper, ChatModel
ChatModel chatModel = new ChatModel(); ChatModel chatModel = new ChatModel();
BeanUtils.copyProperties(chatModelDO, chatModel); BeanUtils.copyProperties(chatModelDO, chatModel);
chatModel.setConfig(JsonUtil.toObject(chatModelDO.getConfig(), ChatModelConfig.class)); chatModel.setConfig(JsonUtil.toObject(chatModelDO.getConfig(), ChatModelConfig.class));
chatModel.setViewers(JsonUtil.toList(chatModelDO.getViewer(), String.class));
return chatModel; return chatModel;
} }

View File

@@ -2,7 +2,6 @@ package com.tencent.supersonic.common.calcite;
import com.tencent.supersonic.common.pojo.enums.EngineType; import com.tencent.supersonic.common.pojo.enums.EngineType;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.calcite.sql.parser.SqlParseException;
import org.junit.Assert; import org.junit.Assert;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -12,7 +11,7 @@ import java.util.Collections;
class SqlWithMergerTest { class SqlWithMergerTest {
@Test @Test
void test1() throws SqlParseException { void test1() throws Exception {
String sql1 = "WITH DepartmentVisits AS (\n" + " SELECT department, SUM(pv) AS 总访问次数\n" String sql1 = "WITH DepartmentVisits AS (\n" + " SELECT department, SUM(pv) AS 总访问次数\n"
+ " FROM t_1\n" + " FROM t_1\n"
+ " WHERE sys_imp_date >= '2024-09-01' AND sys_imp_date <= '2024-09-29'\n" + " WHERE sys_imp_date >= '2024-09-01' AND sys_imp_date <= '2024-09-29'\n"
@@ -38,7 +37,7 @@ class SqlWithMergerTest {
} }
@Test @Test
void test2() throws SqlParseException { void test2() throws Exception {
String sql1 = String sql1 =
"WITH DepartmentVisits AS (SELECT department, SUM(pv) AS 总访问次数 FROM t_1 WHERE sys_imp_date >= '2024-08-28' " "WITH DepartmentVisits AS (SELECT department, SUM(pv) AS 总访问次数 FROM t_1 WHERE sys_imp_date >= '2024-08-28' "
@@ -65,7 +64,7 @@ class SqlWithMergerTest {
} }
@Test @Test
void test3() throws SqlParseException { void test3() throws Exception {
String sql1 = "SELECT COUNT(*) FROM DepartmentVisits WHERE 总访问次数 > 100 LIMIT 1000"; String sql1 = "SELECT COUNT(*) FROM DepartmentVisits WHERE 总访问次数 > 100 LIMIT 1000";
@@ -89,7 +88,7 @@ class SqlWithMergerTest {
} }
@Test @Test
void test4() throws SqlParseException { void test4() throws Exception {
String sql1 = "SELECT COUNT(*) FROM DepartmentVisits WHERE 总访问次数 > 100"; String sql1 = "SELECT COUNT(*) FROM DepartmentVisits WHERE 总访问次数 > 100";
String sql2 = String sql2 =
@@ -112,7 +111,7 @@ class SqlWithMergerTest {
} }
@Test @Test
void test5() throws SqlParseException { void test5() throws Exception {
String sql1 = "SELECT COUNT(*) FROM Department join Visits WHERE 总访问次数 > 100"; String sql1 = "SELECT COUNT(*) FROM Department join Visits WHERE 总访问次数 > 100";
@@ -132,13 +131,13 @@ class SqlWithMergerTest {
"WITH t_1 AS (SELECT `t3`.`sys_imp_date`, `t2`.`department`, `t3`.`s2_pv_uv_statis_pv` AS `pv` " "WITH t_1 AS (SELECT `t3`.`sys_imp_date`, `t2`.`department`, `t3`.`s2_pv_uv_statis_pv` AS `pv` "
+ "FROM (SELECT `user_name`, `department` FROM `s2_user_department`) AS `t2` LEFT JOIN " + "FROM (SELECT `user_name`, `department` FROM `s2_user_department`) AS `t2` LEFT JOIN "
+ "(SELECT 1 AS `s2_pv_uv_statis_pv`, `imp_date` AS `sys_imp_date`, `user_name` FROM `s2_pv_uv_statis`) " + "(SELECT 1 AS `s2_pv_uv_statis_pv`, `imp_date` AS `sys_imp_date`, `user_name` FROM `s2_pv_uv_statis`) "
+ "AS `t3` ON `t2`.`user_name` = `t3`.`user_name`) SELECT COUNT(*) FROM Department INNER JOIN Visits " + "AS `t3` ON `t2`.`user_name` = `t3`.`user_name`) SELECT COUNT(*) FROM Department JOIN Visits "
+ "WHERE 总访问次数 > 100"); + "WHERE 总访问次数 > 100");
} }
@Test @Test
void test6() throws SqlParseException { void test6() throws Exception {
String sql1 = String sql1 =
"SELECT COUNT(*) FROM Department join Visits WHERE 总访问次数 > 100 ORDER BY 总访问次数 LIMIT 10"; "SELECT COUNT(*) FROM Department join Visits WHERE 总访问次数 > 100 ORDER BY 总访问次数 LIMIT 10";
@@ -159,7 +158,36 @@ class SqlWithMergerTest {
"WITH t_1 AS (SELECT `t3`.`sys_imp_date`, `t2`.`department`, `t3`.`s2_pv_uv_statis_pv` AS `pv` FROM " "WITH t_1 AS (SELECT `t3`.`sys_imp_date`, `t2`.`department`, `t3`.`s2_pv_uv_statis_pv` AS `pv` FROM "
+ "(SELECT `user_name`, `department` FROM `s2_user_department`) AS `t2` LEFT JOIN (SELECT 1 AS `s2_pv_uv_statis_pv`," + "(SELECT `user_name`, `department` FROM `s2_user_department`) AS `t2` LEFT JOIN (SELECT 1 AS `s2_pv_uv_statis_pv`,"
+ " `imp_date` AS `sys_imp_date`, `user_name` FROM `s2_pv_uv_statis`) AS `t3` ON `t2`.`user_name` = `t3`.`user_name`) " + " `imp_date` AS `sys_imp_date`, `user_name` FROM `s2_pv_uv_statis`) AS `t3` ON `t2`.`user_name` = `t3`.`user_name`) "
+ "SELECT COUNT(*) FROM Department INNER JOIN Visits WHERE 总访问次数 > 100 ORDER BY 总访问次数 LIMIT 10"); + "SELECT COUNT(*) FROM Department JOIN Visits WHERE 总访问次数 > 100 ORDER BY 总访问次数 LIMIT 10");
}
@Test
void test7() throws Exception {
String sql1 =
"SELECT COUNT(*) FROM Department join Visits WHERE 总访问次数 > 100 AND imp_date >= CURRENT_DATE - "
+ "INTERVAL '1 year' AND sys_imp_date < CURRENT_DATE ORDER"
+ " BY 总访问次数 LIMIT 10";
String sql2 =
"SELECT `t3`.`sys_imp_date`, `t2`.`department`, `t3`.`s2_pv_uv_statis_pv` AS `pv`\n"
+ "FROM\n" + "(SELECT `user_name`, `department`\n" + "FROM\n"
+ "`s2_user_department`) AS `t2`\n"
+ "LEFT JOIN (SELECT 1 AS `s2_pv_uv_statis_pv`, `imp_date` AS `sys_imp_date`, `user_name`\n"
+ "FROM\n"
+ "`s2_pv_uv_statis`) AS `t3` ON `t2`.`user_name` = `t3`.`user_name`";
String mergeSql = SqlMergeWithUtils.mergeWith(EngineType.MYSQL, sql1,
Collections.singletonList(sql2), Collections.singletonList("t_1"));
Assert.assertEquals(format(mergeSql),
"WITH t_1 AS (SELECT `t3`.`sys_imp_date`, `t2`.`department`, `t3`.`s2_pv_uv_statis_pv` AS `pv` FROM "
+ "(SELECT `user_name`, `department` FROM `s2_user_department`) AS `t2` LEFT JOIN (SELECT 1 AS `s2_pv_uv_statis_pv`,"
+ " `imp_date` AS `sys_imp_date`, `user_name` FROM `s2_pv_uv_statis`) AS `t3` ON `t2`.`user_name` = `t3`.`user_name`) "
+ "SELECT COUNT(*) FROM Department JOIN Visits WHERE 总访问次数 > 100 AND imp_date >= "
+ "CURRENT_DATE - INTERVAL '1 year' AND sys_imp_date < CURRENT_DATE ORDER BY 总访问次数 "
+ "LIMIT 10");
} }
private static String format(String mergeSql) { private static String format(String mergeSql) {

View File

@@ -27,4 +27,4 @@ WORKDIR /usr/src/app/supersonic-standalone-${SUPERSONIC_VERSION}
EXPOSE 9080 EXPOSE 9080
# Command to run the supersonic daemon # Command to run the supersonic daemon
RUN chmod +x bin/supersonic-daemon.sh RUN chmod +x bin/supersonic-daemon.sh
CMD ["bash", "-c", "bin/supersonic-daemon.sh restart standalone docker && tail -f /dev/null"] CMD ["bash", "-c", "bin/supersonic-daemon.sh restart standalone ${S2_DB_TYPE} && tail -f /dev/null"]

View File

@@ -2,8 +2,8 @@
# Function to execute the build script # Function to execute the build script
execute_build_script() { execute_build_script() {
echo "Executing build script: assembly/bin/supersonic-build.sh" echo "Executing build script: sh assembly/bin/supersonic-build.sh"
assembly/bin/supersonic-build.sh sh assembly/bin/supersonic-build.sh
} }
# Function to build the Docker image # Function to build the Docker image

View File

@@ -1,3 +0,0 @@
#!/usr/bin/env bash
SUPERSONIC_VERSION=latest docker-compose -f docker-compose.yml -p supersonic up

View File

@@ -12,7 +12,7 @@ services:
ports: ports:
- "15432:5432" - "15432:5432"
# volumes: # volumes:
# - postgres_data:/var/lib/postgresql # - postgres_data:/var/lib/postgresql/data
networks: networks:
- supersonic_network - supersonic_network
dns: dns:
@@ -30,10 +30,12 @@ services:
privileged: true privileged: true
container_name: supersonic_standalone container_name: supersonic_standalone
environment: environment:
DB_HOST: supersonic_postgres S2_DB_TYPE: postgres
DB_NAME: postgres S2_DB_HOST: supersonic_postgres
DB_USERNAME: supersonic_user S2_DB_PORT: 5432
DB_PASSWORD: supersonic_password S2_DB_DATABASE: postgres
S2_DB_USER: supersonic_user
S2_DB_PASSWORD: supersonic_password
ports: ports:
- "9080:9080" - "9080:9080"
depends_on: depends_on:
@@ -62,4 +64,4 @@ services:
# supersonic_data: # supersonic_data:
networks: networks:
supersonic_network: supersonic_network:

View File

@@ -23,12 +23,16 @@ public class Measure {
private String alias; private String alias;
public Measure(String name, String bizName, String expr, String agg, Integer isCreateMetric) { private String unit;
public Measure(String name, String bizName, String expr, String agg, String unit,
Integer isCreateMetric) {
this.name = name; this.name = name;
this.agg = agg; this.agg = agg;
this.isCreateMetric = isCreateMetric; this.isCreateMetric = isCreateMetric;
this.bizName = bizName; this.bizName = bizName;
this.expr = expr; this.expr = expr;
this.unit = unit;
} }
public Measure(String name, String bizName, String agg, Integer isCreateMetric) { public Measure(String name, String bizName, String agg, Integer isCreateMetric) {

View File

@@ -24,6 +24,8 @@ public class ModelDetail {
private String tableQuery; private String tableQuery;
private String filterSql;
private List<Identify> identifiers = Lists.newArrayList(); private List<Identify> identifiers = Lists.newArrayList();
private List<Dimension> dimensions = Lists.newArrayList(); private List<Dimension> dimensions = Lists.newArrayList();
@@ -49,27 +51,4 @@ public class ModelDetail {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public List<Field> getFields() {
if (!CollectionUtils.isEmpty(fields)) {
return fields;
}
List<Field> fieldList = Lists.newArrayList();
// Compatible with older versions
if (!CollectionUtils.isEmpty(identifiers)) {
fieldList.addAll(identifiers.stream()
.map(identify -> Field.builder().fieldName(identify.getFieldName()).build())
.collect(Collectors.toSet()));
}
if (!CollectionUtils.isEmpty(dimensions)) {
fieldList.addAll(dimensions.stream()
.map(dim -> Field.builder().fieldName(dim.getFieldName()).build())
.collect(Collectors.toSet()));
}
if (!CollectionUtils.isEmpty(measures)) {
fieldList.addAll(measures.stream()
.map(measure -> Field.builder().fieldName(measure.getFieldName()).build())
.collect(Collectors.toSet()));
}
return fieldList;
}
} }

View File

@@ -14,11 +14,11 @@ public class ModelSchema {
private String description; private String description;
private List<ColumnSchema> columnSchemas; private List<SemanticColumn> semanticColumns;
@JsonIgnore @JsonIgnore
public ColumnSchema getColumnByName(String columnName) { public SemanticColumn getColumnByName(String columnName) {
for (ColumnSchema fieldSchema : columnSchemas) { for (SemanticColumn fieldSchema : semanticColumns) {
if (fieldSchema.getColumnName().equalsIgnoreCase(columnName)) { if (fieldSchema.getColumnName().equalsIgnoreCase(columnName)) {
return fieldSchema; return fieldSchema;
} }

View File

@@ -5,7 +5,7 @@ import com.tencent.supersonic.headless.api.pojo.enums.FieldType;
import lombok.Data; import lombok.Data;
@Data @Data
public class ColumnSchema { public class SemanticColumn {
private String columnName; private String columnName;
@@ -19,4 +19,8 @@ public class ColumnSchema {
private String name; private String name;
private String expr;
private String unit;
} }

View File

@@ -32,9 +32,12 @@ public class DatabaseReq extends RecordInfo {
private String description; private String description;
private String schema; private String schema;
private String url; private String url;
private List<String> admins = Lists.newArrayList(); private List<String> admins = Lists.newArrayList();
private List<String> viewers = Lists.newArrayList(); private List<String> viewers = Lists.newArrayList();
private Integer isOpen = 0;
} }

View File

@@ -19,6 +19,8 @@ public class ModelBuildReq {
private String sql; private String sql;
private String filterSql;
private String catalog; private String catalog;
private String db; private String db;

View File

@@ -28,6 +28,8 @@ public class DatabaseResp extends RecordInfo {
private List<String> viewers = Lists.newArrayList(); private List<String> viewers = Lists.newArrayList();
private Integer isOpen = 0;
private String type; private String type;
private String url; private String url;
@@ -48,6 +50,10 @@ public class DatabaseResp extends RecordInfo {
private boolean hasEditPermission = false; private boolean hasEditPermission = false;
public boolean isPublic() {
return isOpen != null && isOpen == 1;
}
public String getHost() { public String getHost() {
Pattern p = Pattern.compile("jdbc:(?<db>\\w+):.*((//)|@)(?<host>.+):(?<port>\\d+).*"); Pattern p = Pattern.compile("jdbc:(?<db>\\w+):.*((//)|@)(?<host>.+):(?<port>\\d+).*");
Matcher m = p.matcher(url); Matcher m = p.matcher(url);

View File

@@ -1,5 +1,6 @@
package com.tencent.supersonic.headless.chat.corrector; package com.tencent.supersonic.headless.chat.corrector;
import com.tencent.supersonic.common.jsqlparser.FieldExpression;
import com.tencent.supersonic.common.jsqlparser.SqlAddHelper; import com.tencent.supersonic.common.jsqlparser.SqlAddHelper;
import com.tencent.supersonic.common.jsqlparser.SqlSelectHelper; import com.tencent.supersonic.common.jsqlparser.SqlSelectHelper;
import com.tencent.supersonic.common.jsqlparser.SqlValidHelper; import com.tencent.supersonic.common.jsqlparser.SqlValidHelper;
@@ -8,10 +9,7 @@ import com.tencent.supersonic.headless.chat.ChatQueryContext;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.util.ArrayList; import java.util.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** Perform SQL corrections on the "Select" section in S2SQL. */ /** Perform SQL corrections on the "Select" section in S2SQL. */
@Slf4j @Slf4j
@@ -46,10 +44,28 @@ public class SelectCorrector extends BaseSemanticCorrector {
return correctS2SQL; return correctS2SQL;
} }
needAddFields.removeAll(selectFields); needAddFields.removeAll(selectFields);
String addFieldsToSelectSql =
SqlAddHelper.addFieldsToSelect(correctS2SQL, new ArrayList<>(needAddFields)); if (!SqlSelectHelper.hasSubSelect(correctS2SQL)) { //优化内容 如果sql 条件包含了这个字段,而且是全等,则不再查询该字段
semanticParseInfo.getSqlInfo().setCorrectedS2SQL(addFieldsToSelectSql); List<FieldExpression> tmp4 = SqlSelectHelper.getWhereExpressions(correctS2SQL);
return addFieldsToSelectSql; Iterator<String> it = needAddFields.iterator();
while (it.hasNext()) {
String field = it.next();
long size = tmp4.stream()
.filter(e -> e.getFieldName().equals(field) && "=".equals(e.getOperator()))
.count();
if (size == 1) {
it.remove();
}
}
}
if (needAddFields.size() > 0) {
String addFieldsToSelectSql =
SqlAddHelper.addFieldsToSelect(correctS2SQL, new ArrayList<>(needAddFields));
semanticParseInfo.getSqlInfo().setCorrectedS2SQL(addFieldsToSelectSql);
return addFieldsToSelectSql;
} else {
return correctS2SQL;
}
} }
} }

View File

@@ -75,6 +75,7 @@ public class KeywordMapper extends BaseMapper {
continue; continue;
} }
Long elementID = NatureHelper.getElementID(nature); Long elementID = NatureHelper.getElementID(nature);
if (elementID == null)continue; // 判空优化
SchemaElement element = getSchemaElement(dataSetId, elementType, elementID, SchemaElement element = getSchemaElement(dataSetId, elementType, elementID,
chatQueryContext.getSemanticSchema()); chatQueryContext.getSemanticSchema());
if (Objects.isNull(element)) { if (Objects.isNull(element)) {

View File

@@ -41,6 +41,28 @@ class AggCorrectorTest {
semanticParseInfo.getSqlInfo().getCorrectedS2SQL()); semanticParseInfo.getSqlInfo().getCorrectedS2SQL());
} }
@Test
void testSchemaCorrector() {
SchemaCorrector corrector = new SchemaCorrector();
Long dataSetId = 1L;
ChatQueryContext chatQueryContext = buildQueryContext(dataSetId);
SemanticParseInfo semanticParseInfo = new SemanticParseInfo();
SchemaElement dataSet = new SchemaElement();
dataSet.setDataSetId(dataSetId);
semanticParseInfo.setDataSet(dataSet);
SqlInfo sqlInfo = new SqlInfo();
String sql =
"WITH 总停留时长 AS (SELECT 用户, SUM(停留时长) AS _总停留时长_ FROM 超音数数据集 WHERE 用户 IN ('alice', 'lucy') AND 数据日期 >= '2025-03-01' AND 数据日期 <= '2025-03-12' GROUP BY 用户) SELECT 用户, _总停留时长_ FROM 总停留时长";
sqlInfo.setParsedS2SQL(sql);
sqlInfo.setCorrectedS2SQL(sql);
semanticParseInfo.setSqlInfo(sqlInfo);
corrector.correct(chatQueryContext, semanticParseInfo);
Assert.assertEquals(
"WITH 总停留时长 AS (SELECT 用户名, SUM(停留时长) AS _总停留时长_ FROM 超音数数据集 WHERE 用户名 IN ('alice', 'lucy') AND 数据日期 "
+ ">= '2025-03-01' AND 数据日期 <= '2025-03-12' GROUP BY 用户名) SELECT 用户名, _总停留时长_ FROM 总停留时长",
semanticParseInfo.getSqlInfo().getCorrectedS2SQL());
}
private ChatQueryContext buildQueryContext(Long dataSetId) { private ChatQueryContext buildQueryContext(Long dataSetId) {
ChatQueryContext chatQueryContext = new ChatQueryContext(); ChatQueryContext chatQueryContext = new ChatQueryContext();
List<DataSetSchema> dataSetSchemaList = new ArrayList<>(); List<DataSetSchema> dataSetSchemaList = new ArrayList<>();
@@ -51,18 +73,18 @@ class AggCorrectorTest {
schemaElement.setDataSetId(dataSetId); schemaElement.setDataSetId(dataSetId);
dataSetSchema.setDataSet(schemaElement); dataSetSchema.setDataSet(schemaElement);
Set<SchemaElement> dimensions = new HashSet<>(); Set<SchemaElement> dimensions = new HashSet<>();
SchemaElement element1 = new SchemaElement();
element1.setDataSetId(1L); dimensions.add(SchemaElement.builder().dataSetId(1L).name("部门").build());
element1.setName("部门");
dimensions.add(element1); dimensions.add(SchemaElement.builder().dataSetId(1L).name("用户名").build());
dataSetSchema.setDimensions(dimensions); dataSetSchema.setDimensions(dimensions);
Set<SchemaElement> metrics = new HashSet<>(); Set<SchemaElement> metrics = new HashSet<>();
SchemaElement metric1 = new SchemaElement();
metric1.setDataSetId(1L); metrics.add(SchemaElement.builder().dataSetId(1L).name("访问次数").build());
metric1.setName("访问次数");
metrics.add(metric1); metrics.add(SchemaElement.builder().dataSetId(1L).name("停留时长").build());
dataSetSchema.setMetrics(metrics); dataSetSchema.setMetrics(metrics);
dataSetSchemaList.add(dataSetSchema); dataSetSchemaList.add(dataSetSchema);

View File

@@ -83,6 +83,7 @@ public class PrestoAdaptor extends BaseDbAdaptor {
@Override @Override
public String rewriteSql(String sql) { public String rewriteSql(String sql) {
sql = sql.replaceAll("`", "\"");
return sql; return sql;
} }
} }

View File

@@ -1,6 +1,7 @@
package com.tencent.supersonic.headless.core.translator; package com.tencent.supersonic.headless.core.translator;
import com.tencent.supersonic.common.calcite.SqlMergeWithUtils; import com.tencent.supersonic.common.calcite.SqlMergeWithUtils;
import com.tencent.supersonic.common.jsqlparser.SqlSelectHelper;
import com.tencent.supersonic.common.pojo.enums.EngineType; import com.tencent.supersonic.common.pojo.enums.EngineType;
import com.tencent.supersonic.headless.api.pojo.response.QueryState; import com.tencent.supersonic.headless.api.pojo.response.QueryState;
import com.tencent.supersonic.headless.core.pojo.OntologyQuery; import com.tencent.supersonic.headless.core.pojo.OntologyQuery;
@@ -73,7 +74,7 @@ public class DefaultSemanticTranslator implements SemanticTranslator {
String finalSql = null; String finalSql = null;
if (sqlQuery.isSupportWith()) { if (sqlQuery.isSupportWith()) {
EngineType engineType = queryStatement.getOntology().getDatabaseType(); EngineType engineType = queryStatement.getOntology().getDatabaseType();
if (!SqlMergeWithUtils.hasWith(engineType, ontologyOuterSql)) { if (!SqlSelectHelper.hasWith(ontologyOuterSql)) {
finalSql = "with " + tables.stream() finalSql = "with " + tables.stream()
.map(t -> String.format("%s as (%s)", t.getLeft(), t.getRight())) .map(t -> String.format("%s as (%s)", t.getLeft(), t.getRight()))
.collect(Collectors.joining(",")) + "\n" + ontologyOuterSql; .collect(Collectors.joining(",")) + "\n" + ontologyOuterSql;

View File

@@ -40,11 +40,23 @@ public class DataModelNode extends SemanticNode {
.equalsIgnoreCase(EngineType.POSTGRESQL.getName())) { .equalsIgnoreCase(EngineType.POSTGRESQL.getName())) {
String fullTableName = String.join(".public.", String fullTableName = String.join(".public.",
dataModel.getModelDetail().getTableQuery().split("\\.")); dataModel.getModelDetail().getTableQuery().split("\\."));
sqlTable = "select * from " + fullTableName; sqlTable = "SELECT * FROM " + fullTableName;
} else { } else {
sqlTable = "select * from " + dataModel.getModelDetail().getTableQuery(); sqlTable = "SELECT * FROM " + dataModel.getModelDetail().getTableQuery();
} }
} }
// String filterSql = dataModel.getFilterSql();
String filterSql = dataModel.getModelDetail().getFilterSql();
if (filterSql != null && !filterSql.isEmpty()) {
boolean sqlContainWhere = sqlTable.toUpperCase().matches("(?s).*\\bWHERE\\b.*");
if (sqlContainWhere) {
sqlTable = String.format("%s AND %s", sqlTable, filterSql);
} else {
sqlTable = String.format("%s WHERE %s", sqlTable, filterSql);
}
}
if (sqlTable.isEmpty()) { if (sqlTable.isEmpty()) {
throw new Exception("DataModelNode build error [tableSqlNode not found]"); throw new Exception("DataModelNode build error [tableSqlNode not found]");
} }

View File

@@ -78,7 +78,8 @@ public abstract class SemanticNode {
scope.getValidator().getCatalogReader().getRootSchema(), engineType); scope.getValidator().getCatalogReader().getRootSchema(), engineType);
if (Configuration.getSqlAdvisor(sqlValidatorWithHints, engineType).getReservedAndKeyWords() if (Configuration.getSqlAdvisor(sqlValidatorWithHints, engineType).getReservedAndKeyWords()
.contains(expression.toUpperCase())) { .contains(expression.toUpperCase())) {
if (engineType == EngineType.HANADB) { if (engineType == EngineType.HANADB || engineType == EngineType.PRESTO
|| engineType == EngineType.TRINO) {
expression = String.format("\"%s\"", expression); expression = String.format("\"%s\"", expression);
} else { } else {
expression = String.format("`%s`", expression); expression = String.format("`%s`", expression);
@@ -166,9 +167,9 @@ public abstract class SemanticNode {
if (sqlNode instanceof SqlBasicCall) { if (sqlNode instanceof SqlBasicCall) {
SqlBasicCall sqlBasicCall = (SqlBasicCall) sqlNode; SqlBasicCall sqlBasicCall = (SqlBasicCall) sqlNode;
if (sqlBasicCall.getOperator().getKind().equals(SqlKind.AS)) { if (sqlBasicCall.getOperator().getKind().equals(SqlKind.AS)) {
if (sqlBasicCall.getOperandList().get(0) instanceof SqlSelect) { SqlNode innerQuery = sqlBasicCall.getOperandList().get(0);
SqlSelect table = (SqlSelect) sqlBasicCall.getOperandList().get(0); if (innerQuery instanceof SqlCall) {
return table; return innerQuery;
} }
} }
} }

View File

@@ -69,7 +69,13 @@ public class SqlBuilder {
SqlNode parserNode = tableView.build(); SqlNode parserNode = tableView.build();
DatabaseResp database = queryStatement.getOntology().getDatabase(); DatabaseResp database = queryStatement.getOntology().getDatabase();
EngineType engineType = EngineType.fromString(database.getType()); EngineType engineType = EngineType.fromString(database.getType());
parserNode = optimizeParseNode(parserNode, engineType); try {
parserNode = optimizeParseNode(parserNode, engineType);
} catch (Exception e) {
// failure in optimization phase doesn't affect the query result,
// just ignore it
log.error("optimizeParseNode error", e);
}
return SemanticNode.getSql(parserNode, engineType); return SemanticNode.getSql(parserNode, engineType);
} }

View File

@@ -24,7 +24,7 @@ public interface SemanticLayerService {
DataSetSchema getDataSetSchema(Long id); DataSetSchema getDataSetSchema(Long id);
List<ItemResp> getDomainDataSetTree(); List<ItemResp> getDomainDataSetTree(User user);
List<DimensionResp> getDimensions(MetaFilter metaFilter); List<DimensionResp> getDimensions(MetaFilter metaFilter);

View File

@@ -5,6 +5,7 @@ import com.google.common.collect.Sets;
import com.tencent.supersonic.common.pojo.Constants; import com.tencent.supersonic.common.pojo.Constants;
import com.tencent.supersonic.common.pojo.QueryColumn; import com.tencent.supersonic.common.pojo.QueryColumn;
import com.tencent.supersonic.common.pojo.User; import com.tencent.supersonic.common.pojo.User;
import com.tencent.supersonic.common.pojo.enums.AuthType;
import com.tencent.supersonic.common.pojo.enums.TaskStatusEnum; import com.tencent.supersonic.common.pojo.enums.TaskStatusEnum;
import com.tencent.supersonic.headless.api.pojo.DataSetSchema; import com.tencent.supersonic.headless.api.pojo.DataSetSchema;
import com.tencent.supersonic.headless.api.pojo.Dimension; import com.tencent.supersonic.headless.api.pojo.Dimension;
@@ -29,10 +30,7 @@ import com.tencent.supersonic.headless.core.utils.ComponentFactory;
import com.tencent.supersonic.headless.server.annotation.S2DataPermission; import com.tencent.supersonic.headless.server.annotation.S2DataPermission;
import com.tencent.supersonic.headless.server.facade.service.SemanticLayerService; import com.tencent.supersonic.headless.server.facade.service.SemanticLayerService;
import com.tencent.supersonic.headless.server.manager.SemanticSchemaManager; import com.tencent.supersonic.headless.server.manager.SemanticSchemaManager;
import com.tencent.supersonic.headless.server.service.DataSetService; import com.tencent.supersonic.headless.server.service.*;
import com.tencent.supersonic.headless.server.service.DimensionService;
import com.tencent.supersonic.headless.server.service.MetricService;
import com.tencent.supersonic.headless.server.service.SchemaService;
import com.tencent.supersonic.headless.server.utils.MetricDrillDownChecker; import com.tencent.supersonic.headless.server.utils.MetricDrillDownChecker;
import com.tencent.supersonic.headless.server.utils.QueryUtils; import com.tencent.supersonic.headless.server.utils.QueryUtils;
import com.tencent.supersonic.headless.server.utils.StatUtils; import com.tencent.supersonic.headless.server.utils.StatUtils;
@@ -59,6 +57,7 @@ public class S2SemanticLayerService implements SemanticLayerService {
private final MetricDrillDownChecker metricDrillDownChecker; private final MetricDrillDownChecker metricDrillDownChecker;
private final KnowledgeBaseService knowledgeBaseService; private final KnowledgeBaseService knowledgeBaseService;
private final MetricService metricService; private final MetricService metricService;
private final DomainService domainService;
private final DimensionService dimensionService; private final DimensionService dimensionService;
private final TranslatorConfig translatorConfig; private final TranslatorConfig translatorConfig;
private final QueryCache queryCache = ComponentFactory.getQueryCache(); private final QueryCache queryCache = ComponentFactory.getQueryCache();
@@ -69,7 +68,8 @@ public class S2SemanticLayerService implements SemanticLayerService {
SchemaService schemaService, SemanticTranslator semanticTranslator, SchemaService schemaService, SemanticTranslator semanticTranslator,
MetricDrillDownChecker metricDrillDownChecker, MetricDrillDownChecker metricDrillDownChecker,
KnowledgeBaseService knowledgeBaseService, MetricService metricService, KnowledgeBaseService knowledgeBaseService, MetricService metricService,
DimensionService dimensionService, TranslatorConfig translatorConfig) { DimensionService dimensionService, DomainService domainService,
TranslatorConfig translatorConfig) {
this.statUtils = statUtils; this.statUtils = statUtils;
this.queryUtils = queryUtils; this.queryUtils = queryUtils;
this.semanticSchemaManager = semanticSchemaManager; this.semanticSchemaManager = semanticSchemaManager;
@@ -80,6 +80,7 @@ public class S2SemanticLayerService implements SemanticLayerService {
this.knowledgeBaseService = knowledgeBaseService; this.knowledgeBaseService = knowledgeBaseService;
this.metricService = metricService; this.metricService = metricService;
this.dimensionService = dimensionService; this.dimensionService = dimensionService;
this.domainService = domainService;
this.translatorConfig = translatorConfig; this.translatorConfig = translatorConfig;
} }
@@ -262,8 +263,11 @@ public class S2SemanticLayerService implements SemanticLayerService {
} }
@Override @Override
public List<ItemResp> getDomainDataSetTree() { public List<ItemResp> getDomainDataSetTree(User user) {
return schemaService.getDomainDataSetTree(); List<Long> domainsWithAuth = domainService.getDomainAuthSet(user, AuthType.VIEWER).stream()
.map(DomainResp::getId).toList();
return schemaService.getDomainDataSetTree().stream()
.filter(item -> domainsWithAuth.contains(item.getId())).toList();
} }
@Override @Override

View File

@@ -36,6 +36,7 @@ public class ModelYamlManager {
} else { } else {
dataModelYamlTpl.setTableQuery(modelDetail.getTableQuery()); dataModelYamlTpl.setTableQuery(modelDetail.getTableQuery());
} }
dataModelYamlTpl.setFilterSql(modelDetail.getFilterSql());
dataModelYamlTpl.setFields(modelResp.getModelDetail().getFields()); dataModelYamlTpl.setFields(modelResp.getModelDetail().getFields());
dataModelYamlTpl.setId(modelResp.getId()); dataModelYamlTpl.setId(modelResp.getId());
return dataModelYamlTpl; return dataModelYamlTpl;

View File

@@ -97,9 +97,11 @@ public class SemanticSchemaManager {
modelDetail.setDbType(d.getType()); modelDetail.setDbType(d.getType());
modelDetail.setSqlQuery(d.getSqlQuery()); modelDetail.setSqlQuery(d.getSqlQuery());
modelDetail.setTableQuery(d.getTableQuery()); modelDetail.setTableQuery(d.getTableQuery());
modelDetail.setFilterSql(d.getFilterSql());
modelDetail.getIdentifiers().addAll(getIdentify(d.getIdentifiers())); modelDetail.getIdentifiers().addAll(getIdentify(d.getIdentifiers()));
modelDetail.getMeasures().addAll(getMeasureParams(d.getMeasures())); modelDetail.getMeasures().addAll(getMeasureParams(d.getMeasures()));
modelDetail.getDimensions().addAll(getDimensions(d.getDimensions())); modelDetail.getDimensions().addAll(getDimensions(d.getDimensions()));
modelDetail.getFields().addAll(d.getFields());
return dataModel; return dataModel;
} }

View File

@@ -75,6 +75,7 @@ public class LLMSemanticModeller implements SemanticModeller {
if (!chatApp.isPresent() || !chatApp.get().isEnable()) { if (!chatApp.isPresent() || !chatApp.get().isEnable()) {
return; return;
} }
List<DbSchema> otherDbSchema = getOtherDbSchema(dbSchema, dbSchemas); List<DbSchema> otherDbSchema = getOtherDbSchema(dbSchema, dbSchemas);
ModelSchemaExtractor extractor = ModelSchemaExtractor extractor =
AiServices.create(ModelSchemaExtractor.class, getChatModel(modelBuildReq)); AiServices.create(ModelSchemaExtractor.class, getChatModel(modelBuildReq));

View File

@@ -1,9 +1,9 @@
package com.tencent.supersonic.headless.server.modeller; package com.tencent.supersonic.headless.server.modeller;
import com.tencent.supersonic.headless.api.pojo.ColumnSchema;
import com.tencent.supersonic.headless.api.pojo.DBColumn; import com.tencent.supersonic.headless.api.pojo.DBColumn;
import com.tencent.supersonic.headless.api.pojo.DbSchema; import com.tencent.supersonic.headless.api.pojo.DbSchema;
import com.tencent.supersonic.headless.api.pojo.ModelSchema; import com.tencent.supersonic.headless.api.pojo.ModelSchema;
import com.tencent.supersonic.headless.api.pojo.SemanticColumn;
import com.tencent.supersonic.headless.api.pojo.request.ModelBuildReq; import com.tencent.supersonic.headless.api.pojo.request.ModelBuildReq;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -16,19 +16,20 @@ public class RuleSemanticModeller implements SemanticModeller {
@Override @Override
public void build(DbSchema dbSchema, List<DbSchema> dbSchemas, ModelSchema modelSchema, public void build(DbSchema dbSchema, List<DbSchema> dbSchemas, ModelSchema modelSchema,
ModelBuildReq modelBuildReq) { ModelBuildReq modelBuildReq) {
List<ColumnSchema> columnSchemas = List<SemanticColumn> semanticColumns =
dbSchema.getDbColumns().stream().map(this::convert).collect(Collectors.toList()); dbSchema.getDbColumns().stream().map(this::convert).collect(Collectors.toList());
modelSchema.setColumnSchemas(columnSchemas); modelSchema.setSemanticColumns(semanticColumns);
} }
private ColumnSchema convert(DBColumn dbColumn) { private SemanticColumn convert(DBColumn dbColumn) {
ColumnSchema columnSchema = new ColumnSchema(); SemanticColumn semanticColumn = new SemanticColumn();
columnSchema.setName(dbColumn.getColumnName()); semanticColumn.setName(dbColumn.getColumnName());
columnSchema.setColumnName(dbColumn.getColumnName()); semanticColumn.setColumnName(dbColumn.getColumnName());
columnSchema.setComment(dbColumn.getComment()); semanticColumn.setExpr(dbColumn.getColumnName());
columnSchema.setDataType(dbColumn.getDataType()); semanticColumn.setComment(dbColumn.getComment());
columnSchema.setFiledType(dbColumn.getFieldType()); semanticColumn.setDataType(dbColumn.getDataType());
return columnSchema; semanticColumn.setFiledType(dbColumn.getFieldType());
return semanticColumn;
} }
} }

View File

@@ -7,6 +7,9 @@ import com.tencent.supersonic.headless.api.pojo.request.ModelBuildReq;
import java.util.List; import java.util.List;
/**
* A semantic modeler builds semantic-layer schemas from database-layer schemas.
*/
public interface SemanticModeller { public interface SemanticModeller {
void build(DbSchema dbSchema, List<DbSchema> otherDbSchema, ModelSchema modelSchema, void build(DbSchema dbSchema, List<DbSchema> otherDbSchema, ModelSchema modelSchema,

View File

@@ -44,6 +44,8 @@ public class DatabaseDO {
/** */ /** */
private String viewer; private String viewer;
private Integer isOpen = 0;
/** 配置信息 */ /** 配置信息 */
private String config; private String config;

View File

@@ -21,6 +21,8 @@ public class DataModelYamlTpl {
private String tableQuery; private String tableQuery;
private String filterSql;
private List<IdentifyYamlTpl> identifiers; private List<IdentifyYamlTpl> identifiers;
private List<DimensionYamlTpl> dimensions; private List<DimensionYamlTpl> dimensions;

View File

@@ -52,8 +52,10 @@ public class ChatModelController {
} }
@RequestMapping("/getModelList") @RequestMapping("/getModelList")
public List<ChatModel> getModelList() { public List<ChatModel> getModelList(HttpServletRequest httpServletRequest,
return chatModelService.getChatModels(); HttpServletResponse httpServletResponse) {
User user = UserHolder.findUser(httpServletRequest, httpServletResponse);
return chatModelService.getChatModels(user);
} }
@RequestMapping("/getModelAppList") @RequestMapping("/getModelAppList")

View File

@@ -2,6 +2,7 @@ package com.tencent.supersonic.headless.server.rest;
import com.tencent.supersonic.auth.api.authentication.utils.UserHolder; import com.tencent.supersonic.auth.api.authentication.utils.UserHolder;
import com.tencent.supersonic.common.pojo.User; import com.tencent.supersonic.common.pojo.User;
import com.tencent.supersonic.common.pojo.enums.StatusEnum;
import com.tencent.supersonic.headless.api.pojo.MetaFilter; import com.tencent.supersonic.headless.api.pojo.MetaFilter;
import com.tencent.supersonic.headless.api.pojo.request.DataSetReq; import com.tencent.supersonic.headless.api.pojo.request.DataSetReq;
import com.tencent.supersonic.headless.api.pojo.response.DataSetResp; import com.tencent.supersonic.headless.api.pojo.response.DataSetResp;
@@ -51,6 +52,7 @@ public class DataSetController {
public List<DataSetResp> getDataSetList(@RequestParam("domainId") Long domainId) { public List<DataSetResp> getDataSetList(@RequestParam("domainId") Long domainId) {
MetaFilter metaFilter = new MetaFilter(); MetaFilter metaFilter = new MetaFilter();
metaFilter.setDomainId(domainId); metaFilter.setDomainId(domainId);
metaFilter.setStatus(StatusEnum.ONLINE.getCode());
return dataSetService.getDataSetList(metaFilter); return dataSetService.getDataSetList(metaFilter);
} }

View File

@@ -56,9 +56,9 @@ public class DataSetServiceImpl extends ServiceImpl<DataSetDOMapper, DataSetDO>
public DataSetResp save(DataSetReq dataSetReq, User user) { public DataSetResp save(DataSetReq dataSetReq, User user) {
dataSetReq.createdBy(user.getName()); dataSetReq.createdBy(user.getName());
DataSetDO dataSetDO = convert(dataSetReq); DataSetDO dataSetDO = convert(dataSetReq);
dataSetDO.setStatus(StatusEnum.ONLINE.getCode()); dataSetDO.setStatus(dataSetReq.getStatus() != null ? dataSetReq.getStatus()
: StatusEnum.ONLINE.getCode());
DataSetResp dataSetResp = convert(dataSetDO); DataSetResp dataSetResp = convert(dataSetDO);
// conflictCheck(dataSetResp);
save(dataSetDO); save(dataSetDO);
dataSetResp.setId(dataSetDO.getId()); dataSetResp.setId(dataSetDO.getId());
return dataSetResp; return dataSetResp;

View File

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.tencent.supersonic.common.pojo.QueryColumn; import com.tencent.supersonic.common.pojo.QueryColumn;
import com.tencent.supersonic.common.pojo.User; import com.tencent.supersonic.common.pojo.User;
import com.tencent.supersonic.common.pojo.enums.AuthType;
import com.tencent.supersonic.common.pojo.enums.EngineType; import com.tencent.supersonic.common.pojo.enums.EngineType;
import com.tencent.supersonic.headless.api.pojo.DBColumn; import com.tencent.supersonic.headless.api.pojo.DBColumn;
import com.tencent.supersonic.headless.api.pojo.enums.DataType; import com.tencent.supersonic.headless.api.pojo.enums.DataType;
@@ -79,8 +80,9 @@ public class DatabaseServiceImpl extends ServiceImpl<DatabaseDOMapper, DatabaseD
@Override @Override
public List<DatabaseResp> getDatabaseList(User user) { public List<DatabaseResp> getDatabaseList(User user) {
List<DatabaseResp> databaseResps = List<DatabaseResp> databaseResps = list().stream().map(DatabaseConverter::convert)
list().stream().map(DatabaseConverter::convert).collect(Collectors.toList()); .filter(database -> filterByAuth(database, user, AuthType.VIEWER))
.collect(Collectors.toList());
fillPermission(databaseResps, user); fillPermission(databaseResps, user);
return databaseResps; return databaseResps;
} }
@@ -94,12 +96,50 @@ public class DatabaseServiceImpl extends ServiceImpl<DatabaseDOMapper, DatabaseD
databaseResp.setHasEditPermission(true); databaseResp.setHasEditPermission(true);
databaseResp.setHasUsePermission(true); databaseResp.setHasUsePermission(true);
} }
if (databaseResp.getViewers().contains(user.getName())) { if (databaseResp.getViewers().contains(user.getName()) || databaseResp.isPublic()) {
databaseResp.setHasUsePermission(true); databaseResp.setHasUsePermission(true);
} }
}); });
} }
private boolean filterByAuth(DatabaseResp database, User user, AuthType authType) {
if (database.isPublic() || user.isSuperAdmin()
|| user.getName().equals(database.getCreatedBy())) {
return true;
}
authType = authType == null ? AuthType.VIEWER : authType;
switch (authType) {
case ADMIN:
return checkAdminPermission(user, database);
case VIEWER:
default:
return checkViewPermission(user, database);
}
}
private boolean checkAdminPermission(User user, DatabaseResp database) {
List<String> admins = database.getAdmins();
if (user.isSuperAdmin()) {
return true;
}
if (admins.contains(user.getName()) || database.getCreatedBy().equals(user.getName())) {
return true;
}
return false;
}
private boolean checkViewPermission(User user, DatabaseResp database) {
if (checkAdminPermission(user, database)) {
return true;
}
List<String> viewers = database.getViewers();
if (viewers.contains(user.getName())) {
return true;
}
return false;
}
@Override @Override
public void deleteDatabase(Long databaseId) { public void deleteDatabase(Long databaseId) {
ModelFilter modelFilter = new ModelFilter(); ModelFilter modelFilter = new ModelFilter();

View File

@@ -277,8 +277,8 @@ public class ModelServiceImpl implements ModelService {
dimensionService.createDimensionBatch(dimensionReqs, user); dimensionService.createDimensionBatch(dimensionReqs, user);
} }
private void batchCreateMetric(ModelDO datasourceDO, User user) throws Exception { private void batchCreateMetric(ModelDO modelDO, User user) throws Exception {
List<MetricReq> metricReqs = ModelConverter.convertMetricList(datasourceDO); List<MetricReq> metricReqs = ModelConverter.convertMetricList(modelDO);
metricService.createMetricBatch(metricReqs, user); metricService.createMetricBatch(metricReqs, user);
} }

View File

@@ -425,8 +425,12 @@ public class DictUtils {
.format(DateTimeFormatter.ofPattern(format)); .format(DateTimeFormatter.ofPattern(format));
String end = LocalDate.now().minusDays(itemValueDateEnd) String end = LocalDate.now().minusDays(itemValueDateEnd)
.format(DateTimeFormatter.ofPattern(format)); .format(DateTimeFormatter.ofPattern(format));
return String.format("( %s >= '%s' and %s <= '%s' )", dateConf.getDateField(), start, if (Objects.nonNull(dateConf)) {
dateConf.getDateField(), end); 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) { private String generateDictDateFilter(DictItemResp dictItemResp) {
@@ -440,7 +444,7 @@ public class DictUtils {
} }
// 未进行设置 // 未进行设置
if (Objects.isNull(config) || Objects.isNull(config.getDateConf())) { if (Objects.isNull(config) || Objects.isNull(config.getDateConf())) {
return defaultDateFilter(config.getDateConf()); return defaultDateFilter(null);
} }
// 全表扫描 // 全表扫描
if (DateConf.DateMode.ALL.equals(config.getDateConf().getDateMode())) { if (DateConf.DateMode.ALL.equals(config.getDateConf().getDateMode())) {

View File

@@ -157,23 +157,29 @@ public class ModelConverter {
modelDetail.setQueryType(ModelDefineType.TABLE_QUERY.getName()); modelDetail.setQueryType(ModelDefineType.TABLE_QUERY.getName());
modelDetail.setTableQuery(String.format("%s.%s", modelBuildReq.getDb(), tableName)); modelDetail.setTableQuery(String.format("%s.%s", modelBuildReq.getDb(), tableName));
} }
for (ColumnSchema columnSchema : modelSchema.getColumnSchemas()) { List<Field> fields = new ArrayList<>();
FieldType fieldType = columnSchema.getFiledType(); for (SemanticColumn semanticColumn : modelSchema.getSemanticColumns()) {
FieldType fieldType = semanticColumn.getFiledType();
fields.add(new Field(semanticColumn.getName(), semanticColumn.getDataType()));
if (getIdentifyType(fieldType) != null) { if (getIdentifyType(fieldType) != null) {
Identify identify = new Identify(columnSchema.getName(), Identify identify = new Identify(semanticColumn.getName(),
getIdentifyType(fieldType).name(), columnSchema.getColumnName(), 1); getIdentifyType(fieldType).name(), semanticColumn.getColumnName(), 1);
modelDetail.getIdentifiers().add(identify); modelDetail.getIdentifiers().add(identify);
} else if (FieldType.measure.equals(fieldType)) { } else if (FieldType.measure.equals(fieldType)) {
Measure measure = new Measure(columnSchema.getName(), columnSchema.getColumnName(), Measure measure = new Measure(semanticColumn.getName(),
columnSchema.getColumnName(), columnSchema.getAgg().getOperator(), 1); semanticColumn.getColumnName(), semanticColumn.getExpr(),
semanticColumn.getAgg().getOperator(), semanticColumn.getUnit(), 1);
modelDetail.getMeasures().add(measure); modelDetail.getMeasures().add(measure);
} else { } else {
Dimension dim = new Dimension(columnSchema.getName(), columnSchema.getColumnName(), Dimension dim = new Dimension(semanticColumn.getName(),
columnSchema.getColumnName(), semanticColumn.getColumnName(), semanticColumn.getExpr(),
DimensionType.valueOf(columnSchema.getFiledType().name()), 1); DimensionType.valueOf(semanticColumn.getFiledType().name()), 1);
modelDetail.getDimensions().add(dim); modelDetail.getDimensions().add(dim);
} }
} }
modelDetail.setFields(fields);
modelDetail.setFilterSql(modelBuildReq.getFilterSql());
modelReq.setModelDetail(modelDetail); modelReq.setModelDetail(modelDetail);
return modelReq; return modelReq;
} }

View File

@@ -140,7 +140,7 @@ public abstract class S2BaseDemo implements CommandLineRunner {
} }
protected ChatModel addChatModelIfNotExist() { protected ChatModel addChatModelIfNotExist() {
List<ChatModel> chatModels = chatModelService.getChatModels(); List<ChatModel> chatModels = chatModelService.getChatModels(defaultUser);
if (!chatModels.isEmpty()) { if (!chatModels.isEmpty()) {
return chatModels.get(0); return chatModels.get(0);
} else { } else {

View File

@@ -333,7 +333,6 @@ public class S2VisitsDemo extends S2BaseDemo {
metricReq.setSensitiveLevel(SensitiveLevelEnum.HIGH.getCode()); metricReq.setSensitiveLevel(SensitiveLevelEnum.HIGH.getCode());
metricReq.setDescription("每个用户平均访问的次数"); metricReq.setDescription("每个用户平均访问的次数");
metricReq.setClassifications(Collections.singletonList("核心指标")); metricReq.setClassifications(Collections.singletonList("核心指标"));
metricReq.setAlias("平均访问次数");
MetricDefineByMetricParams metricTypeParams = new MetricDefineByMetricParams(); MetricDefineByMetricParams metricTypeParams = new MetricDefineByMetricParams();
metricTypeParams.setExpr("pv/uv"); metricTypeParams.setExpr("pv/uv");
List<MetricParam> metrics = new ArrayList<>(); List<MetricParam> metrics = new ArrayList<>();

View File

@@ -1,26 +0,0 @@
spring:
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://${DB_HOST}:${DB_PORT:5432}/${DB_NAME}?stringtype=unspecified
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
sql:
init:
continue-on-error: true
mode: always
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
schema-locations: classpath:db/schema-postgres.sql,classpath:db/schema-postgres-demo.sql
data-locations: classpath:db/data-postgres.sql,classpath:db/data-postgres-demo.sql
s2:
embedding:
store:
provider: PGVECTOR
base:
url: ${DB_HOST}
port: ${DB_PORT:5432}
databaseName: ${DB_NAME}
user: ${DB_USERNAME}
password: ${DB_PASSWORD}
dimension: 512

View File

@@ -410,4 +410,11 @@ ALTER TABLE s2_query_stat_info RENAME COLUMN `sql` TO `query_sql`;
--20250224 --20250224
ALTER TABLE s2_agent add column `admin_org` varchar(3000) DEFAULT NULL COMMENT '管理员组织'; ALTER TABLE s2_agent add column `admin_org` varchar(3000) DEFAULT NULL COMMENT '管理员组织';
ALTER TABLE s2_agent add column `view_org` varchar(3000) DEFAULT NULL COMMENT '可用组织'; ALTER TABLE s2_agent add column `view_org` varchar(3000) DEFAULT NULL COMMENT '可用组织';
ALTER TABLE s2_agent add column `is_open` tinyint DEFAULT NULL COMMENT '是否公开'; ALTER TABLE s2_agent add column `is_open` tinyint DEFAULT NULL COMMENT '是否公开';
--20250309
ALTER TABLE s2_model_rela alter column join_condition type text;
--20250310
ALTER TABLE s2_chat_model add column is_open tinyint DEFAULT NULL COMMENT '是否公开';
ALTER TABLE s2_database add column is_open tinyint DEFAULT NULL COMMENT '是否公开';

View File

@@ -116,6 +116,7 @@ CREATE TABLE IF NOT EXISTS `s2_chat_model`
`updated_by` varchar(100) NOT NULL, `updated_by` varchar(100) NOT NULL,
`admin` varchar(500) NOT NULL, `admin` varchar(500) NOT NULL,
`viewer` varchar(500) DEFAULT NULL, `viewer` varchar(500) DEFAULT NULL,
`is_open` TINYINT DEFAULT NULL , -- whether public
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
); COMMENT ON TABLE s2_chat_model IS 'chat model table'; ); COMMENT ON TABLE s2_chat_model IS 'chat model table';
@@ -197,6 +198,7 @@ CREATE TABLE IF NOT EXISTS `s2_database` (
`updated_by` varchar(100) NOT NULL, `updated_by` varchar(100) NOT NULL,
`admin` varchar(500) NOT NULL, `admin` varchar(500) NOT NULL,
`viewer` varchar(500) DEFAULT NULL, `viewer` varchar(500) DEFAULT NULL,
`is_open` TINYINT DEFAULT NULL , -- whether public
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
); );
COMMENT ON TABLE s2_database IS 'database instance table'; COMMENT ON TABLE s2_database IS 'database instance table';
@@ -268,7 +270,7 @@ CREATE TABLE IF NOT EXISTS s2_model_rela
from_model_id BIGINT, from_model_id BIGINT,
to_model_id BIGINT, to_model_id BIGINT,
join_type VARCHAR(255), join_type VARCHAR(255),
join_condition VARCHAR(255), join_condition TEXT,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
); );
@@ -379,7 +381,7 @@ CREATE TABLE IF NOT EXISTS s2_agent
enable_feedback int null, enable_feedback int null,
`admin` varchar(3000) DEFAULT NULL , -- administrator `admin` varchar(3000) DEFAULT NULL , -- administrator
`admin_org` varchar(3000) DEFAULT NULL , -- administrators organization `admin_org` varchar(3000) DEFAULT NULL , -- administrators organization
`is_open` TINYINT DEFAULT NULL , -- whether the public `is_open` TINYINT DEFAULT NULL , -- whether public
`viewer` varchar(3000) DEFAULT NULL , -- available users `viewer` varchar(3000) DEFAULT NULL , -- available users
`view_org` varchar(3000) DEFAULT NULL , -- available organization `view_org` varchar(3000) DEFAULT NULL , -- available organization
PRIMARY KEY (`id`) PRIMARY KEY (`id`)

View File

@@ -161,6 +161,7 @@ CREATE TABLE IF NOT EXISTS `s2_chat_model` (
`updated_by` varchar(100) NOT NULL COMMENT '更新人', `updated_by` varchar(100) NOT NULL COMMENT '更新人',
`admin` varchar(500) DEFAULT NULL, `admin` varchar(500) DEFAULT NULL,
`viewer` varchar(500) DEFAULT NULL, `viewer` varchar(500) DEFAULT NULL,
`is_open` tinyint DEFAULT NULL COMMENT '是否公开',
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='对话大模型实例表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='对话大模型实例表';
@@ -177,6 +178,7 @@ CREATE TABLE IF NOT EXISTS `s2_database` (
`updated_by` varchar(100) NOT NULL COMMENT '更新人', `updated_by` varchar(100) NOT NULL COMMENT '更新人',
`admin` varchar(500) DEFAULT NULL, `admin` varchar(500) DEFAULT NULL,
`viewer` varchar(500) DEFAULT NULL, `viewer` varchar(500) DEFAULT NULL,
`is_open` tinyint DEFAULT NULL COMMENT '是否公开',
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='数据库实例表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='数据库实例表';
@@ -404,7 +406,7 @@ CREATE TABLE IF NOT EXISTS s2_model_rela
from_model_id bigint, from_model_id bigint,
to_model_id bigint, to_model_id bigint,
join_type VARCHAR(255), join_type VARCHAR(255),
join_condition VARCHAR(255) join_condition text
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `s2_collect` ( CREATE TABLE IF NOT EXISTS `s2_collect` (

View File

@@ -147,7 +147,8 @@ CREATE TABLE IF NOT EXISTS s2_chat_model (
updated_at timestamp NOT NULL, updated_at timestamp NOT NULL,
updated_by varchar(100) NOT NULL, updated_by varchar(100) NOT NULL,
admin varchar(500) DEFAULT NULL, admin varchar(500) DEFAULT NULL,
viewer varchar(500) DEFAULT NULL viewer varchar(500) DEFAULT NULL,
is_open smallint DEFAULT NULL
); );
CREATE TABLE IF NOT EXISTS s2_database ( CREATE TABLE IF NOT EXISTS s2_database (
@@ -162,7 +163,8 @@ CREATE TABLE IF NOT EXISTS s2_database (
updated_at timestamp NOT NULL, updated_at timestamp NOT NULL,
updated_by varchar(100) NOT NULL, updated_by varchar(100) NOT NULL,
admin varchar(500) DEFAULT NULL, admin varchar(500) DEFAULT NULL,
viewer varchar(500) DEFAULT NULL viewer varchar(500) DEFAULT NULL,
is_open smallint DEFAULT NULL
); );
@@ -465,7 +467,7 @@ CREATE TABLE IF NOT EXISTS s2_model_rela (
from_model_id bigint, from_model_id bigint,
to_model_id bigint, to_model_id bigint,
join_type VARCHAR(255), join_type VARCHAR(255),
join_condition VARCHAR(255) join_condition text
); );
CREATE TABLE IF NOT EXISTS s2_collect ( CREATE TABLE IF NOT EXISTS s2_collect (

View File

@@ -155,16 +155,6 @@ public class Text2SQLEval extends BaseTest {
assert result.getTextResult().contains("3"); assert result.getTextResult().contains("3");
} }
@Test
public void test_detail_query() throws Exception {
long start = System.currentTimeMillis();
QueryResult result = submitNewChat("特斯拉旗下有哪些品牌", agent.getId());
durations.add(System.currentTimeMillis() - start);
assert result.getQueryColumns().size() >= 1;
assert result.getTextResult().contains("Model Y");
assert result.getTextResult().contains("Model 3");
}
public Agent getLLMAgent() { public Agent getLLMAgent() {
Agent agent = new Agent(); Agent agent = new Agent();
agent.setName("Agent for Test"); agent.setName("Agent for Test");

View File

@@ -39,14 +39,14 @@ public class SemanticModellerTest extends BaseTest {
Map<String, ModelSchema> modelSchemaMap = modelService.buildModelSchema(modelSchemaReq); Map<String, ModelSchema> modelSchemaMap = modelService.buildModelSchema(modelSchemaReq);
ModelSchema userModelSchema = modelSchemaMap.get("s2_user_department"); ModelSchema userModelSchema = modelSchemaMap.get("s2_user_department");
Assertions.assertEquals(2, userModelSchema.getColumnSchemas().size()); Assertions.assertEquals(2, userModelSchema.getSemanticColumns().size());
Assertions.assertEquals(FieldType.primary_key, Assertions.assertEquals(FieldType.primary_key,
userModelSchema.getColumnByName("user_name").getFiledType()); userModelSchema.getColumnByName("user_name").getFiledType());
Assertions.assertEquals(FieldType.categorical, Assertions.assertEquals(FieldType.categorical,
userModelSchema.getColumnByName("department").getFiledType()); userModelSchema.getColumnByName("department").getFiledType());
ModelSchema stayTimeModelSchema = modelSchemaMap.get("s2_stay_time_statis"); ModelSchema stayTimeModelSchema = modelSchemaMap.get("s2_stay_time_statis");
Assertions.assertEquals(4, stayTimeModelSchema.getColumnSchemas().size()); Assertions.assertEquals(4, stayTimeModelSchema.getSemanticColumns().size());
Assertions.assertEquals(FieldType.foreign_key, Assertions.assertEquals(FieldType.foreign_key,
stayTimeModelSchema.getColumnByName("user_name").getFiledType()); stayTimeModelSchema.getColumnByName("user_name").getFiledType());
Assertions.assertEquals(FieldType.partition_time, Assertions.assertEquals(FieldType.partition_time,
@@ -72,7 +72,7 @@ public class SemanticModellerTest extends BaseTest {
Map<String, ModelSchema> modelSchemaMap = modelService.buildModelSchema(modelSchemaReq); Map<String, ModelSchema> modelSchemaMap = modelService.buildModelSchema(modelSchemaReq);
ModelSchema pvModelSchema = modelSchemaMap.values().iterator().next(); ModelSchema pvModelSchema = modelSchemaMap.values().iterator().next();
Assertions.assertEquals(5, pvModelSchema.getColumnSchemas().size()); Assertions.assertEquals(5, pvModelSchema.getSemanticColumns().size());
Assertions.assertEquals(FieldType.partition_time, Assertions.assertEquals(FieldType.partition_time,
pvModelSchema.getColumnByName("imp_date").getFiledType()); pvModelSchema.getColumnByName("imp_date").getFiledType());
Assertions.assertEquals(FieldType.categorical, Assertions.assertEquals(FieldType.categorical,

View File

@@ -26,7 +26,7 @@
</parent> </parent>
<properties> <properties>
<revision>0.9.10</revision> <revision>1.0.0-SNAPSHOT</revision>
<java.source.version>21</java.source.version> <java.source.version>21</java.source.version>
<java.target.version>21</java.target.version> <java.target.version>21</java.target.version>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
@@ -37,7 +37,7 @@
<pagehelper.spring.version>2.1.0</pagehelper.spring.version> <pagehelper.spring.version>2.1.0</pagehelper.spring.version>
<mybatis.version>3.5.3</mybatis.version> <mybatis.version>3.5.3</mybatis.version>
<guava.version>32.0.0-jre</guava.version> <guava.version>32.0.0-jre</guava.version>
<hanlp.version>portable-1.8.3</hanlp.version> <hanlp.version>portable-1.8.4</hanlp.version>
<hadoop.version>2.7.2</hadoop.version> <hadoop.version>2.7.2</hadoop.version>
<commons.lang.version>2.6</commons.lang.version> <commons.lang.version>2.6</commons.lang.version>
<commons.lang3.version>3.7</commons.lang3.version> <commons.lang3.version>3.7</commons.lang3.version>

View File

@@ -1,5 +1,5 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Form, Button, Modal, Input } from 'antd'; import { Form, Button, Modal, Input, message } from 'antd';
import type { RegisterFormDetail } from './types'; import type { RegisterFormDetail } from './types';
@@ -37,8 +37,9 @@ const RegisterForm: React.FC<RegisterFormProps> = (props) => {
try { try {
await handleUpdate(formValus); await handleUpdate(formValus);
setSaveLoading(false); setSaveLoading(false);
} catch (error) { } catch (error: any) {
setSaveLoading(false); setSaveLoading(false);
message.error(error.message);
} }
}; };

View File

@@ -41,8 +41,9 @@ const LoginPage: React.FC = () => {
} }
history.push('/'); history.push('/');
return; return;
} else {
message.error(msg);
} }
message.success(msg);
}; };
// 处理登录按钮响应 // 处理登录按钮响应
@@ -55,12 +56,14 @@ const LoginPage: React.FC = () => {
// 处理注册弹窗确定按钮 // 处理注册弹窗确定按钮
const handleRegister = async (values: RegisterFormDetail) => { const handleRegister = async (values: RegisterFormDetail) => {
const enCodeValues = { ...values, password: encryptPassword(values.password, encryptKey) }; const enCodeValues = { ...values, password: encryptPassword(values.password, encryptKey) };
const { code } = await userRegister(enCodeValues); const { code, msg } = await userRegister(enCodeValues);
if (code === 200) { if (code === 200) {
message.success('注册成功'); message.success('注册成功');
setCreateModalVisible(false); setCreateModalVisible(false);
// 注册完自动帮用户登录 // 注册完自动帮用户登录
await loginDone(enCodeValues); await loginDone(enCodeValues);
} else {
message.error(msg);
} }
}; };

View File

@@ -107,20 +107,8 @@ const MetricDetail: React.FC<Props> = () => {
return ( return (
<> <>
<Helmet
title={`${metircData?.id ? `[指标]${metircData?.name}-${BASE_TITLE}` : '新建指标'}`}
/>
<div className={styles.metricDetailWrapper}> <div className={styles.metricDetailWrapper}>
<div className={styles.metricDetail}> <div className={styles.metricDetail}>
<div className={styles.siderContainer}>
<MetricInfoSider
relationDimensionOptions={relationDimensionOptions}
metircData={metircData}
onDimensionRelationBtnClick={() => {
setMetricRelationModalOpenState(true);
}}
/>
</div>
<div className={styles.tabContainer}> <div className={styles.tabContainer}>
<Tabs <Tabs
defaultActiveKey="metricCaliberInput" defaultActiveKey="metricCaliberInput"
@@ -146,6 +134,15 @@ const MetricDetail: React.FC<Props> = () => {
className={styles.metricDetailTab} className={styles.metricDetailTab}
/> />
</div> </div>
<div className={styles.siderContainer}>
<MetricInfoSider
relationDimensionOptions={relationDimensionOptions}
metircData={metircData}
onDimensionRelationBtnClick={() => {
setMetricRelationModalOpenState(true);
}}
/>
</div>
</div> </div>
<DimensionAndMetricRelationModal <DimensionAndMetricRelationModal
metricItem={metircData} metricItem={metircData}

View File

@@ -1,4 +1,3 @@
.metricWrapper { .metricWrapper {
width: 100%; width: 100%;
margin-left: auto; margin-left: auto;
@@ -12,7 +11,6 @@
flex-direction: column; flex-direction: column;
} }
.metricFilterWrapper { .metricFilterWrapper {
margin: 20px; margin: 20px;
padding: 20px; padding: 20px;
@@ -183,7 +181,68 @@
} }
} }
.metricEditWrapper {
.metricDetailTab {
:global {
.ant-tabs-nav {
margin: 10px 20px 0 20px;
padding: 0 20px;
background-color: rgb(255, 255, 255);
border-radius: 8px;
transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
}
.ant-tabs-tab {
padding: 12px 0;
color: #344767;
font-weight: 500;
}
}
}
.metricDetail {
position: relative;
display: flex;
flex-direction: row;
width: 100%;
padding: 0px;
background-color: transparent;
height: 100%;
.tabContainer {
padding: 24px;
min-height: calc(100vh - 78px);
width: calc(100vw - 350px);
background-color: #fafafb;
}
.metricInfoContent {
padding: 25px;
.title {
position: relative;
margin-bottom: 12px;
color: #0e73ff;
font-weight: bold;
font-size: 16px;
&::before {
position: absolute;
top: 10px;
left: -10px;
display: block;
width: 3px;
height: 14px;
font-size: 0;
background: #0e73ff;
border: 1px solid #0e73ff;
border-radius: 2px;
content: '';
}
}
}
.siderContainer {
width: 350px;
min-height: calc(100vh - 78px);
border-radius: 6px;
padding: 24px 0 24px 24px;
}
}
}
.metricDetailWrapper { .metricDetailWrapper {
height: calc(100vh - 56px); height: calc(100vh - 56px);
@@ -215,8 +274,7 @@
.tabContainer { .tabContainer {
height: 100%; height: 100%;
min-height: calc(100vh - 78px); min-height: calc(100vh - 78px);
// width: calc(100vw - 450px); width: calc(100vw - 450px);
width: 100%;
background-color: #fafafb; background-color: #fafafb;
} }
.metricInfoContent { .metricInfoContent {
@@ -245,8 +303,9 @@
.siderContainer { .siderContainer {
width: 450px; width: 450px;
min-height: calc(100vh - 78px); min-height: calc(100vh - 78px);
margin: 10px 20px 20px 0;
background-color: rgb(255, 255, 255);
border-radius: 6px; border-radius: 6px;
padding: 10px 0 24px 24px;
box-shadow: rgba(0, 0, 0, 0.08) 6px 0px 16px 0px, rgba(0, 0, 0, 0.12) 3px 0px 6px -4px, box-shadow: rgba(0, 0, 0, 0.08) 6px 0px 16px 0px, rgba(0, 0, 0, 0.12) 3px 0px 6px -4px,
rgba(0, 0, 0, 0.05) 9px 0px 28px 8px; rgba(0, 0, 0, 0.05) 9px 0px 28px 8px;
} }
@@ -265,5 +324,260 @@
transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
} }
.metricInfoSider {
padding: 20px;
color: #344767;
background-color: #fff;
height: 100%;
border: 1px solid #e6ebf1;
border-radius: 6px;
.createTitle {
margin-bottom: 10px;
color: #344767;
font-weight: 500;
font-size: 16px;
font-family: var(--tencent-font-family);
}
.gotoMetricListIcon {
color: #3182ce;
cursor: pointer;
&:hover {
color: #5493ff;
}
}
.title {
margin-bottom: 20px;
.name {
font-weight: 600;
font-size: 18px;
}
.bizName {
margin: 5px 0 0 25px;
color: #7b809a;
font-weight: 400;
}
}
.desc {
display: block;
margin-top: 8px;
color: #7b809a;
font-weight: 500;
font-size: 14px;
line-height: 1.9;
}
.subTitle {
margin: 0px;
color: rgb(123, 128, 154);
font-weight: 700;
font-size: 14px;
line-height: 1.25;
letter-spacing: 0.03333em;
text-transform: uppercase;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
.sectionContainer {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
overflow: scroll;
overflow: hidden;
background-image: none;
border-radius: 6px;
.section {
padding: 16px;
color: rgb(52, 71, 103);
line-height: 1.25;
background: transparent;
box-shadow: none;
opacity: 1;
.sectionTitleBox {
padding: 8px 0;
color: rgb(52, 71, 103);
background: transparent;
box-shadow: none;
opacity: 1;
.sectionTitle {
margin: 0px;
color: rgb(52, 71, 103);
font-weight: 600;
font-size: 16px;
line-height: 1.625;
letter-spacing: 0.0075em;
text-transform: capitalize;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
}
.item {
display: flex;
padding-top: 8px;
padding-right: 16px;
padding-bottom: 8px;
color: rgb(52, 71, 103);
background: transparent;
box-shadow: none;
opacity: 1;
.itemLable {
min-width: fit-content;
margin: 0px;
margin-right: 10px;
color: #344767;
font-weight: 700;
font-size: 14px;
line-height: 1.5;
letter-spacing: 0.02857em;
text-transform: capitalize;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
.itemValue {
margin: 0px;
color: #7b809a;
font-weight: 400;
font-size: 14px;
line-height: 1.5;
letter-spacing: 0.02857em;
text-transform: none;
text-decoration: none;
vertical-align: unset;
opacity: 1;
}
}
}
.hr {
flex-shrink: 0;
margin: 0px;
border-color: rgb(242, 244, 247);
// border-width: 0px 0px thin;
border-style: solid;
}
.ctrlBox {
.ctrlList {
position: relative;
margin: 0px;
padding: 8px 0px;
list-style: none;
background-color: rgb(249, 250, 251);
li {
position: relative;
display: flex;
flex-grow: 1;
align-items: center;
justify-content: flex-start;
box-sizing: border-box;
min-width: 0px;
margin: 4px;
padding: 4px 16px;
color: inherit;
text-align: left;
text-decoration: none;
vertical-align: middle;
background-color: transparent;
border: 0px;
border-radius: 0px;
outline: 0px;
cursor: pointer;
transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
appearance: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
-webkit-box-flex: 1;
-webkit-box-pack: start;
-webkit-box-align: center;
&:hover {
color: #3182ce;
text-decoration: none;
background-color: rgba(16, 24, 40, 0.04);
}
}
.ctrlItemIcon {
flex-shrink: 0;
min-width: unset;
margin-right: 5px;
font-size: 14px;
}
.styles.ctrlItemLable {
display: block;
margin: 0px;
font-weight: 400;
font-size: 14px;
line-height: 1.6;
}
}
}
}
}
.settingList {
list-style: none;
margin: 0px;
position: relative;
padding: 0px;
li {
-webkit-tap-highlight-color: transparent;
background-color: transparent;
outline: 0px;
border: 0px;
margin: 0px;
border-radius: 0px;
cursor: pointer;
user-select: none;
vertical-align: middle;
appearance: none;
display: flex;
flex-grow: 1;
justify-content: flex-start;
align-items: center;
position: relative;
text-decoration: none;
min-width: 0px;
box-sizing: border-box;
text-align: left;
padding: 8px 16px;
transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
&.active {
background-color: rgba(22, 119, 255, 0.08);
.icon {
color: rgb(22, 119, 255);
}
.content {
.text {
color: rgb(22, 119, 255);
}
}
}
.icon {
min-width: 32px;
color: #344767;
flex-shrink: 0;
display: inline-flex;
}
.content {
flex: 1 1 auto;
min-width: 0px;
margin-top: 4px;
margin-bottom: 4px;
.text {
margin: 0px;
color: #344767;
font-size: 16px;
// line-height: 1.57;
// font-family: var(--tencent-font-family);
font-weight: 600;
display: block;
}
}
&:hover {
text-decoration: none;
background-color: rgba(0, 0, 0, 0.04);
}
}
}

View File

@@ -99,6 +99,7 @@ const DatasetCreateForm: React.FC<ModelCreateFormModalProps> = forwardRef(
setSaveLoading(false); setSaveLoading(false);
if (code === 200) { if (code === 200) {
onSubmit?.(queryData); onSubmit?.(queryData);
message.success('保存成功');
} else { } else {
message.error(msg); message.error(msg);
} }

View File

@@ -109,6 +109,7 @@ const ViewCreateFormModal: React.FC<ModelCreateFormModalProps> = ({
setSaveLoading(false); setSaveLoading(false);
if (code === 200) { if (code === 200) {
onSubmit?.(queryData); onSubmit?.(queryData);
message.success('保存成功');
} else { } else {
message.error(msg); message.error(msg);
} }

View File

@@ -87,6 +87,7 @@ const ViewSearchFormModal: React.FC<ModelCreateFormModalProps> = ({
setSaveLoading(false); setSaveLoading(false);
if (code === 200) { if (code === 200) {
onSubmit?.(queryData); onSubmit?.(queryData);
message.success('保存成功');
} else { } else {
message.error(msg); message.error(msg);
} }