2 Commits

Author SHA1 Message Date
pisces
48985f5a61 Merge 5b45cfbad7 into 2e28a4c7a5 2024-12-13 11:31:29 +08:00
pisces
5b45cfbad7 feat(chat-sdk/chatitem): 消息支持导出图表图片 2024-12-02 15:16:51 +08:00
383 changed files with 19499 additions and 22364 deletions

View File

@@ -11,28 +11,59 @@ body:
If it is an idea or help wanted, please go to:
[Github Discussion](https://github.com/tencentmusic/supersonic/discussions)
- type: input
id: version
- type: checkboxes
attributes:
label: SuperSonic version
description: Please tell us which version you are using.
placeholder: "0.9.8"
validations:
required: true
label: Search before asking
description: >
Please make sure to search in the [issues](https://github.com/tencentmusic/supersonic/issues?q=is%3Aissue) first to see
whether the same issue was reported already.
options:
- label: >
I had searched in the [issues](https://github.com/tencentmusic/supersonic/issues?q=is%3Aissue) and found no similar
issues.
required: true
- type: input
id: organization
- type: textarea
attributes:
label: Your organization
description: Please tell us your organization so that we can provide you better support and advice.
placeholder: "TME..."
label: Version
description: What is the current version
placeholder: >
Please provide the version you are using.
If it is the trunk version, please input commit id.
validations:
required: true
- type: textarea
attributes:
label: Description
description: Describe the bug you met.
label: What's Wrong?
description: Describe the bug.
placeholder: >
Describe the specific problem, the more detailed the better.
validations:
required: true
- type: textarea
attributes:
label: What You Expected?
validations:
required: true
- type: textarea
attributes:
label: How to Reproduce?
placeholder: >
Please try to give reproducing steps to facilitate quick location of the problem.
- What actions were performed
- Table building statement
- Import statement
- Cluster information: number of nodes, configuration, etc.
If it is hard to reproduce, please also explain the general scene.
- type: textarea
attributes:
label: Anything Else?
- type: checkboxes
attributes:
@@ -43,6 +74,16 @@ body:
options:
- label: Yes I am willing to submit a PR!
- type: checkboxes
attributes:
label: Code of Conduct
description: The Code of Conduct helps create a safe space for everyone. We require that everyone agrees to it.
options:
- label: >
I agree to follow this project's
[Code of Conduct](https://www.apache.org/foundation/policies/conduct)
required: true
- type: markdown
attributes:
value: "Thanks for completing our form!"

View File

@@ -8,20 +8,30 @@ body:
attributes:
value: |
Thank you very much for your good enhancement for SuperSonic.
- type: checkboxes
attributes:
label: Search before asking
description: >
Please make sure to search in the [issues](https://github.com/tencentmusic/supersonic/issues?q=is%3Aissue) first to see
whether the same issue was reported already.
options:
- label: >
I had searched in the [issues](https://github.com/tencentmusic/supersonic/issues?q=is%3Aissue) and found no similar
issues.
required: true
- type: textarea
attributes:
label: Description
description: Describe the enhancement what you want, including motivation if it exists.
- type: input
id: organization
- type: textarea
attributes:
label: Your organization
description: Please tell us your organization so that we can provide you better support and advice.
placeholder: "TME..."
validations:
required: true
label: Solution
placeholder: >
Add overview of proposed solution.
Add related materials like links if they exist.
- type: checkboxes
attributes:
@@ -32,6 +42,16 @@ body:
options:
- label: Yes I am willing to submit a PR!
- type: checkboxes
attributes:
label: Code of Conduct
description: The Code of Conduct helps create a safe space for everyone. We require that everyone agrees to it.
options:
- label: >
I agree to follow this project's
[Code of Conduct](https://www.apache.org/foundation/policies/conduct)
required: true
- type: markdown
attributes:
value: "Thanks for completing our form!"

View File

@@ -8,19 +8,33 @@ body:
value: |
Thank you very much for your good ideas and suggestions for SuperSonic
- type: checkboxes
attributes:
label: Search before asking
description: >
Please make sure to search in the [issues](https://github.com/tencentmusic/supersonic/issues?q=is%3Aissue) first to see
whether the same issue was reported already.
options:
- label: >
I had searched in the [issues](https://github.com/tencentmusic/supersonic/issues?q=is%3Aissue) and found no similar
issues.
required: true
- type: textarea
attributes:
label: Description
description: Describe your ideas and needs.
- type: input
id: organization
- type: textarea
attributes:
label: Your organization
description: Please tell us your organization so that we can provide you better support and advice.
placeholder: "TME..."
validations:
required: true
label: Use case
placeholder: >
What problem does this feature mainly solve, or what scenarios it is suitable for.
- type: textarea
attributes:
label: Related issues
description: Is there currently another issue associated with this?
- type: checkboxes
attributes:
@@ -31,4 +45,16 @@ body:
options:
- label: Yes I am willing to submit a PR!
- type: checkboxes
attributes:
label: Code of Conduct
description: The Code of Conduct helps create a safe space for everyone. We require that everyone agrees to it.
options:
- label: >
I agree to follow this project's
[Code of Conduct](https://www.apache.org/foundation/policies/conduct)
required: true
- type: markdown
attributes:
value: "Thanks for completing our form!"

View File

@@ -8,7 +8,6 @@ body:
value: |
## Ask a Question about SuperSonic
Please provide a detailed description of your question or the clarification you seek regarding the SuperSonic project.
- type: textarea
id: describe-question
attributes:
@@ -17,12 +16,43 @@ body:
placeholder: "Type your question here..."
validations:
required: true
- type: input
id: organization
- type: textarea
id: additional-context
attributes:
label: Your organization
description: Please tell us your organization so that we can provide you better support and advice.
placeholder: "TME..."
label: Provide any additional context or information
description: If your question is related to a specific part of the SuperSonic project or if you have already looked through certain documentation, please provide that information here.
placeholder: "Add context here..."
validations:
required: true
required: false
- type: textarea
id: tried-to-resolve
attributes:
label: What have you tried to resolve your question
description: Let us know what you have done to try and understand or resolve your question. This can help us provide you with the most useful guidance.
placeholder: "I've already tried..."
validations:
required: false
- type: textarea
id: environment
attributes:
label: Your environment
description: Share details about your environment to help us reproduce the issue. Include your operating system, version of SuperSonic, and any other relevant details.
placeholder: "OS, SuperSonic version, etc..."
validations:
required: false
- type: textarea
id: screenshots-logs
attributes:
label: Screenshots or Logs
description: If applicable, add screenshots or logs to help explain your problem.
placeholder: "Paste your logs or attach screenshots here..."
validations:
required: false
- type: textarea
id: additional-information
attributes:
label: Additional information
description: Add any other context or details you think might be helpful for understanding your question.
placeholder: "Any other information..."
validations:
required: false

View File

@@ -28,5 +28,7 @@ jobs:
- name: Build and publish Docker image
run: |
VERSION=${{ github.event.inputs.version }}
chmod +x docker/docker-build-publish.sh
sh docker/docker-build-publish.sh $VERSION
chmod +x docker/docker-build.sh
chmod +x docker/docker-publish.sh
sh docker/docker-build.sh $VERSION
sh docker/docker-publish.sh $VERSION

3
.gitignore vendored
View File

@@ -19,5 +19,4 @@ assembly/runtime/*
chm_db/
__pycache__/
/dict
assembly/build/*-SNAPSHOT
**/node_modules/
assembly/build/*-SNAPSHOT

490
LICENSE
View File

@@ -1,41 +1,489 @@
Apache License Version 2.0
SuperSonic is licensed under the MIT License, with the following additional conditions:
Copyright (2025) The SuperSonic Project Authors. All rights reserved.
1. You may provide SuperSonic to third parties as a commercial software or service. However,
when the following conditions are met, you must contact the producer to obtain a commercial license:
----------
SuperSonic is licensed under the Apache License 2.0, with the following additional conditions:
1. The commercial usage of SuperSonic:
a. SuperSonic may be utilized commercially, including as a frontend and backend service without modifying the source
code and logo.
b. a commercial license must be obtained from the author if you want to develop and distribute a derivative work based
on SuperSonic.
Please contact zhangjun2915@163.com by email to inquire about licensing matters.
a. Multi-tenant SaaS service: Unless explicitly authorized by SuperSonic in writing, you may not use the
SuperSonic source code to operate a multi-tenant SaaS service.
b. LOGO and copyright information: In the process of using SuperSonic, you may not remove or modify
the LOGO or copyright information on the SuperSonic UI. This restriction is inapplicable to uses of
SuperSonic that do not involve its frontend components.
Please contact jerryjzhang@tencent.com by email to inquire about licensing matters.
2. As a contributor, you should agree that:
a. The producer can adjust the open-source agreement to be more strict or relaxed as deemed necessary.
b. Your contributed code may be used for commercial purposes, including but not limited to its business operations.
b. Your contributed code may be used for commercial purposes, including but not limited to its cloud edition.
Terms of the MIT License:
--------------------------------------------------------------------
MIT License
Apart from the specific conditions mentioned above, all other rights and restrictions follow the Apache License 2.0.
Detailed information about the Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0.
Copyright (c) 2023 Tencent Music Entertainment
----------
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
END OF TERMS AND CONDITIONS
Other dependencies and licenses:
Open Source Software Licensed under the MIT License:
--------------------------------------------------------------------
1. Mybatis-PageHelper 1.2.10
Copyright (c) 2014-2022 abel533@gmail.com
2. lombok
Copyright (C) 2009-2021 The Project Lombok Authors.
3. react
Copyright (c) Facebook, Inc. and its affiliates.
4. ant-design
Copyright (c) 2015-present Ant UED, https://xtech.antfin.com/
5. ant-design-pro
Copyright (c) 2019 Alipay.inc
6. @ant-design/charts
Copyright (c) 2021 Ant Design
7. @ant-design/icons
Copyright (c) 2018-present Ant UED, https://xtech.antfin.com/
8. @antv/layout
Copyright (c) 2018 Alipay.inc
9. @antv/xflow
Copyright (c) 2021-2023 Alipay.inc
10. umi
Copyright (c) 2017-present ChenCheng (sorrycc@gmail.com)
11. @umijs/route-utils
Copyright (c) 2019-present chenshuai2144 (qixian.cs@outlook.com)
12. ahooks
Copyright (c) 2020 ahooks
13. axios
Copyright (c) 2014-present Matt Zabriskie & Collaborators
14. classnames
Copyright (c) 2018 Jed Watson
15. crypto-js
Copyright (c) 2009-2013 Jeff Mott
Copyright (c) 2013-2016 Evan Vosberg
16. immutability-helper
Copyright (c) 2017 Moshe Kolodny
17. lodash
Copyright JS Foundation and other contributors <https://js.foundation/>
18. moment
Copyright (c) JS Foundation and other contributors
19. numeral
Copyright (c) 2016 Adam Draper
20. omit.js
Copyright (c) 2016 Benjy Cui
21. rc-menu
Copyright (c) 2014-present yiminghe
22. rc-util
Copyright (c) 2014-present yiminghe
Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
23. react-ace
Copyright (c) 2014 James Hrisho
24. react-dev-inspector
Copyright (c) zthxxx (https://blog.zthxxx.me)
25. react-lazyload
Copyright (c) 2015 Sen Yang
26. react-spinners
Copyright (c) 2017 David Hu
27.react-split-pane
Copyright (c) 2015 tomkp
28. snappyjs
Copyright (c) 2016 Zhipeng Jia
29. sql-formatter
Copyright (c) 2016-2020 ZeroTurnaround LLC
Copyright (c) 2020-2021 George Leslie-Waksman and other contributors
Copyright (c) 2021-Present inferrinizzard and other contributors
30. @ant-design/pro-cli
Copyright (c) 2017-2018 Alipay
31. cross-env
Copyright (c) 2017 Kent C. Dodds
32.cross-port-killer
Copyright (c) 2017 Rafael Milewski
33.detect-installer
Copyright (c) 2019-present chenshuai2144 (qixian.cs@outlook.com)
34.eslint
Copyright OpenJS Foundation and other contributors, <www.openjsf.org>
35.express
Copyright (c) 2009-2014 TJ Holowaychuk <tj@vision-media.ca>
Copyright (c) 2013-2014 Roman Shtylman <shtylman+expressjs@gmail.com>
Copyright (c) 2014-2015 Douglas Christopher Wilson <doug@somethingdoug.com>
36.gh-pages
Copyright (c) 2014 Tim Schaub
37.inflect
Copyright (C) 2020 Pavan Kumar Sunkara
38.lint-staged
Copyright (c) 2016 Andrey Okonetchnikov
39.prettier
Copyright © James Long and contributors
40.stylelint
Copyright (c) 2015 - present Maxime Thirouin, David Clark & Richard Hallows
41.umi-serve
Copyright (c) 2017-present ChenCheng (sorrycc@gmail.com)
42.webpack
Copyright JS Foundation and other contributors
43.react-dnd
Copyright (c) 2015 Dan Abramov
44.react-grid-layout
Copyright (c) 2016 Samuel Reed
45.slat
Copyright © 20162023, Ian Storm Taylor
46.html2canvas
Copyright (c) 2012 Niklas von Hertzen
47.core-js
Copyright (c) 2014-2020 Denis Pushkarev
48.immer 4.0.2
Copyright (c) 2017 Michel Weststrate
49.redux
Copyright (c) 2015-present Dan Abramov
The Redux logo is dedicated to the public domain and licensed under CC0.
50.redux-saga
Copyright (c) 2015 Yassine Elouafi
The Redux-Saga logo is dedicated to the public domain and licensed under CC0.
51.ts-loader
Copyright (c) 2015 TypeStrong
52.minimist
Fileshttps://github.com/minimistjs/minimist/tree/v1.2.3
License Detailshttps://github.com/minimistjs/minimist/blob/main/LICENSE
53.intl
copyright (c) 2013 Andy Earnshaw
A copy of the MIT License is included in this file.
Open Source Software Licensed under the Apache License Version 2.0:
--------------------------------------------------------------------
1. HanLP
Files: https://github.com/hankcs/HanLP/tree/v1.8.3
License Details: https://github.com/hankcs/HanLP/blob/v1.8.3/LICENSE
2. mybatis
iBATIS
This product includes software developed by
The Apache Software Foundation (http://www.apache.org/).
Copyright 2010 The Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
3. guava
Files: https://github.com/google/guava/tree/v20.0
License Details: https://github.com/google/guava/blob/master/LICENSE
4. hadoop
This product includes software developed by The Apache Software Foundation (http://www.apache.org/).
5. Jackson
Files: https://github.com/FasterXML/jackson-core/tree/2.11
License Details: https://github.com/FasterXML/jackson-core/blob/2.11/LICENSE
6. commons-lang
Apache Commons Lang
Copyright 2001-2017 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
This product includes software from the Spring Framework,
under the Apache License 2.0 (see: StringUtils.containsWhitespace())
7. testng
Fileshttps://github.com/testng-team/testng/tree/6.13.1
License Detailshttps://github.com/testng-team/testng/blob/6.13.1/LICENSE.txt
8. jackson-dataformat-yaml
Fileshttps://github.com/FasterXML/jackson-dataformat-yaml/tree/jackson-dataformat-yaml-2.8.11
License Detailshttps://www.apache.org/licenses/LICENSE-2.0.txt
9. druid
Copyright 1999-2018 Alibaba Group Holding Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
limitations under the License.
10. davinci
Licensed to Apereo under one or more contributor license
agreements. See the NOTICE file distributed with this work
for additional information regarding copyright ownership.
Apereo licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a
copy of the License at the following location:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
11. echarts
Apache ECharts
Copyright 2017-2023 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (https://www.apache.org/).
12. echarts-wordcloud
Apache ECharts
Copyright 2017-2023 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (https://www.apache.org/).
13. carlo
Fileshttps://github.com/GoogleChromeLabs/carlo
License Detailshttps://github.com/GoogleChromeLabs/carlo/blob/master/LICENSE
14. puppeteer-core
Copyright 2017 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
15. swagger-ui-react
swagger-ui
Copyright 2020-2021 SmartBear Software Inc.
16. typescript
fileshttps://github.com/microsoft/TypeScript
License Detailshttps://github.com/microsoft/TypeScript/blob/main/LICENSE.txt
17. io.jsonwebtoken
Copyright (C) 2014 jsonwebtoken.io
Files: https://repo1.maven.org/maven2/io/jsonwebtoken/jjwt/0.9.1/jjwt-0.9.1.jar
Terms of the Apache License Version 2.0:
--------------------------------------------------------------------
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Open Source Software Licensed under the Modified BSD License:
--------------------------------------------------------------------
1. node-sha1
Copyright © 2009, Jeff Mott. All rights reserved.
Copyright © 2011, Paul Vorbach. All rights reserved.
This project is licensed under the terms of the Modified BSD License, as follows:
-------------------------------------------------------------------
Copyright (c) 2005-2023, NumPy Developers.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
Neither the name oCrypto-JS nor the names of any contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2. ace-builds
Copyright (c) 2010, Ajax.org B.V.
All rights reserved.
This project is licensed under the terms of the Modified BSD License, as follows:
-------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Ajax.org B.V. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Other Open Source Software
--------------------------------------------------------------------
1. jsencrypt
Fileshttps://github.com/travist/jsencrypt
License Detailshttps://github.com/travist/jsencrypt/blob/master/LICENSE.txt

View File

@@ -35,15 +35,10 @@ function buildWebapp {
chmod +x $projectDir/webapp/start-fe-prod.sh
cd $projectDir/webapp
sh ./start-fe-prod.sh
# check build result
if [ $? -ne 0 ]; then
echo "Failed to build frontend webapp."
exit 1
fi
cp -fr ./supersonic-webapp.tar.gz ${buildDir}/
# check build result
if [ $? -ne 0 ]; then
echo "Failed to get supersonic webapp package."
echo "Failed to build frontend webapp."
exit 1
fi
echo "finished building supersonic webapp"
@@ -61,11 +56,6 @@ function packageRelease {
# package webapp
tar xvf supersonic-webapp.tar.gz
mv supersonic-webapp webapp
# check webapp build result
if [ $? -ne 0 ]; then
echo "Failed to get supersonic webapp package."
exit 1
fi
json='{"env": "''"}'
echo $json > webapp/supersonic.config.json
mv webapp $release_dir/

View File

@@ -4,7 +4,6 @@ chcp 65001
set "sbinDir=%~dp0"
call %sbinDir%/supersonic-common.bat %*
call %sbinDir%/supersonic-env.bat %*
set "command=%~1"
set "service=%~2"
@@ -15,7 +14,7 @@ if "%service%"=="" (
)
if "%profile%"=="" (
set "profile=%S2_DB_TYPE%"
set "profile=local"
)
set "model_name=%service%"
@@ -55,8 +54,7 @@ if "%command%"=="restart" (
set "webDir=%baseDir%\webapp"
set "logDir=%baseDir%\logs"
set "classpath=%baseDir%;%webDir%;%libDir%\*;%confDir%"
set "property=-Dfile.encoding=UTF-8 -Duser.language=Zh -Duser.region=CN -Duser.timezone=GMT+08 -Dspring.profiles.active=%profile%"
set "java-command=%property% -Xms1024m -Xmx1024m -cp %CLASSPATH% %MAIN_CLASS%"
set "java-command=-Dfile.encoding=UTF-8 -Duser.language=Zh -Duser.region=CN -Duser.timezone=GMT+08 -Dspring.profiles.active=%profile% -Xms1024m -Xmx1024m -cp %CLASSPATH% %MAIN_CLASS%"
if not exist %logDir% mkdir %logDir%
start /B java %java-command% >nul 2>&1
timeout /t 10 >nul

View File

@@ -2,7 +2,6 @@
sbinDir=$(cd "$(dirname "$0")"; pwd)
source $sbinDir/supersonic-common.sh
source $sbinDir/supersonic-env.sh
command=$1
service=$2
@@ -13,7 +12,7 @@ if [ -z "$service" ]; then
fi
if [ -z "$profile" ]; then
profile=${S2_DB_TYPE}
profile="local"
fi
model_name=$service

View File

@@ -1,8 +0,0 @@
:: Set below DB configs to connect to your own database
:: Supported DB_TYPE: h2, mysql, postgres
set "S2_DB_TYPE=h2"
set "S2_DB_HOST="
set "S2_DB_PORT="
set "S2_DB_USER="
set "S2_DB_PASSWORD="
set "S2_DB_DATABASE="

View File

@@ -1,10 +0,0 @@
#!/usr/bin/env bash
#### 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=

View File

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

View File

@@ -1,13 +1,20 @@
package com.tencent.supersonic.auth.authentication.interceptor;
import com.tencent.supersonic.auth.api.authentication.config.AuthenticationConfig;
import com.tencent.supersonic.auth.api.authentication.constant.UserConstants;
import com.tencent.supersonic.auth.authentication.service.UserServiceImpl;
import com.tencent.supersonic.auth.authentication.utils.TokenService;
import com.tencent.supersonic.common.util.S2ThreadContext;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.RequestFacade;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.http.MimeHeaders;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;
import org.springframework.web.servlet.HandlerInterceptor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
@@ -20,6 +27,8 @@ public abstract class AuthenticationInterceptor implements HandlerInterceptor {
protected TokenService tokenService;
protected S2ThreadContext s2ThreadContext;
protected boolean isExcludedUri(String uri) {
String excludePathStr = authenticationConfig.getExcludePath();
if (StringUtils.isEmpty(excludePathStr)) {
@@ -44,4 +53,52 @@ public abstract class AuthenticationInterceptor implements HandlerInterceptor {
return includePaths.stream().anyMatch(uri::startsWith);
}
protected boolean isInternalRequest(HttpServletRequest request) {
String internal = request.getHeader(UserConstants.INTERNAL);
return "true".equalsIgnoreCase(internal);
}
protected boolean isAppRequest(HttpServletRequest request) {
String appId = request.getHeader(authenticationConfig.getAppId());
return StringUtils.isNotBlank(appId);
}
protected void reflectSetParam(HttpServletRequest request, String key, String value) {
try {
if (request instanceof StandardMultipartHttpServletRequest) {
RequestFacade servletRequest =
(RequestFacade) ((StandardMultipartHttpServletRequest) request)
.getRequest();
Class<? extends HttpServletRequest> servletRequestClazz = servletRequest.getClass();
Field request1 = servletRequestClazz.getDeclaredField("request");
request1.setAccessible(true);
Object o = request1.get(servletRequest);
Field coyoteRequest = o.getClass().getDeclaredField("coyoteRequest");
coyoteRequest.setAccessible(true);
Object o1 = coyoteRequest.get(o);
Field headers = o1.getClass().getDeclaredField("headers");
headers.setAccessible(true);
MimeHeaders o2 = (MimeHeaders) headers.get(o1);
if (o2.getValue(key) != null) {
o2.setValue(key).setString(value);
} else {
o2.addValue(key).setString(value);
}
} else {
Class<? extends HttpServletRequest> requestClass = request.getClass();
Field request1 = requestClass.getDeclaredField("request");
request1.setAccessible(true);
Object o = request1.get(request);
Field coyoteRequest = o.getClass().getDeclaredField("coyoteRequest");
coyoteRequest.setAccessible(true);
Object o1 = coyoteRequest.get(o);
Field headers = o1.getClass().getDeclaredField("headers");
headers.setAccessible(true);
MimeHeaders o2 = (MimeHeaders) headers.get(o1);
o2.addValue(key).setString(value);
}
} catch (Exception e) {
log.error("reflectSetParam error:", e);
}
}
}

View File

@@ -5,8 +5,11 @@ import com.tencent.supersonic.auth.api.authentication.config.AuthenticationConfi
import com.tencent.supersonic.auth.api.authentication.pojo.UserWithPassword;
import com.tencent.supersonic.auth.authentication.service.UserServiceImpl;
import com.tencent.supersonic.auth.authentication.utils.TokenService;
import com.tencent.supersonic.common.pojo.User;
import com.tencent.supersonic.common.pojo.exception.AccessException;
import com.tencent.supersonic.common.util.ContextUtils;
import com.tencent.supersonic.common.util.S2ThreadContext;
import com.tencent.supersonic.common.util.ThreadContext;
import io.jsonwebtoken.Claims;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -32,10 +35,19 @@ public class DefaultAuthenticationInterceptor extends AuthenticationInterceptor
authenticationConfig = ContextUtils.getBean(AuthenticationConfig.class);
userServiceImpl = ContextUtils.getBean(UserServiceImpl.class);
tokenService = ContextUtils.getBean(TokenService.class);
s2ThreadContext = ContextUtils.getBean(S2ThreadContext.class);
if (!authenticationConfig.isEnabled()) {
setFakerUser(request);
return true;
}
if (isInternalRequest(request)) {
setFakerUser(request);
return true;
}
if (isAppRequest(request)) {
setFakerUser(request);
return true;
}
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
@@ -56,11 +68,35 @@ public class DefaultAuthenticationInterceptor extends AuthenticationInterceptor
UserWithPassword user = getUserWithPassword(request);
if (user != null) {
setContext(user.getName(), request);
return true;
}
throw new AccessException("authentication failed, please login");
}
private void setFakerUser(HttpServletRequest request) {
String token = generateAdminToken(request);
reflectSetParam(request, authenticationConfig.getTokenHttpHeaderKey(), token);
setContext(User.getDefaultUser().getName(), request);
}
private void setContext(String userName, HttpServletRequest request) {
ThreadContext threadContext = ThreadContext.builder()
.token(request.getHeader(authenticationConfig.getTokenHttpHeaderKey()))
.userName(userName).build();
s2ThreadContext.set(threadContext);
}
public String generateAdminToken(HttpServletRequest request) {
UserWithPassword admin = new UserWithPassword("admin");
admin.setId(1L);
admin.setName("admin");
admin.setPassword("c3VwZXJzb25pY0BiaWNvbdktJJYWw6A3rEmBUPzbn/6DNeYnD+y3mAwDKEMS3KVT");
admin.setDisplayName("admin");
admin.setIsAdmin(1);
return tokenService.generateToken(UserWithPassword.convert(admin), request);
}
public UserWithPassword getUserWithPassword(HttpServletRequest request) {
final Optional<Claims> claimsOptional = tokenService.getClaims(request);
if (!claimsOptional.isPresent()) {

View File

@@ -1,13 +1,7 @@
package com.tencent.supersonic.auth.authentication.persistence.dataobject;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("s2_user")
public class UserDO {
@TableId(type = IdType.AUTO)
/** */
private Long id;
/** */

View File

@@ -1,7 +1,5 @@
package com.tencent.supersonic.auth.authentication.persistence.mapper;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDO;
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDOExample;
import org.apache.ibatis.annotations.Mapper;
@@ -9,8 +7,12 @@ import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserDOMapper extends BaseMapper<UserDO> {
public interface UserDOMapper {
/** @mbg.generated */
int insert(UserDO record);
/** @mbg.generated */
List<UserDO> selectByExample(UserDOExample example);
void updateByPrimaryKey(UserDO userDO);

View File

@@ -72,13 +72,6 @@ public class UserController {
return userService.login(userCmd, request);
}
@PostMapping("/resetPassword")
public void resetPassword(@RequestBody UserReq userCmd, HttpServletRequest request,
HttpServletResponse response) {
User user = userService.getCurrentUser(request, response);
userService.resetPassword(user.getName(), userCmd.getPassword(), userCmd.getNewPassword());
}
@PostMapping("/generateToken")
public UserToken generateToken(@RequestBody UserTokenReq userTokenReq,
HttpServletRequest request, HttpServletResponse response) {

View File

@@ -59,6 +59,14 @@
limit #{limitStart} , #{limitEnd}
</if>
</select>
<insert id="insert" parameterType="com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDO">
insert into s2_user (id, name, password, salt,
display_name, email, is_admin
)
values (#{id,jdbcType=BIGINT}, #{name,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{salt,jdbcType=VARCHAR},
#{displayName,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{isAdmin,jdbcType=INTEGER}
)
</insert>
<insert id="insertSelective" parameterType="com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDO">
insert into s2_user
<trim prefix="(" suffix=")" suffixOverrides=",">

View File

@@ -17,8 +17,6 @@ public class ChatMemoryFilter {
private Integer agentId;
private Long queryId;
private String question;
private List<String> questions;

View File

@@ -26,8 +26,4 @@ public class ChatMemoryUpdateReq {
private MemoryReviewResult humanReviewRet;
private String humanReviewCmt;
private MemoryReviewResult llmReviewRet;
private String llmReviewCmt;
}

View File

@@ -38,9 +38,6 @@ public class Agent extends RecordInfo {
private VisualConfig visualConfig;
private List<String> admins = Lists.newArrayList();
private List<String> viewers = Lists.newArrayList();
private List<String> adminOrgs = Lists.newArrayList();
private List<String> viewOrgs = Lists.newArrayList();
private Integer isOpen = 0;
public List<String> getTools(AgentToolType type) {
Map<String, Object> map = JSONObject.parseObject(toolConfig, Map.class);
@@ -118,8 +115,4 @@ public class Agent extends RecordInfo {
return list.apply(this).contains(user.getName());
}
public boolean openToAll() {
return isOpen != null && isOpen == 1;
}
}

View File

@@ -44,7 +44,7 @@ public class SqlExecutor implements ChatQueryExecutor {
Text2SQLExemplar.class);
MemoryService memoryService = ContextUtils.getBean(MemoryService.class);
memoryService.createMemory(ChatMemory.builder().queryId(queryResult.getQueryId())
memoryService.createMemory(ChatMemory.builder()
.agentId(executeContext.getAgent().getId()).status(MemoryStatus.PENDING)
.question(exemplar.getQuestion()).sideInfo(exemplar.getSideInfo())
.dbSchema(exemplar.getDbSchema()).s2sql(exemplar.getSql())
@@ -77,7 +77,6 @@ public class SqlExecutor implements ChatQueryExecutor {
long startTime = System.currentTimeMillis();
QueryResult queryResult = new QueryResult();
queryResult.setQueryId(executeContext.getRequest().getQueryId());
queryResult.setChatContext(parseInfo);
queryResult.setQueryMode(parseInfo.getQueryMode());
queryResult.setQueryTimeCost(System.currentTimeMillis() - startTime);

View File

@@ -3,13 +3,11 @@ package com.tencent.supersonic.chat.server.memory;
import com.tencent.supersonic.chat.api.pojo.enums.MemoryReviewResult;
import com.tencent.supersonic.chat.api.pojo.enums.MemoryStatus;
import com.tencent.supersonic.chat.api.pojo.request.ChatMemoryFilter;
import com.tencent.supersonic.chat.api.pojo.request.ChatMemoryUpdateReq;
import com.tencent.supersonic.chat.server.agent.Agent;
import com.tencent.supersonic.chat.server.pojo.ChatMemory;
import com.tencent.supersonic.chat.server.service.AgentService;
import com.tencent.supersonic.chat.server.service.MemoryService;
import com.tencent.supersonic.common.pojo.ChatApp;
import com.tencent.supersonic.common.pojo.User;
import com.tencent.supersonic.common.pojo.enums.AppModule;
import com.tencent.supersonic.common.util.ChatAppManager;
import com.tencent.supersonic.headless.server.utils.ModelConfigHelper;
@@ -125,10 +123,7 @@ public class MemoryReviewTask {
if (MemoryReviewResult.POSITIVE.equals(m.getLlmReviewRet())) {
m.setStatus(MemoryStatus.ENABLED);
}
ChatMemoryUpdateReq memoryUpdateReq = ChatMemoryUpdateReq.builder().id(m.getId())
.status(m.getStatus()).llmReviewRet(m.getLlmReviewRet())
.llmReviewCmt(m.getLlmReviewCmt()).build();
memoryService.updateMemory(memoryUpdateReq, User.getDefaultUser());
memoryService.updateMemory(m);
}
}
}

View File

@@ -100,12 +100,10 @@ public class NL2SQLParser implements ChatQueryParser {
queryNLReq.setMapModeEnum(mode);
doParse(queryNLReq, parseResp);
}
if (parseResp.getSelectedParses().isEmpty() && candidateParses.isEmpty()) {
if (parseResp.getSelectedParses().isEmpty()) {
queryNLReq.setMapModeEnum(MapModeEnum.LOOSE);
doParse(queryNLReq, parseResp);
}
if (parseResp.getSelectedParses().isEmpty()) {
errMsg.append(parseResp.getErrorMsg());
continue;
@@ -139,18 +137,11 @@ public class NL2SQLParser implements ChatQueryParser {
SemanticParseInfo userSelectParse = parseContext.getRequest().getSelectedParse();
queryNLReq.setSelectedParseInfo(Objects.nonNull(userSelectParse) ? userSelectParse
: parseContext.getResponse().getSelectedParses().get(0));
parseContext.setResponse(new ChatParseResp(parseContext.getResponse().getQueryId()));
parseContext.setResponse(new ChatParseResp(parseContext.getResponse().getQueryId()));
rewriteMultiTurn(parseContext, queryNLReq);
addDynamicExemplars(parseContext, queryNLReq);
doParse(queryNLReq, parseContext.getResponse());
// try again with all semantic fields passed to LLM
if (parseContext.getResponse().getState().equals(ParseResp.ParseState.FAILED)) {
queryNLReq.setSelectedParseInfo(null);
queryNLReq.setMapModeEnum(MapModeEnum.ALL);
doParse(queryNLReq, parseContext.getResponse());
}
}
}

View File

@@ -44,10 +44,4 @@ public class AgentDO {
private String admin;
private String viewer;
private String adminOrg;
private String viewOrg;
private Integer isOpen;
}

View File

@@ -23,9 +23,6 @@ public class ChatMemoryDO {
@TableField("agent_id")
private Integer agentId;
@TableField("query_id")
private Long queryId;
@TableField("question")
private String question;

View File

@@ -17,7 +17,6 @@ import com.tencent.supersonic.chat.server.persistence.mapper.ChatParseMapper;
import com.tencent.supersonic.chat.server.persistence.mapper.ChatQueryDOMapper;
import com.tencent.supersonic.chat.server.persistence.mapper.custom.ShowCaseCustomMapper;
import com.tencent.supersonic.chat.server.persistence.repository.ChatQueryRepository;
import com.tencent.supersonic.common.pojo.QueryColumn;
import com.tencent.supersonic.common.util.JsonUtil;
import com.tencent.supersonic.common.util.PageUtils;
import com.tencent.supersonic.headless.api.pojo.SemanticParseInfo;
@@ -117,20 +116,6 @@ public class ChatQueryRepositoryImpl implements ChatQueryRepository {
JsonUtil.toObject(chatQueryDO.getQueryResult(), QueryResult.class);
if (queryResult != null) {
queryResult.setQueryId(chatQueryDO.getQuestionId());
// fix bugs, compatible with bugs caused by history field changes
if (!CollectionUtils.isEmpty(queryResult.getQueryColumns())) {
List<QueryColumn> queryColumns = queryResult.getQueryColumns().stream().peek(x -> {
if (StringUtils.isEmpty(x.getBizName())
&& StringUtils.isNotEmpty(x.getNameEn())) {
x.setBizName(x.getNameEn());
}
if (StringUtils.isNotEmpty(x.getBizName())
&& StringUtils.isEmpty(x.getNameEn())) {
x.setNameEn(x.getBizName());
}
}).collect(Collectors.toList());
queryResult.setQueryColumns(queryColumns);
}
queryResp.setQueryResult(queryResult);
}
queryResp.setSimilarQueries(JSONObject.parseArray(chatQueryDO.getSimilarQueries(),

View File

@@ -2,7 +2,11 @@ package com.tencent.supersonic.chat.server.pojo;
import com.tencent.supersonic.chat.api.pojo.enums.MemoryReviewResult;
import com.tencent.supersonic.chat.api.pojo.enums.MemoryStatus;
import lombok.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.Date;
@@ -16,8 +20,6 @@ public class ChatMemory {
private Integer agentId;
private Long queryId;
private String question;
private String sideInfo;

View File

@@ -1,7 +1,6 @@
package com.tencent.supersonic.chat.server.pojo;
import com.tencent.supersonic.chat.api.pojo.request.ChatExecuteReq;
import com.tencent.supersonic.chat.api.pojo.response.QueryResult;
import com.tencent.supersonic.chat.server.agent.Agent;
import com.tencent.supersonic.headless.api.pojo.SemanticParseInfo;
import lombok.Data;
@@ -9,7 +8,6 @@ import lombok.Data;
@Data
public class ExecuteContext {
private ChatExecuteReq request;
private QueryResult response;
private Agent agent;
private SemanticParseInfo parseInfo;

View File

@@ -43,17 +43,12 @@ public class DataInterpretProcessor implements ExecuteResultProcessor {
@Override
public boolean accept(ExecuteContext executeContext) {
Agent agent = executeContext.getAgent();
ChatApp chatApp = agent.getChatAppConfig().get(APP_KEY);
return Objects.nonNull(chatApp) && chatApp.isEnable();
}
@Override
public void process(ExecuteContext executeContext) {
QueryResult queryResult = executeContext.getResponse();
public void process(ExecuteContext executeContext, QueryResult queryResult) {
Agent agent = executeContext.getAgent();
ChatApp chatApp = agent.getChatAppConfig().get(APP_KEY);
if (Objects.isNull(chatApp) || !chatApp.isEnable()) {
return;
}
Map<String, Object> variable = new HashMap<>();
variable.put("question", executeContext.getRequest().getQueryText());

View File

@@ -27,18 +27,17 @@ public class DimensionRecommendProcessor implements ExecuteResultProcessor {
private static final int recommend_dimension_size = 5;
@Override
public boolean accept(ExecuteContext executeContext) {
SemanticParseInfo semanticParseInfo = executeContext.getParseInfo();
return QueryType.AGGREGATE.equals(semanticParseInfo.getQueryType())
&& !CollectionUtils.isEmpty(semanticParseInfo.getMetrics());
}
@Override
public void process(ExecuteContext executeContext) {
QueryResult queryResult = executeContext.getResponse();
public void process(ExecuteContext executeContext, QueryResult queryResult) {
SemanticParseInfo semanticParseInfo = executeContext.getParseInfo();
if (!QueryType.AGGREGATE.equals(semanticParseInfo.getQueryType())
|| CollectionUtils.isEmpty(semanticParseInfo.getMetrics())) {
return;
}
Long dataSetId = semanticParseInfo.getDataSetId();
Optional<SchemaElement> firstMetric = semanticParseInfo.getMetrics().stream().findFirst();
if (!firstMetric.isPresent()) {
return;
}
List<SchemaElement> dimensionRecommended =
getDimensions(firstMetric.get().getId(), dataSetId);
queryResult.setRecommendedDimensions(dimensionRecommended);

View File

@@ -1,12 +1,11 @@
package com.tencent.supersonic.chat.server.processor.execute;
import com.tencent.supersonic.chat.api.pojo.response.QueryResult;
import com.tencent.supersonic.chat.server.pojo.ExecuteContext;
import com.tencent.supersonic.chat.server.processor.ResultProcessor;
/** A ExecuteResultProcessor wraps things up before returning execution results to the users. */
public interface ExecuteResultProcessor extends ResultProcessor {
boolean accept(ExecuteContext executeContext);
void process(ExecuteContext executeContext);
void process(ExecuteContext executeContext, QueryResult queryResult);
}

View File

@@ -59,18 +59,14 @@ import static com.tencent.supersonic.common.pojo.Constants.TIME_FORMAT;
public class MetricRatioCalcProcessor implements ExecuteResultProcessor {
@Override
public boolean accept(ExecuteContext executeContext) {
public void process(ExecuteContext executeContext, QueryResult queryResult) {
SemanticParseInfo semanticParseInfo = executeContext.getParseInfo();
AggregatorConfig aggregatorConfig = ContextUtils.getBean(AggregatorConfig.class);
return !CollectionUtils.isEmpty(semanticParseInfo.getMetrics())
&& aggregatorConfig.getEnableRatio()
&& QueryType.AGGREGATE.equals(semanticParseInfo.getQueryType());
}
@Override
public void process(ExecuteContext executeContext) {
QueryResult queryResult = executeContext.getResponse();
SemanticParseInfo semanticParseInfo = executeContext.getParseInfo();
if (CollectionUtils.isEmpty(semanticParseInfo.getMetrics())
|| !aggregatorConfig.getEnableRatio()
|| !QueryType.AGGREGATE.equals(semanticParseInfo.getQueryType())) {
return;
}
AggregateInfo aggregateInfo = getAggregateInfo(executeContext.getRequest().getUser(),
semanticParseInfo, queryResult);
queryResult.setAggregateInfo(aggregateInfo);
@@ -142,7 +138,7 @@ public class MetricRatioCalcProcessor implements ExecuteResultProcessor {
return new HashSet<>();
}
return queryResult.getQueryColumns().stream()
.flatMap(c -> SqlSelectHelper.getFieldsFromExpr(c.getBizName()).stream())
.flatMap(c -> SqlSelectHelper.getColumnFromExpr(c.getNameEn()).stream())
.collect(Collectors.toSet());
}
@@ -167,16 +163,16 @@ public class MetricRatioCalcProcessor implements ExecuteResultProcessor {
Map<String, Object> result = queryResp.getResultList().get(0);
Optional<QueryColumn> valueColumn = queryResp.getColumns().stream()
.filter(c -> c.getBizName().equals(metric.getBizName())).findFirst();
.filter(c -> c.getNameEn().equals(metric.getBizName())).findFirst();
if (!valueColumn.isPresent()) {
return metricInfo;
}
String valueField = String.format("%s_%s", valueColumn.get().getBizName(),
String valueField = String.format("%s_%s", valueColumn.get().getNameEn(),
aggOperatorEnum.getOperator());
if (result.containsKey(valueColumn.get().getBizName())) {
if (result.containsKey(valueColumn.get().getNameEn())) {
DecimalFormat df = new DecimalFormat("#.####");
metricInfo.setValue(df.format(result.get(valueColumn.get().getBizName())));
metricInfo.setValue(df.format(result.get(valueColumn.get().getNameEn())));
}
String ratio = "";
if (Objects.nonNull(result.get(valueField))) {

View File

@@ -1,6 +1,7 @@
package com.tencent.supersonic.chat.server.processor.execute;
import com.alibaba.fastjson.JSONObject;
import com.tencent.supersonic.chat.api.pojo.response.QueryResult;
import com.tencent.supersonic.chat.server.pojo.ExecuteContext;
import com.tencent.supersonic.common.pojo.Constants;
import com.tencent.supersonic.common.pojo.enums.DictWordType;
@@ -15,7 +16,14 @@ import dev.langchain4j.store.embedding.RetrieveQuery;
import dev.langchain4j.store.embedding.RetrieveQueryResult;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -26,20 +34,17 @@ public class MetricRecommendProcessor implements ExecuteResultProcessor {
private static final int METRIC_RECOMMEND_SIZE = 5;
@Override
public boolean accept(ExecuteContext executeContext) {
SemanticParseInfo parseInfo = executeContext.getParseInfo();
return Objects.nonNull(parseInfo.getQueryType())
&& parseInfo.getQueryType().equals(QueryType.AGGREGATE)
&& !CollectionUtils.isEmpty(parseInfo.getMetrics())
&& parseInfo.getMetrics().size() <= METRIC_RECOMMEND_SIZE;
}
@Override
public void process(ExecuteContext executeContext) {
public void process(ExecuteContext executeContext, QueryResult queryResult) {
fillSimilarMetric(executeContext.getParseInfo());
}
private void fillSimilarMetric(SemanticParseInfo parseInfo) {
if (Objects.isNull(parseInfo.getQueryType())
|| !parseInfo.getQueryType().equals(QueryType.AGGREGATE)
|| parseInfo.getMetrics().size() > METRIC_RECOMMEND_SIZE
|| CollectionUtils.isEmpty(parseInfo.getMetrics())) {
return;
}
List<String> metricNames =
Collections.singletonList(parseInfo.getMetrics().iterator().next().getName());
Map<String, Object> filterCondition = new HashMap<>();

View File

@@ -4,7 +4,6 @@ import com.tencent.supersonic.chat.server.pojo.ParseContext;
import com.tencent.supersonic.common.pojo.ChatApp;
import com.tencent.supersonic.common.pojo.enums.AppModule;
import com.tencent.supersonic.common.util.ChatAppManager;
import com.tencent.supersonic.headless.api.pojo.response.ParseResp;
import com.tencent.supersonic.headless.server.utils.ModelConfigHelper;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.chat.ChatLanguageModel;
@@ -27,7 +26,7 @@ public class ErrorMsgRewriteProcessor implements ParseResultProcessor {
private static final Logger keyPipelineLog = LoggerFactory.getLogger("keyPipeline");
public static final String APP_KEY = "REWRITE_ERROR_MESSAGE";
public static final String APP_KEY_ERROR_MESSAGE = "REWRITE_ERROR_MESSAGE";
private static final String REWRITE_ERROR_MESSAGE_INSTRUCTION = ""
+ "#Role: You are a data business partner who closely interacts with business people.\n"
+ "#Task: Your will be provided with user input, system output and some examples, "
@@ -38,36 +37,29 @@ public class ErrorMsgRewriteProcessor implements ParseResultProcessor {
+ "#Examples: {{examples}}\n" + "#Response: ";
public ErrorMsgRewriteProcessor() {
ChatAppManager.register(APP_KEY,
ChatAppManager.register(APP_KEY_ERROR_MESSAGE,
ChatApp.builder().prompt(REWRITE_ERROR_MESSAGE_INSTRUCTION).name("异常提示改写")
.appModule(AppModule.CHAT).description("通过大模型将异常信息改写为更友好和引导性的提示用语")
.enable(true).build());
}
@Override
public boolean accept(ParseContext parseContext) {
ChatApp chatApp = parseContext.getAgent().getChatAppConfig().get(APP_KEY);
return StringUtils.isNotBlank(parseContext.getResponse().getErrorMsg())
&& Objects.nonNull(chatApp) && chatApp.isEnable();
.enable(false).build());
}
@Override
public void process(ParseContext parseContext) {
String errMsg = parseContext.getResponse().getErrorMsg();
ChatApp chatApp = parseContext.getAgent().getChatAppConfig().get(APP_KEY);
ChatApp chatApp = parseContext.getAgent().getChatAppConfig().get(APP_KEY_ERROR_MESSAGE);
if (StringUtils.isBlank(errMsg) || Objects.isNull(chatApp) || !chatApp.isEnable()) {
return;
}
Map<String, Object> variables = new HashMap<>();
variables.put("user_question", parseContext.getRequest().getQueryText());
variables.put("system_message", errMsg);
StringBuilder exampleStr = new StringBuilder();
if (parseContext.getResponse().getUsedExemplars() != null) {
parseContext.getResponse().getUsedExemplars().forEach(e -> exampleStr.append(String
.format("<Question:{%s},Schema:{%s}> ", e.getQuestion(), e.getDbSchema())));
}
if (parseContext.getAgent().getExamples() != null) {
parseContext.getAgent().getExamples()
.forEach(e -> exampleStr.append(String.format("<Question:{%s}> ", e)));
}
parseContext.getResponse().getUsedExemplars().forEach(e -> exampleStr.append(
String.format("<Question:{%s},Schema:{%s}> ", e.getQuestion(), e.getDbSchema())));
parseContext.getAgent().getExamples()
.forEach(e -> exampleStr.append(String.format("<Question:{%s}> ", e)));
variables.put("examples", exampleStr);
Prompt prompt = PromptTemplate.from(chatApp.getPrompt()).apply(variables);
@@ -76,7 +68,6 @@ public class ErrorMsgRewriteProcessor implements ParseResultProcessor {
Response<AiMessage> response = chatLanguageModel.generate(prompt.toUserMessage());
String rewrittenMsg = response.content().text();
parseContext.getResponse().setErrorMsg(rewrittenMsg);
parseContext.getResponse().setState(ParseResp.ParseState.FAILED);
keyPipelineLog.info("ErrorMessageProcessor modelReq:\n{} \nmodelResp:\n{}", prompt.text(),
rewrittenMsg);
}

View File

@@ -8,6 +8,7 @@ import com.tencent.supersonic.common.jsqlparser.SqlSelectHelper;
import com.tencent.supersonic.common.pojo.DateConf;
import com.tencent.supersonic.common.pojo.enums.FilterOperatorEnum;
import com.tencent.supersonic.common.pojo.enums.QueryType;
import com.tencent.supersonic.common.pojo.enums.TimeDimensionEnum;
import com.tencent.supersonic.common.util.ContextUtils;
import com.tencent.supersonic.headless.api.pojo.DataSetSchema;
import com.tencent.supersonic.headless.api.pojo.SchemaElement;
@@ -20,7 +21,12 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -28,12 +34,6 @@ import java.util.stream.Collectors;
**/
@Slf4j
public class ParseInfoFormatProcessor implements ParseResultProcessor {
@Override
public boolean accept(ParseContext parseContext) {
return !parseContext.getResponse().getSelectedParses().isEmpty();
}
@Override
public void process(ParseContext parseContext) {
parseContext.getResponse().getSelectedParses().forEach(p -> {
@@ -96,7 +96,7 @@ public class ParseInfoFormatProcessor implements ParseResultProcessor {
// extract date filter from S2SQL
try {
if (!CollectionUtils.isEmpty(expressions)) {
if (parseInfo.getDateInfo() == null && !CollectionUtils.isEmpty(expressions)) {
parseInfo.setDateInfo(extractDateFilter(expressions, dsSchema));
}
} catch (Exception e) {
@@ -216,6 +216,9 @@ public class ParseInfoFormatProcessor implements ParseResultProcessor {
}
private static boolean isPartitionDimension(DataSetSchema dataSetSchema, String sqlFieldName) {
if (TimeDimensionEnum.containsTimeDimension(sqlFieldName)) {
return true;
}
if (Objects.isNull(dataSetSchema) || Objects.isNull(dataSetSchema.getPartitionDimension())
|| Objects.isNull(dataSetSchema.getPartitionDimension().getName())) {
return false;

View File

@@ -6,7 +6,5 @@ import com.tencent.supersonic.chat.server.processor.ResultProcessor;
/** A ParseResultProcessor wraps things up before returning parsing results to the users. */
public interface ParseResultProcessor extends ResultProcessor {
boolean accept(ParseContext parseContext);
void process(ParseContext parseContext);
}

View File

@@ -23,11 +23,6 @@ import java.util.stream.Collectors;
@Slf4j
public class QueryRecommendProcessor implements ParseResultProcessor {
@Override
public boolean accept(ParseContext parseContext) {
return true;
}
@Override
public void process(ParseContext parseContext) {
CompletableFuture.runAsync(() -> doProcess(parseContext));

View File

@@ -10,11 +10,6 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TimeCostCalcProcessor implements ParseResultProcessor {
@Override
public boolean accept(ParseContext parseContext) {
return true;
}
@Override
public void process(ParseContext parseContext) {
ChatParseResp parseResp = parseContext.getResponse();

View File

@@ -53,7 +53,7 @@ public class ChatController {
}
@PostMapping("/updateQAFeedback")
public Boolean updateQAFeedback(@RequestParam(value = "id") Long id,
public Boolean updateQAFeedback(@RequestParam(value = "id") Integer id,
@RequestParam(value = "score") Integer score,
@RequestParam(value = "feedback", required = false) String feedback) {
return chatService.updateFeedback(id, score, feedback);

View File

@@ -13,6 +13,7 @@ import com.tencent.supersonic.chat.server.persistence.dataobject.ChatParseDO;
import com.tencent.supersonic.chat.server.persistence.dataobject.ChatQueryDO;
import com.tencent.supersonic.common.pojo.User;
import com.tencent.supersonic.headless.api.pojo.SemanticParseInfo;
import com.tencent.supersonic.headless.api.pojo.response.ParseResp;
import java.util.List;
@@ -23,7 +24,7 @@ public interface ChatManageService {
boolean updateChatName(Long chatId, String chatName, String userName);
boolean updateFeedback(Long id, Integer score, String feedback);
boolean updateFeedback(Integer id, Integer score, String feedback);
boolean updateChatIsTop(Long chatId, int isTop);

View File

@@ -14,6 +14,8 @@ public interface MemoryService {
void updateMemory(ChatMemoryUpdateReq chatMemoryUpdateReq, User user);
void updateMemory(ChatMemory memory);
void batchDelete(List<Long> ids);
PageInfo<ChatMemory> pageMemories(PageMemoryReq pageMemoryReq);

View File

@@ -1,7 +1,6 @@
package com.tencent.supersonic.chat.server.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tencent.supersonic.auth.api.authentication.service.UserService;
import com.tencent.supersonic.chat.api.pojo.request.ChatMemoryFilter;
import com.tencent.supersonic.chat.api.pojo.request.ChatParseReq;
import com.tencent.supersonic.chat.server.agent.Agent;
@@ -21,14 +20,13 @@ import com.tencent.supersonic.common.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
@Slf4j
@@ -44,12 +42,7 @@ public class AgentServiceImpl extends ServiceImpl<AgentDOMapper, AgentDO> implem
@Autowired
private ChatModelService chatModelService;
@Autowired
private UserService userService;
@Autowired
@Qualifier("chatExecutor")
private ThreadPoolExecutor executor;
private ExecutorService executorService = Executors.newFixedThreadPool(1);
@Override
public List<Agent> getAgents(User user, AuthType authType) {
@@ -58,19 +51,17 @@ public class AgentServiceImpl extends ServiceImpl<AgentDOMapper, AgentDO> implem
}
private boolean filterByAuth(Agent agent, User user, AuthType authType) {
Set<String> orgIds = userService.getUserAllOrgId(user.getName());
if (user.isSuperAdmin() || agent.openToAll()
|| user.getName().equals(agent.getCreatedBy())) {
if (user.isSuperAdmin() || user.getName().equals(agent.getCreatedBy())) {
return true;
}
authType = authType == null ? AuthType.VIEWER : authType;
switch (authType) {
case ADMIN:
return checkAdminPermission(orgIds, user, agent);
return agent.contains(user, Agent::getAdmins);
case VIEWER:
default:
return checkViewPermission(orgIds, user, agent);
return agent.contains(user, Agent::getAdmins)
|| agent.contains(user, Agent::getViewers);
}
}
@@ -117,7 +108,7 @@ public class AgentServiceImpl extends ServiceImpl<AgentDOMapper, AgentDO> implem
* @param agent
*/
private void executeAgentExamplesAsync(Agent agent) {
executor.execute(() -> doExecuteAgentExamples(agent));
executorService.execute(() -> doExecuteAgentExamples(agent));
}
private synchronized void doExecuteAgentExamples(Agent agent) {
@@ -168,9 +159,6 @@ public class AgentServiceImpl extends ServiceImpl<AgentDOMapper, AgentDO> implem
});
agent.setAdmins(JsonUtil.toList(agentDO.getAdmin(), String.class));
agent.setViewers(JsonUtil.toList(agentDO.getViewer(), String.class));
agent.setAdminOrgs(JsonUtil.toList(agentDO.getAdminOrg(), String.class));
agent.setViewOrgs(JsonUtil.toList(agentDO.getViewOrg(), String.class));
agent.setIsOpen(agentDO.getIsOpen());
return agent;
}
@@ -183,56 +171,9 @@ public class AgentServiceImpl extends ServiceImpl<AgentDOMapper, AgentDO> implem
agentDO.setVisualConfig(JsonUtil.toString(agent.getVisualConfig()));
agentDO.setAdmin(JsonUtil.toString(agent.getAdmins()));
agentDO.setViewer(JsonUtil.toString(agent.getViewers()));
agentDO.setAdminOrg(JsonUtil.toString(agent.getAdminOrgs()));
agentDO.setViewOrg(JsonUtil.toString(agent.getViewOrgs()));
agentDO.setIsOpen(agent.getIsOpen());
if (agentDO.getStatus() == null) {
agentDO.setStatus(1);
}
return agentDO;
}
private boolean checkAdminPermission(Set<String> orgIds, User user, Agent agent) {
List<String> admins = agent.getAdmins();
List<String> adminOrgs = agent.getAdminOrgs();
if (user.isSuperAdmin()) {
return true;
}
if (admins.contains(user.getName()) || agent.getCreatedBy().equals(user.getName())) {
return true;
}
if (CollectionUtils.isEmpty(adminOrgs)) {
return false;
}
for (String orgId : orgIds) {
if (adminOrgs.contains(orgId)) {
return true;
}
}
return false;
}
private boolean checkViewPermission(Set<String> orgIds, User user, Agent agent) {
if (checkAdminPermission(orgIds, user, agent)) {
return true;
}
List<String> viewers = agent.getViewers();
List<String> viewOrgs = agent.getViewOrgs();
if (agent.openToAll()) {
return true;
}
if (viewers.contains(user.getName())) {
return true;
}
if (CollectionUtils.isEmpty(viewOrgs)) {
return false;
}
for (String orgId : orgIds) {
if (viewOrgs.contains(orgId)) {
return true;
}
}
return false;
}
}

View File

@@ -2,9 +2,9 @@ package com.tencent.supersonic.chat.server.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageInfo;
import com.tencent.supersonic.chat.api.pojo.enums.MemoryReviewResult;
import com.tencent.supersonic.chat.api.pojo.enums.MemoryStatus;
import com.tencent.supersonic.chat.api.pojo.request.*;
import com.tencent.supersonic.chat.api.pojo.request.ChatExecuteReq;
import com.tencent.supersonic.chat.api.pojo.request.ChatParseReq;
import com.tencent.supersonic.chat.api.pojo.request.PageQueryInfoReq;
import com.tencent.supersonic.chat.api.pojo.response.ChatParseResp;
import com.tencent.supersonic.chat.api.pojo.response.QueryResp;
import com.tencent.supersonic.chat.api.pojo.response.QueryResult;
@@ -15,12 +15,11 @@ import com.tencent.supersonic.chat.server.persistence.dataobject.ChatQueryDO;
import com.tencent.supersonic.chat.server.persistence.dataobject.QueryDO;
import com.tencent.supersonic.chat.server.persistence.repository.ChatQueryRepository;
import com.tencent.supersonic.chat.server.persistence.repository.ChatRepository;
import com.tencent.supersonic.chat.server.pojo.ChatMemory;
import com.tencent.supersonic.chat.server.service.ChatManageService;
import com.tencent.supersonic.chat.server.service.MemoryService;
import com.tencent.supersonic.common.pojo.User;
import com.tencent.supersonic.common.util.JsonUtil;
import com.tencent.supersonic.headless.api.pojo.SemanticParseInfo;
import com.tencent.supersonic.headless.api.pojo.response.ParseResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -39,8 +38,6 @@ public class ChatManageServiceImpl implements ChatManageService {
private ChatRepository chatRepository;
@Autowired
private ChatQueryRepository chatQueryRepository;
@Autowired
private MemoryService memoryService;
@Override
public Long addChat(User user, String chatName, Integer agentId) {
@@ -67,28 +64,11 @@ public class ChatManageServiceImpl implements ChatManageService {
}
@Override
public boolean updateFeedback(Long id, Integer score, String feedback) {
public boolean updateFeedback(Integer id, Integer score, String feedback) {
QueryDO intelligentQueryDO = new QueryDO();
intelligentQueryDO.setId(id);
intelligentQueryDO.setQuestionId(id);
intelligentQueryDO.setScore(score);
intelligentQueryDO.setFeedback(feedback);
// enable or disable memory based on user feedback
if (score >= 5 || score <= 1) {
ChatMemoryFilter memoryFilter = ChatMemoryFilter.builder().queryId(id).build();
List<ChatMemory> memories = memoryService.getMemories(memoryFilter);
memories.forEach(m -> {
MemoryStatus status = score >= 5 ? MemoryStatus.ENABLED : MemoryStatus.DISABLED;
MemoryReviewResult reviewResult =
score >= 5 ? MemoryReviewResult.POSITIVE : MemoryReviewResult.NEGATIVE;
ChatMemoryUpdateReq memoryUpdateReq = ChatMemoryUpdateReq.builder().id(m.getId())
.status(status).humanReviewRet(reviewResult)
.humanReviewCmt("Reviewed as per user feedback").build();
memoryService.updateMemory(memoryUpdateReq, User.getDefaultUser());
});
}
return chatRepository.updateFeedback(intelligentQueryDO);
}

View File

@@ -91,17 +91,11 @@ public class ChatQueryServiceImpl implements ChatQueryService {
Long queryId = chatParseReq.getQueryId();
if (Objects.isNull(queryId)) {
queryId = chatManageService.createChatQuery(chatParseReq);
chatParseReq.setQueryId(queryId);
}
ParseContext parseContext = buildParseContext(chatParseReq, new ChatParseResp(queryId));
chatQueryParsers.forEach(p -> p.parse(parseContext));
for (ParseResultProcessor processor : parseResultProcessors) {
if (processor.accept(parseContext)) {
processor.process(parseContext);
}
}
parseResultProcessors.forEach(p -> p.process(parseContext));
if (!parseContext.needFeedback()) {
chatManageService.batchAddParse(chatParseReq, parseContext.getResponse());
@@ -122,12 +116,9 @@ public class ChatQueryServiceImpl implements ChatQueryService {
}
}
executeContext.setResponse(queryResult);
if (queryResult != null) {
for (ExecuteResultProcessor processor : executeResultProcessors) {
if (processor.accept(executeContext)) {
processor.process(executeContext);
}
processor.process(executeContext, queryResult);
}
saveQueryResult(chatExecuteReq, queryResult);
}

View File

@@ -1,7 +1,6 @@
package com.tencent.supersonic.chat.server.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tencent.supersonic.chat.api.pojo.enums.MemoryReviewResult;
@@ -10,7 +9,6 @@ import com.tencent.supersonic.chat.api.pojo.request.ChatMemoryFilter;
import com.tencent.supersonic.chat.api.pojo.request.ChatMemoryUpdateReq;
import com.tencent.supersonic.chat.api.pojo.request.PageMemoryReq;
import com.tencent.supersonic.chat.server.persistence.dataobject.ChatMemoryDO;
import com.tencent.supersonic.chat.server.persistence.mapper.ChatMemoryMapper;
import com.tencent.supersonic.chat.server.persistence.repository.ChatMemoryRepository;
import com.tencent.supersonic.chat.server.pojo.ChatMemory;
import com.tencent.supersonic.chat.server.service.MemoryService;
@@ -18,6 +16,7 @@ import com.tencent.supersonic.common.config.EmbeddingConfig;
import com.tencent.supersonic.common.pojo.Text2SQLExemplar;
import com.tencent.supersonic.common.pojo.User;
import com.tencent.supersonic.common.service.ExemplarService;
import com.tencent.supersonic.common.util.BeanMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -35,9 +34,6 @@ public class MemoryServiceImpl implements MemoryService {
@Autowired
private ChatMemoryRepository chatMemoryRepository;
@Autowired
private ChatMemoryMapper chatMemoryMapper;
@Autowired
private ExemplarService exemplarService;
@@ -61,36 +57,20 @@ public class MemoryServiceImpl implements MemoryService {
ChatMemoryDO chatMemoryDO = chatMemoryRepository.getMemory(chatMemoryUpdateReq.getId());
boolean hadEnabled =
MemoryStatus.ENABLED.toString().equals(chatMemoryDO.getStatus().trim());
chatMemoryDO.setUpdatedBy(user.getName());
chatMemoryDO.setUpdatedAt(new Date());
BeanMapper.mapper(chatMemoryUpdateReq, chatMemoryDO);
if (MemoryStatus.ENABLED.equals(chatMemoryUpdateReq.getStatus()) && !hadEnabled) {
enableMemory(chatMemoryDO);
} else if (MemoryStatus.DISABLED.equals(chatMemoryUpdateReq.getStatus()) && hadEnabled) {
disableMemory(chatMemoryDO);
}
chatMemoryRepository.updateMemory(chatMemoryDO);
}
LambdaUpdateWrapper<ChatMemoryDO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(ChatMemoryDO::getId, chatMemoryDO.getId());
if (Objects.nonNull(chatMemoryUpdateReq.getStatus())) {
updateWrapper.set(ChatMemoryDO::getStatus, chatMemoryUpdateReq.getStatus());
}
if (Objects.nonNull(chatMemoryUpdateReq.getLlmReviewRet())) {
updateWrapper.set(ChatMemoryDO::getLlmReviewRet,
chatMemoryUpdateReq.getLlmReviewRet().toString());
}
if (Objects.nonNull(chatMemoryUpdateReq.getLlmReviewCmt())) {
updateWrapper.set(ChatMemoryDO::getLlmReviewCmt, chatMemoryUpdateReq.getLlmReviewCmt());
}
if (Objects.nonNull(chatMemoryUpdateReq.getHumanReviewRet())) {
updateWrapper.set(ChatMemoryDO::getHumanReviewRet,
chatMemoryUpdateReq.getHumanReviewRet().toString());
}
if (Objects.nonNull(chatMemoryUpdateReq.getHumanReviewCmt())) {
updateWrapper.set(ChatMemoryDO::getHumanReviewCmt,
chatMemoryUpdateReq.getHumanReviewCmt());
}
updateWrapper.set(ChatMemoryDO::getUpdatedAt, new Date());
updateWrapper.set(ChatMemoryDO::getUpdatedBy, user.getName());
chatMemoryMapper.update(updateWrapper);
@Override
public void updateMemory(ChatMemory memory) {
chatMemoryRepository.updateMemory(getMemoryDO(memory));
}
@Override
@@ -113,9 +93,6 @@ public class MemoryServiceImpl implements MemoryService {
if (chatMemoryFilter.getAgentId() != null) {
queryWrapper.lambda().eq(ChatMemoryDO::getAgentId, chatMemoryFilter.getAgentId());
}
if (chatMemoryFilter.getQueryId() != null) {
queryWrapper.lambda().eq(ChatMemoryDO::getQueryId, chatMemoryFilter.getQueryId());
}
if (StringUtils.isNotBlank(chatMemoryFilter.getQuestion())) {
queryWrapper.lambda().like(ChatMemoryDO::getQuestion, chatMemoryFilter.getQuestion());
}
@@ -143,7 +120,7 @@ public class MemoryServiceImpl implements MemoryService {
return chatMemoryDOS.stream().map(this::getMemory).collect(Collectors.toList());
}
public void enableMemory(ChatMemoryDO memory) {
private void enableMemory(ChatMemoryDO memory) {
memory.setStatus(MemoryStatus.ENABLED.toString());
exemplarService.storeExemplar(embeddingConfig.getMemoryCollectionName(memory.getAgentId()),
Text2SQLExemplar.builder().question(memory.getQuestion())
@@ -151,7 +128,7 @@ public class MemoryServiceImpl implements MemoryService {
.sql(memory.getS2sql()).build());
}
public void disableMemory(ChatMemoryDO memory) {
private void disableMemory(ChatMemoryDO memory) {
memory.setStatus(MemoryStatus.DISABLED.toString());
exemplarService.removeExemplar(embeddingConfig.getMemoryCollectionName(memory.getAgentId()),
Text2SQLExemplar.builder().question(memory.getQuestion())

View File

@@ -28,7 +28,7 @@ public class ResultFormatter {
}
for (Map<String, Object> row : queryResults) {
for (QueryColumn column : queryColumns) {
String columnKey = column.getBizName();
String columnKey = column.getNameEn();
Object value = row.get(columnKey);
table.append("| ").append(value != null ? value.toString() : "").append(" ");
}

View File

@@ -174,10 +174,6 @@
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-milvus</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-opensearch</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-pgvector</artifactId>
@@ -246,10 +242,6 @@
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.woodstox</groupId>

View File

@@ -81,7 +81,7 @@ public class Configuration {
.setUnquotedCasing(Casing.TO_UPPER).setConformance(sqlDialect.getConformance())
.setLex(Lex.BIG_QUERY);
if (EngineType.HANADB.equals(engineType)) {
parserConfig = parserConfig.setQuoting(Quoting.DOUBLE_QUOTE);
parserConfig = parserConfig.setQuoting(Quoting.DOUBLE_QUOTE);
}
parserConfig = parserConfig.setQuotedCasing(Casing.UNCHANGED);
parserConfig = parserConfig.setUnquotedCasing(Casing.UNCHANGED);

View File

@@ -21,11 +21,10 @@ public class SqlDialectFactory {
.withDatabaseProduct(DatabaseProduct.BIG_QUERY).withLiteralQuoteString("'")
.withLiteralEscapedQuoteString("''").withUnquotedCasing(Casing.UNCHANGED)
.withQuotedCasing(Casing.UNCHANGED).withCaseSensitive(false);
public static final Context HANADB_CONTEXT =
SqlDialect.EMPTY_CONTEXT.withDatabaseProduct(DatabaseProduct.BIG_QUERY)
.withLiteralQuoteString("'").withIdentifierQuoteString("\"")
.withLiteralEscapedQuoteString("''").withUnquotedCasing(Casing.UNCHANGED)
.withQuotedCasing(Casing.UNCHANGED).withCaseSensitive(true);
public static final Context HANADB_CONTEXT = SqlDialect.EMPTY_CONTEXT
.withDatabaseProduct(DatabaseProduct.BIG_QUERY).withLiteralQuoteString("'")
.withIdentifierQuoteString("\"").withLiteralEscapedQuoteString("''").withUnquotedCasing(Casing.UNCHANGED)
.withQuotedCasing(Casing.UNCHANGED).withCaseSensitive(true);
private static Map<EngineType, SemanticSqlDialect> sqlDialectMap;
static {

View File

@@ -17,10 +17,9 @@ import java.util.List;
public class EmbeddingStoreParameterConfig extends ParameterConfig {
private static final String MODULE_NAME = "向量数据库配置";
public static final Parameter EMBEDDING_STORE_PROVIDER =
new Parameter("s2.embedding.store.provider", EmbeddingStoreType.IN_MEMORY.name(),
"向量库类型", "目前支持四种类型IN_MEMORY、MILVUS、CHROMA、PGVECTOR、OPENSEARCH", "list",
MODULE_NAME, getCandidateValues());
public static final Parameter EMBEDDING_STORE_PROVIDER = new Parameter(
"s2.embedding.store.provider", EmbeddingStoreType.IN_MEMORY.name(), "向量库类型",
"目前支持四种类型IN_MEMORY、MILVUS、CHROMA、PGVECTOR", "list", MODULE_NAME, getCandidateValues());
public static final Parameter EMBEDDING_STORE_BASE_URL =
new Parameter("s2.embedding.store.base.url", "", "BaseUrl", "", "string", MODULE_NAME,
@@ -88,18 +87,16 @@ public class EmbeddingStoreParameterConfig extends ParameterConfig {
private static ArrayList<String> getCandidateValues() {
return Lists.newArrayList(EmbeddingStoreType.IN_MEMORY.name(),
EmbeddingStoreType.MILVUS.name(), EmbeddingStoreType.CHROMA.name(),
EmbeddingStoreType.PGVECTOR.name(), EmbeddingStoreType.OPENSEARCH.name());
EmbeddingStoreType.PGVECTOR.name());
}
private static List<Parameter.Dependency> getBaseUrlDependency() {
return getDependency(EMBEDDING_STORE_PROVIDER.getName(),
Lists.newArrayList(EmbeddingStoreType.MILVUS.name(),
EmbeddingStoreType.CHROMA.name(), EmbeddingStoreType.PGVECTOR.name(),
EmbeddingStoreType.OPENSEARCH.name()),
EmbeddingStoreType.CHROMA.name(), EmbeddingStoreType.PGVECTOR.name()),
ImmutableMap.of(EmbeddingStoreType.MILVUS.name(), "http://localhost:19530",
EmbeddingStoreType.CHROMA.name(), "http://localhost:8000",
EmbeddingStoreType.PGVECTOR.name(), "127.0.0.1",
EmbeddingStoreType.OPENSEARCH.name(), "http://localhost:9200"));
EmbeddingStoreType.PGVECTOR.name(), "127.0.0.1"));
}
private static List<Parameter.Dependency> getApiKeyDependency() {
@@ -117,19 +114,17 @@ public class EmbeddingStoreParameterConfig extends ParameterConfig {
private static List<Parameter.Dependency> getDimensionDependency() {
return getDependency(EMBEDDING_STORE_PROVIDER.getName(),
Lists.newArrayList(EmbeddingStoreType.MILVUS.name(),
EmbeddingStoreType.PGVECTOR.name(), EmbeddingStoreType.OPENSEARCH.name()),
EmbeddingStoreType.PGVECTOR.name()),
ImmutableMap.of(EmbeddingStoreType.MILVUS.name(), "384",
EmbeddingStoreType.PGVECTOR.name(), "512",
EmbeddingStoreType.OPENSEARCH.name(), "512"));
EmbeddingStoreType.PGVECTOR.name(), "512"));
}
private static List<Parameter.Dependency> getDatabaseNameDependency() {
return getDependency(EMBEDDING_STORE_PROVIDER.getName(),
Lists.newArrayList(EmbeddingStoreType.MILVUS.name(),
EmbeddingStoreType.PGVECTOR.name(), EmbeddingStoreType.OPENSEARCH.name()),
EmbeddingStoreType.PGVECTOR.name()),
ImmutableMap.of(EmbeddingStoreType.MILVUS.name(), "",
EmbeddingStoreType.PGVECTOR.name(), "postgres",
EmbeddingStoreType.OPENSEARCH.name(), "ai_sql"));
EmbeddingStoreType.PGVECTOR.name(), "postgres"));
}
private static List<Parameter.Dependency> getPortDependency() {
@@ -141,18 +136,16 @@ public class EmbeddingStoreParameterConfig extends ParameterConfig {
private static List<Parameter.Dependency> getUserDependency() {
return getDependency(EMBEDDING_STORE_PROVIDER.getName(),
Lists.newArrayList(EmbeddingStoreType.MILVUS.name(),
EmbeddingStoreType.PGVECTOR.name(), EmbeddingStoreType.OPENSEARCH.name()),
EmbeddingStoreType.PGVECTOR.name()),
ImmutableMap.of(EmbeddingStoreType.MILVUS.name(), "milvus",
EmbeddingStoreType.PGVECTOR.name(), "postgres",
EmbeddingStoreType.OPENSEARCH.name(), "opensearch"));
EmbeddingStoreType.PGVECTOR.name(), "postgres"));
}
private static List<Parameter.Dependency> getPasswordDependency() {
return getDependency(EMBEDDING_STORE_PROVIDER.getName(),
Lists.newArrayList(EmbeddingStoreType.MILVUS.name(),
EmbeddingStoreType.PGVECTOR.name(), EmbeddingStoreType.OPENSEARCH.name()),
EmbeddingStoreType.PGVECTOR.name()),
ImmutableMap.of(EmbeddingStoreType.MILVUS.name(), "milvus",
EmbeddingStoreType.PGVECTOR.name(), "postgres",
EmbeddingStoreType.OPENSEARCH.name(), "opensearch"));
EmbeddingStoreType.PGVECTOR.name(), "postgres"));
}
}

View File

@@ -0,0 +1,14 @@
package com.tencent.supersonic.common.config;
import com.tencent.supersonic.common.util.S2ThreadContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ThreadContextConfig {
@Bean
public S2ThreadContext s2ThreadContext() {
return new S2ThreadContext();
}
}

View File

@@ -1,44 +0,0 @@
package com.tencent.supersonic.common.config;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Component
public class ThreadPoolConfig {
@Bean("eventExecutor")
public ThreadPoolExecutor getTaskEventExecutor() {
return new ThreadPoolExecutor(4, 8, 60 * 3, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1024),
new ThreadFactoryBuilder().setNameFormat("supersonic-event-pool-").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
@Bean("commonExecutor")
public ThreadPoolExecutor getCommonExecutor() {
return new ThreadPoolExecutor(8, 16, 60 * 3, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1024),
new ThreadFactoryBuilder().setNameFormat("supersonic-common-pool-").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
@Bean("mapExecutor")
public ThreadPoolExecutor getMapExecutor() {
return new ThreadPoolExecutor(8, 16, 60 * 3, TimeUnit.SECONDS, new LinkedBlockingQueue<>(),
new ThreadFactoryBuilder().setNameFormat("supersonic-map-pool-").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
@Bean("chatExecutor")
public ThreadPoolExecutor getChatExecutor() {
return new ThreadPoolExecutor(8, 16, 60 * 3, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1024),
new ThreadFactoryBuilder().setNameFormat("supersonic-chat-pool-").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
}

View File

@@ -1,24 +0,0 @@
package com.tencent.supersonic.common.jsqlparser;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
import net.sf.jsqlparser.statement.select.SelectItem;
import java.util.Set;
public class AliasAcquireVisitor extends ExpressionVisitorAdapter {
private Set<String> aliases;
public AliasAcquireVisitor(Set<String> aliases) {
this.aliases = aliases;
}
@Override
public void visit(SelectItem selectItem) {
Alias alias = selectItem.getAlias();
if (alias != null) {
aliases.add(alias.getName());
}
}
}

View File

@@ -1,6 +1,5 @@
package com.tencent.supersonic.common.jsqlparser;
import com.google.common.collect.Sets;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
@@ -12,7 +11,6 @@ import java.util.Set;
public class FieldAcquireVisitor extends ExpressionVisitorAdapter {
private Set<String> fields;
private Set<String> aliases = Sets.newHashSet();
public FieldAcquireVisitor(Set<String> fields) {
this.fields = fields;
@@ -28,9 +26,8 @@ public class FieldAcquireVisitor extends ExpressionVisitorAdapter {
public void visit(SelectItem selectItem) {
Alias alias = selectItem.getAlias();
if (alias != null) {
aliases.add(alias.getName());
fields.add(alias.getName());
}
Expression expression = selectItem.getExpression();
if (expression != null) {
expression.accept(this);

View File

@@ -1,39 +0,0 @@
package com.tencent.supersonic.common.jsqlparser;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.statement.select.SelectItemVisitorAdapter;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
public class FieldAliasReplaceNameVisitor extends SelectItemVisitorAdapter {
private Map<String, String> fieldNameMap;
private Map<String, String> aliasToActualExpression = new HashMap<>();
public FieldAliasReplaceNameVisitor(Map<String, String> fieldNameMap) {
this.fieldNameMap = fieldNameMap;
}
@Override
public void visit(SelectItem selectExpressionItem) {
Alias alias = selectExpressionItem.getAlias();
if (alias == null) {
return;
}
String aliasName = alias.getName();
String replaceValue = fieldNameMap.get(aliasName);
if (StringUtils.isBlank(replaceValue)) {
return;
}
aliasToActualExpression.put(aliasName, replaceValue);
alias.setName(replaceValue);
}
public Map<String, String> getAliasToActualExpression() {
return aliasToActualExpression;
}
}

View File

@@ -1,43 +0,0 @@
package com.tencent.supersonic.common.jsqlparser;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.statement.select.SelectItemVisitorAdapter;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
public class FieldAliasReplaceWithBackticksVisitor extends SelectItemVisitorAdapter {
private Map<String, String> fieldAliasReplacedMap = new HashMap<>();
@Override
public void visit(SelectItem selectExpressionItem) {
Alias alias = selectExpressionItem.getAlias();
if (alias == null) {
return;
}
String aliasName = alias.getName();
String replaceValue = addBackticks(aliasName);
if (StringUtils.isBlank(replaceValue)) {
return;
}
alias.setName(replaceValue);
fieldAliasReplacedMap.put(aliasName, replaceValue);
}
private String addBackticks(String aliasName) {
if (StringUtils.isBlank(aliasName)) {
return "";
}
if (aliasName.startsWith("`") && aliasName.endsWith("`")) {
return "";
}
return "`" + aliasName + "`";
}
public Map<String, String> getFieldAliasReplacedMap() {
return fieldAliasReplacedMap;
}
}

View File

@@ -7,7 +7,15 @@ import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.expression.operators.relational.ComparisonOperator;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.GreaterThan;
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.MinorThan;
import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals;
import net.sf.jsqlparser.schema.Column;
import org.apache.commons.collections.CollectionUtils;
@@ -26,29 +34,6 @@ public class FieldAndValueAcquireVisitor extends ExpressionVisitorAdapter {
this.fieldExpressions = fieldExpressions;
}
public void visit(Between between) {
Expression leftExpression = between.getLeftExpression();
String columnName = null;
if (leftExpression instanceof Column) {
Column column = (Column) leftExpression;
columnName = column.getColumnName();
}
Expression betweenExpressionStart = between.getBetweenExpressionStart();
Expression betweenExpressionEnd = between.getBetweenExpressionEnd();
FieldExpression fieldExpressionStart = new FieldExpression();
fieldExpressionStart.setFieldName(columnName);
fieldExpressionStart.setFieldValue(getFieldValue(betweenExpressionStart));
fieldExpressionStart.setOperator(JsqlConstants.GREATER_THAN_EQUALS);
fieldExpressions.add(fieldExpressionStart);
FieldExpression fieldExpressionEnd = new FieldExpression();
fieldExpressionEnd.setFieldName(columnName);
fieldExpressionEnd.setFieldValue(getFieldValue(betweenExpressionEnd));
fieldExpressionEnd.setOperator(JsqlConstants.MINOR_THAN_EQUALS);
fieldExpressions.add(fieldExpressionEnd);
}
public void visit(LikeExpression expr) {
Expression leftExpression = expr.getLeftExpression();
Expression rightExpression = expr.getRightExpression();

View File

@@ -1,5 +1,6 @@
package com.tencent.supersonic.common.jsqlparser;
import com.tencent.supersonic.common.util.ContextUtils;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.AnalyticExpression;
import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
@@ -13,7 +14,6 @@ import java.util.Map;
@Slf4j
public class FieldReplaceVisitor extends ExpressionVisitorAdapter {
private Map<String, String> fieldNameMap;
private ThreadLocal<Boolean> exactReplace = ThreadLocal.withInitial(() -> false);
@@ -24,7 +24,8 @@ public class FieldReplaceVisitor extends ExpressionVisitorAdapter {
@Override
public void visit(Column column) {
SqlReplaceHelper.replaceColumn(column, fieldNameMap, exactReplace.get());
ReplaceService replaceService = ContextUtils.getBean(ReplaceService.class);
replaceService.replaceColumn(column, fieldNameMap, exactReplace.get());
}
@Override

View File

@@ -1,8 +1,21 @@
package com.tencent.supersonic.common.jsqlparser;
import com.tencent.supersonic.common.util.ContextUtils;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.expression.DoubleValue;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.relational.ComparisonOperator;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.GreaterThan;
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.MinorThan;
import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals;
import net.sf.jsqlparser.schema.Column;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
@@ -15,14 +28,13 @@ import java.util.Objects;
@Slf4j
public class FieldValueReplaceVisitor extends ExpressionVisitorAdapter {
private final boolean exactReplace;
private final Map<String, Map<String, String>> filedNameToValueMap;
private boolean exactReplace;
private Map<String, Map<String, String>> filedNameToValueMap;
public FieldValueReplaceVisitor(boolean exactReplace,
Map<String, Map<String, String>> filedNameToValueMap) {
this.filedNameToValueMap = filedNameToValueMap;
this.exactReplace = exactReplace;
this.filedNameToValueMap = filedNameToValueMap;
}
@Override
@@ -125,8 +137,9 @@ public class FieldValueReplaceVisitor extends ExpressionVisitorAdapter {
private String getReplaceValue(Map<String, String> valueMap, String beforeValue) {
String afterValue = valueMap.get(String.valueOf(beforeValue));
if (StringUtils.isEmpty(afterValue)) {
return SqlReplaceHelper.getReplaceValue(beforeValue, valueMap, exactReplace);
if (StringUtils.isEmpty(afterValue) && !exactReplace) {
ReplaceService replaceService = ContextUtils.getBean(ReplaceService.class);
return replaceService.getReplaceValue(beforeValue, valueMap, false);
}
return afterValue;
}

View File

@@ -1,5 +1,6 @@
package com.tencent.supersonic.common.jsqlparser;
import com.tencent.supersonic.common.util.ContextUtils;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
@@ -14,8 +15,8 @@ import java.util.Map;
@Slf4j
public class GroupByReplaceVisitor implements GroupByVisitor {
private final boolean exactReplace;
private final Map<String, String> fieldNameMap;
private Map<String, String> fieldNameMap;
private boolean exactReplace;
public GroupByReplaceVisitor(Map<String, String> fieldNameMap, boolean exactReplace) {
this.fieldNameMap = fieldNameMap;
@@ -33,10 +34,11 @@ public class GroupByReplaceVisitor implements GroupByVisitor {
}
private void replaceExpression(Expression expression) {
ReplaceService replaceService = ContextUtils.getBean(ReplaceService.class);
if (expression instanceof Column) {
SqlReplaceHelper.replaceColumn((Column) expression, fieldNameMap, exactReplace);
replaceService.replaceColumn((Column) expression, fieldNameMap, exactReplace);
} else if (expression instanceof Function) {
SqlReplaceHelper.replaceFunction((Function) expression, fieldNameMap, exactReplace);
replaceService.replaceFunction((Function) expression, fieldNameMap, exactReplace);
}
}
}

View File

@@ -26,7 +26,6 @@ public class JsqlConstants {
public static final String EQUAL_CONSTANT = " 1 = 1 ";
public static final String IN_CONSTANT = " 1 in (1) ";
public static final String LIKE_CONSTANT = "1 like 1";
public static final String BETWEEN_AND_CONSTANT = "1 between 2 and 3";
public static final String IN = "IN";
public static final Map<String, String> rightMap = Stream.of(
new AbstractMap.SimpleEntry<>("<=", "<="), new AbstractMap.SimpleEntry<>("<", "<"),

View File

@@ -1,5 +1,6 @@
package com.tencent.supersonic.common.jsqlparser;
import com.tencent.supersonic.common.util.ContextUtils;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.schema.Column;
@@ -9,9 +10,8 @@ import net.sf.jsqlparser.statement.select.OrderByVisitorAdapter;
import java.util.Map;
public class OrderByReplaceVisitor extends OrderByVisitorAdapter {
private final boolean exactReplace;
private final Map<String, String> fieldNameMap;
private Map<String, String> fieldNameMap;
private boolean exactReplace;
public OrderByReplaceVisitor(Map<String, String> fieldNameMap, boolean exactReplace) {
this.fieldNameMap = fieldNameMap;
@@ -21,11 +21,12 @@ public class OrderByReplaceVisitor extends OrderByVisitorAdapter {
@Override
public void visit(OrderByElement orderBy) {
Expression expression = orderBy.getExpression();
ReplaceService replaceService = ContextUtils.getBean(ReplaceService.class);
if (expression instanceof Column) {
SqlReplaceHelper.replaceColumn((Column) expression, fieldNameMap, exactReplace);
replaceService.replaceColumn((Column) expression, fieldNameMap, exactReplace);
}
if (expression instanceof Function) {
SqlReplaceHelper.replaceFunction((Function) expression, fieldNameMap, exactReplace);
replaceService.replaceFunction((Function) expression, fieldNameMap, exactReplace);
}
super.visit(orderBy);
}

View File

@@ -1,10 +1,13 @@
package com.tencent.supersonic.common.jsqlparser;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.SelectItem;
import org.apache.commons.lang3.StringUtils;
import java.util.Map;
import java.util.Objects;
@@ -12,53 +15,58 @@ import java.util.Objects;
public class QueryExpressionReplaceVisitor extends ExpressionVisitorAdapter {
private Map<String, String> fieldExprMap;
private String lastColumnName;
public QueryExpressionReplaceVisitor(Map<String, String> fieldExprMap) {
this.fieldExprMap = fieldExprMap;
}
protected void visitBinaryExpression(BinaryExpression expr) {
expr.setLeftExpression(replace(expr.getLeftExpression(), fieldExprMap));
expr.setRightExpression(replace(expr.getRightExpression(), fieldExprMap));
Expression left = expr.getLeftExpression();
String toReplace = "";
if (left instanceof Function) {
Function leftFunc = (Function) left;
if (leftFunc.getParameters().getExpressions().get(0) instanceof Column) {
toReplace = getReplaceExpr(leftFunc, fieldExprMap);
}
}
if (left instanceof Column) {
toReplace = getReplaceExpr((Column) left, fieldExprMap);
}
if (!toReplace.isEmpty()) {
Expression expression = getExpression(toReplace);
if (Objects.nonNull(expression)) {
expr.setLeftExpression(expression);
return;
}
}
expr.getLeftExpression().accept(this);
expr.getRightExpression().accept(this);
}
public void visit(SelectItem selectExpressionItem) {
Expression expression = selectExpressionItem.getExpression();
String toReplace = "";
String columnName = "";
if (expression instanceof Function) {
Function leftFunc = (Function) expression;
if (Objects.nonNull(leftFunc.getParameters())
&& leftFunc.getParameters().getExpressions().get(0) instanceof Column) {
if (leftFunc.getParameters().getExpressions().get(0) instanceof Column) {
Column column = (Column) leftFunc.getParameters().getExpressions().get(0);
lastColumnName = column.getColumnName();
columnName = column.getColumnName();
toReplace = getReplaceExpr(leftFunc, fieldExprMap);
}
} else if (expression instanceof Column) {
}
if (expression instanceof Column) {
Column column = (Column) expression;
lastColumnName = column.getColumnName();
columnName = column.getColumnName();
toReplace = getReplaceExpr((Column) expression, fieldExprMap);
} else if (expression instanceof BinaryExpression) {
BinaryExpression expr = (BinaryExpression) expression;
expr.setLeftExpression(replace(expr.getLeftExpression(), fieldExprMap));
expr.setRightExpression(replace(expr.getRightExpression(), fieldExprMap));
}
if (expression instanceof BinaryExpression) {
BinaryExpression binaryExpression = (BinaryExpression) expression;
visitBinaryExpression(binaryExpression);
}
if (expression instanceof Parenthesis) {
replace(expression, fieldExprMap);
}
if (!toReplace.isEmpty()) {
Expression toReplaceExpr = getExpression(toReplace);
if (Objects.nonNull(toReplaceExpr)) {
selectExpressionItem.setExpression(toReplaceExpr);
if (Objects.isNull(selectExpressionItem.getAlias())) {
selectExpressionItem.setAlias(new Alias(lastColumnName, true));
selectExpressionItem.setAlias(new Alias(columnName, true));
}
}
}
@@ -67,26 +75,11 @@ public class QueryExpressionReplaceVisitor extends ExpressionVisitorAdapter {
public static Expression replace(Expression expression, Map<String, String> fieldExprMap) {
String toReplace = "";
if (expression instanceof Function) {
Function function = (Function) expression;
if (function.getParameters().getExpressions().get(0) instanceof Column) {
toReplace = getReplaceExpr((Function) expression, fieldExprMap);
}
toReplace = getReplaceExpr((Function) expression, fieldExprMap);
}
if (expression instanceof Column) {
toReplace = getReplaceExpr((Column) expression, fieldExprMap);
}
if (expression instanceof BinaryExpression) {
BinaryExpression binaryExpression = (BinaryExpression) expression;
binaryExpression
.setLeftExpression(replace(binaryExpression.getLeftExpression(), fieldExprMap));
binaryExpression.setRightExpression(
replace(binaryExpression.getRightExpression(), fieldExprMap));
}
if (expression instanceof Parenthesis) {
Parenthesis parenthesis = (Parenthesis) expression;
parenthesis.setExpression(replace(parenthesis.getExpression(), fieldExprMap));
}
if (!toReplace.isEmpty()) {
Expression replace = getExpression(toReplace);
if (Objects.nonNull(replace)) {
@@ -116,16 +109,6 @@ public class QueryExpressionReplaceVisitor extends ExpressionVisitorAdapter {
public static String getReplaceExpr(Function function, Map<String, String> fieldExprMap) {
Column column = (Column) function.getParameters().getExpressions().get(0);
String expr = getReplaceExpr(column, fieldExprMap);
// if metric expr itself has agg function then replace original function in the SQL
if (StringUtils.isBlank(expr)) {
return expr;
} else if (!SqlSelectFunctionHelper.getAggregateFunctions(expr).isEmpty()) {
return expr;
} else {
String col = getReplaceExpr(column, fieldExprMap);
column.setColumnName(col);
return function.toString();
}
return getReplaceExpr(column, fieldExprMap);
}
}

View File

@@ -0,0 +1,74 @@
package com.tencent.supersonic.common.jsqlparser;
import com.tencent.supersonic.common.util.EditDistanceUtils;
import com.tencent.supersonic.common.util.StringUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.schema.Column;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
@Slf4j
@Service
@Data
public class ReplaceService {
@Value("${s2.replace.threshold:0.4}")
private double replaceColumnThreshold;
public void replaceFunction(Function expression, Map<String, String> fieldNameMap,
boolean exactReplace) {
Function function = expression;
ExpressionList<?> expressions = function.getParameters();
for (Expression column : expressions) {
if (column instanceof Column) {
replaceColumn((Column) column, fieldNameMap, exactReplace);
}
}
}
public void replaceColumn(Column column, Map<String, String> fieldNameMap,
boolean exactReplace) {
String columnName = StringUtil.replaceBackticks(column.getColumnName());
String replaceColumn = getReplaceValue(columnName, fieldNameMap, exactReplace);
if (StringUtils.isNotBlank(replaceColumn)) {
column.setColumnName(replaceColumn);
}
}
public String getReplaceValue(String beforeValue, Map<String, String> valueMap,
boolean exactReplace) {
String replaceValue = valueMap.get(beforeValue);
if (StringUtils.isNotBlank(replaceValue)) {
return replaceValue;
}
if (exactReplace) {
return null;
}
Optional<Entry<String, String>> first = valueMap.entrySet().stream().sorted((k1, k2) -> {
String k1Value = k1.getKey();
String k2Value = k2.getKey();
Double k1Similarity = EditDistanceUtils.getSimilarity(beforeValue, k1Value);
Double k2Similarity = EditDistanceUtils.getSimilarity(beforeValue, k2Value);
return k2Similarity.compareTo(k1Similarity);
}).collect(Collectors.toList()).stream().findFirst();
if (first.isPresent()) {
replaceValue = first.get().getValue();
double similarity = EditDistanceUtils.getSimilarity(beforeValue, replaceValue);
if (similarity > replaceColumnThreshold) {
return replaceValue;
}
}
return beforeValue;
}
}

View File

@@ -1,17 +1,35 @@
package com.tencent.supersonic.common.jsqlparser;
import com.tencent.supersonic.common.pojo.enums.TimeDimensionEnum;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.ComparisonOperator;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.GroupByElement;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.statement.select.SelectVisitorAdapter;
import net.sf.jsqlparser.statement.select.SetOperationList;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/** Sql Parser add Helper */
@Slf4j
@@ -126,7 +144,42 @@ public class SqlAddHelper {
return sql;
}
PlainSelect plainSelect = (PlainSelect) selectStatement;
List<String> chNameList = TimeDimensionEnum.getChNameList();
Boolean dateWhere = false;
for (String chName : chNameList) {
if (expression.toString().contains(chName)) {
dateWhere = true;
}
}
List<PlainSelect> plainSelectList = SqlSelectHelper.getWithItem(selectStatement);
if (!CollectionUtils.isEmpty(plainSelectList) && dateWhere) {
List<String> withNameList = SqlSelectHelper.getWithName(sql);
for (int i = 0; i < plainSelectList.size(); i++) {
if (plainSelectList.get(i).getFromItem() instanceof Table) {
Table table = (Table) plainSelectList.get(i).getFromItem();
if (withNameList.contains(table.getName())) {
continue;
}
}
Set<String> result = new HashSet<>();
List<PlainSelect> subPlainSelectList = new ArrayList<>();
subPlainSelectList.add(plainSelectList.get(i));
SqlSelectHelper.getWhereFields(subPlainSelectList, result);
if (TimeDimensionEnum.containsZhTimeDimension(new ArrayList<>(result))) {
continue;
}
Expression subWhere = plainSelectList.get(i).getWhere();
addWhere(plainSelectList.get(i), subWhere, expression);
}
return selectStatement.toString();
}
if (plainSelect.getFromItem() instanceof ParenthesedSelect && dateWhere) {
ParenthesedSelect parenthesedSelect = (ParenthesedSelect) plainSelect.getFromItem();
PlainSelect subPlainSelect = parenthesedSelect.getPlainSelect();
Expression subWhere = subPlainSelect.getWhere();
addWhere(subPlainSelect, subWhere, expression);
return selectStatement.toString();
}
Expression where = plainSelect.getWhere();
addWhere(plainSelect, where, expression);

View File

@@ -1,10 +1,10 @@
package com.tencent.supersonic.common.jsqlparser;
import com.tencent.supersonic.common.pojo.enums.TimeDimensionEnum;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.statement.select.PlainSelect;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -12,7 +12,7 @@ import java.util.Objects;
@Slf4j
public class SqlDateSelectHelper {
public static DateVisitor.DateBoundInfo getDateBoundInfo(String sql, String dateField) {
public static DateVisitor.DateBoundInfo getDateBoundInfo(String sql) {
List<PlainSelect> plainSelectList = SqlSelectHelper.getPlainSelect(sql);
if (plainSelectList.size() != 1) {
return null;
@@ -25,7 +25,7 @@ public class SqlDateSelectHelper {
if (Objects.isNull(where)) {
return null;
}
DateVisitor dateVisitor = new DateVisitor(Collections.singletonList(dateField));
DateVisitor dateVisitor = new DateVisitor(TimeDimensionEnum.getChNameList());
where.accept(dateVisitor);
return dateVisitor.getDateBoundInfo();
}

View File

@@ -8,7 +8,16 @@ import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.expression.operators.relational.ComparisonOperator;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.GreaterThan;
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.MinorThan;
import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals;
import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.AllColumns;
@@ -174,8 +183,6 @@ public class SqlRemoveHelper {
handleInExpression((InExpression) expression, removeFieldNames);
} else if (expression instanceof LikeExpression) {
handleLikeExpression((LikeExpression) expression, removeFieldNames);
} else if (expression instanceof Between) {
handleBetweenExpression((Between) expression, removeFieldNames);
}
} catch (JSQLParserException e) {
log.error("JSQLParserException", e);
@@ -219,17 +226,6 @@ public class SqlRemoveHelper {
updateLikeExpression(likeExpression, constantExpression);
}
private static void handleBetweenExpression(Between between, Set<String> removeFieldNames)
throws JSQLParserException {
String columnName = SqlSelectHelper.getColumnName(between.getLeftExpression());
if (!removeFieldNames.contains(columnName)) {
return;
}
Between constantExpression =
(Between) CCJSqlParserUtil.parseCondExpression(JsqlConstants.BETWEEN_AND_CONSTANT);
updateBetweenExpression(between, constantExpression);
}
private static void updateComparisonOperator(ComparisonOperator original,
ComparisonOperator constantExpression) {
original.setLeftExpression(constantExpression.getLeftExpression());
@@ -249,12 +245,6 @@ public class SqlRemoveHelper {
original.setRightExpression(constantExpression.getRightExpression());
}
private static void updateBetweenExpression(Between between, Between constantExpression) {
between.setBetweenExpressionEnd(constantExpression.getBetweenExpressionEnd());
between.setBetweenExpressionStart(constantExpression.getBetweenExpressionStart());
between.setLeftExpression(constantExpression.getLeftExpression());
}
public static String removeHavingCondition(String sql, Set<String> removeFieldNames) {
Select selectStatement = SqlSelectHelper.getSelect(sql);
if (!(selectStatement instanceof PlainSelect)) {
@@ -383,10 +373,6 @@ public class SqlRemoveHelper {
LikeExpression likeExpression = (LikeExpression) expression;
Expression leftExpression = likeExpression.getLeftExpression();
return recursionBase(leftExpression, expression, sqlEditEnum);
} else if (expression instanceof Between) {
Between between = (Between) expression;
Expression leftExpression = between.getLeftExpression();
return recursionBase(leftExpression, expression, sqlEditEnum);
}
return expression;
}

View File

@@ -1,7 +1,6 @@
package com.tencent.supersonic.common.jsqlparser;
import com.tencent.supersonic.common.pojo.enums.AggOperatorEnum;
import com.tencent.supersonic.common.util.EditDistanceUtils;
import com.tencent.supersonic.common.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
@@ -26,22 +25,21 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
/**
* Sql Parser replace Helper
*/
@Slf4j
public class SqlReplaceHelper {
private final static double replaceColumnThreshold = 0.4;
public static String escapeTableName(String table) {
return String.format("`%s`", table);
}
public static String replaceAggFields(String sql,
Map<String, Pair<String, String>> fieldNameToAggMap) {
Select selectStatement = SqlSelectHelper.getSelect(sql);
@@ -231,26 +229,6 @@ public class SqlReplaceHelper {
orderByElement.accept(new OrderByReplaceVisitor(fieldNameMap, exactReplace));
}
}
List<Select> selects = operationList.getSelects();
if (!CollectionUtils.isEmpty(selects)) {
for (Select select : selects) {
if (select instanceof PlainSelect) {
replaceFieldsInPlainOneSelect(fieldNameMap, exactReplace, (PlainSelect) select);
}
}
}
List<WithItem> withItems = operationList.getWithItemsList();
if (!CollectionUtils.isEmpty(withItems)) {
for (WithItem withItem : withItems) {
Select select = withItem.getSelect();
if (select instanceof PlainSelect) {
replaceFieldsInPlainOneSelect(fieldNameMap, exactReplace, (PlainSelect) select);
} else if (select instanceof ParenthesedSelect) {
replaceFieldsInPlainOneSelect(fieldNameMap, exactReplace,
select.getPlainSelect());
}
}
}
}
public static String replaceFunction(String sql, Map<String, String> functionMap) {
@@ -425,10 +403,8 @@ public class SqlReplaceHelper {
painSelect.accept(new SelectVisitorAdapter() {
@Override
public void visit(PlainSelect plainSelect) {
if (Objects.nonNull(plainSelect.getFromItem())) {
plainSelect.getFromItem().accept(new TableNameReplaceVisitor(tableName,
new HashSet<>(withNameList)));
}
plainSelect.getFromItem().accept(
new TableNameReplaceVisitor(tableName, new HashSet<>(withNameList)));
}
});
replaceJoins(painSelect, tableName, withNameList);
@@ -473,69 +449,6 @@ public class SqlReplaceHelper {
}
}
public static String replaceAliasFieldName(String sql, Map<String, String> fieldNameMap) {
Select selectStatement = SqlSelectHelper.getSelect(sql);
if (!(selectStatement instanceof PlainSelect)) {
return sql;
}
PlainSelect plainSelect = (PlainSelect) selectStatement;
FieldAliasReplaceNameVisitor visitor = new FieldAliasReplaceNameVisitor(fieldNameMap);
for (SelectItem selectItem : plainSelect.getSelectItems()) {
selectItem.accept(visitor);
}
Map<String, String> aliasToActualExpression = visitor.getAliasToActualExpression();
if (Objects.nonNull(aliasToActualExpression) && !aliasToActualExpression.isEmpty()) {
return replaceFields(selectStatement.toString(), aliasToActualExpression, true);
}
return selectStatement.toString();
}
public static String replaceAliasWithBackticks(String sql) {
Select selectStatement = SqlSelectHelper.getSelect(sql);
if (!(selectStatement instanceof PlainSelect)) {
return sql;
}
PlainSelect plainSelect = (PlainSelect) selectStatement;
FieldAliasReplaceWithBackticksVisitor visitor = new FieldAliasReplaceWithBackticksVisitor();
for (SelectItem selectItem : plainSelect.getSelectItems()) {
selectItem.accept(visitor);
}
// Replace `order by` and `group by`
// Get the map of field aliases that have been replaced
Map<String, String> aliasReplacedMap = visitor.getFieldAliasReplacedMap();
// If no aliases have been replaced, return the original SQL statement as a string
if (aliasReplacedMap.isEmpty()) {
return selectStatement.toString();
}
// Order by elements
List<OrderByElement> orderByElements = selectStatement.getOrderByElements();
if (!CollectionUtils.isEmpty(orderByElements)) {
for (OrderByElement orderByElement : orderByElements) {
orderByElement.accept(new OrderByReplaceVisitor(aliasReplacedMap, true));
}
}
// Group by elements
GroupByElement groupByElement = plainSelect.getGroupBy();
if (Objects.nonNull(groupByElement)) {
groupByElement.accept(new GroupByReplaceVisitor(aliasReplacedMap, true));
}
// Alias columns
for (SelectItem<?> selectItem : plainSelect.getSelectItems()) {
if (selectItem.getExpression() instanceof Column) {
replaceColumn((Column) selectItem.getExpression(), aliasReplacedMap, true);
}
}
// Having
Expression having = plainSelect.getHaving();
if (Objects.nonNull(having)) {
ExpressionReplaceVisitor expressionReplaceVisitor =
new ExpressionReplaceVisitor(aliasReplacedMap);
having.accept(expressionReplaceVisitor);
}
return selectStatement.toString();
}
public static String replaceAlias(String sql) {
Select selectStatement = SqlSelectHelper.getSelect(sql);
if (!(selectStatement instanceof PlainSelect)) {
@@ -677,17 +590,10 @@ public class SqlReplaceHelper {
return expr;
}
public static String replaceSqlByExpression(String tableName, String sql,
Map<String, String> replace) {
public static String replaceSqlByExpression(String sql, Map<String, String> replace) {
Select selectStatement = SqlSelectHelper.getSelect(sql);
List<PlainSelect> plainSelectList = new ArrayList<>();
if (selectStatement instanceof PlainSelect) {
// if with statement exists, replace expression in the with statement.
if (!CollectionUtils.isEmpty(selectStatement.getWithItemsList())) {
selectStatement.getWithItemsList().forEach(withItem -> {
plainSelectList.add(withItem.getSelect().getPlainSelect());
});
}
plainSelectList.add((PlainSelect) selectStatement);
} else if (selectStatement instanceof SetOperationList) {
SetOperationList setOperationList = (SetOperationList) selectStatement;
@@ -697,40 +603,12 @@ public class SqlReplaceHelper {
plainSelectList.add(subPlainSelect);
});
}
List<Select> selects = setOperationList.getSelects();
if (!CollectionUtils.isEmpty(selects)) {
for (Select select : selects) {
if (select instanceof PlainSelect) {
plainSelectList.add((PlainSelect) select);
}
}
}
List<WithItem> withItems = setOperationList.getWithItemsList();
if (!CollectionUtils.isEmpty(withItems)) {
for (WithItem withItem : withItems) {
Select select = withItem.getSelect();
if (select instanceof PlainSelect) {
plainSelectList.add((PlainSelect) select);
} else if (select instanceof ParenthesedSelect) {
plainSelectList.add(select.getPlainSelect());
}
}
}
} else {
return sql;
}
List<PlainSelect> plainSelects = SqlSelectHelper.getPlainSelects(plainSelectList);
for (PlainSelect plainSelect : plainSelects) {
if (Objects.nonNull(plainSelect.getFromItem())) {
Table table = (Table) plainSelect.getFromItem();
if (table.getName().equals(tableName)) {
replacePlainSelectByExpr(plainSelect, replace);
if (SqlSelectHelper.hasAggregateFunction(plainSelect)) {
SqlSelectHelper.addMissingGroupby(plainSelect);
}
}
}
replacePlainSelectByExpr(plainSelect, replace);
}
return selectStatement.toString();
}
@@ -817,54 +695,4 @@ public class SqlReplaceHelper {
}
}
}
public static void replaceFunction(Function expression, Map<String, String> fieldNameMap,
boolean exactReplace) {
Function function = expression;
ExpressionList<?> expressions = function.getParameters();
for (Expression column : expressions) {
if (column instanceof Column) {
replaceColumn((Column) column, fieldNameMap, exactReplace);
}
}
}
public static void replaceColumn(Column column, Map<String, String> fieldNameMap,
boolean exactReplace) {
String columnName = StringUtil.replaceBackticks(column.getColumnName());
String replaceColumn = getReplaceValue(columnName, fieldNameMap, exactReplace);
if (StringUtils.isNotBlank(replaceColumn)) {
log.debug("Replaced column {} to {}", column.getColumnName(), replaceColumn);
column.setColumnName(replaceColumn);
}
}
public static String getReplaceValue(String beforeValue, Map<String, String> valueMap,
boolean exactReplace) {
String replaceValue = valueMap.get(beforeValue);
if (StringUtils.isNotBlank(replaceValue)) {
return replaceValue;
}
if (exactReplace) {
return null;
}
Optional<Map.Entry<String, String>> first =
valueMap.entrySet().stream().sorted((k1, k2) -> {
String k1Value = k1.getKey();
String k2Value = k2.getKey();
Double k1Similarity = EditDistanceUtils.getSimilarity(beforeValue, k1Value);
Double k2Similarity = EditDistanceUtils.getSimilarity(beforeValue, k2Value);
return k2Similarity.compareTo(k1Similarity);
}).collect(Collectors.toList()).stream().findFirst();
if (first.isPresent()) {
replaceValue = first.get().getValue();
double similarity = EditDistanceUtils.getSimilarity(beforeValue, replaceValue);
if (similarity > replaceColumnThreshold) {
return replaceValue;
}
}
return beforeValue;
}
}

View File

@@ -1,7 +1,5 @@
package com.tencent.supersonic.common.jsqlparser;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.tencent.supersonic.common.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
@@ -135,24 +133,6 @@ public class SqlSelectHelper {
return result;
}
public static Set<String> getAliasFields(PlainSelect plainSelect) {
Set<String> result = new HashSet<>();
List<SelectItem<?>> selectItems = plainSelect.getSelectItems();
for (SelectItem selectItem : selectItems) {
selectItem.accept(new AliasAcquireVisitor(result));
}
return result;
}
public static Set<String> getAliasFields(String sql) {
List<PlainSelect> plainSelects = getPlainSelects(getPlainSelect(sql));
Set<String> aliasFields = new HashSet<>();
plainSelects.forEach(select -> {
aliasFields.addAll(getAliasFields(select));
});
return aliasFields;
}
public static List<PlainSelect> getPlainSelect(Select selectStatement) {
if (selectStatement == null) {
return null;
@@ -228,7 +208,7 @@ public class SqlSelectHelper {
statement = CCJSqlParserUtil.parse(sql);
} catch (JSQLParserException e) {
log.error("parse error, sql:{}", sql, e);
throw new RuntimeException(e);
return null;
}
if (statement instanceof ParenthesedSelect) {
@@ -284,18 +264,11 @@ public class SqlSelectHelper {
public static List<String> getAllSelectFields(String sql) {
List<PlainSelect> plainSelects = getPlainSelects(getPlainSelect(sql));
Set<String> results = new HashSet<>();
Set<String> aliases = new HashSet<>();
for (PlainSelect plainSelect : plainSelects) {
List<String> fields = getFieldsByPlainSelect(plainSelect);
Set<String> subaliases = getAliasFields(plainSelect);
subaliases.removeAll(fields);
results.addAll(fields);
aliases.addAll(subaliases);
}
// do not account in aliases
results.removeAll(aliases);
return new ArrayList<>(
results.stream().map(r -> r.replaceAll("`", "")).collect(Collectors.toList()));
return new ArrayList<>(results);
}
private static List<String> getFieldsByPlainSelect(PlainSelect plainSelect) {
@@ -304,37 +277,19 @@ public class SqlSelectHelper {
}
List<PlainSelect> plainSelectList = new ArrayList<>();
plainSelectList.add(plainSelect);
Set<String> selectFields = getSelectFields(plainSelectList);
Set<String> aliases = getAliasFields(plainSelect);
Set<String> result = getSelectFields(plainSelectList);
Set<String> groupByFields = Sets.newHashSet();
getGroupByFields(plainSelect, groupByFields);
groupByFields.removeAll(aliases);
getGroupByFields(plainSelect, result);
Set<String> orderByFields = Sets.newHashSet();
getOrderByFields(plainSelect, orderByFields);
orderByFields.removeAll(aliases);
getOrderByFields(plainSelect, result);
Set<String> whereFields = Sets.newHashSet();
getWhereFields(plainSelectList, whereFields);
whereFields.removeAll(aliases);
getWhereFields(plainSelectList, result);
Set<String> havingFields = Sets.newHashSet();
getHavingFields(plainSelect, havingFields);
havingFields.removeAll(aliases);
getHavingFields(plainSelect, result);
Set<String> lateralFields = Sets.newHashSet();
getLateralViewsFields(plainSelect, lateralFields);
lateralFields.removeAll(aliases);
getLateralViewsFields(plainSelect, result);
List<String> results = Lists.newArrayList();
results.addAll(selectFields);
results.addAll(groupByFields);
results.addAll(orderByFields);
results.addAll(whereFields);
results.addAll(havingFields);
results.addAll(lateralFields);
return new ArrayList<>(results);
return new ArrayList<>(result);
}
private static void getHavingFields(PlainSelect plainSelect, Set<String> result) {
@@ -460,9 +415,6 @@ public class SqlSelectHelper {
.map(fieldExpression -> fieldExpression.getFieldName()).filter(Objects::nonNull)
.collect(Collectors.toSet());
result.addAll(collect);
Set<String> aliases = getAliasFields(plainSelect);
result.removeAll(aliases);
}
public static List<FieldExpression> getOrderByExpressions(String sql) {
@@ -667,9 +619,8 @@ public class SqlSelectHelper {
}
if (withSelect instanceof ParenthesedSelect) {
ParenthesedSelect parenthesedSelect = (ParenthesedSelect) withSelect;
List<PlainSelect> plainSelects = new ArrayList<>();
SqlReplaceHelper.getFromSelect(parenthesedSelect, plainSelects);
plainSelectList.addAll(plainSelects);
PlainSelect withPlainSelect = parenthesedSelect.getPlainSelect();
plainSelectList.add(withPlainSelect);
}
}
}
@@ -728,61 +679,61 @@ public class SqlSelectHelper {
return table.getFullyQualifiedName();
}
public static Set<String> getFieldsFromExpr(String expr) {
public static Set<String> getColumnFromExpr(String expr) {
Expression expression = QueryExpressionReplaceVisitor.getExpression(expr);
Set<String> columns = new HashSet<>();
if (Objects.nonNull(expression)) {
getFieldsFromExpr(expression, columns);
getColumnFromExpr(expression, columns);
}
return columns;
}
public static void getFieldsFromExpr(Expression expression, Set<String> columns) {
public static void getColumnFromExpr(Expression expression, Set<String> columns) {
if (expression instanceof Column) {
columns.add(((Column) expression).getColumnName());
}
if (expression instanceof Function) {
ExpressionList<?> expressionList = ((Function) expression).getParameters();
for (Expression expr : expressionList) {
getFieldsFromExpr(expr, columns);
getColumnFromExpr(expr, columns);
}
}
if (expression instanceof CaseExpression) {
CaseExpression expr = (CaseExpression) expression;
if (Objects.nonNull(expr.getWhenClauses())) {
for (WhenClause whenClause : expr.getWhenClauses()) {
getFieldsFromExpr(whenClause.getWhenExpression(), columns);
getFieldsFromExpr(whenClause.getThenExpression(), columns);
getColumnFromExpr(whenClause.getWhenExpression(), columns);
getColumnFromExpr(whenClause.getThenExpression(), columns);
}
}
if (Objects.nonNull(expr.getElseExpression())) {
getFieldsFromExpr(expr.getElseExpression(), columns);
getColumnFromExpr(expr.getElseExpression(), columns);
}
}
if (expression instanceof BinaryExpression) {
BinaryExpression expr = (BinaryExpression) expression;
getFieldsFromExpr(expr.getLeftExpression(), columns);
getFieldsFromExpr(expr.getRightExpression(), columns);
getColumnFromExpr(expr.getLeftExpression(), columns);
getColumnFromExpr(expr.getRightExpression(), columns);
}
if (expression instanceof InExpression) {
InExpression inExpression = (InExpression) expression;
getFieldsFromExpr(inExpression.getLeftExpression(), columns);
getColumnFromExpr(inExpression.getLeftExpression(), columns);
}
if (expression instanceof Between) {
Between between = (Between) expression;
getFieldsFromExpr(between.getLeftExpression(), columns);
getColumnFromExpr(between.getLeftExpression(), columns);
}
if (expression instanceof IsBooleanExpression) {
IsBooleanExpression isBooleanExpression = (IsBooleanExpression) expression;
getFieldsFromExpr(isBooleanExpression.getLeftExpression(), columns);
getColumnFromExpr(isBooleanExpression.getLeftExpression(), columns);
}
if (expression instanceof IsNullExpression) {
IsNullExpression isNullExpression = (IsNullExpression) expression;
getFieldsFromExpr(isNullExpression.getLeftExpression(), columns);
getColumnFromExpr(isNullExpression.getLeftExpression(), columns);
}
if (expression instanceof Parenthesis) {
Parenthesis expr = (Parenthesis) expression;
getFieldsFromExpr(expr.getExpression(), columns);
getColumnFromExpr(expr.getExpression(), columns);
}
}
@@ -895,9 +846,7 @@ public class SqlSelectHelper {
collectSelects(withItem.getSelect(), selects);
} else if (select instanceof ParenthesedSelect) {
ParenthesedSelect parenthesedSelect = (ParenthesedSelect) select;
List<PlainSelect> plainSelects = new ArrayList<>();
SqlReplaceHelper.getFromSelect(parenthesedSelect, plainSelects);
plainSelects.forEach(s -> collectSelects(s, selects));
collectSelects(parenthesedSelect.getPlainSelect(), selects);
}
}
@@ -965,31 +914,4 @@ public class SqlSelectHelper {
}
});
}
public static void addMissingGroupby(PlainSelect plainSelect) {
if (Objects.nonNull(plainSelect.getGroupBy())
&& !plainSelect.getGroupBy().getGroupByExpressionList().isEmpty()) {
return;
}
GroupByElement groupBy = new GroupByElement();
for (SelectItem selectItem : plainSelect.getSelectItems()) {
Expression expression = selectItem.getExpression();
if (expression instanceof Column) {
groupBy.addGroupByExpression(expression);
}
}
if (!groupBy.getGroupByExpressionList().isEmpty()) {
plainSelect.setGroupByElement(groupBy);
}
}
public static boolean hasAggregateFunction(PlainSelect plainSelect) {
List<SelectItem<?>> selectItems = plainSelect.getSelectItems();
FunctionVisitor visitor = new FunctionVisitor();
for (SelectItem selectItem : selectItems) {
selectItem.accept(visitor);
}
return !visitor.getFunctionNames().isEmpty();
}
}

View File

@@ -18,7 +18,6 @@ public class ChatModelConfig implements Serializable {
private String baseUrl;
private String apiKey;
private String modelName;
private String apiVersion;
private Double temperature = 0.0d;
private Long timeOut = 60L;
private String endpoint;

View File

@@ -34,9 +34,6 @@ public class ChatModelParameters {
public static final Parameter CHAT_MODEL_API_KEY = new Parameter("apiKey", "", "ApiKey", "",
"password", MODULE_NAME, null, getApiKeyDependency());
public static final Parameter CHAT_MODEL_API_VERSION = new Parameter("apiVersion", "2024-02-01",
"ApiVersion", "", "string", MODULE_NAME, null, getApiVersionDependency());
public static final Parameter CHAT_MODEL_ENDPOINT = new Parameter("endpoint", "llama_2_70b",
"Endpoint", "", "string", MODULE_NAME, null, getEndpointDependency());
@@ -54,7 +51,7 @@ public class ChatModelParameters {
public static List<Parameter> getParameters() {
return Lists.newArrayList(CHAT_MODEL_PROVIDER, CHAT_MODEL_BASE_URL, CHAT_MODEL_ENDPOINT,
CHAT_MODEL_API_KEY, CHAT_MODEL_SECRET_KEY, CHAT_MODEL_NAME, CHAT_MODEL_API_VERSION,
CHAT_MODEL_API_KEY, CHAT_MODEL_SECRET_KEY, CHAT_MODEL_NAME,
CHAT_MODEL_ENABLE_SEARCH, CHAT_MODEL_TEMPERATURE, CHAT_MODEL_TIMEOUT);
}
@@ -93,12 +90,6 @@ public class ChatModelParameters {
ModelProvider.DEMO_CHAT_MODEL.getApiKey()));
}
private static List<Parameter.Dependency> getApiVersionDependency() {
return getDependency(CHAT_MODEL_PROVIDER.getName(),
Lists.newArrayList(OpenAiModelFactory.PROVIDER), ImmutableMap
.of(OpenAiModelFactory.PROVIDER, OpenAiModelFactory.DEFAULT_API_VERSION));
}
private static List<Parameter.Dependency> getModelNameDependency() {
return getDependency(CHAT_MODEL_PROVIDER.getName(), getCandidateValues(),
ImmutableMap.of(OpenAiModelFactory.PROVIDER, OpenAiModelFactory.DEFAULT_MODEL_NAME,

View File

@@ -1,11 +1,11 @@
package com.tencent.supersonic.common.pojo;
import com.tencent.supersonic.common.pojo.enums.DatePeriodEnum;
import com.tencent.supersonic.common.pojo.enums.TimeDimensionEnum;
import com.tencent.supersonic.common.util.DateUtils;
import lombok.Data;
import org.springframework.util.CollectionUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -13,7 +13,7 @@ import java.util.Objects;
import static java.time.LocalDate.now;
@Data
public class DateConf implements Serializable {
public class DateConf {
private static final long serialVersionUID = 3074129990945004340L;
@@ -40,8 +40,6 @@ public class DateConf implements Serializable {
private boolean groupByDate;
private String dateField;
public List<String> getDateList() {
if (!CollectionUtils.isEmpty(dateList)) {
return dateList;
@@ -51,6 +49,18 @@ public class DateConf implements Serializable {
return DateUtils.getDateList(startDateStr, endDateStr, getPeriod());
}
public String getGroupByTimeDimension() {
if (DatePeriodEnum.DAY.equals(period)) {
return TimeDimensionEnum.DAY.getName();
} else if (DatePeriodEnum.WEEK.equals(period)) {
return TimeDimensionEnum.WEEK.getName();
} else if (DatePeriodEnum.MONTH.equals(period)) {
return TimeDimensionEnum.MONTH.getName();
} else {
return TimeDimensionEnum.DAY.getName();
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -60,15 +70,9 @@ public class DateConf implements Serializable {
return false;
}
DateConf dateConf = (DateConf) o;
if (dateMode != dateConf.dateMode) {
return false;
}
if (dateMode == DateMode.RECENT) {
return Objects.equals(unit, dateConf.unit) && Objects.equals(period, dateConf.period);
} else {
return Objects.equals(startDate, dateConf.startDate)
&& Objects.equals(endDate, dateConf.endDate);
}
return dateMode == dateConf.dateMode && Objects.equals(startDate, dateConf.startDate)
&& Objects.equals(endDate, dateConf.endDate) && Objects.equals(unit, dateConf.unit)
&& Objects.equals(period, dateConf.period);
}
@Override

View File

@@ -3,13 +3,11 @@ package com.tencent.supersonic.common.pojo;
import com.google.common.base.Objects;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.io.Serializable;
import static com.tencent.supersonic.common.pojo.Constants.ASC_UPPER;
@Data
public class Order implements Serializable {
private static final long serialVersionUID = 1L;
public class Order {
@NotBlank(message = "Invalid order column")
private String column;

View File

@@ -2,11 +2,8 @@ package com.tencent.supersonic.common.pojo;
import lombok.Data;
import java.io.Serializable;
@Data
public class PageBaseReq implements Serializable {
private static final long serialVersionUID = 1L;
public class PageBaseReq {
private static final Integer MAX_PAGESIZE = 100;
private Integer current = 1;

View File

@@ -11,36 +11,26 @@ public class QueryColumn {
private String name;
private String type;
private String bizName;
private String nameEn;
private String showType;
private Boolean authorized = true;
private String dataFormatType;
private DataFormat dataFormat;
private String comment;
private Long modelId;
public QueryColumn(String bizName, String type) {
public QueryColumn(String nameEn, String type) {
this.type = type;
this.bizName = bizName;
this.nameEn = bizName;
this.name = bizName;
this.nameEn = nameEn;
}
public QueryColumn(String name, String type, String bizName) {
public QueryColumn(String name, String type, String nameEn) {
this.name = name;
this.type = type;
this.bizName = bizName;
this.nameEn = bizName;
this.nameEn = nameEn;
this.showType = "CATEGORY";
}
public void setType(String type) {
this.type = type == null ? null : type;
}
public void setBizName(String bizName) {
this.bizName = bizName;
this.nameEn = bizName;
}
}

View File

@@ -1,8 +1,6 @@
package com.tencent.supersonic.common.pojo.enums;
public enum AggOperatorEnum {
NONE(""),
MAX("MAX"),
MIN("MIN"),

View File

@@ -1,19 +1,16 @@
package com.tencent.supersonic.common.pojo.enums;
public enum EngineType {
TDW(0, "TDW"),
MYSQL(1, "MYSQL"),
DORIS(2, "DORIS"),
CLICKHOUSE(3, "CLICKHOUSE"),
H2(5, "H2"),
POSTGRESQL(6, "POSTGRESQL"),
OTHER(7, "OTHER"),
DUCKDB(8, "DUCKDB"),
HANADB(9, "HANADB"),
STARROCKS(10, "STARROCKS"),
KYUUBI(11, "KYUUBI"),
PRESTO(12, "PRESTO"),
TRINO(13, "TRINO"),;
TDW(0, "tdw"),
MYSQL(1, "mysql"),
DORIS(2, "doris"),
CLICKHOUSE(3, "clickhouse"),
KAFKA(4, "kafka"),
H2(5, "h2"),
POSTGRESQL(6, "postgresql"),
OTHER(7, "other"),
DUCKDB(8, "duckdb"),
HANADB(9, "hanadb");
private Integer code;

View File

@@ -1,5 +1,73 @@
package com.tencent.supersonic.common.pojo.enums;
import org.springframework.util.CollectionUtils;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public enum TimeDimensionEnum {
DAY, WEEK, MONTH;
DAY("sys_imp_date", "数据日期"),
WEEK("sys_imp_week", "数据日期_周"),
MONTH("sys_imp_month", "数据日期_月");
private String name;
private String chName;
TimeDimensionEnum(String name, String chName) {
this.name = name;
this.chName = chName;
}
public static boolean containsTimeDimension(String fieldName) {
if (getNameList().contains(fieldName) || getChNameList().contains(fieldName)) {
return true;
}
return false;
}
public static List<String> getNameList() {
return Arrays.stream(TimeDimensionEnum.values()).map(TimeDimensionEnum::getName)
.collect(Collectors.toList());
}
public static List<String> getChNameList() {
return Arrays.stream(TimeDimensionEnum.values()).map(TimeDimensionEnum::getChName)
.collect(Collectors.toList());
}
public static Map<String, String> getChNameToNameMap() {
return Arrays.stream(TimeDimensionEnum.values()).collect(Collectors
.toMap(TimeDimensionEnum::getChName, TimeDimensionEnum::getName, (k1, k2) -> k1));
}
public static Map<String, String> getNameToNameMap() {
return Arrays.stream(TimeDimensionEnum.values()).collect(Collectors
.toMap(TimeDimensionEnum::getName, TimeDimensionEnum::getName, (k1, k2) -> k1));
}
public String getName() {
return name;
}
public String getChName() {
return chName;
}
/**
* Determine if a time dimension field is included in a Chinese/English text field
*
* @param fields field
* @return true/false
*/
public static boolean containsZhTimeDimension(List<String> fields) {
if (CollectionUtils.isEmpty(fields)) {
return false;
}
return fields.stream().anyMatch(field -> containsTimeDimension(field));
}
}

View File

@@ -4,6 +4,7 @@ import com.tencent.supersonic.common.pojo.Constants;
import com.tencent.supersonic.common.pojo.DateConf;
import com.tencent.supersonic.common.pojo.ItemDateResp;
import com.tencent.supersonic.common.pojo.enums.DatePeriodEnum;
import com.tencent.supersonic.common.pojo.enums.TimeDimensionEnum;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -20,14 +21,22 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.regex.Pattern;
import static com.tencent.supersonic.common.pojo.Constants.*;
import static com.tencent.supersonic.common.pojo.Constants.APOSTROPHE;
import static com.tencent.supersonic.common.pojo.Constants.COMMA;
import static com.tencent.supersonic.common.pojo.Constants.DAY_FORMAT;
import static com.tencent.supersonic.common.pojo.Constants.MONTH_FORMAT;
@Slf4j
@Component
@Data
public class DateModeUtils {
private final String sysDateCol = TimeDimensionEnum.DAY.getName();
private final String sysDateMonthCol = TimeDimensionEnum.MONTH.getName();
private final String sysDateWeekCol = TimeDimensionEnum.WEEK.getName();
@Value("${s2.query.parameter.sys.zipper.begin:start_}")
private String sysZipperDateColBegin;
@@ -51,8 +60,8 @@ public class DateModeUtils {
public String hasDataModeStr(ItemDateResp dateDate, DateConf dateInfo) {
if (Objects.isNull(dateDate) || StringUtils.isEmpty(dateDate.getStartDate())
|| StringUtils.isEmpty(dateDate.getStartDate())) {
return String.format("(%s >= '%s' and %s <= '%s')", dateInfo.getDateField(),
dateInfo.getStartDate(), dateInfo.getDateField(), dateInfo.getEndDate());
return String.format("(%s >= '%s' and %s <= '%s')", sysDateCol, dateInfo.getStartDate(),
sysDateCol, dateInfo.getEndDate());
} else {
log.info("dateDate:{}", dateDate);
}
@@ -70,28 +79,27 @@ public class DateModeUtils {
dateFormatStr, ChronoUnit.DAYS);
LocalDate dateMax = endData;
LocalDate dateMin = dateMax.minusDays(unit - 1);
return String.format("(%s >= '%s' and %s <= '%s')", dateInfo.getDateField(),
dateMin, dateInfo.getDateField(), dateMax);
return String.format("(%s >= '%s' and %s <= '%s')", sysDateCol, dateMin, sysDateCol,
dateMax);
}
if (DatePeriodEnum.MONTH.equals(dateInfo.getPeriod())) {
Long unit = getInterval(dateInfo.getStartDate(), dateInfo.getEndDate(),
dateFormatStr, ChronoUnit.MONTHS);
return generateMonthSql(endData, unit, dateFormatStr, dateInfo);
return generateMonthSql(endData, unit, dateFormatStr);
}
}
return String.format("(%s >= '%s' and %s <= '%s')", dateInfo.getDateField(),
dateInfo.getStartDate(), dateInfo.getDateField(), dateInfo.getEndDate());
return String.format("(%s >= '%s' and %s <= '%s')", sysDateCol, dateInfo.getStartDate(),
sysDateCol, dateInfo.getEndDate());
}
public String generateMonthSql(LocalDate endData, Long unit, String dateFormatStr,
DateConf dateConf) {
public String generateMonthSql(LocalDate endData, Long unit, String dateFormatStr) {
LocalDate dateMax = endData;
List<String> months = generateMonthStr(dateMax, unit, dateFormatStr);
if (!CollectionUtils.isEmpty(months)) {
StringJoiner joiner = new StringJoiner(",");
months.stream().forEach(month -> joiner.add("'" + month + "'"));
return String.format("(%s in (%s))", dateConf.getDateField(), joiner.toString());
return String.format("(%s in (%s))", sysDateCol, joiner.toString());
}
return "";
}
@@ -108,8 +116,8 @@ public class DateModeUtils {
public String recentDayStr(ItemDateResp dateDate, DateConf dateInfo) {
ImmutablePair<String, String> dayRange = recentDay(dateDate, dateInfo);
return String.format("(%s >= '%s' and %s <= '%s')", dateInfo.getDateField(), dayRange.left,
dateInfo.getDateField(), dayRange.right);
return String.format("(%s >= '%s' and %s <= '%s')", sysDateCol, dayRange.left, sysDateCol,
dayRange.right);
}
public ImmutablePair<String, String> recentDay(ItemDateResp dateDate, DateConf dateInfo) {
@@ -126,25 +134,24 @@ public class DateModeUtils {
return ImmutablePair.of(start, dateDate.getEndDate());
}
public String recentMonthStr(LocalDate endData, Long unit, String dateFormatStr,
DateConf dateInfo) {
public String recentMonthStr(LocalDate endData, Long unit, String dateFormatStr) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormatStr);
String endStr = endData.format(formatter);
String start = endData.minusMonths(unit).format(formatter);
return String.format("(%s >= '%s' and %s <= '%s')", dateInfo.getDateField(), start,
dateInfo.getDateField(), endStr);
return String.format("(%s >= '%s' and %s <= '%s')", sysDateMonthCol, start, sysDateMonthCol,
endStr);
}
public String recentMonthStr(ItemDateResp dateDate, DateConf dateInfo) {
List<ImmutablePair<String, String>> range = recentMonth(dateDate, dateInfo);
if (range.size() == 1) {
return String.format("(%s >= '%s' and %s <= '%s')", dateInfo.getDateField(),
range.get(0).left, dateInfo.getDateField(), range.get(0).right);
return String.format("(%s >= '%s' and %s <= '%s')", sysDateMonthCol, range.get(0).left,
sysDateMonthCol, range.get(0).right);
}
if (range.size() > 0) {
StringJoiner joiner = new StringJoiner(",");
range.stream().forEach(month -> joiner.add("'" + month.left + "'"));
return String.format("(%s in (%s))", dateInfo.getDateField(), joiner.toString());
return String.format("(%s in (%s))", sysDateCol, joiner.toString());
}
return "";
}
@@ -174,17 +181,17 @@ public class DateModeUtils {
return ret;
}
public String recentWeekStr(LocalDate endData, Long unit, DateConf dataInfo) {
public String recentWeekStr(LocalDate endData, Long unit) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DAY_FORMAT);
String start = endData.minusDays(unit * 7).format(formatter);
return String.format("(%s >= '%s' and %s <= '%s')", dataInfo.getDateField(), start,
dataInfo.getDateField(), endData.format(formatter));
return String.format("(%s >= '%s' and %s <= '%s')", sysDateWeekCol, start, sysDateWeekCol,
endData.format(formatter));
}
public String recentWeekStr(ItemDateResp dateDate, DateConf dateInfo) {
ImmutablePair<String, String> dayRange = recentWeek(dateDate, dateInfo);
return String.format("(%s >= '%s' and %s <= '%s')", dateInfo.getDateField(), dayRange.left,
dateInfo.getDateField(), dayRange.right);
return String.format("(%s >= '%s' and %s <= '%s')", sysDateWeekCol, dayRange.left,
sysDateWeekCol, dayRange.right);
}
public ImmutablePair<String, String> recentWeek(ItemDateResp dateDate, DateConf dateInfo) {
@@ -235,27 +242,26 @@ public class DateModeUtils {
* @return
*/
public String betweenDateStr(DateConf dateInfo) {
String dateField = dateInfo.getDateField();
if (DatePeriodEnum.MONTH.equals(dateInfo.getPeriod())) {
// startDate YYYYMM
if (!dateInfo.getStartDate().contains(Constants.MINUS)) {
return String.format("%s >= '%s' and %s <= '%s'", dateField,
dateInfo.getStartDate(), dateField, dateInfo.getEndDate());
return String.format("%s >= '%s' and %s <= '%s'", sysDateMonthCol,
dateInfo.getStartDate(), sysDateMonthCol, dateInfo.getEndDate());
}
LocalDate endData =
LocalDate.parse(dateInfo.getEndDate(), DateTimeFormatter.ofPattern(DAY_FORMAT));
LocalDate startData = LocalDate.parse(dateInfo.getStartDate(),
DateTimeFormatter.ofPattern(DAY_FORMAT));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(MONTH_FORMAT);
return String.format("%s >= '%s' and %s <= '%s'", dateField,
startData.format(formatter), dateField, endData.format(formatter));
return String.format("%s >= '%s' and %s <= '%s'", sysDateMonthCol,
startData.format(formatter), sysDateMonthCol, endData.format(formatter));
}
if (DatePeriodEnum.WEEK.equals(dateInfo.getPeriod())) {
return String.format("%s >= '%s' and %s <= '%s'", dateField, dateInfo.getStartDate(),
dateField, dateInfo.getEndDate());
return String.format("%s >= '%s' and %s <= '%s'", sysDateWeekCol,
dateInfo.getStartDate(), sysDateWeekCol, dateInfo.getEndDate());
}
return String.format("%s >= '%s' and %s <= '%s'", dateField, dateInfo.getStartDate(),
dateField, dateInfo.getEndDate());
return String.format("%s >= '%s' and %s <= '%s'", sysDateCol, dateInfo.getStartDate(),
sysDateCol, dateInfo.getEndDate());
}
/**
@@ -267,12 +273,12 @@ public class DateModeUtils {
public String listDateStr(DateConf dateInfo) {
StringJoiner joiner = new StringJoiner(COMMA);
dateInfo.getDateList().stream().forEach(date -> joiner.add(APOSTROPHE + date + APOSTROPHE));
String dateCol = dateInfo.getDateField();
String dateCol = sysDateCol;
if (DatePeriodEnum.MONTH.equals(dateInfo.getPeriod())) {
dateCol = dateInfo.getDateField();
dateCol = sysDateMonthCol;
}
if (DatePeriodEnum.WEEK.equals(dateInfo.getPeriod())) {
dateCol = dateInfo.getDateField();
dateCol = sysDateWeekCol;
}
return String.format("(%s in (%s))", dateCol, joiner.toString());
}
@@ -293,26 +299,25 @@ public class DateModeUtils {
if (DatePeriodEnum.DAY.equals(dateInfo.getPeriod())) {
LocalDate dateMax = LocalDate.now().minusDays(1);
LocalDate dateMin = dateMax.minusDays(unit - 1);
return String.format("(%s >= '%s' and %s <= '%s')", dateInfo.getDateField(), dateMin,
dateInfo.getDateField(), dateMax);
return String.format("(%s >= '%s' and %s <= '%s')", sysDateCol, dateMin, sysDateCol,
dateMax);
}
if (DatePeriodEnum.WEEK.equals(dateInfo.getPeriod())) {
LocalDate dateMax = LocalDate.now().minusDays(1);
return recentWeekStr(dateMax, unit.longValue(), dateInfo);
return recentWeekStr(dateMax, unit.longValue());
}
if (DatePeriodEnum.MONTH.equals(dateInfo.getPeriod())) {
LocalDate dateMax = LocalDate.now().minusDays(1);
return recentMonthStr(dateMax, unit.longValue(), MONTH_FORMAT, dateInfo);
return recentMonthStr(dateMax, unit.longValue(), MONTH_FORMAT);
}
if (DatePeriodEnum.YEAR.equals(dateInfo.getPeriod())) {
LocalDate dateMax = LocalDate.now().minusDays(1);
return recentMonthStr(dateMax, unit.longValue() * 12, MONTH_FORMAT, dateInfo);
return recentMonthStr(dateMax, unit.longValue() * 12, MONTH_FORMAT);
}
return String.format("(%s >= '%s' and %s <= '%s')", dateInfo.getDateField(),
LocalDate.now().minusDays(2), dateInfo.getDateField(),
LocalDate.now().minusDays(1));
return String.format("(%s >= '%s' and %s <= '%s')", sysDateCol,
LocalDate.now().minusDays(2), sysDateCol, LocalDate.now().minusDays(1));
}
public String getDateWhereStr(DateConf dateInfo) {
@@ -344,7 +349,32 @@ public class DateModeUtils {
}
public String getSysDateCol(DateConf dateInfo) {
return dateInfo.getDateField();
if (DatePeriodEnum.DAY.equals(dateInfo.getPeriod())) {
return sysDateCol;
}
if (DatePeriodEnum.WEEK.equals(dateInfo.getPeriod())) {
return sysDateWeekCol;
}
if (DatePeriodEnum.MONTH.equals(dateInfo.getPeriod())) {
return sysDateMonthCol;
}
return "";
}
public boolean isDateStr(String date) {
return Pattern.matches("[\\d\\s-:]+", date);
}
public DatePeriodEnum getPeriodByCol(String col) {
if (sysDateCol.equalsIgnoreCase(col)) {
return DatePeriodEnum.DAY;
}
if (sysDateWeekCol.equalsIgnoreCase(col)) {
return DatePeriodEnum.WEEK;
}
if (sysDateMonthCol.equalsIgnoreCase(col)) {
return DatePeriodEnum.MONTH;
}
return null;
}
}

View File

@@ -0,0 +1,21 @@
package com.tencent.supersonic.common.util;
import com.alibaba.ttl.TransmittableThreadLocal;
public class S2ThreadContext {
private static final TransmittableThreadLocal<ThreadContext> context =
new TransmittableThreadLocal<>();
public ThreadContext get() {
return context.get();
}
public void set(ThreadContext value) {
context.set(value);
}
public void remove() {
context.remove();
}
}

View File

@@ -1,6 +1,5 @@
package com.tencent.supersonic.common.util;
import com.tencent.supersonic.common.pojo.User;
import lombok.Builder;
import lombok.Data;
import lombok.ToString;
@@ -14,7 +13,7 @@ public class ThreadContext {
private String traceId;
private User user;
private String userName;
private String token;

View File

@@ -70,7 +70,6 @@ public class OpenAiChatModel implements ChatLanguageModel, TokenCountEstimator {
private final OpenAiClient client;
private final String baseUrl;
private final String modelName;
private final String apiVersion;
private final Double temperature;
private final Double topP;
private final List<String> stop;
@@ -90,13 +89,12 @@ public class OpenAiChatModel implements ChatLanguageModel, TokenCountEstimator {
@Builder
public OpenAiChatModel(String baseUrl, String apiKey, String organizationId, String modelName,
String apiVersion, Double temperature, Double topP, List<String> stop,
Integer maxTokens, Double presencePenalty, Double frequencyPenalty,
Map<String, Integer> logitBias, String responseFormat, Boolean strictJsonSchema,
Integer seed, String user, Boolean strictTools, Boolean parallelToolCalls,
Duration timeout, Integer maxRetries, Proxy proxy, Boolean logRequests,
Boolean logResponses, Tokenizer tokenizer, Map<String, String> customHeaders,
List<ChatModelListener> listeners) {
Double temperature, Double topP, List<String> stop, Integer maxTokens,
Double presencePenalty, Double frequencyPenalty, Map<String, Integer> logitBias,
String responseFormat, Boolean strictJsonSchema, Integer seed, String user,
Boolean strictTools, Boolean parallelToolCalls, Duration timeout, Integer maxRetries,
Proxy proxy, Boolean logRequests, Boolean logResponses, Tokenizer tokenizer,
Map<String, String> customHeaders, List<ChatModelListener> listeners) {
baseUrl = getOrDefault(baseUrl, OPENAI_URL);
if (OPENAI_DEMO_API_KEY.equals(apiKey)) {
@@ -107,12 +105,11 @@ public class OpenAiChatModel implements ChatLanguageModel, TokenCountEstimator {
timeout = getOrDefault(timeout, ofSeconds(60));
this.client = OpenAiClient.builder().openAiApiKey(apiKey).baseUrl(baseUrl)
.apiVersion(apiVersion).organizationId(organizationId).callTimeout(timeout)
.connectTimeout(timeout).readTimeout(timeout).writeTimeout(timeout).proxy(proxy)
.logRequests(logRequests).logResponses(logResponses).userAgent(DEFAULT_USER_AGENT)
.organizationId(organizationId).callTimeout(timeout).connectTimeout(timeout)
.readTimeout(timeout).writeTimeout(timeout).proxy(proxy).logRequests(logRequests)
.logResponses(logResponses).userAgent(DEFAULT_USER_AGENT)
.customHeaders(customHeaders).build();
this.modelName = getOrDefault(modelName, GPT_3_5_TURBO);
this.apiVersion = apiVersion;
this.temperature = getOrDefault(temperature, 0.7);
this.topP = topP;
this.stop = stop;

View File

@@ -1,29 +0,0 @@
package dev.langchain4j.opensearch.spring;
import io.milvus.common.clientenum.ConsistencyLevelEnum;
import io.milvus.param.IndexType;
import io.milvus.param.MetricType;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
class EmbeddingStoreProperties {
private String uri;
private String host;
private Integer port;
private String serviceName;
private String region;
private String collectionName;
private Integer dimension;
private IndexType indexType;
private MetricType metricType;
private String token;
private String user;
private String password;
private ConsistencyLevelEnum consistencyLevel;
private Boolean retrieveEmbeddingsOnSearch;
private String databaseName;
private Boolean autoFlushOnInsert;
}

View File

@@ -1,20 +0,0 @@
package dev.langchain4j.opensearch.spring;
import dev.langchain4j.store.embedding.EmbeddingStoreFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static dev.langchain4j.opensearch.spring.Properties.PREFIX;
@Configuration
@EnableConfigurationProperties(dev.langchain4j.opensearch.spring.Properties.class)
public class OpenSearchAutoConfig {
@Bean
@ConditionalOnProperty(PREFIX + ".embedding-store.uri")
EmbeddingStoreFactory milvusChatModel(Properties properties) {
return new OpenSearchEmbeddingStoreFactory(properties.getEmbeddingStore());
}
}

View File

@@ -1,58 +0,0 @@
package dev.langchain4j.opensearch.spring;
import com.tencent.supersonic.common.pojo.EmbeddingStoreConfig;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.store.embedding.BaseEmbeddingStoreFactory;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.opensearch.OpenSearchEmbeddingStore;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.core5.http.HttpHost;
import org.opensearch.client.transport.aws.AwsSdk2TransportOptions;
import org.springframework.beans.BeanUtils;
import java.net.URI;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
/**
* @author zyc
*/
public class OpenSearchEmbeddingStoreFactory extends BaseEmbeddingStoreFactory {
private final EmbeddingStoreProperties storeProperties;
public OpenSearchEmbeddingStoreFactory(EmbeddingStoreConfig storeConfig) {
this(createPropertiesFromConfig(storeConfig));
}
public OpenSearchEmbeddingStoreFactory(EmbeddingStoreProperties storeProperties) {
this.storeProperties = storeProperties;
}
private static EmbeddingStoreProperties createPropertiesFromConfig(
EmbeddingStoreConfig storeConfig) {
EmbeddingStoreProperties embeddingStore = new EmbeddingStoreProperties();
BeanUtils.copyProperties(storeConfig, embeddingStore);
embeddingStore.setUri(storeConfig.getBaseUrl());
embeddingStore.setToken(storeConfig.getApiKey());
embeddingStore.setDatabaseName(storeConfig.getDatabaseName());
return embeddingStore;
}
@Override
public EmbeddingStore<TextSegment> createEmbeddingStore(String collectionName) {
final AwsSdk2TransportOptions options =
AwsSdk2TransportOptions.builder()
.setCredentials(StaticCredentialsProvider.create(AwsBasicCredentials
.create(storeProperties.getUser(), storeProperties.getPassword())))
.build();
final String indexName = storeProperties.getDatabaseName() + "_" + collectionName;
return OpenSearchEmbeddingStore.builder().serviceName(storeProperties.getServiceName())
.serverUrl(storeProperties.getUri()).region(storeProperties.getRegion())
.indexName(indexName).userName(storeProperties.getUser())
.password(storeProperties.getPassword()).apiKey(storeProperties.getToken())
.options(options).build();
}
}

View File

@@ -1,17 +0,0 @@
package dev.langchain4j.opensearch.spring;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
@Getter
@Setter
@ConfigurationProperties(prefix = Properties.PREFIX)
public class Properties {
static final String PREFIX = "langchain4j.opensearch";
@NestedConfigurationProperty
dev.langchain4j.opensearch.spring.EmbeddingStoreProperties embeddingStore;
}

View File

@@ -18,14 +18,13 @@ public class OpenAiModelFactory implements ModelFactory, InitializingBean {
public static final String DEFAULT_BASE_URL = "https://api.openai.com/v1";
public static final String DEFAULT_MODEL_NAME = "gpt-4o-mini";
public static final String DEFAULT_EMBEDDING_MODEL_NAME = "text-embedding-ada-002";
public static final String DEFAULT_API_VERSION = "2024-02-01";
@Override
public ChatLanguageModel createChatModel(ChatModelConfig modelConfig) {
return OpenAiChatModel.builder().baseUrl(modelConfig.getBaseUrl())
.modelName(modelConfig.getModelName()).apiKey(modelConfig.keyDecrypt())
.apiVersion(modelConfig.getApiVersion()).temperature(modelConfig.getTemperature())
.topP(modelConfig.getTopP()).maxRetries(modelConfig.getMaxRetries())
.temperature(modelConfig.getTemperature()).topP(modelConfig.getTopP())
.maxRetries(modelConfig.getMaxRetries())
.timeout(Duration.ofSeconds(modelConfig.getTimeOut()))
.logRequests(modelConfig.getLogRequests())
.logResponses(modelConfig.getLogResponses()).build();

View File

@@ -10,8 +10,6 @@ import dev.langchain4j.model.zhipu.ZhipuAiEmbeddingModel;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import static java.time.Duration.ofSeconds;
@Service
public class ZhipuModelFactory implements ModelFactory, InitializingBean {
public static final String PROVIDER = "ZHIPU";
@@ -32,9 +30,8 @@ public class ZhipuModelFactory implements ModelFactory, InitializingBean {
public EmbeddingModel createEmbeddingModel(EmbeddingModelConfig embeddingModelConfig) {
return ZhipuAiEmbeddingModel.builder().baseUrl(embeddingModelConfig.getBaseUrl())
.apiKey(embeddingModelConfig.getApiKey()).model(embeddingModelConfig.getModelName())
.maxRetries(embeddingModelConfig.getMaxRetries()).callTimeout(ofSeconds(60))
.connectTimeout(ofSeconds(60)).writeTimeout(ofSeconds(60))
.readTimeout(ofSeconds(60)).logRequests(embeddingModelConfig.getLogRequests())
.maxRetries(embeddingModelConfig.getMaxRetries())
.logRequests(embeddingModelConfig.getLogRequests())
.logResponses(embeddingModelConfig.getLogResponses()).build();
}

View File

@@ -6,7 +6,6 @@ import com.tencent.supersonic.common.util.ContextUtils;
import dev.langchain4j.chroma.spring.ChromaEmbeddingStoreFactory;
import dev.langchain4j.inmemory.spring.InMemoryEmbeddingStoreFactory;
import dev.langchain4j.milvus.spring.MilvusEmbeddingStoreFactory;
import dev.langchain4j.opensearch.spring.OpenSearchEmbeddingStoreFactory;
import dev.langchain4j.pgvector.spring.PgvectorEmbeddingStoreFactory;
import org.apache.commons.lang3.StringUtils;
@@ -46,11 +45,6 @@ public class EmbeddingStoreFactoryProvider {
return factoryMap.computeIfAbsent(embeddingStoreConfig,
storeConfig -> new InMemoryEmbeddingStoreFactory(storeConfig));
}
if (EmbeddingStoreType.OPENSEARCH.name()
.equalsIgnoreCase(embeddingStoreConfig.getProvider())) {
return factoryMap.computeIfAbsent(embeddingStoreConfig,
storeConfig -> new OpenSearchEmbeddingStoreFactory(storeConfig));
}
throw new RuntimeException("Unsupported EmbeddingStoreFactory provider: "
+ embeddingStoreConfig.getProvider());
}

View File

@@ -1,5 +1,5 @@
package dev.langchain4j.store.embedding;
public enum EmbeddingStoreType {
IN_MEMORY, MILVUS, CHROMA, PGVECTOR, OPENSEARCH
IN_MEMORY, MILVUS, CHROMA, PGVECTOR
}

View File

@@ -2,10 +2,8 @@ package com.tencent.supersonic.common.jsqlparser;
import com.tencent.supersonic.common.jsqlparser.DateVisitor.DateBoundInfo;
import org.junit.Assert;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@Disabled
class SqlDateSelectHelperTest {
@Test
@@ -13,31 +11,31 @@ class SqlDateSelectHelperTest {
String sql = "SELECT 维度1,sum(播放量) FROM 数据库 "
+ "WHERE (歌手名 = '张三') AND 数据日期 >= '2023-11-17' GROUP BY 维度1";
DateBoundInfo dateBoundInfo = SqlDateSelectHelper.getDateBoundInfo(sql, "数据日期");
DateBoundInfo dateBoundInfo = SqlDateSelectHelper.getDateBoundInfo(sql);
Assert.assertEquals(dateBoundInfo.getLowerBound(), ">=");
Assert.assertEquals(dateBoundInfo.getLowerDate(), "2023-11-17");
sql = "SELECT 维度1,sum(播放量) FROM 数据库 "
+ "WHERE (歌手名 = '张三') AND 数据日期 > '2023-11-17' GROUP BY 维度1";
dateBoundInfo = SqlDateSelectHelper.getDateBoundInfo(sql, "数据日期");
dateBoundInfo = SqlDateSelectHelper.getDateBoundInfo(sql);
Assert.assertEquals(dateBoundInfo.getLowerBound(), ">");
Assert.assertEquals(dateBoundInfo.getLowerDate(), "2023-11-17");
sql = "SELECT 维度1,sum(播放量) FROM 数据库 "
+ "WHERE (歌手名 = '张三') AND 数据日期 <= '2023-11-17' GROUP BY 维度1";
dateBoundInfo = SqlDateSelectHelper.getDateBoundInfo(sql, "数据日期");
dateBoundInfo = SqlDateSelectHelper.getDateBoundInfo(sql);
Assert.assertEquals(dateBoundInfo.getUpperBound(), "<=");
Assert.assertEquals(dateBoundInfo.getUpperDate(), "2023-11-17");
sql = "SELECT 维度1,sum(播放量) FROM 数据库 "
+ "WHERE (歌手名 = '张三') AND 数据日期 < '2023-11-17' GROUP BY 维度1";
dateBoundInfo = SqlDateSelectHelper.getDateBoundInfo(sql, "数据日期");
dateBoundInfo = SqlDateSelectHelper.getDateBoundInfo(sql);
Assert.assertEquals(dateBoundInfo.getUpperBound(), "<");
Assert.assertEquals(dateBoundInfo.getUpperDate(), "2023-11-17");
sql = "SELECT 维度1,sum(播放量) FROM 数据库 " + "WHERE (歌手名 = '张三') AND 数据日期 >= '2023-10-17' "
+ "AND 数据日期 <= '2023-11-17' GROUP BY 维度1";
dateBoundInfo = SqlDateSelectHelper.getDateBoundInfo(sql, "数据日期");
dateBoundInfo = SqlDateSelectHelper.getDateBoundInfo(sql);
Assert.assertEquals(dateBoundInfo.getUpperBound(), "<=");
Assert.assertEquals(dateBoundInfo.getUpperDate(), "2023-11-17");
Assert.assertEquals(dateBoundInfo.getLowerBound(), ">=");

View File

@@ -157,14 +157,6 @@ class SqlRemoveHelperTest {
replaceSql = SqlRemoveHelper.removeWhereCondition(sql, removeFieldNames);
Assert.assertEquals("SELECT 歌曲名 FROM 歌曲库 WHERE (datediff('day', 发布日期, '2023-08-09') <= 1) "
+ "AND 数据日期 = '2023-08-09' ORDER BY 播放量 DESC LIMIT 11", replaceSql);
sql = "select 歌曲名 from 歌曲库 where datediff('day', 发布日期, '2023-08-09') <= 1 "
+ "and 歌曲名 between '2023-08-09' and '2024-08-09' and 数据日期 = '2023-08-09' and 歌曲发布时 = '2023-08-01'"
+ " order by 播放量 desc limit 11";
replaceSql = SqlRemoveHelper.removeWhereCondition(sql, removeFieldNames);
Assert.assertEquals("SELECT 歌曲名 FROM 歌曲库 WHERE datediff('day', 发布日期, '2023-08-09') <= 1 "
+ "AND 数据日期 = '2023-08-09' AND 歌曲发布时 = '2023-08-01' "
+ "ORDER BY 播放量 DESC LIMIT 11", replaceSql);
}
@Test

View File

@@ -1,16 +1,38 @@
package com.tencent.supersonic.common.jsqlparser;
import com.tencent.supersonic.common.pojo.enums.AggOperatorEnum;
import com.tencent.supersonic.common.util.ContextUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.Assert;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import java.util.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static org.mockito.Mockito.mockStatic;
/**
* SqlParserReplaceHelperTest
*/
class SqlReplaceHelperTest {
private MockedStatic<ContextUtils> mockedContextUtils;
@BeforeEach
public void setUp() {
ReplaceService replaceService = new ReplaceService();
replaceService.setReplaceColumnThreshold(0.4);
// Mock the static method ContextUtils.getBean
mockedContextUtils = mockStatic(ContextUtils.class);
mockedContextUtils.when(() -> ContextUtils.getBean(ReplaceService.class))
.thenReturn(replaceService);
}
@Test
void testReplaceAggField() {
@@ -302,70 +324,6 @@ class SqlReplaceHelperTest {
replaceSql);
}
@Test
void testReplaceAliasWithBackticks() {
String sql = "SELECT 部门, SUM(访问次数) AS 总访问次数 FROM 超音数 WHERE "
+ "datediff('day', 数据日期, '2023-09-05') <= 3 GROUP BY 部门 ORDER BY 总访问次数 DESC LIMIT 10";
String replaceSql = SqlReplaceHelper.replaceAliasWithBackticks(sql);
System.out.println(replaceSql);
Assert.assertEquals("SELECT 部门, SUM(访问次数) AS `总访问次数` FROM 超音数 WHERE "
+ "datediff('day', 数据日期, '2023-09-05') <= 3 GROUP BY 部门 ORDER BY `总访问次数` DESC LIMIT 10",
replaceSql);
sql = "select 部门, sum(访问次数) as 访问次数 from 超音数 where "
+ "(datediff('day', 数据日期, '2023-09-05') <= 3) and 数据日期 = '2023-10-10' "
+ "group by 部门 order by 访问次数 desc limit 10";
replaceSql = SqlReplaceHelper.replaceAliasWithBackticks(sql);
System.out.println(replaceSql);
Assert.assertEquals("SELECT 部门, sum(访问次数) AS `访问次数` FROM 超音数 WHERE (datediff('day', 数据日期, "
+ "'2023-09-05') <= 3) AND 数据日期 = '2023-10-10' GROUP BY 部门 ORDER BY `访问次数` DESC LIMIT 10",
replaceSql);
sql = "select 部门, sum(访问次数) as 访问次数, count(部门) as 部门数, count(部门) as 部门数2, 访问次数 from 超音数 where "
+ "(datediff('day', 数据日期, '2023-09-05') <= 3) and 数据日期 = '2023-10-10' "
+ "group by 部门, 部门数, 部门数2 having 访问次数 > 1 AND 部门数2 > 2 AND 部门数 > 1 AND 访问次数 > 1 order by 访问次数 desc limit 10";
replaceSql = SqlReplaceHelper.replaceAliasWithBackticks(sql);
System.out.println(replaceSql);
Assert.assertEquals(
"SELECT 部门, sum(访问次数) AS `访问次数`, count(部门) AS `部门数`, count(部门) AS `部门数2`, `访问次数` FROM 超音数 WHERE (datediff('day', 数据日期, "
+ "'2023-09-05') <= 3) AND 数据日期 = '2023-10-10' GROUP BY 部门, `部门数`, `部门数2` HAVING `访问次数` > 1 AND `部门数2` > 2 AND `部门数` > 1 AND `访问次数` > 1 ORDER BY `访问次数` DESC LIMIT 10",
replaceSql);
}
@Test
void testReplaceAliasFieldName() {
Map<String, String> map = new HashMap<>();
map.put("总访问次数", "\"总访问次数\"");
map.put("访问次数", "\"访问次数\"");
String sql = "select 部门, sum(访问次数) as 总访问次数 from 超音数 where "
+ "datediff('day', 数据日期, '2023-09-05') <= 3 group by 部门 order by 总访问次数 desc limit 10";
String replaceSql = SqlReplaceHelper.replaceAliasFieldName(sql, map);
System.out.println(replaceSql);
Assert.assertEquals("SELECT 部门, sum(访问次数) AS \"总访问次数\" FROM 超音数 WHERE "
+ "datediff('day', 数据日期, '2023-09-05') <= 3 GROUP BY 部门 ORDER BY \"总访问次数\" DESC LIMIT 10",
replaceSql);
sql = "select 部门, sum(访问次数) as 总访问次数 from 超音数 where "
+ "(datediff('day', 数据日期, '2023-09-05') <= 3) and 数据日期 = '2023-10-10' "
+ "group by 部门 order by 总访问次数 desc limit 10";
replaceSql = SqlReplaceHelper.replaceAliasFieldName(sql, map);
System.out.println(replaceSql);
Assert.assertEquals("SELECT 部门, sum(访问次数) AS \"总访问次数\" FROM 超音数 WHERE "
+ "(datediff('day', 数据日期, '2023-09-05') <= 3) AND 数据日期 = '2023-10-10' "
+ "GROUP BY 部门 ORDER BY \"总访问次数\" DESC LIMIT 10", replaceSql);
sql = "select 部门, sum(访问次数) as 访问次数 from 超音数 where "
+ "(datediff('day', 数据日期, '2023-09-05') <= 3) and 数据日期 = '2023-10-10' "
+ "group by 部门 order by 访问次数 desc limit 10";
replaceSql = SqlReplaceHelper.replaceAliasFieldName(sql, map);
System.out.println(replaceSql);
Assert.assertEquals(
"SELECT 部门, sum(\"访问次数\") AS \"访问次数\" FROM 超音数 WHERE (datediff('day', 数据日期, "
+ "'2023-09-05') <= 3) AND 数据日期 = '2023-10-10' GROUP BY 部门 ORDER BY \"访问次数\" DESC LIMIT 10",
replaceSql);
}
@Test
void testReplaceAggAliasOrderbyField() {
String sql = "SELECT SUM(访问次数) AS top10总播放量 FROM (SELECT 部门, SUM(访问次数) AS 访问次数 FROM 超音数 "
@@ -394,4 +352,11 @@ class SqlReplaceHelperTest {
return fieldToBizName;
}
@AfterEach
public void tearDown() {
// Close the mocked static context
if (mockedContextUtils != null) {
mockedContextUtils.close();
}
}
}

View File

@@ -1,15 +1,27 @@
FROM supersonicbi/supersonic:0.9.10-SNAPSHOT
# Use an official OpenJDK runtime as a parent image
FROM openjdk:8-jdk
# Set the working directory in the container
WORKDIR /usr/src/app
# Delete old supersonic installation directory and the symbolic link
RUN rm -rf /usr/src/app/supersonic-standalone-0.9.10-SNAPSHOT
RUN rm -f /usr/src/app/supersonic-standalone-latest
# Argument to pass in the supersonic version at build time
ARG SUPERSONIC_VERSION
RUN apt-get update
# Install necessary packages, including Postgres client
RUN apt-get update && apt-get install -y postgresql-client
# Install the vim editor.
RUN apt-get update && apt-get install -y vim && \
rm -rf /var/lib/apt/lists/*
# Update the package list and install iputils-ping.
RUN apt-get update && apt-get install -y iputils-ping
# 更新包列表并安装 dnsutils 包
RUN apt-get update && apt-get install -y dnsutils
# Copy the supersonic standalone zip file into the container
COPY assembly/build/supersonic-standalone-${SUPERSONIC_VERSION}.zip .

View File

@@ -1,34 +0,0 @@
#!/usr/bin/env bash
# Function to execute the build script
execute_build_script() {
echo "Executing build script: assembly/bin/supersonic-build.sh"
assembly/bin/supersonic-build.sh
}
# Function to build the Docker image
build_docker_image() {
local version=$1
echo "Building Docker image: supersonic:$version"
docker buildx build --no-cache --platform linux/amd64,linux/arm64 \
--build-arg SUPERSONIC_VERSION=$version \
-t supersonicbi/supersonic:$version \
-t supersonicbi/supersonic:latest \
-f docker/Dockerfile \
--push .
if [ $? -ne 0 ]; then
echo "Docker build failed. Exiting."
exit 1
fi
echo "Docker image supersonic:$version built successfully."
}
# Main script execution
VERSION=$1
if [ -z "$VERSION" ]; then
echo "Usage: $0 <version>"
exit 1
fi
execute_build_script
build_docker_image $VERSION

View File

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

View File

@@ -1,5 +1,5 @@
services:
supersonic_postgres:
postgres:
image: pgvector/pgvector:pg17
privileged: true
container_name: supersonic_postgres
@@ -11,8 +11,8 @@ services:
POSTGRES_PASSWORD: supersonic_password
ports:
- "15432:5432"
# volumes:
# - postgres_data:/var/lib/postgresql
volumes:
- postgres_data:/var/lib/postgresql
networks:
- supersonic_network
dns:
@@ -21,10 +21,35 @@ services:
- 8.8.4.4
healthcheck:
test: ["CMD-SHELL", "sh -c 'pg_isready -U supersonic_user -d postgres'"]
interval: 10s
interval: 30s
timeout: 10s
retries: 5
db_init:
image: supersonicbi/supersonic:${SUPERSONIC_VERSION:-latest}
privileged: true
container_name: supersonic_db_init
depends_on:
postgres:
condition: service_healthy
networks:
- supersonic_network
command: >
sh -c "
if ! PGPASSWORD=supersonic_password psql -h supersonic_postgres -U supersonic_user -d postgres -c 'select * from s2_database limit 1' > /dev/null;
then
PGPASSWORD=supersonic_password psql -hsupersonic_postgres -U supersonic_user -d postgres < /usr/src/app/supersonic-standalone-${SUPERSONIC_VERSION:-latest}/conf/db/schema-postgres.sql
PGPASSWORD=supersonic_password psql -hsupersonic_postgres -U supersonic_user -d postgres < /usr/src/app/supersonic-standalone-${SUPERSONIC_VERSION:-latest}/conf/db/schema-postgres-demo.sql
PGPASSWORD=supersonic_password psql -hsupersonic_postgres -U supersonic_user -d postgres < /usr/src/app/supersonic-standalone-${SUPERSONIC_VERSION:-latest}/conf/db/data-postgres.sql
PGPASSWORD=supersonic_password psql -hsupersonic_postgres -U supersonic_user -d postgres < /usr/src/app/supersonic-standalone-${SUPERSONIC_VERSION:-latest}/conf/db/data-postgres-demo.sql
else
echo 'Database already initialized.'
fi"
dns:
- 114.114.114.114
- 8.8.8.8
- 8.8.4.4
supersonic_standalone:
image: supersonicbi/supersonic:${SUPERSONIC_VERSION:-latest}
privileged: true
@@ -37,17 +62,19 @@ services:
ports:
- "9080:9080"
depends_on:
supersonic_postgres:
postgres:
condition: service_healthy
db_init:
condition: service_completed_successfully
networks:
- supersonic_network
dns:
- 114.114.114.114
- 8.8.8.8
- 8.8.4.4
#volumes:
volumes:
#1.Named Volumes are best for persistent data managed by Docker.
#- supersonic_data:/usr/src/app/supersonic-standalone-${SUPERSONIC_VERSION:-latest}
- supersonic_data:/usr/src/app/supersonic-standalone-${SUPERSONIC_VERSION:-latest}
#2.Bind Mounts are suitable for frequent modifications and debugging.
# - ./conf/application-prd.yaml:/usr/src/app/supersonic-standalone-${SUPERSONIC_VERSION:-latest}/conf/application-prd.yaml
#3.Detailed Bind Mounts offer more control over the mount behavior.
@@ -57,9 +84,9 @@ services:
# bind:
# propagation: rprivate
# create_host_path: true
#volumes:
# postgres_data:
# supersonic_data:
volumes:
postgres_data:
supersonic_data:
networks:
supersonic_network:

24
docker/docker-publish.sh Normal file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Exit immediately if a command exits with a non-zero status
set -e
VERSION=$1
# Image name
IMAGE_NAME="supersonicbi/supersonic"
# Default tag is latest
TAGS="latest"
# If VERSION is provided, add it to TAGS and tag the image as latest
if [ -n "$VERSION" ]; then
TAGS="$TAGS $VERSION"
docker tag $IMAGE_NAME:$VERSION $IMAGE_NAME:latest
fi
# Push Docker images
for TAG in $TAGS; do
echo "Pushing Docker image $IMAGE_NAME:$TAG"
docker push $IMAGE_NAME:$TAG
done
echo "Docker images pushed successfully."

Some files were not shown because too many files have changed in this diff Show More