├── LICENSE ├── LICENSE_PYBIND11 ├── README.md ├── README_CN.md ├── README_CN_NEW ├── 1_QuickStart │ ├── 1.1_Install.md │ └── 1.2_Demo.md ├── 2_BasicOperations │ ├── 2.1_Session │ │ ├── 2.1.1_Constructor.md │ │ ├── 2.1.2_Connect.md │ │ └── 2.1.3_OtherParams.md │ ├── 2.2_DBConnectionPool │ │ ├── 2.2.1_Constructor.md │ │ └── 2.2.2_AsyncMethodsAndOthers.md │ ├── 2.3_AutoFitTableAppender │ │ ├── 2.3.1_TableAppender.md │ │ ├── 2.3.2_TableUpserter.md │ │ └── 2.3.3_PartitionedTableAppender.md │ ├── 2.4_Subscription │ │ └── 2.4_Subscription.md │ └── 2.5_AsyncWrites │ │ ├── 2.5.1_SessionAsyncMode.md │ │ ├── 2.5.2_MultithreadedTableWriter.md │ │ └── 2.5.3_BatchTableWriter.md ├── 3_AdvancedOperations │ ├── 3.1_DataTypeCasting │ │ ├── 3.1.0_TypeCasting.md │ │ ├── 3.1.1_PROTOCOL_DDB.md │ │ ├── 3.1.2_PROTOCOL_PICKLE.md │ │ ├── 3.1.3_PROTOCOL_ARROW.md │ │ └── 3.1.4_ForceTypeCasting.md │ ├── 3.2_WriteOptions │ │ └── 3.2_WriteOptions.md │ ├── 3.3_SubscriptionOptions │ │ └── 3.3_SubscriptionOptions.md │ ├── 3.4_ObjectOrientedOperationsOnDdbOBjects │ │ ├── 3.4.1_Database.md │ │ └── 3.4.2_Table.md │ └── 3.5_OtherFunctions │ │ └── 3.5_OtherFunctions.md ├── Introduction.md └── data │ ├── example.csv │ ├── quotes.csv │ └── trades.csv ├── README_NEW └── README.md ├── ReleaseNotes ├── 1.30.19.1.md ├── 1.30.19.2.md ├── 1.30.19.3.md ├── 1.30.19.4.md ├── 1.30.21.1.md ├── 1.30.21.2.md ├── 1.30.22.1.md ├── 1.30.22.2.md ├── 1.30.22.3.md ├── 1.30.22.4.md ├── 1.30.22.5.md ├── 1.30.22.6.md └── README_EN.md ├── data ├── US.rar ├── USPrices_FIRST.csv ├── USPrices_FIRST_Delimiter.csv ├── example.csv ├── k_line │ └── trades.csv ├── loadTextExTest1.csv ├── loadTextExTest2.csv ├── quotes.csv └── trades.csv ├── images └── K-line.png ├── sample.py ├── test.py └── test ├── README.md ├── setup └── settings.py ├── testAll.py ├── test_AutoFitTableAppender.py ├── test_DBConnectionPool.py ├── test_basic_datatypes.py ├── test_database.py ├── test_dropDatabase.py ├── test_dropPartition.py ├── test_dropTable.py ├── test_enableStreaming.py ├── test_existsDatabase.py ├── test_existsTable.py ├── test_hashBucket.py ├── test_loadTable.py ├── test_loadTableBySQL.py ├── test_loadText.py ├── test_loadTextEx.py ├── test_run_function.py ├── test_saveTable.py ├── test_subscribe.py ├── test_table.py ├── test_toDF.py └── test_upload.py /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2019 it 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE_PYBIND11: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Wenzel Jakob , All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | Please also refer to the file CONTRIBUTING.md, which clarifies licensing of 29 | external contributions to this project including patches, pull requests, etc. 30 | -------------------------------------------------------------------------------- /README_CN_NEW/1_QuickStart/1.1_Install.md: -------------------------------------------------------------------------------- 1 | # 安装 2 | 3 | 在安装 dolphindb 前,请确定已部署 Python 执行环境。若无,推荐使用 [Anaconda Distribution](https://www.anaconda.com/products/distribution) 下载 Python 及常用库。 4 | 5 | ## 支持版本 6 | 7 | dolphindb 的详细版本列表和离线下载链接请参照 [pypi dolphindb](https://pypi.org/project/dolphindb/1.30.21.1/#files)。下表展示 dolphindb 在不同操作系统中对应支持的 Python 版本号。 8 | 9 | | 操作系统 | Python 版本号 | 10 | | :------------ | :----------------------------------- | 11 | | Windows(amd64)| Python 3.6-3.11 | 12 | | Linux(x86_64) | Python 3.6-3.11 | 13 | | Linux(aarch64)| Conda 环境下的 Python 3.7-3.11 | 14 | | Mac(x86_64) | Conda 环境下的 Python 3.6-3.11 | 15 | | Mac(arm64) | Conda 环境下的 Python 3.8-3.11 | 16 | 17 | > 注意:为保证正常使用 dolphindb ,您需要同时安装以下依赖库: 18 | 19 | * future 20 | * NumPy(建议使用版本 1.18~1.24.4) 21 | * pandas(建议使用 1.0.0 及以上版本,且暂不支持版本 1.3.0) 22 | 23 | ## 安装示例 24 | 25 | 目前仅支持通过 `pip` 指令安装 DolphinDB Python API,暂不支持 `conda` 安装。安装示例如下: 26 | 27 | ```Console 28 | $ pip install dolphindb 29 | ``` 30 | 31 | ## 常见问题 32 | 33 | **Q1:安装失败,或安装成功后无法导入 dolphindb 包。** 34 | 35 | 建议检查并重装 whl 包。具体操作如下: 36 | 37 | 1. 通过 [PyPI](https://pypi.org/) 确认是否存在支持当前操作系统(例如 Linux arm 架构、Mac ARM 等)的 DolphinDB API 安装包。若存在,则将该 whl 包下载至本地。 38 | 2. 通过如下命令查看适合当前系统环境支持的 whl 包后缀。 39 | 40 | ```python 41 | pip debug --verbose 42 | ``` 43 | 44 | 3. 根据 Compatible tags 的显示信息,将 DolphinDB 的 whl 包名修改为适合系统架构的名称。以 Mac(x86_64) 系统为例:安装包名为 "dolphindb-1.30.19.2-cp37-cp37m-macosx_10_16_x86_64.whl"。但查询到 pip 支持的当前系统版本为10.13,则将 whl 包名中的“10_16”替换成“10_13”。 45 | 4. 尝试安装更名后的 whl 包。 46 | 47 | 若执行完上述操作后,仍无法安装或导入,可在 [DolphinDB 社区](https://ask.dolphindb.net)中进行反馈。 48 | -------------------------------------------------------------------------------- /README_CN_NEW/1_QuickStart/1.2_Demo.md: -------------------------------------------------------------------------------- 1 | # 快速上手 2 | 3 | 本节将展示通过 dolphindb 连接、使用及操作单节点 DolphinDB 服务器的完整操作。 4 | 5 | 通过阅读,您将了解到如何使用 dolphindb 连接 DolphinDB 并与之交互、以及如何操作数据库表。 6 | 7 | ## 1. 使用前准备 8 | 9 | ### 1.1 Python 基础知识 10 | 11 | 在学习使用 dolphindb 前,建议您先了解 Python 的相关知识。您可以查阅 [Python 官方教程](https://docs.python.org/3/tutorial/),了解有关 Python 的更多信息和教程。 12 | 13 | ### 1.2 搭建 DolphinDB 服务器 14 | 15 | 在 [DolphinDB 官网](https://dolphindb.cn/)下载 DolphinDB 服务器,并参考[单节点部署教程](https://gitee.com/dolphindb/Tutorials_CN/blob/master/standalone_server.md)启动 DolphinDB 服务。 16 | 17 | 有关快速部署 DolphinDB 服务器, 请参考[用新服务器从零开始部署 DolphinDB](https://gitee.com/dolphindb/Tutorials_CN/blob/master/deploy_dolphindb_on_new_server.md )。 18 | 19 | 如果您想尝试更多部署方式,请参阅 [DolphinDB 安装使用指南](https://gitee.com/dolphindb/Tutorials_CN/blob/master/dolphindb_user_guide.md)。 20 | 21 | ## 2. 建立连接 22 | 23 | session(会话控制)可以实现客户端与服务器之间的信息交互。DolphinDB Python API 通过 session 在 DolphinDB 服务器上执行脚本和函数,同时实现双向的数据传递。 24 | 25 | > **注:** dolphindb 自 1.30.22.1 版本调整 session 类名为 Session,同时增加别名 session 以确保兼容性。 26 | 27 | ### session 连接及使用示例 28 | 29 | 在如下脚本中,先通过 `import` 语句导入 dolphindb ,在 Python 中创建一个 session,然后使用指定的域名(或 IP 地址)和端口号把该会话连接到 DolphinDB 服务器。最后展示一个简单的计算示例,执行 `1+1` 的脚本,得到返回值 `2`。 30 | 31 | ```python 32 | >>> import dolphindb as ddb 33 | >>> s = ddb.session() 34 | >>> s.connect("localhost", 8848) 35 | True 36 | >>> s.run("1+1;") 37 | 2 38 | >>> s.close() 39 | ``` 40 | 41 | > **注意:** 42 | > 43 | > * 建立连接前,须先启动 DolphinDB 服务器。 44 | > * 若当前 session 不再使用,建议立即调用 `close()` 关闭会话。否则可能出现因连接数过多,导致其它会话无法连接服务器的情况。 45 | 46 | ## 3. 数据交互 47 | 48 | dolphindb 目前支持多种数据交互的方法,本节仅介绍通过 run, upload 等方式上传和下载数据。更多交互方式请参考章节 [2.1.3 Session 的常用方法](../2_BasicOperations/2.1_Session/2.1.3_OtherParams.md) 和 [2.2.2 DBConnectionPool 的常用方法](../2_BasicOperations/2.2_DBConnectionPool/2.2.2_AsyncMethodsAndOthers.md)。 49 | 50 | 下表为 [DolphinDB 的各种数据形式](https://www.dolphindb.cn/cn/help/130/DataTypesandStructures/DataForms/index.html)与 Python 对象的对应关系。 51 | 52 | | DolphinDB DataForm | Python | 53 | | :------------------ | :-----| 54 | | Scalar | int/str/float/... | 55 | | Vector | numpy.ndarray | 56 | | Pair | list | 57 | | Matrix | [numpy.ndarray, numpy.ndarray, numpy.ndarray] | 58 | | Set | set | 59 | | Dictionary | dict | 60 | | Table | pandas.DataFrame | 61 | 62 | > **注意:** 63 | > 64 | > * 由于 DolphinDB 中的 Matrix 对象可以设置行名和列名,故下载的 Matrix 形式的数据将转换成 list 类型的 Python 对象。该 list 对象中将包含一个表示数据的二维数组和两个分别表示行名、列名的一维数组。 65 | > * 若 API 使用不同的下载方式,同样的 DolphinDB 数据将会对应不同的 Python 对象。详情请参考章节 [3.1 类型转换](../3_AdvancedOperations/3.1_DataTypeCasting/3.1.0_TypeCasting.md)。 66 | 67 | 在下载数据前,首先导入 dolphindb,然后创建一个 session 并连接到 DolphinDB 服务端。 68 | 69 | ```python 70 | >>> import dolphindb as ddb 71 | >>> s = ddb.session() 72 | >>> s.connect("localhost", 8848) 73 | True 74 | ``` 75 | 76 | ### 3.1 下载数据 77 | 78 | 使用 `s.run` 的方式运行脚本,成功执行后将下载具体数据。 79 | 80 | 以下依次创建并下载数据形式分别为标量(Scalar)、向量(Vector)、数据对(Pair)、矩阵(Matrix)、集合(Set)、字典(Dictionary)和表(Table)的数据。 81 | 82 | **标量(Scalar)** 83 | 84 | ```python 85 | >>> s.run("1;") 86 | 1 87 | ``` 88 | 89 | **向量(Vector)** 90 | 91 | ```python 92 | >>> s.run("1..10;") 93 | array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype=int32) 94 | ``` 95 | 96 | **数据对(Pair)** 97 | 98 | ```python 99 | >>> s.run("1:5;") 100 | [1, 5] 101 | ``` 102 | 103 | **矩阵(Matrix)** 104 | 105 | ```python 106 | >>> s.run("1..6$2:3;") 107 | [array([[1, 3, 5], 108 | [2, 4, 6]], dtype=int32), None, None] 109 | ``` 110 | 111 | **集合(Set)** 112 | 113 | ```python 114 | >>> s.run("set([1,5,9]);") 115 | {1, 5, 9} 116 | ``` 117 | 118 | **字典(Dictionary)** 119 | 120 | ```python 121 | >>> s.run("dict(1 2 3, 4.5 7.8 4.3);") 122 | {2: 7.8, 1: 4.5, 3: 4.3} 123 | ``` 124 | 125 | **表(Table)** 126 | 127 | ```python 128 | >>> s.run("table(`XOM`GS`AAPL as id, 102.1 33.4 73.6 as x);") 129 | id x 130 | 0 XOM 102.1 131 | 1 GS 33.4 132 | 2 AAPL 73.6 133 | ``` 134 | 135 | ### 3.2 上传数据 136 | 137 | 导入库 numpy 和 pandas,示例如下: 138 | 139 | ```python 140 | >>> import numpy as np 141 | >>> import pandas as pd 142 | ``` 143 | 144 | 通过 `s.upload` 的方式上传数据,输入参数 `变量名称:数据`,成功执行后将返回该变量在服务器端的地址。 145 | 146 | 以下将依次展示上传 Python 对象对应 DolphinDB 数据形式为标量(Scalar)、向量(Vector)、矩阵(Matrix)、集合(Set)、字典(Dictionary)和表(Table)的数据。 147 | 148 | > **注意:** Python API 暂不支持上传数据对(Pair)形式的数据。 149 | 150 | 以下脚本中将展示成功上传后,通过 typestr 函数查询数据的 DolphinDB 类型名称,以及使用 `s.run` 的方式重新下载数据。 151 | 152 | **标量(Scalar)** 153 | 154 | ```python 155 | >>> s.upload({'scalar_sample': 1}) 156 | 62776640 157 | >>> s.run("typestr(scalar_sample);") 158 | 'LONG' 159 | >>> s.run("scalar_sample;") 160 | 1 161 | ``` 162 | 163 | **向量(Vector)** 164 | 165 | ```python 166 | >>> s.upload({'vector_sample': np.array([1, 3])}) 167 | 65583680 168 | >>> s.run("typestr(vector_sample);") 169 | 'FAST LONG VECTOR' 170 | >>> s.run("vector_sample;") 171 | array([1, 3]) 172 | ``` 173 | 174 | **矩阵(Matrix)** 175 | 176 | ```python 177 | >>> s.upload({'matrix_sample': np.array([[1, 2, 3], [4, 5, 6]])}) 178 | 65484832 179 | >>> s.run("typestr(matrix_sample);") 180 | 'FAST LONG MATRIX' 181 | >>> s.run("matrix_sample;") 182 | [array([[1, 2, 3], 183 | [4, 5, 6]]), None, None] 184 | ``` 185 | 186 | **集合(Set)** 187 | 188 | ```python 189 | >>> s.upload({'set_sample': {1, 4, 7}}) 190 | 65578432 191 | >>> s.run("typestr(set_sample);") 192 | 'LONG SET' 193 | >>> s.run("set_sample;") 194 | {1, 4, 7} 195 | ``` 196 | 197 | **字典(Dictionary)** 198 | 199 | ```python 200 | >>> s.upload({'dict_sample': {'a': 1}}) 201 | 58318576 202 | >>> s.run("typestr(dict_sample);") 203 | 'STRING->LONG DICTIONARY' 204 | >>> s.run("dict_sample;") 205 | {'a': 1} 206 | ``` 207 | 208 | **表(Table)** 209 | 210 | ```python 211 | >>> df = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']}) 212 | >>> s.upload({'table_sample': df}) 213 | 63409760 214 | >>> s.run("typestr(table_sample);") 215 | 'IN-MEMORY TABLE' 216 | >>> s.run("table_sample;") 217 | a b 218 | 0 1 a 219 | 1 2 b 220 | 2 3 c 221 | ``` 222 | 223 | ## 4. 数据库操作 224 | 225 | Python API 支持通过如下两种方式操作 DolphinDB: 226 | 227 | * 方式一:通过 `session.run()` 执行 DolphinDB 脚本。 228 | * 方式二:使用 Python API 封装的方法操作服务器。 229 | 230 | 以下展示两种方式的使用示例。 231 | 232 | ### 方式一:执行 DolphinDB 脚本 233 | 234 | 在以下脚本中,先导入 dolphindb,创建一个 session 并连接到 DolphinDB 服务端。由于后续脚本中涉及到执行数据库相关的操作,须在连接服务端的并且登录具有相应权限的账户。 235 | 236 | 在完成上述操作后,通过 `s.run()` 执行创建变量、数据库 db 和数据表 pt,并向表 pt 中写入数据等操作,最后执行 SQL 语句返回表 pt 的行数。 237 | 238 | ```python 239 | import dolphindb as ddb 240 | s = ddb.session() 241 | s.connect("localhost", 8848, "admin", "123456") 242 | s.run(""" 243 | n=1000000 244 | ID=rand(10, n) 245 | x=rand(1.0, n) 246 | t=table(ID, x) 247 | db=database(directory="dfs://hashdb", partitionType=HASH, partitionScheme=[INT, 2]) 248 | pt = db.createPartitionedTable(t, `pt, `ID) 249 | pt.append!(t); 250 | """) 251 | re = s.run("select count(x) from pt;") 252 | print(re) 253 | 254 | # output 255 | count_x 256 | 0 1000000 257 | ``` 258 | 259 | ### 方式二:使用 API 接口 260 | 261 | 在以下脚本中,首先创建一个 session 并连接到 DolphinDB 服务端,同时登录具有相应权限的账户。然后创建 schema_t 用于定义分布式表的表结构。使用 API 接口执行 `session.table`, `session.database` 和 `Database.createPartitionedTable` 以创建一个内存表、数据库 db 和 分区表 pt,再通过 `pt.append` 方法向表中追加数据。最后使用 `Table.toDF` 获取表 pt 的数据。 262 | 263 | ```python 264 | import pandas as pd 265 | import numpy as np 266 | import dolphindb as ddb 267 | import dolphindb.settings as keys 268 | 269 | s = ddb.session("192.168.1.113", 8848, "admin", "123456") 270 | n = 1000000 271 | df = pd.DataFrame({ 272 | 'ID': np.random.randint(0, 10, n), 273 | 'x': np.random.rand(n), 274 | }) 275 | s.run("schema_t = table(100000:0, `ID`x,[INT, DOUBLE])") 276 | schema_t = s.table(data="schema_t") 277 | if s.existsDatabase("dfs://hashdb"): 278 | s.dropDatabase("dfs://hashdb") 279 | db = s.database(dbPath="dfs://hashdb", partitionType=keys.HASH, partitions=[keys.DT_INT, 2]) 280 | pt: ddb.Table = db.createPartitionedTable(table=schema_t, tableName="pt", partitionColumns=["ID"]) 281 | data = s.table(data=df) 282 | pt.append(data) 283 | print(pt.toDF()) 284 | 285 | # output 286 | ID x 287 | 0 4 0.320935 288 | 1 8 0.426056 289 | 2 8 0.505221 290 | 3 4 0.692984 291 | 4 4 0.709175 292 | ... .. ... 293 | 999995 5 0.479531 294 | 999996 3 0.805629 295 | 999997 5 0.873164 296 | 999998 7 0.158090 297 | 999999 5 0.530824 298 | 299 | [1000000 rows x 2 columns] 300 | ``` 301 | 302 | ## 参考链接 303 | 304 | * [DolphinDB 用户手册-数据形式](https://www.dolphindb.cn/cn/help/200/DataTypesandStructures/DataForms/index.html) 305 | * [DolphinDB 用户手册-数据库操作](https://www.dolphindb.cn/cn/help/200/DatabaseandDistributedComputing/DatabaseOperations/index.html) 306 | -------------------------------------------------------------------------------- /README_CN_NEW/2_BasicOperations/2.1_Session/2.1.1_Constructor.md: -------------------------------------------------------------------------------- 1 | # session 2 | 3 | session(会话控制)可以实现 API 客户端与 DolphinDB 服务端之间的信息交互。dolphindb 通过 session 在 DolphinDB 上执行脚本和函数,同时实现双向的数据传递。 4 | 5 | > **注意:** DolphinDB Python API 自1.30.22.1版本起调整 session 类名为 Session,同时增加别名 session 以确保兼容性。 6 | 7 | 如下展示创建一个 session 的完整示例。 8 | 9 | ```python 10 | session(host=None, port=None, userid="", password="", 11 | enableSSL=False, enableASYNC=False, 12 | keepAliveTime=30, enableChunkGranularityConfig=False, compress=False, 13 | enablePickle=None, protocol=PROTOCOL_DEFAULT, 14 | python=False, show_output=True) 15 | ``` 16 | 17 | 由上述脚本可知,session 的创建涉及到多个参数。以下内容将对参数进行详细说明。 18 | 19 | ## 1. 连接参数 *host*, *port*, *userid*, *password* 20 | 21 | * **host**:所连接服务器的地址。 22 | * **port**:所连接服务器的端口。 23 | * **userid** :登录时的用户名。 24 | * **password** :登录时用户名对应的密码。 25 | 26 | 用户可以使用指定的域名(或 IP 地址)和端口号把该会话连接到 DolphinDB,并且在建立连接的同时登录账号。 27 | 28 | 使用示例如下: 29 | 30 | ```python 31 | import dolphindb as ddb 32 | 33 | # 创建session,同时连接地址为localhost,端口为8848的DolphinDB 34 | s = ddb.session("localhost", 8848) 35 | 36 | # 创建session,同时连接地址为localhost,端口为8848的DolphinDB,登录用户名为admin,密码为123456的账户 37 | s = ddb.session("localhost", 8848, "admin", "123456") 38 | ``` 39 | 40 | > **注意:** 41 | > 42 | > * 在构造 session 时,可以不指定参数 *host*, *port*, *userid*, *password*,之后通过 connect 建立连接时再进行指定。 43 | > * 如果在构造时输入错误的参数值,将无法连接 DolphinDB,但是仍能正常创建 session 对象。 44 | 45 | ## 2. 加密参数 *enableSSL* 46 | 47 | * **enableSSL**:是否支持加密通讯,默认值为 False。 48 | 49 | API 端设置脚本示例如下: 50 | 51 | ```python 52 | # 开启加密通讯 53 | s = ddb.session(enableSSL=True) 54 | ``` 55 | 56 | > **注意:** 57 | > 58 | > * DolphinDB 自 1.10.17 与 1.20.6 版本起支持加密通讯参数 *enableSSL*。 59 | > 60 | > * DolphinDB 须同时设置配置项 `enableHTTPS=true` 方可启动 SSL 通讯。详情可参考[集群通信与连接](https://www.dolphindb.cn/cn/help/DatabaseandDistributedComputing/Configuration/ConfigParamRef.html?highlight=%E9%9B%86%E7%BE%A4%E9%80%9A%E4%BF%A1#id17)。 61 | 62 | ## 3. 异步参数 *enableASYNC* 63 | 64 | * **enableASYNC**:是否支持异步通讯,默认值为 False。 65 | 66 | 开启异步通讯后,API 端只能通过 `session.run()` 方法与 DolphinDB 端进行通讯,该情况下无返回值。该模式适用于异步写入数据,可节省 API 端检测返回值的时间。 67 | 68 | 使用示例如下: 69 | 70 | ```python 71 | # 开启异步通讯 72 | s = ddb.session(enableASYNC=True) 73 | ``` 74 | 75 | > **注意:** DolphinDB 自 1.10.17 与 1.20.6 版本起支持异步通讯参数 *enableASYNC*。 76 | 77 | ## 4. 保活参数 *keepAliveTime* 78 | 79 | * **keepAliveTime**:在 TCP 连接状态下两次保活传输之间的持续时间,默认参数为 60,单位秒(s)。 80 | 81 | 使用示例如下: 82 | 83 | ```python 84 | # 设置保活时间为120秒 85 | s = ddb.session(keepAliveTime=120) 86 | ``` 87 | 88 | > **注意:** 89 | > 90 | > * 该参数在 Linux、Windows、MacOS 平台均可生效。 91 | > 92 | > * TCP 超时时间 Timeout 设置可参考章节 [2.1.3 Session 相关的常用方法](./2.1.3_OtherParams.md)。 93 | 94 | ## 5. 压缩参数 *compress* 95 | 96 | * **compress**:是否开启压缩,默认参数为 False。 97 | 98 | 该模式适用于大数据量的写入或查询。压缩数据后再进行传输,这可以节省网络带宽,但也会增加 DolphinDB 和 API 端的计算量。 99 | 100 | 使用示例如下: 101 | 102 | ```python 103 | import dolphindb.settings as keys 104 | 105 | # api version >= 1.30.21.1,开启压缩 106 | s = ddb.session(compress=True, protocol=keys.PROTOCOL_DDB) 107 | 108 | # api version <= 1.30.19.4,开启压缩 109 | s = ddb.session(compress=True, enablePickle=False) 110 | ``` 111 | 112 | > **注意:** 113 | > 114 | > * DolphinDB 自1.30.6版本起支持压缩参数 *compress*。 115 | > 116 | > * 开启压缩时,若 API 为 1.30.21.1 版本前,须指定 `enablePickle=False`;若 API 为 1.30.21.1 版本及之后,须指定协议参数 *protocol* 为 PROTOCOL_DDB。(下一小节将介绍协议参数) 117 | 118 | ## 6. 协议参数 *protocol*, *enablePickle* 119 | 120 | * **protocol**:API 与 DolphinDB 交互时使用的数据格式协议,默认值为 PROTOCOL_DEFAULT,表示使用 PROTOCOL_PICKLE。 121 | * **enablePickle**:API 与 DolphinDB 交互时是否使用 PROTOCOL_PICKLE 作为数据格式协议,默认值为 True。 122 | 123 | 目前 DolphinDB 支持三种协议:PROTOCOL_DDB, PROTOCOL_PICKLE, PROTOCOL_ARROW。使用不同的协议,会影响 API 在执行 DolphinDB 脚本后接收到的数据格式。有关协议的详细说明请参考章节 [3.1 类型转换](../../3_AdvancedOperations/3.1_DataTypeCasting/3.1.0_TypeCasting.md)。 124 | 125 | 在 1.30.21.1 版本前,API 仅支持使用 *enablePickle* 来指定数据格式协议,可设置使用协议 PROTOCOL_PICKLE, PROTOCOL_DDB。 126 | 127 | 使用示例如下: 128 | 129 | ```python 130 | # 使用协议 PROTOCOL_PICKLE 131 | s = ddb.session(enablePickle=True) 132 | 133 | # 使用协议 PROTOCOL_DDB 134 | s = ddb.session(enablePickle=False) 135 | ``` 136 | 137 | 在 1.30.21.1 版本及之后,API 支持使用 *protocol* 来指定数据格式协议,可设置使用协议 PROTOCOL_DDB, PROTOCOL_PICKLE, PROTOCOL_ARROW。 138 | 139 | 使用示例如下: 140 | 141 | ```python 142 | import dolphindb.settings as keys 143 | 144 | # 使用协议 PROTOCOL_DDB 145 | s = ddb.session(protocol=keys.PROTOCOL_DDB) 146 | 147 | # 使用协议 PROTOCOL_PICKLE 148 | s = ddb.session(protocol=keys.PROTOCOL_PICKLE) 149 | 150 | # 使用协议 PROTOCOL_ARROW 151 | s = ddb.session(protocol=keys.PROTOCOL_ARROW) 152 | ``` 153 | 154 | > **注意:** 在设置 *protocol* 时,建议不要同时设置参数 *enablePickle*,以避免产生冲突。 155 | 156 | ## 7. 其他参数 *enableChunkGranularityConfig* 157 | 158 | * **enableChunkGranularityConfig**:是否支持在使用 `session.database()` 创建数据库时允许配置 *chunkGranularity* 参数,默认值为 False。 159 | 160 | 该参数会影响 `session.database()` 函数的正常使用。session 中必须指定 `enableChunkGranularityConfig=True`,否则 `session.database()` 的参数 [*chunkGranularity*](https://www.dolphindb.cn/cn/help/FunctionsandCommands/FunctionReferences/d/database.html?highlight=chunkgranularity) 将会失效。 161 | 162 | > **注意:** 使用 *enableChunkGranularityConfig* 须在 server 的[配置文件](https://www.dolphindb.cn/cn/help/DatabaseandDistributedComputing/Configuration/ConfigParamRef.html?highlight=enablechunkgranularityconfig#id37)中将对应参数设置为 true 时才可正常使用。 163 | 164 | 在如下脚本中,设置参数 *enableChunkGranularityConfig* 为 True,并展示参数 *chunkGranularity* 已生效: 165 | 166 | ```python 167 | 168 | import dolphindb as ddb 169 | import dolphindb.settings as keys 170 | 171 | # 设置参数enableChunkGranularityConfig为True,即允许配置session.database()中的chunkGranularity参数 172 | s = ddb.session("localhost", 8848, "admin", "123456", enableChunkGranularityConfig=True) 173 | 174 | # 以下部分仅为展示参数chunkGranularity已生效 175 | if s.existsDatabase("dfs://testdb"): 176 | s.dropDatabase("dfs://testdb") 177 | db = s.database("db", partitionType=keys.VALUE, partitions=[1, 2, 3], dbPath="dfs://testdb", chunkGranularity="DATABASE") 178 | print(s.run("schema(db)")["chunkGranularity"]) 179 | 180 | # 输出结果为 DATABASE 181 | ``` 182 | 183 | 在如下脚本中,设置参数 *enableChunkGranularityConfig* 为 False,并展示参数 *chunkGranularity* 已失效: 184 | 185 | ```python 186 | import dolphindb as ddb 187 | import dolphindb.settings as keys 188 | 189 | # 设置参数enableChunkGranularityConfig为False,即不允许配置session.database()中的chunkGranularity参数 190 | s = ddb.session("localhost", 8848, "admin", "123456", enableChunkGranularityConfig=False) 191 | 192 | # 以下部分仅为展示参数chunkGranularity已失效 193 | if s.existsDatabase("dfs://testdb"): 194 | s.dropDatabase("dfs://testdb") 195 | db = s.database("db", partitionType=keys.VALUE, partitions=[1, 2, 3], dbPath="dfs://testdb", chunkGranularity="TABLE") 196 | print(s.run("schema(db)")["chunkGranularity"]) 197 | 198 | # 输出结果为 TABLE 199 | ``` 200 | 201 | ## 8. 其他参数 *show_output* 202 | 203 | * **show_output**:是否在执行后打印脚本中 print 语句的输出。默认值为 True,表示打印 print 语句输出。 204 | 205 | 使用示例如下: 206 | 207 | ```python 208 | # 启用 show_output 209 | s = ddb.session(show_output=True) 210 | s.connect("localhost", 8848) 211 | s.run("print(1);2") 212 | 213 | # 输出结果 214 | 1 215 | 2 216 | 217 | # 不启用 show_output 218 | s = ddb.session(show_output=False) 219 | s.connect("localhost", 8848) 220 | s.run("print(1);2") 221 | 222 | # 输出结果 223 | 2 224 | ``` 225 | 226 | ## 9. 其他参数 *python* 227 | 228 | * **python**:是否启用 python parser 特性。 229 | 230 | 使用示例如下: 231 | 232 | ```python 233 | # 启用 python parser 特性 234 | s = ddb.session(python=True) 235 | 236 | # 不启用 python parser 特性 237 | s = ddb.session(python=False) 238 | ``` 239 | 240 | > **注意:** 仅支持 DolphinDB 3.00 版本。(暂未发布,敬请期待) 241 | -------------------------------------------------------------------------------- /README_CN_NEW/2_BasicOperations/2.1_Session/2.1.2_Connect.md: -------------------------------------------------------------------------------- 1 | # Connect 2 | 3 | dolphindb 支持以下两种创建连接的方式: 4 | 5 | * 方式一:在构造 session 的同时传入相应参数,在构造的同时创建连接。 6 | * 方式二:在构造 session 后,通过 connect 方法建立连接。 7 | 8 | 本节将介绍方式二即在构造 session 后通过 connect 方法建立连接。如下展示创建一个 Connect 的完整示例: 9 | 10 | ```python 11 | connect(host, port, 12 | userid=None, password=None, startup=None, 13 | highAvailability=False, highAvailabilitySites=None, 14 | keepAliveTime=None, reconnect=False) 15 | ``` 16 | 17 | 由上述脚本可知,使用 Connect 方法建立连接涉及到多个参数。以下内容将对参数进行详细说明。 18 | 19 | ## 1. 连接参数 *host*, *port*, *userid*, *password* 20 | 21 | * **host**:所连接服务器的地址。 22 | * **port**:所连接服务器的端口。 23 | * **userid**:登录时的用户名。 24 | * **password**:登录时用户名对应的密码。 25 | 26 | 用户可以使用指定的域名(或 IP 地址)和端口号把该会话连接到 DolphinDB,并且在建立连接的同时登录账号。 27 | 28 | 使用示例如下: 29 | 30 | ```python 31 | import dolphindb as ddb 32 | s = ddb.session() 33 | 34 | # 连接地址为localhost,端口为8848的DolphinDB 35 | s.connect("localhost", 8848) 36 | 37 | # 连接地址为localhost,端口为8848的DolphinDB,登录用户名为admin,密码为123456的账户 38 | s.connect("localhost", 8848, "admin", "123456") 39 | ``` 40 | 41 | 若 session 过期,或者初始化会话时没有指定登录信息(用户名与密码),可使用 `login` 方法登录 DolphinDB。 42 | 43 | ```python 44 | import dolphindb as ddb 45 | s = ddb.session() 46 | # 连接地址为localhost,端口为8848的DolphinDB,未指定登录信息(用户名与密码) 47 | s.connect("localhost", 8848) 48 | # 使用login函数登录 DolphinDB 49 | s.login("admin","123456") 50 | ``` 51 | 52 | ## 2. 高可用参数 *highAvailability*, *highAvailabilitySites* 53 | 54 | * **highAvailability**:是否开启 API 高可用,默认值为 False。 55 | * **highAvailabilitySites**:所有可用节点的地址和端口,格式为 `ip:port`。 56 | 57 | *highAvailability* 和 *highAvailabilitySites* 都是 API 高可用的相关配置参数。在高可用模式下,Python API 在连接集群节点时会查询负载最小的节点,并与其建立连接。当使用单线程方式有一定延迟地(**注1**)创建多个 session 时,Python API 可以保证所有可用节点上连接的负载均衡;但在使用多线程方式同时创建多个 session 时,由于同时建立连接,每个 session 建立时查询的最小负载节点可能为同一个,不能保证节点的负载均衡。 58 | 59 | 下例中为简化问题使用节点连接数代替节点负载: 60 | 61 | 1. 假如当前三个节点的负载值分别为 \[1, 9, 10],则单线程依次、有延迟地建立20个连接后,每次建立连接都会向服务器查询当前最小负载的节点,因此最后负载值分别为\[14, 13, 13]。 62 | 2. 如果多线程**同时**建立 20 个连接,或单线程快速创建,则同一时刻服务器查询得到的最小负载节点为同一个,最后负载值分别为\[21, 9, 10]。 63 | 64 | 节点负载的计算公式为: 65 | 66 | ```math 67 | load = (connectionNum + workerNum + executorNum)/3.0 68 | ``` 69 | 70 | * connectionNum:连接到本地节点的连接数。 71 | * workerNum:常规作业的工作线程的数量。 72 | * executorNum:本地执行线程的数量。 73 | 74 | 上述变量可在任意节点通过执行 `rpc(getControllerAlias(), getClusterPerf)` 获得,相关函数介绍请参考 [DolphinDB 用户手册-getClusterPerf](https://www.dolphindb.cn/cn/help/200/FunctionsandCommands/FunctionReferences/g/getClusterPerf.html)。 75 | 76 | 若要开启 API 高可用,则需要指定 *highAvailability* 参数为 True,同时通过 *highAvailabilitySites* 指定所有可用节点的地址和端口。 77 | 78 | 示例脚本如下: 79 | 80 | ```python 81 | import dolphindb as ddb 82 | s = ddb.session() 83 | # 创建向量,包含所有可用节点的地址和端口 84 | sites = ["192.168.1.2:24120", "192.168.1.3:24120", "192.168.1.4:24120"] 85 | # 创建连接;开启高可用,并指定sites为所有可用节点的ip:port 86 | s.connect(host="192.168.1.2", port=24120, userid="admin", password="123456", highAvailability=True, highAvailabilitySites=sites) 87 | ``` 88 | 89 | > **注1:** 如果连续建立多个 session,服务端集群间可能尚未同步负载信息,此时查询到的结果可能始终为同一个最小负载节点,无法保证节点的负载均衡。 90 | > 91 | > **注2:** 若开启高可用后不指定 *highAvailabilitySites*,则默认高可用组为集群全部节点。 92 | > 93 | > **注3:** 开启高可用相当于启用自动重连,当连接断开时,如果返回的错误信息为 \ 则继续尝试重连连接建立时的节点;如果返回的错误信息为其他,则尝试重连 highAvailabilitySites 列表中上一次成功连接的节点的下一个节点。 94 | 95 | ## 3. 保活参数 *keepAliveTime* 96 | 97 | * **keepAliveTime**:在 TCP 连接状态下两次保活传输之间的持续时间,默认参数为 60,单位秒(s)。 98 | 99 | 通过配置 *keepAliveTime* 参数可以设置 TCP 的存活检测机制的检测时长,以实现即便在网络不稳定的条件下,仍可及时释放半打开的 TCP 连接。若不指定保活参数 *keepAliveTime*,则默认使用构造 session 时使用的 [*keepAliveTime*(详见 4.保活参数)](./2.1.1_Constructor.md)。指定参数的示例如下: 100 | 101 | ```python 102 | import dolphindb as ddb 103 | s = ddb.session() 104 | # 创建连接;设置保活时间为120秒 105 | s.connect(keepAliveTime=120) 106 | ``` 107 | 108 | ## 4. 重连参数 *reconnect* 109 | 110 | * **reconnect**:在不开启高可用的情况下,是否在 API 检测到连接异常时进行重连,默认值为 False。 111 | 112 | 若开启高可用模式,则 API 在检测到连接异常时将自动进行重连,不需要设置参数 *reconnect*。若未开启高可用,通过配置 `reconnect = True`,即可实现 API 在检测到连接异常时进行重连。使用示例如下: 113 | 114 | ```python 115 | import dolphindb as ddb 116 | s = ddb.session() 117 | # 创建连接;开启重连 118 | s.connect(host="localhost", port=8848, reconnect=True) 119 | ``` 120 | 121 | ## 5. 其他参数 122 | 123 | * **startup**:启动脚本。 124 | 125 | 该参数可以用于执行一些预加载任务。包含加载插件、加载分布式表、定义并加载流数据表等脚本。 126 | 127 | ```python 128 | import dolphindb as ddb 129 | s = ddb.session() 130 | # 创建连接;设置启动脚本 "clearAllCache();" 131 | s.connect(host="localhost", port=8848, startup="clearAllCache();") 132 | ``` 133 | -------------------------------------------------------------------------------- /README_CN_NEW/2_BasicOperations/2.1_Session/2.1.3_OtherParams.md: -------------------------------------------------------------------------------- 1 | # 常用方法 2 | 3 | 本节将介绍 session 中常用的方法。 4 | 5 | ## 1. 执行脚本 6 | 7 | 在 session 中执行脚本可以调用 **session.run** 方法,方法接口如下: 8 | 9 | ```python 10 | run(script, *args, **kwargs) 11 | ``` 12 | 13 | ### 1.1 基础功能参数 14 | 15 | - **script**:待执行脚本或函数名。 16 | 17 | - **args**:待执行函数的参数。 18 | 19 | #### 1.1.1 script:执行脚本 20 | 21 | 单独传入 *script* 且不传入 *args* 不定长位置参数时,表示执行脚本。 22 | 23 | 示例如下: 24 | 25 | ```python 26 | >>> s.run("x = 1;") 27 | ``` 28 | 29 | #### 1.1.2 script + args:执行函数和传参 30 | 31 | 除了运行脚本之外,run 命令可以直接在 DolphinDB 服务器上执行 DolphinDB 内置或用户自定义的函数。对于这种用法,run 方法的第一个参数是 DolphinDB 中的函数名,之后的参数则是该函数的参数。 32 | 33 | 下面的示例展示 Python 程序通过 run 调用 DolphinDB 内置的 [add 函数](https://www.dolphindb.cn/cn/help/FunctionsandCommands/FunctionReferences/a/add.html?highlight=add)。add 函数有 x 和 y 两个参数。根据参数是否已在 DolphinDB 服务端被赋值,有以下三种调用方式: 34 | 35 | **(1) 所有参数均已在 DolphinDB 服务端被赋值** 36 | 37 | 若变量 x 和 y 已经通过 Python 程序在 DolphinDB 服务端被赋值: 38 | 39 | ```python 40 | >>> s.run("x = [1,3,5];y = [2,4,6]") 41 | ``` 42 | 43 | 那么在 Python 端要对这两个向量做加法运算,只需直接使用 `run(script)` 即可: 44 | 45 | ```python 46 | >>> s.run("add(x,y)") 47 | array([3, 7, 11], dtype=int32) 48 | ``` 49 | 50 | **(2) 仅有一个参数 DolphinDB 服务端被赋值** 51 | 52 | 若仅变量 x 已通过 Python 程序在服务器端被赋值: 53 | 54 | ```python 55 | >>> s.run("x = [1,3,5];") 56 | ``` 57 | 58 | 而参数 y 要在调用 add 函数时一并赋值,需要使用 “部分应用” 方式把参数 x 固化在 add 函数内。具体请参考 [DolphinDB用户手册-部分应用](https://www.dolphindb.cn/cn/help/Functionalprogramming/PartialApplication.html)。 59 | 60 | ```python 61 | >>> import numpy as np 62 | >>> y = np.array([1,2,3]) 63 | >>> result = s.run("add{x,}", y) 64 | >>> result 65 | array([2,5,8]) 66 | >>> result.dtype 67 | dtype('int64') 68 | ``` 69 | 70 | **(3) 两个参数都待由 Python 客户端赋值** 71 | 72 | ```python 73 | >>> import numpy as np 74 | >>> x = np.array([1.5,2.5,7]) 75 | >>> y = np.array([8.5,7.5,3]) 76 | >>> result = s.run("add", x, y) 77 | >>> result 78 | array([10., 10., 10.]) 79 | >>> result.dtype 80 | dtype('float64') 81 | ``` 82 | 83 | 通过 run 调用 DolphinDB 的内置函数时,客户端上传参数的数据结构可以是标量 (scalar),列表 (list),字典 (dict),NumPy 的对象,pandas 的 DataFrame 和 Series 等等。 84 | 85 | > **注意:** 86 | > 87 | > 1. NumPy array 的维度不能超过 2。 88 | > 89 | > 2. pandas 的 DataFrame 和 Series 若有 index,在上传到 DolphinDB 以后会丢失。如果需要保留 index 列,则需要使用 pandas 的 DataFrame 函数 reset_index。 90 | > 91 | > 3. 如果 DolphinDB 函数的参数是时间或日期类型,Python 客户端上传时,参数应该先转换为 numpy.datetime64 类型。 92 | 93 | ### 1.2 高级功能参数 94 | 95 | #### *clearMemory* 96 | 97 | 使用 run 方法时,有时为减少内存占用,希望 server 能在执行完毕后自动释放 run 语句中创建的变量。此时可通过指定 session 以及 DBConnectionPool 对象的 run 方法的参数 `clearMemory=True` 来实现。 98 | 99 | ```python 100 | >>> s.run("t=1", clearMemory = True) 101 | >>> s.run("t") 102 | ``` 103 | 104 | 由于 t 在执行完毕后就被清除了,所以执行 `s.run("t")` 会抛出异常: 105 | 106 | ``` 107 | RuntimeError: in run: Server response: 'Syntax Error: [line #1] Cannot recognize the token t' script: 't' 108 | ``` 109 | 110 | #### *pickleTableToList* 111 | 112 | 当 session 构造时指定的 *protocol* 为 PROTOCOL_DDB 或者 PROTOCOL_PICKLE 时,该参数有效。开启该参数后,如果返回值为 Table,则对应 Python 对象为一个 numpy.ndarray 的列表,列表中每一个元素表示原 Table 中的一列。有关数据格式的相关内容,请参考章节 [3.1 类型转换](../../3_AdvancedOperations/3.1_DataTypeCasting/3.1.0_TypeCasting.md)。 113 | 114 | ```python 115 | >>> import dolphindb.settings as keys 116 | >>> s = ddb.session(protocol=keys.PROTOCOL_DDB) 117 | >>> s.connect("localhost", 8848) 118 | True 119 | >>> s.run("table(1..3 as a)") 120 | a 121 | 0 1 122 | 1 2 123 | 2 3 124 | >>> s.run("table(1..3 as a)", pickleTableToList=True) 125 | [array([1, 2, 3], dtype=int32)] 126 | ``` 127 | 128 | #### *fetchSize* 129 | 130 | 对于大数据量的表,API 提供了分段读取方法(仅适用于 DolphinDB 1.20.5 及以上版本,Python API 1.30.0.6 及以上版本) 131 | 132 | 在 Python 客户端执行以下代码创建一个大数据量的表: 133 | 134 | ```python 135 | >>> s = ddb.session() 136 | >>> s.connect("localhost", 8848, "admin", "123456") 137 | True 138 | >>> script = """ 139 | ... rows=100000; 140 | ... testblock=table(take(1,rows) as id,take(`A,rows) as symbol,take(2020.08.01..2020.10.01,rows) as date, rand(50,rows) as size,rand(50.5,rows) as price); 141 | ... """ 142 | >>> s.run(script) 143 | ``` 144 | 145 | 在 run 方法中使用参数 *fetchSize* 指定分段大小 ,会返回一个 BlockReader 对象,可通过 `read()` 方法一段段的读取数据。需要注意的是 *fetchSize* 取值不能小于 8192(记录条数),示例如下: 146 | 147 | ```python 148 | >>> script1 = "select * from testblock" 149 | >>> block= s.run(script1, fetchSize = 8192) 150 | >>> total = 0 151 | >>> while block.hasNext(): 152 | ... tem = block.read() 153 | ... total+=len(tem) 154 | ... 155 | >>> total 156 | 100000 157 | ``` 158 | 159 | 使用上述分段读取的方法时,若数据未读取完毕,需要调用 skipAll 方法来放弃读取后续数据,才能继续执行后续代码。否则会导致套接字缓冲区滞留数据,引发后续数据的反序列化失败。示例代码如下: 160 | 161 | ```python 162 | >>> block= s.run(script1, fetchSize = 8192) 163 | >>> re = block.read() 164 | >>> block.skipAll() 165 | >>> s.run("1+1;") # 若没有调用 skipAll,执行此代码会抛出异常。 166 | 2 167 | ``` 168 | 169 | #### *priority* 170 | 171 | 优先级表示系统赋予作业的优先数,用于决定作业调度的先后顺序。 172 | 173 | 在 DolphinDB 中,作业按照[优先级](https://docs.dolphindb.cn/help/dita/md/job_management_tutorial.html?hl=priority#%E4%BD%9C%E4%B8%9A%E4%BC%98%E5%85%88%E7%BA%A7)进行调度。优先级的取值范围为 0-9,取值越高则表明优先级越高。对于优先级高的作业,系统会优先给与计算资源。基于作业的优先级,DolphinDB 设计了多级反馈队列来调度作业的执行。具体来说,系统共维护了10个队列,分别对应10个优先级。系统总是将线程资源分配给高优先级的作业;当一个高优先级队列为空时,系统才会处理低优先级队列中的作业;对于处于相同优先级的作业,系统会以 round-robin 的方式将线程资源分配给作业。 174 | 175 | Python API 自 1.30.22.2 版本起,session 和 DBConnectionPool 中 run 方法提供 *priority* 参数,其用于指定任务的优先级,默认值为 4。使用示例如下: 176 | 177 | ```python 178 | >>> s.run("1+1", priority=9) 179 | ``` 180 | 181 | #### *parallelism* 182 | 183 | 并行度表示在一个数据节点上,最多同时可以用多少个线程来执行该作业产生的子任务。 184 | 185 | Python API 自 1.30.22.2 版本起,session 和 DBConnectionPool 中 run 方法提供 *parallelism* 参数,其用于指定任务的并行度,默认值为 2。使用示例如下: 186 | 187 | ```python 188 | >>> s.run("1+1", parallelism=16) 189 | ``` 190 | 191 | 可参考 [DolphinDB 关于作业并行度的介绍](https://docs.dolphindb.cn/help/dita/md/job_management_tutorial.html?hl=priority#%E4%BD%9C%E4%B8%9A%E5%B9%B6%E8%A1%8C%E5%BA%A6)。 192 | 193 | ### 1.3 相关方法 194 | 195 | #### runFile 196 | 197 | **runFile** 方法可读取文件所有内容作为脚本执行,可以如 run 方法一样传入不定长位置参数和不定长关键字参数。 198 | 199 | > **注:** 该文件路径为客户端的本地路径。 200 | 201 | 使用示例如下: 202 | 203 | ```python 204 | >>> with open("./test.dos", "w+") as f: 205 | ... f.write(""" 206 | ... t = table(1..3 as a); 207 | ... t; 208 | ... """) 209 | ... 210 | 47 211 | >>> s.runFile("./test.dos") 212 | a 213 | 0 1 214 | 1 2 215 | 2 3 216 | ``` 217 | 218 | ## 2.上传变量 219 | 220 | ```python 221 | upload(nameObjectDict) 222 | ``` 223 | 224 | **upload** 方法用于上传 Python 对象到服务端,接收一个 dict 对象,其中字典的键表示待上传变量的变量名,字典的值则表示待上传的变量,可以是 int, str, pd.DataFrame, np.ndarray, dict 和 set 等。上传成功则返回上传对象在服务端的内存地址。关于如何上传各类型的 Python 对象,以及对应的服务端数据类型,请参考章节 [3.1](../../3_AdvancedOperations/3.1_DataTypeCasting/3.1.0_TypeCasting.md)。 225 | 226 | ```python 227 | >>> s.upload({'a': 8, 'b': "abc", 'c': {'a':1, 'b':2}}) 228 | [59763200, 60161968, 54696752] 229 | >>> s.run("a") 230 | 8 231 | >>> s.run("b") 232 | abc 233 | >>> s.run("c") 234 | {'a': 1, 'b': 2} 235 | ``` 236 | 237 | ## 3. 加载数据 238 | 239 | 本节将用到 [example.csv](../../data/example.csv) 文件。 240 | 241 | ### 3.1 table 242 | 243 | ```python 244 | table(dbPath=None, data=None, tableAliasName=None, inMem=False, partitions=None) 245 | ``` 246 | 247 | - **data**:字符串或字典、DataFrame。如果 *data* 为字符串,表示服务端数据表表名;如果 *data* 为字典或 DataFrame,则表示将本地数据作为临时表上传到服务器。 248 | - **dbPath**:字符串,表示待加载数据表所在的数据库地址。 249 | - **tableAliasName**:用于指定待加载表的别名。 250 | - **inMem**:是否将数据从服务器磁盘加载到服务器内存中。 251 | - **partitions**:表示要加载的分区。 252 | 253 | > **注:** table 函数在 *data* 为字符串时,实际是封装了 DolphinDB 的 loadTable 函数,从指定数据库中加载对应表,并获取其句柄。关于 *inMem* 和 *partitions* 参数的详细含义请参考[DolphinDB用户手册-loadTable](https://www.dolphindb.cn/cn/help/200/FunctionsandCommands/FunctionReferences/l/loadTable.html)。 254 | 255 | 对数据表句柄的操作以及面向对象的 SQL 查询,请参考章节 [3.4 面向对象操作](../../3_AdvancedOperations/3.4_ObjectOrientedOperationsOnDdbOBjects/3.4.2_Table.md)。 256 | 257 | ### 3.2 loadTable 258 | 259 | ```python 260 | loadTable(tableName, dbPath=None, partitions=None, memoryMode=False) 261 | ``` 262 | 263 | loadTable 方法的使用与 table 方法相似,但该方法仅用于加载服务器端指定表,获取其句柄。 264 | 265 | ### 3.3 loadTableBySQL 266 | 267 | ```python 268 | loadTableBySQL(tableName, dbPath, sql) 269 | ``` 270 | 271 | 该方法封装 DolphinDB loadTableBySQL 函数,将满足 SQL 查询中筛选条件的记录加载为内存中的分区表,返回内存表句柄给 API。函数详细说明请参考 [DolphinDB 用户手册-loadTableBySQL](https://www.dolphindb.cn/cn/help/200/FunctionsandCommands/FunctionReferences/l/loadTableBySQL.html)。 272 | 273 | - **tableName/dbPath**:根据 *tableName* 和 *dbPath* 加载 *sql* 中使用到的分区表。 274 | - **sql**:SQL 查询的元代码。它可以用 WHERE 子句来过滤分区或记录行,也可以用 SELECT 语句选择包括计算列在内的列,但不能包含 TOP 子句、GROUP BY 子句、 ORDER BY 子句、CONTEXT BY 子句和 LIMIT 子句。 275 | 276 | ### 3.4 loadText 277 | 278 | ```python 279 | loadText(remoteFilePath, delimiter=",") 280 | ``` 281 | 282 | 可使用 loadText 方法把文本文件导入到服务端的内存表中,文本文件必须和服务端在同一个服务器。该方法会在 Python 中返回一个 DolphinDB 内存表句柄。可使用 toDF 方法把 Python 中的内存表句柄对象 Table 转换为 pandas.DataFrame。 283 | 284 | > **注意:** 使用 loadText 方法时,载入的内存表数据量必须小于 DolphinDB 服务器可用内存。 285 | 286 | ```python 287 | >>> WORK_DIR = "C:/DolphinDB/Data" 288 | >>> trade = s.loadText(WORK_DIR+"/example.csv") 289 | ``` 290 | 291 | 将返回的 DolphinDB 表对象转化为 pandas DataFrame。表的数据传输发生在此步骤。 292 | 293 | ```python 294 | >>> trade.toDF() 295 | TICKER date VOL PRC BID ASK 296 | 0 AMZN 1997.05.16 6029815 23.50000 23.50000 23.6250 297 | 1 AMZN 1997.05.17 1232226 20.75000 20.50000 21.0000 298 | 2 AMZN 1997.05.20 512070 20.50000 20.50000 20.6250 299 | 3 AMZN 1997.05.21 456357 19.62500 19.62500 19.7500 300 | 4 AMZN 1997.05.22 1577414 17.12500 17.12500 17.2500 301 | 5 AMZN 1997.05.23 983855 16.75000 16.62500 16.7500 302 | ... 303 | 13134 NFLX 2016.12.29 3444729 125.33000 125.31000 125.3300 304 | 13135 NFLX 2016.12.30 4455012 123.80000 123.80000 123.8300 305 | ``` 306 | 307 | loadText 函数导入文件时的默认分隔符是','。用户也可指定其他符号作为分隔符。例如,导入'\t'分割的表格形式文本文件: 308 | 309 | ```python 310 | >>> t1 = s.loadText(WORK_DIR+"/t1.tsv", '\t') 311 | ``` 312 | 313 | > **注:** loadText / ploadText / loadTextEx 都是将文件加载到服务端,并非加载本地文件。 314 | 315 | ### 3.5 ploadText 316 | 317 | ploadText 函数可以并行加文本文件到内存分区表中。其加载速度比 loadText 函数快。 318 | 319 | ```python 320 | >>> trade = s.ploadText(WORK_DIR+"/example.csv") 321 | >>> trade.rows 322 | 13136 323 | ``` 324 | 325 | ### 3.6 loadTextEx 326 | 327 | ```python 328 | loadTextEx(dbPath, tableName, partitionColumns=None, remoteFilePath=None, delimiter=",") 329 | ``` 330 | 331 | 可使用函数 loadTextEx 把文本文件导入到分区数据库的分区表中。如果分区表不存在,函数会自动生成该分区表并把数据追加到表中。如果分区表已经存在,则直接把数据追加到分区表中。 332 | 333 | 函数 loadTextEx 的各个参数如下: 334 | 335 | - **dbPath**:数据库路径。 336 | - **tableName**:分区表的名称。 337 | - **partitionColumns**:分区列。 338 | - **remoteFilePath**:文本文件在 DolphinDB 服务器上的绝对路径。 339 | - **delimiter**:文本文件的分隔符(默认分隔符是逗号)。 340 | 341 | 下面的例子使用函数 loadTextEx 创建了分区表 trade,并把 [example.csv](../../data/example.csv) 中的数据加载到表中。 342 | 343 | ```python 344 | >>> import dolphindb.settings as keys 345 | >>> if s.existsDatabase("dfs://valuedb"): 346 | ... s.dropDatabase("dfs://valuedb") 347 | ... 348 | >>> s.database(dbName='mydb', partitionType=keys.VALUE, partitions=["AMZN","NFLX", "NVDA"], dbPath="dfs://valuedb") 349 | >>> trade = s.loadTextEx(dbPath="mydb", tableName='trade',partitionColumns=["TICKER"], remoteFilePath=WORK_DIR + "/example.csv") 350 | >>> trade.toDF() 351 | TICKER date VOL PRC BID ASK 352 | 0 AMZN 1997-05-15 6029815 23.500 23.500 23.625 353 | 1 AMZN 1997-05-16 1232226 20.750 20.500 21.000 354 | 2 AMZN 1997-05-19 512070 20.500 20.500 20.625 355 | 3 AMZN 1997-05-20 456357 19.625 19.625 19.750 356 | 4 AMZN 1997-05-21 1577414 17.125 17.125 17.250 357 | ... ... ... ... ... ... ... 358 | 13131 NVDA 2016-12-23 16193331 109.780 109.770 109.790 359 | 13132 NVDA 2016-12-27 29857132 117.320 117.310 117.320 360 | 13133 NVDA 2016-12-28 57384116 109.250 109.250 109.290 361 | 13134 NVDA 2016-12-29 54384676 111.430 111.260 111.420 362 | 13135 NVDA 2016-12-30 30323259 106.740 106.730 106.750 363 | 364 | [13136 rows x 6 columns] 365 | ``` 366 | 367 | 返回表中的行数: 368 | 369 | ```python 370 | >>> trade.rows 371 | 13136 372 | ``` 373 | 374 | 返回表中的列数: 375 | 376 | ```python 377 | >>> trade.cols 378 | 6 379 | ``` 380 | 381 | 展示表的结构: 382 | 383 | ```python 384 | >>> trade.schema 385 | name typeString typeInt comment 386 | 0 TICKER SYMBOL 17 387 | 1 date DATE 6 388 | 2 VOL INT 4 389 | 3 PRC DOUBLE 16 390 | 4 BID DOUBLE 16 391 | 5 ASK DOUBLE 16 392 | ``` 393 | 394 | ## 4. 数据库表管理 395 | 396 | 特别的,Python API 封装了部分服务端常用数据库表管理函数作为 session 的方法,用户可以调用这些方法对数据库表进行管理。 397 | 398 | ### 4.1 database 399 | 400 | ```python 401 | database(dbName=None, partitionType=None, partitions=None, dbPath=None, engine=None, atomic=None, chunkGranularity=None) 402 | ``` 403 | 404 | 如果需要持久保存导入数据,或者需要导入的文件超过可用内存,可将数据导入 DFS 分区数据库保存。下面将使用一个例子来介绍如何创建分区数据库。 405 | 406 | 本节例子中会使用数据库 valuedb。首先检查该数据库是否存在,如果存在,将其删除: 407 | 408 | ```python 409 | >>> if s.existsDatabase("dfs://valuedb"): 410 | ... s.dropDatabase("dfs://valuedb") 411 | ... 412 | ``` 413 | 414 | 使用 database 方法创建值分区(VALUE)的数据库,使用股票代码作为分区字段。参数 *partitions* 表示分区方案。下例中,先导入 DolphinDB 的关键字,再创建数据库。 415 | 416 | ```python 417 | >>> import dolphindb.settings as keys 418 | >>> s.database(dbName='mydb', partitionType=keys.VALUE, partitions=['AMZN','NFLX', 'NVDA'], dbPath='dfs://valuedb') 419 | ``` 420 | 421 | 上述语句等同于在DolphinDB中执行脚本 `db=database('dfs://valuedb', VALUE, ['AMZN','NFLX','NVDA'])`。 422 | 423 | 除了值分区(VALUE),DolphinDB 还支持哈希分区(HASH)、范围分区(RANGE)、列表分区(LIST)与组合分区(COMPO),具体请参见 [DolphinDB用户手册-database](https://www.dolphindb.cn/cn/help/FunctionsandCommands/FunctionReferences/d/database.html)。 424 | 425 | 创建了分区数据库后,不可更改分区类型,一般也不可更改分区方案,但是创建值分区或范围分区(或者复合分区中的值分区或范围分区)后,DolphinDB 脚本中可以分别使用 addValuePartitions 与 addRangePartitions 函数添加分区。 426 | 427 | database 方法的详细参数以及使用方法,请参考章节 [3.4 面向对象操作](../../3_AdvancedOperations/3.4_ObjectOrientedOperationsOnDdbOBjects/3.4.1_Database.md)。 428 | 429 | ### 4.2 existsDatabase 430 | 431 | ```python 432 | >>> s.existsDatabase(dbUrl="dfs://testDB") 433 | False 434 | ``` 435 | 436 | 该函数用于判断 DolphinDB 服务端是否存在 dbUrl 对应的数据库,函数使用请参考 [DolphinDB 用户手册-existsDatabase](https://www.dolphindb.cn/cn/help/200/FunctionsandCommands/FunctionReferences/e/existsDatabase.html)。 437 | 438 | ### 4.3 existsTable 439 | 440 | ```python 441 | >>> s.existsTable(dbUrl="dfs://valuedb", tableName="trade") 442 | True 443 | ``` 444 | 445 | 检查指定表是否存在于指定数据库中,函数使用请参考 [DolphinDB 用户手册-existsTable](https://www.dolphindb.cn/cn/help/200/FunctionsandCommands/FunctionReferences/e/existsTable.html)。 446 | 447 | ### 4.4 dropDatabase 448 | 449 | ```python 450 | >>> s.dropDatabase(dbPath="dfs://valuedb") 451 | ``` 452 | 453 | 删除指定数据库的所有物理文件,函数使用请参考 [DolphinDB 用户手册-dropDatabase](https://www.dolphindb.cn/cn/help/FunctionsandCommands/CommandsReferences/d/dropDatabase.html?highlight=dropdatabase)。 454 | 455 | ### 4.5 dropPartition 456 | 457 | ```python 458 | >>> s.dropPartition(dbPath="dfs://valuedb", partitionPaths="AMZN", tableName="trade") 459 | ``` 460 | 461 | 删除数据库中指定分区的数据。如果指定了 *tableName*,则删除指定表中符合指定条件的分区数据。否则,删除指定数据库所有表中符合指定条件的分区数据。函数使用请参考 [DolphinDB 用户手册-dropPartition](https://www.dolphindb.cn/cn/help/FunctionsandCommands/CommandsReferences/d/dropPartition.html?highlight=droppartition)。 462 | 463 | > **注意:** 如果创建数据库时指定分区粒度为 DATABASE,则可以不填 *tableName*,否则必须指定表名。 464 | 465 | ### 4.6 dropTable 466 | 467 | ```python 468 | >>> s.dropTable(dbPath="dfs://valuedb", tableName="trade") 469 | ``` 470 | 471 | 删除指定数据库中的数据表,函数使用请参考 [DolphinDB 用户手册-dropTable](https://www.dolphindb.cn/cn/help/FunctionsandCommands/CommandsReferences/d/dropTable.html?highlight=droptable)。 472 | 473 | ## 5. 其他方法 474 | 475 | 除了上述方法,session 中还提供封装了一些其他常用的函数。 476 | 477 | ### 5.1 undef/undefAll 478 | 479 | ```python 480 | >>> s.undef("t1", "VAR") 481 | >>> s.undefAll() 482 | ``` 483 | 484 | undef 方法释放 session 中的指定对象;undefAll 方法释放 session 中的全部对象。undef 支持的对象类型包括:"VAR"(变量)、"SHARED"(共享变量) 与 "DEF"(函数定义)。默认类型为变量 "VAR"。"SHARED" 指内存中跨 session 的共享变量,例如流数据表。 485 | 486 | 假设 session 中有一个 DolphinDB 的表对象 t1,可以通过 `undef("t1","VAR")` 将该表释放掉。释放后,并不一定能够看到内存在服务端马上释放。这与 DolphinDB 的内存管理机制有关。DolphinDB 从操作系统申请的内存,释放后不会立即还给操作系统,因为这些释放的内存在 DolphinDB 中可以立即使用。申请内存首先从 DolphinDB 内部的池中申请内存,不足时才会向操作系统去申请。配置文件 (dolphindb.cfg) 中参数 *maxMemSize* 设置的内存上限会尽量保证。譬如设置为 8GB,那么 DolphinDB 会尽可能利用 8GB 内存。所以若用户需要反复 undef 内存中的一个变量以释放内存,为后续 server 的运行腾出更多内存空间,则需要将 *maxMemSize* 调整到一个合理的数值(不超过内存上限),否则当前内存没有释放,而后面需要的内存超过了系统的最大内存,DolphinDB 的进程就有可能被操作系统杀掉或者出现 `out of memory` 的错误。 487 | 488 | ### 5.2 clearAllCache 489 | 490 | ```python 491 | >>> s.clearAllCache() 492 | >>> s.clearAllCache(dfs=True) 493 | ``` 494 | 495 | clearAllCache 方法会调用服务端同名方法来清理服务端缓存,如果 *dfs* 参数为 True,将会在所有节点上清理缓存;否则只会在连接节点上清理。 496 | 497 | ### 5.3 setTimeout 498 | 499 | 与 session 建立连接时使用的参数 *keepAliveTime* 不同,setTimeout 是 session 的类方法,用于设置 TCP 连接 TCP_USER_TIMEOUT 选项。可以设置用户允许 TCP 连接在没有端到端连接的情况下的生存时间(单位 秒/s)。参考 [Linux Socket options](https://man7.org/linux/man-pages/man7/tcp.7.html)。 500 | 501 | ```python 502 | >>> ddb.session.setTimeout(3600) 503 | ``` 504 | 505 | > **注:** 本方法仅在 Linux 系统生效。默认时间为 30 秒。 506 | 507 | ### 5.4 流订阅相关 508 | 509 | dolphindb 提供流数据订阅接口,可以从服务器订阅流数据表,并获取其数据,详细介绍请参考章节 [2.4 流订阅(基础)](../2.4_Subscription/2.4_Subscription.md)和 [3.3 流订阅(进阶)](../../3_AdvancedOperations/3.3_SubscriptionOptions/3.3_SubscriptionOptions.md)。 510 | 511 | 相关方法:enableStreaming / subscribe / unsubscribe / getSubscriptionTopics 512 | -------------------------------------------------------------------------------- /README_CN_NEW/2_BasicOperations/2.2_DBConnectionPool/2.2.1_Constructor.md: -------------------------------------------------------------------------------- 1 | # 构造 DBConnectionPool 2 | 3 | DBConnectionPool (连接池)可以实现并发执行脚本。由前一章节的内容可知,session(会话控制)可以实现 API 客户端与 DolphinDB 之间的信息交互。Python API 通过 session 在 DolphinDB 上执行脚本和函数,同时实现双向的数据传递。但由于 session 只能调用 `run()` 方法来串行执行脚本,且无法在多线程中使用同一 session 执行脚本。因此,若需要并发地执行脚本,建议使用 DBConnectionPool 以提高任务运行的效率。 4 | 5 | DBConnectionPool 通过创建多个线程以实现并发执行任务。如下展示创建一个 DBConnectionPool 的完整示例: 6 | 7 | ```python 8 | DBConnectionPool(host, port, threadNum=10, userid=None, password=None, 9 | loadBalance=False, highAvailability=False, compress=False, 10 | reConnect=False, python=False, protocol=PROTOCOL_DEFAULT, 11 | show_output=True) 12 | ``` 13 | 14 | 通过调用方法函数 `getSessionId()` 来获取 DBConnectionPool 对象创建的所有线程会话的 session id。若不再使用当前 DBConnectionPool,API 会在析构时自动释放连接。 15 | 16 | 以下内容将对创建 DBConnectionPool 的相关参数进行详细说明。 17 | 18 | ## 1. 连接参数 *host*, *port*, *threadNum*, *userid*, *password* 19 | 20 | * **host**:所连接服务器的地址。 21 | * **port**:所连接服务器的端口。 22 | * **threadNum**:建立连接的数量,默认为10。 23 | * **userid**:登录时的用户名。 24 | * **password**:登录时用户名对应的密码。 25 | 26 | 用户可以使用指定的域名(或 IP 地址)和端口号把 DBConnectionPool 连接到 DolphinDB,并且在建立连接的同时登录账号。使用示例如下: 27 | 28 | ```python 29 | import dolphindb as ddb 30 | 31 | # 连接地址为localhost,端口为8848的DolphinDB,连接数为10 32 | pool = ddb.DBConnectionPool("localhost", 8848) 33 | 34 | # 连接地址为localhost,端口为8848的DolphinDB,登录用户名为admin,密码为123456的账户,连接数为8 35 | pool = ddb.DBConnectionPool("localhost", 8848, 8, "admin", "123456") 36 | ``` 37 | 38 | > **注意:** 39 | > 40 | > * 在构造 DBConnectionPool 时,必须指定参数 *host*, *port*。 41 | 42 | ## 2. 负载均衡参数 *loadBalance* 43 | 44 | * **loadBalance**:连接池负载均衡相关配置参数,默认值为 False。 45 | 46 | 该参数的默认值为 False,表示不开启负载均衡。若要开启负载均衡,则将参数设置为 True。示例脚本如下: 47 | 48 | ```python 49 | import dolphindb as ddb 50 | 51 | # 创建连接池;开启负载均衡 52 | pool = ddb.DBConnectionPool("localhost", 8848, 8, loadBalance=True) 53 | ``` 54 | 55 | > **注意,在负载均衡模式下:** 56 | > 57 | > * 如果开启高可用,则可连接节点为集群中所有数据节点。此时负载均衡参数无效。 58 | > * 如果不开启高可用模式,则 DBConnectionPool 会向所有可连接的数据节点均匀建立连接。例如,集群中有3个节点,当前连接数分别为[5, 12, 13],DBConnectionPool 的连接数为6,则在建立连接后,集群中3个节点的连接数分别为[7, 14, 15],即每个节点均增加 2 个连接数。 59 | 60 | ## 3. 高可用参数 *highAvailability* 61 | 62 | * **highAvailability** :是否在集群所有节点上进行高可用配置,默认值为 False。 63 | 64 | 在高可用模式下,如果不启用负载均衡模式,DBConnectionPool 会和当前集群中负载最小的节点建立连接。但由于 DBConnectionPool 中的连接为同时建立,每个连接计算出的负载值几乎一致,导致所有连接会和同一个节点建立连接,故无法保证节点资源的负载均衡。 65 | 66 | 示例脚本如下: 67 | 68 | ```python 69 | import dolphindb as ddb 70 | 71 | # 创建连接池;开启高可用,使用集群所有节点作为高可用节点 72 | pool = ddb.DBConnectionPool("localhost", 8848, 8, "admin", "123456", highAvailability=True) 73 | ``` 74 | 75 | ## 4. 压缩参数 *compress* 76 | 77 | * **compress**:当前连接是否开启压缩,默认参数为 False。 78 | 79 | 该模式适用于大数据量的写入或查询。压缩数据后再传输,这可以节省网络带宽,但会增加 DolphinDB 和 API 端的计算量。使用示例如下: 80 | 81 | ```python 82 | import dolphindb as ddb 83 | import dolphindb.settings as keys 84 | 85 | # api version >= 1.30.21.1,开启压缩,需指定协议为PROTOCOL_DDB 86 | pool = ddb.DBConnectionPool("localhost", 8848, 8, compress=True, protocol=keys.PROTOCOL_DDB) 87 | 88 | # api version <= 1.30.19.4,开启压缩,默认使用协议为PROTOCOL_DDB,即enablePickle=False 89 | pool = ddb.DBConnectionPool("localhost", 8848, 8, compress=True) 90 | ``` 91 | 92 | > **注意:** 93 | > 94 | > * DolphinDB 自1.30.6版本起支持压缩参数 *compress*。 95 | > * 目前仅在配置协议参数 *protocol* 为 PROTOCOL_DDB 的情况下支持开启压缩。(API version<=1.30.19.4 时,默认协议使用PROTOCOL_DDB,支持开启压缩) 96 | 97 | ## 5. 重连参数 *reconnect* 98 | 99 | * **reconnect**:在不开启高可用的情况下,是否在 API 检测到连接异常时进行重连,默认值为 False。 100 | 101 | 若开启高可用模式,则 API 在检测到连接异常时将自动进行重连,不需要设置参数 *reconnect*。若未开启高可用,通过配置 `reconnect = True`,即可实现 API 在检测到连接异常时进行重连。使用示例如下: 102 | 103 | ```python 104 | import dolphindb as ddb 105 | 106 | # 创建连接池;开启重连 107 | pool = ddb.DBConnectionPool("localhost", 8848, 8, reconnect=True) 108 | ``` 109 | 110 | ## 6. 协议参数 *protocol* 111 | 112 | * **protocol**:API 与 DolphinDB 交互时使用的数据格式协议,默认值为 PROTOCOL_DEFAULT,表示使用 PROTOCOL_PICKLE。 113 | 114 | 目前 DolphinDB 支持三种协议:PROTOCOL_DDB, PROTOCOL_PICKLE, PROTOCOL_ARROW。使用不同的协议,会影响 API 执行 DolphinDB 脚本后接收到的数据格式。有关协议的详细说明请参考章节 [3.1 类型转换](../../3_AdvancedOperations/3.1_DataTypeCasting/3.1.0_TypeCasting.md)。 115 | 116 | ```python 117 | import dolphindb.settings as keys 118 | 119 | # 使用协议 PROTOCOL_DDB 120 | pool = ddb.DBConnectionPool("localhost", 8848, 10, protocol=keys.PROTOCOL_DDB) 121 | 122 | # 使用协议 PROTOCOL_PICKLE 123 | pool = ddb.DBConnectionPool("localhost", 8848, 10, protocol=keys.PROTOCOL_PICKLE) 124 | 125 | # 使用协议 PROTOCOL_ARROW 126 | pool = ddb.DBConnectionPool("localhost", 8848, 10, protocol=keys.PROTOCOL_ARROW) 127 | ``` 128 | 129 | > **注意:** 在 1.30.21.1 版本及之后,API 支持使用 *protocol* 来指定数据格式协议。1.30.19.4 版本及之前,默认 API 内部使用 PROTOCOL_DDB,即 `enablePickle=False`。 130 | 131 | ## 7. 其他参数 *show_output* 132 | 133 | * **show_output**:是否在执行后打印脚本中 print 语句的输出。默认值为 True,表示打印 print 语句输出。 134 | 135 | 使用示例如下: 136 | 137 | ```python 138 | # 启用 show_output 139 | pool = ddb.DBConnectionPool("localhost", 8848, 8, show_output=True) 140 | taskid = 12 141 | pool.addTask("print(1);2", taskId=taskid) 142 | while True: 143 | if pool.isFinished(taskId=taskid): 144 | break 145 | time.sleep(0.01) 146 | 147 | res = pool.getData(taskId=taskid) 148 | print(res) 149 | 150 | # output: 151 | 1 152 | 2 153 | 154 | 155 | # 不启用 show_output 156 | pool = ddb.DBConnectionPool("localhost", 8848, 8, show_output=False) 157 | taskid = 12 158 | pool.addTask("print(1);2", taskId=taskid) 159 | while True: 160 | if pool.isFinished(taskId=taskid): 161 | break 162 | time.sleep(0.01) 163 | 164 | res = pool.getData(taskId=taskid) 165 | print(res) 166 | 167 | # output: 168 | 2 169 | ``` 170 | 171 | ## 8. 其他参数 *python* 172 | 173 | * **python**:是否启用 python parser 特性。 174 | 175 | 指定该参数后,可以在 `DBConnectionPool.run` 执行脚本时启用 python parser 特性.使用示例如下: 176 | 177 | ```python 178 | import dolphindb as ddb 179 | 180 | # 启用 python parser 特性 181 | pool = ddb.DBConnectionPool("localhost", 8848, 10, python=True) 182 | ``` 183 | 184 | > **注意:** 仅支持 DolphinDB 3.00 版本。(暂未发布,敬请期待) 185 | -------------------------------------------------------------------------------- /README_CN_NEW/2_BasicOperations/2.2_DBConnectionPool/2.2.2_AsyncMethodsAndOthers.md: -------------------------------------------------------------------------------- 1 | # 方法介绍 2 | 3 | 本节将介绍连接池 DBConnectionPool 的三类常用方法,该三类方法兼可用于异步执行脚本,用户可根据实际需求选用不同的方法。 4 | 5 | * run 是一个协程函数,其内部维护递增的 taskId,可以使用协程方式进行调用。 6 | * addTask,isFinished,getData 方法是将脚本任务提交给 DBConnectionPool,由 DBConnectionpool 直接维护任务的异步进行和获得返回值,在使用时需要用户自行指定 taskId。 7 | * runTaskAsync 是在 DBConnectionPool 的内部维护一个事件循环,被调用后将使用 run 方法在该内部事件循环中执行脚本任务,并返回一个 `concurrent.futures.Future` 对象。 8 | 9 | > **注:** 由于第一种和第三种方法的 taskId 由系统自动生成,而第二种方法的 taskId 由用户自行指定,故为了避免 taskId 冲突,建议用户在实际使用中不要将第二种方法和第一/三种方法混用。 10 | 11 | ## 1. run 12 | 13 | ```python 14 | run(script, *args, **kwargs) 15 | ``` 16 | 17 | * **script**:待执行的 DolphinDB 脚本。 18 | * **\*args**:传递给 DolphinDB 函数的参数。 19 | * **\*\*kwargs**: 20 | * **clearMemory**:是否在查询后释放变量。默认值为 True,表示释放。 21 | * **pickleTableToList**:是否将结果中的 Table 类型对象转换为 list 类型对象。True 表示转换为 list 类型对象,False 表示转换为 DataFrame 类型对象,该参数默认值为 False。 22 | 23 | 为了提高效率,DBConnectionPool 中的 run 方法被包装成了协程函数,通过 run 方法将脚本传入线程池中调用线程运行,因此在 Python 中调用 run 方法时需要使用协程以进行使用。 24 | 25 | ### 示例 1 26 | 27 | 以下内容介绍一个简单的固定任务示例。 28 | 29 | 首先,创建一个最大连接数为8的连接池 DBConnectionPool。和通常连接池有所不同,当不再使用连接时,API 不会立刻销毁该连接,而是直到析构 DBConnectionPool 时才进行销毁,或者手动执行 `shutDown()` 关闭 DBConnectionPool 时才会销毁连接。 30 | 31 | ```python 32 | import dolphindb as ddb 33 | import time 34 | import asyncio 35 | 36 | pool = ddb.DBConnectionPool("localhost", 8848, 8) 37 | ``` 38 | 39 | 创建一个协程任务函数,使用 `sleep` 模拟一段运行的时间。 40 | 41 | ```python 42 | async def test_run(i): 43 | try: 44 | return await pool.run(f"sleep(2000);1+{i}") 45 | except Exception as e: 46 | print(e) 47 | ``` 48 | 49 | 定义任务列表,并创建一个事件循环对象,运行任务列表直到完成全部任务。 50 | 51 | ```python 52 | tasks = [ 53 | asyncio.ensure_future(test_run(1)), 54 | asyncio.ensure_future(test_run(3)), 55 | asyncio.ensure_future(test_run(5)), 56 | asyncio.ensure_future(test_run(7)), 57 | ] 58 | 59 | loop = asyncio.get_event_loop() 60 | try: 61 | time_st = time.time() 62 | loop.run_until_complete(asyncio.wait(tasks)) 63 | time_ed = time.time() 64 | except Exception as e: 65 | print("catch e:") 66 | print(e) 67 | ``` 68 | 69 | 任务结束后,打印执行时间和各个任务的结果,并关闭连接池对象。 70 | 71 | ```python 72 | print("time: ", time_ed-time_st) 73 | 74 | for task in tasks: 75 | print(task.result()) 76 | 77 | pool.shutDown() 78 | ``` 79 | 80 | 期望的输出结果如下所示: 81 | 82 | ``` 83 | time: 2.0017542839050293 84 | 2 85 | 4 86 | 6 87 | 8 88 | ``` 89 | 90 | 上述例子展示了已固定脚本任务调用 DBConnectionPool 的用法,在 Python 中只有一个主线程,但使用了协程创建子任务并调用 DBConnectionPool 以实现运行。须注意,实际上 Python API 的底层实现是通过使用 C++ 线程以维护每一个连接。若提交任务数超出实际线程数,则可能出现任务迟迟没有执行的情况,与通常的协程并发有一定区别。 91 | 92 | 此外,用户也可以自定义传入脚本的对象,可参考下述示例2。 93 | 94 | ### 示例 2 95 | 96 | 下例定义了一个可以传入自定义脚本作为参数的类,并配合 Python 的多线程机制动态添加子任务。 97 | 98 | ```python 99 | import dolphindb as ddb 100 | import time 101 | import asyncio 102 | import threading 103 | 104 | # 在该例子中主线程负责创建协程对象传入自定义脚本并调用自定义的对象去运行,并新起子线程运行事件循环防止阻塞主线程。 105 | class DolphinDBHelper(object): 106 | pool = ddb.DBConnectionPool("localhost", 8848, 10) 107 | @classmethod 108 | async def test_run(cls,script): 109 | print(f"run script: [{script}]") 110 | return await cls.pool.run(script) 111 | 112 | @classmethod 113 | async def runTest(cls,script): 114 | start = time.time() 115 | task = loop.create_task(cls.test_run(script)) 116 | result = await asyncio.gather(task) 117 | print(f"""[{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}] time: {time.time()-start} result: {result}""") 118 | return result 119 | 120 | #定义一个跑事件循环的线程函数 121 | def start_thread_loop(loop): 122 | asyncio.set_event_loop(loop) 123 | loop.run_forever() 124 | 125 | if __name__=="__main__": 126 | start = time.time() 127 | print("In main thread",threading.current_thread()) 128 | loop = asyncio.get_event_loop() 129 | # 在子线程中运行事件循环, 让它 run_forever 130 | t = threading.Thread(target= start_thread_loop, args=(loop,)) 131 | t.start() 132 | task1 = asyncio.run_coroutine_threadsafe(DolphinDBHelper.runTest("sleep(1000);1+1"),loop) 133 | task2 = asyncio.run_coroutine_threadsafe(DolphinDBHelper.runTest("sleep(3000);1+2"),loop) 134 | task3 = asyncio.run_coroutine_threadsafe(DolphinDBHelper.runTest("sleep(5000);1+3"),loop) 135 | task4 = asyncio.run_coroutine_threadsafe(DolphinDBHelper.runTest("sleep(1000);1+4"),loop) 136 | 137 | end = time.time() 138 | print("main thread time: ", end - start) 139 | ``` 140 | 141 | 运行结果如下: 142 | 143 | ``` 144 | In main thread <_MainThread(MainThread, started 139838803788160)> 145 | main thread time: 0.00039839744567871094 146 | run script: [sleep(1000);1+1] 147 | run script: [sleep(3000);1+2] 148 | run script: [sleep(5000);1+3] 149 | run script: [sleep(1000);1+4] 150 | [2023-03-14 16:46:56] time: 1.0044968128204346 result: [2] 151 | [2023-03-14 16:46:56] time: 1.0042989253997803 result: [5] 152 | [2023-03-14 16:46:58] time: 3.0064148902893066 result: [3] 153 | [2023-03-14 16:47:00] time: 5.005709409713745 result: [4] 154 | ``` 155 | 156 | 上述例子中,在主线程中创建子线程开启事件循环,并指定该事件循环一直保持运行。随后向该事件循环中加入四个脚本执行任务,每个任务分别需要耗时 1s、3s、5s和1s。从主线程打印 `main thread time: 0.00039839744567871094` 可以看出,四个事件放入事件循环后实现了异步执行,随后每个协程都打印自身执行的结束时间和时长。由于任务1和任务4耗时一致,因此同时打印结果;2s后任务2执行结束;再过2s后任务3也执行结束。由结果可知,四个任务的执行结果符合并发执行的预期。 157 | 158 | ## 2. addTask, isFinished, getData 159 | 160 | 不同于协程函数 run,addTask 会将用户脚本任务按照 taskId 直接提交给 DBConnectionPool 执行。用户可以通过 `isFinished` 判断线程池中的任务是否结束,并使用 `getData` 获取任务的返回结果。下述内容将依次介绍三个函数。 161 | 162 | ### 2.1 addTask 163 | 164 | ```python 165 | addTask(script, taskId, *args, **kwargs) 166 | ``` 167 | 168 | * **script**:待执行的 DolphinDB 脚本。 169 | * **taskId**:指定的任务 Id。 170 | * **\*args**:传递给 DolphinDB 函数的参数。 171 | * **\*\*kwargs**: 172 | * **clearMemory**:是否查询后释放变量。True 表示释放,默认值为 True。 173 | * **pickleTableToList**:是否将结果的 Table 类型对象转换为 list 类型对象。True 表示转换为 list 类型对象,False 表示转换为 DataFrame 类型对象,默认值为 False。 174 | 175 | addTask 根据 taskId 将任务提交至 DBConnectionPool 的连接池中,由连接池分配连接执行脚本任务。如下所示,调用 `addTask` 向连接池中添加一个 taskId 为 12 的任务。 176 | 177 | ```python 178 | pool.addTask("sleep(1000);1+2", taskId=12) 179 | ``` 180 | 181 | ### 2.2 isFinished 182 | 183 | ```python 184 | isFinished(taskId) 185 | ``` 186 | 187 | * **taskId**:查询的任务 Id。 188 | 189 | 该函数通过 taskId 来查询对应任务是否已经完成。如果已完成,则返回 True;反之返回 False。 190 | 191 | 简单使用示例如下: 192 | 193 | ```python 194 | if pool.isFinished(taskId=12): 195 | print("task has done!") 196 | ``` 197 | 198 | ### 2.3 getData 199 | 200 | ```python 201 | getData(taskId) 202 | ``` 203 | 204 | * **taskId**:查询的任务 Id。 205 | 206 | 该函数通过 taskId 来查询对应任务的返回结果。 207 | 208 | 简单使用示例如下: 209 | 210 | ```python 211 | res = pool.getData(taskId=12) 212 | ``` 213 | 214 | > **注意:** 每次执行 `addTask` 指定 taskId 并创建任务后,使用 `getData` 方法只能对该 taskID 对应任务的返回结果执行一次查询。若在创建任务后未调用 `getData` 方法,则在下次使用 `addTask` 指定同一 taskId 并创建任务时,其执行结果将覆盖掉前一次该 taskId 对应任务的执行结果。 215 | 216 | ### 2.4 综合示例 217 | 218 | 在如下脚本中,首先创建一个 DBConnectionPool 连接池对象,然后调用 `addTask` 向连接池中添加一个 taskId 为12的任务,随后通过 `isFinished` 方法判断任务是否执行完毕,执行完毕后跳出循环,调用 `getData` 方法获取任务结果。 219 | 220 | ```python 221 | import dolphindb as ddb 222 | import time 223 | 224 | pool = ddb.DBConnectionPool("localhost", 8848, 8) 225 | taskid = 12 226 | pool.addTask("sleep(1500);1+2", taskId=taskid) 227 | while True: 228 | if pool.isFinished(taskId=taskid): 229 | break 230 | time.sleep(0.01) 231 | 232 | res = pool.getData(taskId=taskid) 233 | print(res) 234 | 235 | # output: 236 | 3 237 | ``` 238 | 239 | ## 3. runTaskAsync 240 | 241 | ```python 242 | runTaskAsync(script, *args, **kwargs) 243 | ``` 244 | 245 | * **script**:待执行的 DolphinDB 脚本。 246 | * **args**:传递给 DolphinDB 函数的参数。 247 | * **kwargs**: 248 | * **clearMemory**:是否查询后释放变量。True 表示释放,默认值为 True。 249 | * **pickleTableToList**:是否将结果中的 Table 类型对象转换为 list 类型对象。True 表示转换为 list 类型对象,False 表示转换为 DataFrame 类型对象,默认值为 False。 250 | 251 | > **注1:** 在1.30.17.4及以前的版本中,该函数的名称为 runTaskAsyn。 252 | > 253 | > **注2:** 若使用该方法异步执行脚本,在任务结束后,用户需要手动调用 `pool.shutDown()` 才能正确析构连接池对象。 254 | 255 | 除了使用 run 和 addTask 的方法来执行脚本,DBConnectionPool 还提供了 runTaskAsync 的方法以实现异步执行脚本。 256 | 257 | 用户可以调用 runTaskAsync 方法向连接池中添加任务,返回一个 `concurrent.futures.Future` 对象。然后调用这个对象的 `result(timeout=None)` 方法获得结果(*timeout*,单位为秒)。如果在 `result()` 方法中设置了 *timeout* 参数,任务还未完成,则继续等待 *timeout* 时间;在 *timeout* 时间内若任务完成,则将返回结果,否则将抛出 timeoutError 异常。下面演示如何使用 runTaskAsync 创建异步任务。 258 | 259 | ```python 260 | import dolphindb as ddb 261 | import time 262 | pool = ddb.DBConnectionPool("localhost", 8848, 10) 263 | 264 | t1 = time.time() 265 | task1 = pool.runTaskAsync("sleep(1000); 1+0;") 266 | task2 = pool.runTaskAsync("sleep(2000); 1+1;") 267 | task3 = pool.runTaskAsync("sleep(4000); 1+2;") 268 | task4 = pool.runTaskAsync("sleep(1000); 1+3;") 269 | t2 = time.time() 270 | print("Task1 Result: ", task1.result()) 271 | t3 = time.time() 272 | print("Task2 Result: ", task2.result()) 273 | t4 = time.time() 274 | print("Task4 Result: ", task4.result()) 275 | t5 = time.time() 276 | print("Task3 Result: ", task3.result()) 277 | t6 = time.time() 278 | 279 | print("Add Tasks: ", t2-t1) 280 | print("Get Task1: ", t3-t1) 281 | print("Get Task2: ", t4-t1) 282 | print("Get Task4: ", t5-t1) 283 | print("Get Task3: ", t6-t1) 284 | pool.shutDown() 285 | ``` 286 | 287 | 输出结果如下: 288 | 289 | ``` 290 | Task1 Result: 1 291 | Task2 Result: 2 292 | Task4 Result: 4 293 | Task3 Result: 3 294 | Add Tasks: 0.0015881061553955078 295 | Get Task1: 1.0128183364868164 296 | Get Task2: 2.0117716789245605 297 | Get Task4: 2.0118134021759033 298 | Get Task3: 4.012163162231445 299 | ``` 300 | 301 | 如上示例中,首先创建一个 DBConnectionPool 连接池对象,调用 `runTaskAsync` 方法执行 4 个耗时不同的脚本,并分别返回 4 个 `concurrent.futures.Future` 对象。再调用其 `result` 方法依次阻塞地获得各个任务的返回值,并将耗时打印出来。如下为打印说明: 302 | 303 | * t2 - t1:表示添加任务所耗时间。 304 | * t3 - t1:表示获取task1结果的总耗时。 305 | * t4 - t1:表示获取task2结果的总耗时。 306 | * t5 - t1:表示获取task4结果的总耗时。 307 | * t6 - t1:表示获取task3结果的总耗时。 308 | 309 | Task1 执行耗时 1s,因此 t3-t1=1s;Task2 执行耗时 2s,因此 t4-t1=2s;Task4 执行耗时 1s,在等待获取 Task2 结果的时候,Task4 任务已经执行结束,因此 t5-t1=2s;Task3 执行耗时 4s,因此 t6-t1=4s。 310 | 311 | ## 4. 其他方法 312 | 313 | ### 4.1 shutDown 314 | 315 | ```python 316 | pool.shutDown() 317 | ``` 318 | 319 | 该方法用于关闭不再使用的 DBConnectionPool,停止线程池中使用的事件循环,并且中止所有的异步任务。调用 shutDown 方法后,关闭的线程池不可继续使用。 320 | 321 | > **注意:** 如果使用了 `runTaskAsync()` 的方式创建异步任务,必须在不使用 DBConnectionPool 时调用该函数。 322 | 323 | ### 4.2 getSessionId 324 | 325 | ```python 326 | sessionids = pool.getSessionId() 327 | ``` 328 | 329 | 该方法用于获得当前线程池中所有 session 会话中的 session Id。 330 | -------------------------------------------------------------------------------- /README_CN_NEW/2_BasicOperations/2.3_AutoFitTableAppender/2.3.1_TableAppender.md: -------------------------------------------------------------------------------- 1 | # tableAppender 2 | 3 | 由于 Python 与 DolphinDB 的数据类型并不是一一对应的,故部分类型数据无法直接进行上传、写入等操作。举例来说,Python 中的 DataFrame 对象中仅支持 datetime64[ns] 时间类型,若直接从 API 上传 DataFrame 数据到 DolphinDB,所有的时间类型列均将转换为 NANOTIMESTAMP 类型。并且每次向内存表或分布式表追加一个带有时间类型列的 DataFrame 时,用户需要专门对时间列进行类型转换,该类情况影响了用户的使用体验。 4 | 5 | 针对以上情况,Python API 提供了 tableAppender 对象,在通过 append 方法向内存表或者分布式表中添加本地 DataFrame 数据时,能够实现对部分类型进行自动转换,用户无须再进行额外的手动转换。 6 | 7 | > **注1:** 1.30.19.4及之前版本的 API 仅支持使用 tableAppender 对时间类型数据进行自动转换;1.30.21.1及之后版本的 API 支持使用 tableAppender 对所有类型数据的自动转换。 8 | > 9 | > **注2:** 1.30.16、2.00.4及之后版本的 DolphinDB 支持使用 tableInsert 对时间类型数据进行自动转换。 10 | > 11 | > **注3:** 1.30.22.1及之后版本的 API,调整 tableAppender 类名为 TableAppender,兼容原有 tableAppender。 12 | 13 | ## 1. 接口说明 14 | 15 | ```python 16 | tableAppender(dbPath=None, tableName=None, ddbSession=None, action="fitColumnType") 17 | ``` 18 | 19 | * **dbPath**: 分布式数据库的地址。若待写入表为内存表,可以不指定该参数。 20 | * **tableName**: 分布式表或内存表的表名。 21 | * **ddbSession**: 已连接 DolphinDB 的 session 对象。 22 | * **action**:指定 append 表时的行为。目前仅支持 fitColumnType,表示对列类型进行转换。 23 | 24 | tableAppender 类仅支持 append 方法,接口如下: 25 | 26 | ```python 27 | append(table) 28 | ``` 29 | 30 | * **table**:待写入数据,通常为 pandas.DataFrame 类型的本地数据。 31 | 32 | 该方法返回一个整数,表示追加的记录条数。 33 | 34 | ## 2. 示例 35 | 36 | 下例创建了一个共享表 t,通过 tableAppender 向表 t 中添加数据。 37 | 38 | ```python 39 | import pandas as pd 40 | import dolphindb as ddb 41 | import numpy as np 42 | s = ddb.session() 43 | s.connect("localhost", 8848, "admin", "123456") 44 | 45 | 46 | s.run("share table(1000:0, `sym`timestamp`qty, [SYMBOL, TIMESTAMP, INT]) as t") 47 | appender = ddb.tableAppender(tableName="t", ddbSession=s) 48 | data = pd.DataFrame({ 49 | 'sym': ['A1', 'A2', 'A3', 'A4', 'A5'], 50 | 'timestamp': np.array(['2012-06-13 13:30:10.008', 'NaT','2012-06-13 13:30:10.008', '2012-06-13 15:30:10.008', 'NaT'], dtype="datetime64[ms]"), 51 | 'qty': np.arange(1, 6).astype("int32"), 52 | }) 53 | num = appender.append(data) 54 | print("append rows: ", num) 55 | t = s.run("t") 56 | print(t) 57 | schema = s.run("schema(t)") 58 | print(schema["colDefs"]) 59 | ``` 60 | 61 | 输出结果如下: 62 | 63 | ``` 64 | append rows: 5 65 | sym timestamp qty 66 | 0 A1 2012-06-13 13:30:10.008 1 67 | 1 A2 NaT 2 68 | 2 A3 2012-06-13 13:30:10.008 3 69 | 3 A4 2012-06-13 15:30:10.008 4 70 | 4 A5 NaT 5 71 | name typeString typeInt extra comment 72 | 0 sym SYMBOL 17 NaN 73 | 1 timestamp TIMESTAMP 12 NaN 74 | 2 qty INT 4 NaN 75 | ``` 76 | 77 | 共享表 t 中的 timestamp 列被定义为 TIMESTAMP 类型,但是在 API 端写入的 pd.DataFrame 对象中该列的数据类型对应为 datetime64[ns]。由上述结果可知,通过 tableAppender 将表 t 从 API 上传至 DolphinDB 后,该列的数据类型将自动转换为TIMESTAMP。 78 | 79 | ## 3. 常见问题 80 | 81 | ### 3.1 自动转换的类型 82 | 83 | 1.30.16、2.00.4 之后版本的 DolphinDB 支持向内存表写入数据时**自动转换时间类型**,因此用户可以不使用本文介绍的 tableAppender 方法,而是使用 run 方法执行 `tableInsert` 将上传的 DataFrame 本地时间类型数据插入到指定表中。 84 | 85 | 此外,由于在 Python API 中 DolphinDB 的 UUID、INT128、IPADDR、BLOB 等类型对应 Python 的 str 类型,故无法直接上传这些类型的数据,进而无法插入到指定表中。在 1.30.19.4 及之后版本的 Python API 中,tableAppender 对象支持**自动识别指定表的类型**,如果上传数据中包含 UUID 等特殊类型,tableAppender 对象将自动识别 str 类型,并将其转换为表中对应的类型。写入数据时的类型转换相关内容,请参考章节 [3.1 类型转换](../../3_AdvancedOperations/3.1_DataTypeCasting/3.1.0_TypeCasting.md)。 86 | 87 | 简单示例如下: 88 | 89 | ```python 90 | import dolphindb as ddb 91 | import dolphindb.settings as keys 92 | import numpy as np 93 | import pandas as pd 94 | 95 | s = ddb.session(protocol=keys.PROTOCOL_DDB) 96 | s.connect("192.168.1.113", 8848, "admin", "123456") 97 | 98 | s.run("share table(1000:0, `sym`uuid`int128`ipaddr`blob, [SYMBOL, UUID, INT128, IPADDR, BLOB]) as t") 99 | appender = ddb.tableAppender(tableName="t", ddbSession=s) 100 | data = pd.DataFrame({ 101 | 'sym': ["A1", "A2", "A3"], 102 | 'uuid': ["5d212a78-cc48-e3b1-4235-b4d91473ee87", "b93b8253-8d5e-c609-260a-86522b99864e", ""], 103 | 'int128': [None, "073dc3bc505dd1643d11a4ac4271d2f2", "e60c84f21b6149959bcf0bd6b509ff6a"], 104 | 'ipaddr': ["2c24:d056:2f77:62c0:c48d:6782:e50:6ad2", "", "192.168.1.0"], 105 | 'blob': ["testBLOB1", "testBLOB2", "testBLOB3"], 106 | }) 107 | 108 | appender.append(data) 109 | 110 | t = s.run("t") 111 | print(t) 112 | ``` 113 | 114 | > **注:** 上例中,由于要下载并展示 BLOB 类型的数据,而 session 默认的数据传输协议 PROTOCOL_PICKLE 并不支持 BLOB 类型数据,故须指定其数据传输协议为 PROTOCOL_DDB。 115 | 116 | 输出结果如下: 117 | 118 | ``` 119 | sym uuid int128 ipaddr blob 120 | 0 A1 5d212a78-cc48-e3b1-4235-b4d91473ee87 00000000000000000000000000000000 2c24:d056:2f77:62c0:c48d:6782:e50:6ad2 testBLOB1 121 | 1 A2 b93b8253-8d5e-c609-260a-86522b99864e 073dc3bc505dd1643d11a4ac4271d2f2 0.0.0.0 testBLOB2 122 | 2 A3 00000000-0000-0000-0000-000000000000 e60c84f21b6149959bcf0bd6b509ff6a 192.168.1.0 testBLOB3 123 | ``` 124 | 125 | ### 3.2 Pandas 警告 126 | 127 | 在 1.30.19.4 及之后版本的 Python API 中,用户在使用 tableAppender 类的 append 方法写入数据时,可能会收到如下警告: 128 | 129 | ``` 130 | UserWarning: Pandas doesn't allow columns to be created via a new attribute name - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute-access 131 | ``` 132 | 133 | 该警告并不会对程序执行造成任何影响,如需屏蔽,可以使用如下方法: 134 | 135 | ```python 136 | import warnings 137 | warnings.filterwarnings("ignore","Pandas doesn't allow columns to be created via a new attribute name - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute-access", UserWarning) 138 | ``` 139 | -------------------------------------------------------------------------------- /README_CN_NEW/2_BasicOperations/2.3_AutoFitTableAppender/2.3.2_TableUpserter.md: -------------------------------------------------------------------------------- 1 | # tableUpsert 2 | 3 | Python API 提供 tableUpsert 对象,可以通过 upsert 方式向索引内存表、键值内存表以及分布式表中追加数据。与 tableAppender 对象类似,使用 tableUpsert 对象向表中添加本地的 DataFrame 数据,能够对时间类型进行自动转换,用户无须再进行额外的手动转换。 4 | 5 | > **注:** 1.30.19.4 及之后版本的 API 同时支持使用 tableUpsert 进行 UUID 等特殊类型的自动转换。 6 | 7 | ## 接口说明 8 | 9 | ```python 10 | tableUpsert(dbPath=None, tableName=None, ddbSession=None, ignoreNull=False, keyColNames=[], sortColumns=[]) 11 | ``` 12 | 13 | * **dbPath**: 分布式数据库地址。若待写入表为内存表,可以不指定该参数。 14 | * **tableName**: 分布式表或索引内存表、键值内存表的表名。 15 | * **ddbSession**: 已连接 DolphinDB 的 session 对象。 16 | * **ignoreNull**:布尔值。若追加的新数据中某元素为 NULL 值,是否对目标表中的相应数据进行更新。默认值为 False。 17 | * **keyColNames**:字符串列表。由于 DFS 表不包含键值列,在更新 DFS 表时,会将该参数指定的列视为键值列。 18 | * **sortColumns**:字符串列表。设置该参数后,更新的分区内的所有数据会根据指定的列进行排序。排序在每个分区内部进行,不会跨分区排序。 19 | 20 | 该类仅支持 upsert 方法,接口如下: 21 | 22 | ```python 23 | upsert(table) 24 | ``` 25 | 26 | * table:待写入数据,通常为 pandas.DataFrame 类型的本地数据。 27 | 28 | ## 示例 1 29 | 30 | 下例创建了一个以 id 列为 key 的共享键值内存表 keyed_t,然后构造 tableAppender 对象,调用其 upsert 方法向表 keyed_t 中添加数据。在构造的数据中,id 列在 0-9 之间循环,text 列则不断递增。最后查询写入的数据,仅保留每个 id 下最后一条写入的数据。 31 | 32 | ```python 33 | import dolphindb as ddb 34 | import dolphindb.settings as keys 35 | import numpy as np 36 | import pandas as pd 37 | s = ddb.session() 38 | s.connect("localhost", 8848, "admin", "123456") 39 | 40 | 41 | script_KEYEDTABLE = """ 42 | testtable=keyedTable(`id,1000:0,`date`text`id,[DATETIME,STRING,LONG]) 43 | share testtable as keyed_t 44 | """ 45 | s.run(script_KEYEDTABLE) 46 | upserter = ddb.tableUpsert(tableName="keyed_t", ddbSession=s) 47 | dates=[] 48 | texts=[] 49 | ids=[] 50 | for i in range(1000): 51 | dates.append(np.datetime64('2012-06-13 13:30:10.008')) 52 | texts.append(f"test_i_{i}") 53 | ids.append(i%10) 54 | df = pd.DataFrame({ 55 | 'date': dates, 56 | 'text': texts, 57 | 'id': ids, 58 | }) 59 | upserter.upsert(df) 60 | keyed_t = s.run("keyed_t") 61 | print(keyed_t) 62 | ``` 63 | 64 | 输出结果符合预期: 65 | 66 | ``` 67 | date text id 68 | 0 2012-06-13 13:30:10 test_i_990 0 69 | 1 2012-06-13 13:30:10 test_i_991 1 70 | 2 2012-06-13 13:30:10 test_i_992 2 71 | 3 2012-06-13 13:30:10 test_i_993 3 72 | 4 2012-06-13 13:30:10 test_i_994 4 73 | 5 2012-06-13 13:30:10 test_i_995 5 74 | 6 2012-06-13 13:30:10 test_i_996 6 75 | 7 2012-06-13 13:30:10 test_i_997 7 76 | 8 2012-06-13 13:30:10 test_i_998 8 77 | 9 2012-06-13 13:30:10 test_i_999 9 78 | ``` 79 | 80 | ## 示例 2 81 | 82 | 若要写入没有键值列的分区表或者内存表,则需要在构造 tableUpsert 时指定键值列。 83 | 84 | 下例中,首先定义一个 VALUE 分区方式的分区表 p_table,然后构造 tableUpsert 对象并指定 id 为键值列,调用其 upsert 方法向表 p_table 中添加数据。最后查询写入的数据。 85 | 86 | ```python 87 | import dolphindb as ddb 88 | import dolphindb.settings as keys 89 | import numpy as np 90 | import pandas as pd 91 | import datetime 92 | 93 | s = ddb.session() 94 | s.connect("localhost", 8848, "admin", "123456") 95 | script_DFS_VALUE = """ 96 | if(existsDatabase("dfs://valuedb")){ 97 | dropDatabase("dfs://valuedb") 98 | } 99 | db = database("dfs://valuedb", VALUE, 0..9) 100 | t = table(1000:0, `date`text`id`flag, [DATETIME, STRING, LONG, INT]) 101 | p_table = db.createPartitionedTable(t, `pt, `flag) 102 | """ 103 | s.run(script_DFS_VALUE) 104 | upserter = ddb.tableUpsert(dbPath="dfs://valuedb", tableName="pt", ddbSession=s, keyColNames=["id"]) 105 | 106 | for i in range(10): 107 | dates = [np.datetime64(datetime.datetime.now()) for _ in range(100)] 108 | texts = [f"test_{i}_{_}" for _ in range(100)] 109 | ids = np.array([ _ % 10 for _ in range(100)], dtype="int32") 110 | flags = [ _ % 10 for _ in range(100)] 111 | df = pd.DataFrame({ 112 | 'date': dates, 113 | 'text': texts, 114 | 'id': ids, 115 | 'flag': flags, 116 | }) 117 | upserter.upsert(df) 118 | 119 | p_table = s.run("select * from p_table") 120 | print(p_table) 121 | ``` 122 | 123 | 输出结果如下所示: 124 | 125 | ``` 126 | date text id flag 127 | 0 2023-03-16 10:09:33 test_9_90 0 0 128 | 1 2023-03-16 10:09:26 test_0_10 0 0 129 | 2 2023-03-16 10:09:26 test_0_20 0 0 130 | 3 2023-03-16 10:09:26 test_0_30 0 0 131 | 4 2023-03-16 10:09:26 test_0_40 0 0 132 | .. ... ... .. ... 133 | 95 2023-03-16 10:09:26 test_0_59 9 9 134 | 96 2023-03-16 10:09:26 test_0_69 9 9 135 | 97 2023-03-16 10:09:26 test_0_79 9 9 136 | 98 2023-03-16 10:09:26 test_0_89 9 9 137 | 99 2023-03-16 10:09:26 test_0_99 9 9 138 | 139 | [100 rows x 4 columns] 140 | ``` 141 | 142 | 由上述结果可知,当键值列在某分区中值不唯一时,执行 `upsert` 时仅会覆盖分区中当前键值列下该键值对应的第一个数据。 143 | 144 | > **注:** tableUpsert 实际上调用了 DolphinDB 的 upsert! 函数,同时传入 pandas.DataFrame 作为参数以实现其功能。upsert! 函数的详细使用方式请参考 [DolphinDB用户手册-upsert!](https://www.dolphindb.cn/cn/help/200/FunctionsandCommands/FunctionReferences/u/upsert!.html)。 145 | -------------------------------------------------------------------------------- /README_CN_NEW/2_BasicOperations/2.3_AutoFitTableAppender/2.3.3_PartitionedTableAppender.md: -------------------------------------------------------------------------------- 1 | # PartitionedTableAppender 2 | 3 | 与 tableAppender 对象类似,使用 PartitionedTableAppender 对象向表中追加时间类型数据时,能够实现对时间类型数据的自动转换和多线程并行写入。其基本原理是在构造时接收一个 DBConnectionPool 对象作为参数,再调用 append 方法,将数据按照分区拆分后传入连接池以实现并发追加。需注意,一个分区在同一时间只能由一个连接写入。 4 | 5 | ## 接口说明 6 | 7 | ```python 8 | PartitionedTableAppender(dbPath=None, tableName=None, partitionColName=None, dbConnectionPool=None) 9 | ``` 10 | 11 | * **dbPath**: 分布式数据库名字。如为内存表则不需要指定。 12 | * **tableName**: 分区表表名。 13 | * **partitionColName**: 字符串,表示分区字段。 14 | * **dbConnectionPool**: DBConnectionPool,连接池对象。 15 | 16 | > **注:** 指定参数 *partitionColName* 时,如果分区表中仅包含一个分区列,则该参数必须指定为该分区列;如果存在多个分区,则该参数可指定为任意分区列。在指定该参数后,API 将根据分区字段进行数据分配。 17 | 18 | 该类仅支持 append 方法,接口如下: 19 | 20 | ```python 21 | append(table) 22 | ``` 23 | 24 | * **table**:待写入数据,通常为 pandas.DataFrame 类型的本地数据。 25 | 26 | ## 示例 27 | 28 | 下例创建了一个分布式数据库 dfs://valuedb 和一个分布式表 pt,同时创建了连接池 pool 并传入 PartitionedTableAppender,然后使用 append 方法向分布式表并发写入本地数据。最后查询写入的数据。 29 | 30 | ```python 31 | import dolphindb as ddb 32 | import pandas as pd 33 | import numpy as np 34 | import random 35 | 36 | s = ddb.session() 37 | s.connect("localhost", 8848, "admin", "123456") 38 | script = """ 39 | dbPath = "dfs://valuedb" 40 | if(existsDatabase(dbPath)){ 41 | dropDatabase(dbPath) 42 | } 43 | t = table(100:0, `id`date`vol, [SYMBOL, DATE, LONG]) 44 | db = database(dbPath, VALUE, `APPL`IBM`AMZN) 45 | pt = db.createPartitionedTable(t, `pt, `id) 46 | """ 47 | s.run(script) 48 | 49 | pool = ddb.DBConnectionPool("localhost", 8848, 3, "admin", "123456") 50 | appender = ddb.PartitionedTableAppender(dbPath="dfs://valuedb", tableName="pt", partitionColName="id", dbConnectionPool=pool) 51 | n = 100 52 | 53 | dates = [] 54 | for i in range(n): 55 | dates.append(np.datetime64( 56 | "201{:d}-0{:1d}-{:2d}".format(random.randint(0, 9), random.randint(1, 9), random.randint(10, 28)))) 57 | 58 | data = pd.DataFrame({ 59 | "id": np.random.choice(['AMZN', 'IBM', 'APPL'], n), 60 | "time": dates, 61 | "vol": np.random.randint(100, size=n) 62 | }) 63 | re = appender.append(data) 64 | 65 | print(re) 66 | print(s.run("pt = loadTable('dfs://valuedb', 'pt'); select * from pt;")) 67 | ``` 68 | 69 | 输出结果如下: 70 | 71 | ``` 72 | 100 73 | id date vol 74 | 0 AMZN 2017-01-22 60 75 | 1 AMZN 2014-08-12 37 76 | 2 AMZN 2012-09-10 68 77 | 3 AMZN 2012-03-14 48 78 | 4 AMZN 2016-07-12 1 79 | .. ... ... ... 80 | 95 IBM 2016-05-15 25 81 | 96 IBM 2012-06-19 6 82 | 97 IBM 2010-05-10 96 83 | 98 IBM 2017-07-10 32 84 | 99 IBM 2012-09-23 68 85 | 86 | [100 rows x 3 columns] 87 | ``` 88 | -------------------------------------------------------------------------------- /README_CN_NEW/2_BasicOperations/2.4_Subscription/2.4_Subscription.md: -------------------------------------------------------------------------------- 1 | # 流数据订阅 2 | 3 | Python API 支持流数据订阅的功能,以下介绍如何启用流数据,订阅流数据,获取订阅主题和取消订阅流数据。 4 | 5 | ## 1. 启用流数据 6 | 7 | 使用 session 提供的 `enableStreaming` 方法启用流数据功能,使用示例如下: 8 | 9 | ```python 10 | enableStreaming(port=0) 11 | ``` 12 | 13 | * **port**:指定开启数据订阅的端口,用于订阅服务器端发送的数据。(2.00.9 及之后版本的 DolphinDB 无需指定该参数) 14 | 15 | 使用时须注意,API 进行订阅时,其订阅行为根据 DolphinDB 的版本有所不同,详细说明如下: 16 | 17 | |DolphinDB Server 版本|Python API 版本|是否需要指定端口| 18 | |---------------|--------------------|------------| 19 | |1.30.x, 2.00.9 之前的版本|与 DolphinDB Server 版本对应的版本|是| 20 | |2.00.9 及之后的版本|与 DolphinDB Server 版本对应的版本|否| 21 | 22 | * 1.30.x, 2.00.9 之前的版本在订阅端提交订阅请求后,发布端需要向 API 端指定端口重新发起一个 TCP 连接用于传输数据。 23 | * 2.00.9 及之后的版本支持发布端通过订阅端的请求连接推送数据。因此,订阅端无需指定端口(默认值为 0);如果指定,该参数无效,会被 API 忽略。 24 | 25 | 使用不同版本 API 启用流数据功能的脚本示例如下: 26 | 27 | ```python 28 | import dolphindb as ddb 29 | s = ddb.session() 30 | # 1.30.x,2.00.9 之前的版本,开启订阅,指定端口8000 31 | s.enableStreaming(8000) 32 | # 2.00.9 及之后的版本,开启订阅,无需指定端口 33 | s.enableStreaming() 34 | ``` 35 | 36 | > **注意**:若同时升级 API 和 Server 至 2.00.9 及之后的版本,须在升级前先取消订阅,完成升级后再重新订阅。 37 | 38 | ## 2. 订阅 39 | 40 | 使用 `subscribe` 方法订阅 DolphinDB 中的流数据表,接口示例如下: 41 | 42 | ```python 43 | subscribe(host, port, handler, tableName, actionName=None, offset=-1, resub=False, 44 | filter=None, msgAsTable=False, batchSize=0, throttle=1.0, 45 | userName=None, password=None, streamDeserializer=None) 46 | ``` 47 | 48 | ### 2.1 参数介绍 49 | 50 | #### 连接参数: *host*, *port*, *userName*, *password* 51 | 52 | * **host** :字符串,必填,表示发布端节点的 IP 地址。 53 | * **port** :字符串,必填,表示发布端节点的端口号。 54 | * **userName** :字符串,可选,表示所连接 DolphinDB 的登录用户名。 55 | * **password** :字符串,可选,表示所连接 DolphinDB 的登录用户名对应的密码。 56 | 57 | #### 回调参数: *handler* 58 | 59 | * **handler** :用户自定义的回调函数,用于处理每次流入的数据。 60 | 61 | 下例定义了一个简单的回调函数: 62 | 63 | ```python 64 | def handler(msg): 65 | print(msg) 66 | ``` 67 | 68 | #### 订阅参数:*tableName*, *actionName*, *offset*, *resub* 69 | 70 | * **tableName**:表示发布表的名称。 71 | * **actionName**:表示订阅任务的名称。 72 | * 订阅时,订阅主题由订阅表所在节点的别名、流数据表名称和订阅任务名称组合而成,使用“/”分隔。注意,如果订阅主题已经存在,将会订阅失败。 73 | * **offset**:整数,表示订阅任务开始后的第一条消息所在的位置。消息即为流数据表中的行。 74 | * 若该参数未指定,或设为 -1,订阅将会从流数据表的当前行开始。 75 | * 若该参数设为 -2,系统会获取持久化到磁盘上的 offset,并从该位置开始订阅。其中,offset 与流数据表创建时的第一行对应。如果某些行因为内存限制被删除,在决定订阅开始的位置时,这些行仍然考虑在内。 76 | 77 | * *resub*:布尔值,表示订阅中断后,API 是否进行自动重订阅。默认值为 False,表示不会自动重订阅。 78 | * *filter*:一个数组,表示过滤条件。在流数据表过滤列中,只有符合 *filter* 过滤条件的数据才会发布到订阅端。 79 | 80 | #### 模式参数:*msgAsTable*, *batchSize*, *throttle*, *streamDeserializer* 81 | 82 | * **msgAsTable**:布尔值。若该参数设置为 True,表示订阅的数据将会转换为 DataFrame 格式。须注意,只有设置了参数 *batchSize*,参数 *msgAsTable* 才会生效。**注意**:若设置了 *streamDeserializer*,则参数 *msgAsTable* 必须设置为 False。 83 | * **batchSize**:整数,表示批处理的消息的数量。 84 | * 如果该参数是正数: 85 | * 设置 `msgAsTable = False` 时,直到消息的数量达到 *batchSize* 时,*handler* 才会处理进来的 *batchSize* 条消息,并返回一个 list,其中每一项都是单条数据。 86 | * 设置 `msgAsTable = True` 时,回调参数 *handler* 将基于消息块(由 DolphinDB 中的参数 *maxMsgNumPerBlock* 进行配置)处理消息。当收到的记录总数大于等于 *batchSize* 时,*handler* 将处理所有达到条件的消息块。举例说明:若设置 `batchSize = 1500`,API 收到 DolphinDB 发送的第一个消息块(包含 1024 条记录),1024<1500,*handler* 将不处理消息;API 收到第 2 个消息块(包含 500 条记录),此时,1024+500>1500,即两个消息块中包含的记录数大于 *batchSize*,*handler* 将开始处理这两个消息块中的 1524 条记录。 87 | * 如果该参数是非正数或者未被指定,消息到达之后,*handler* 将立刻逐条处理消息,返回的数据将为单条数据组成的 list。 88 | * **throttle**:浮点数,表示 *handler* 处理到达的消息之前等待的时间。单位为秒,默认值为 1.0。如果未指定 *batchSize*,参数 *throttle* 将无法产生作用。 89 | * **streamDeserializer**:表示订阅的异构流表对应的反序列化器。 90 | 91 | > **注意:** 订阅流表时,参数 *batchsize*、*msgAsTable*、*streamDeserializer* 的值都将影响传入回调函数 *handler* 中变量的格式,各种参数的订阅示例请参考章节 [3.3 流订阅](../../3_AdvancedOperations/3.3_SubscriptionOptions/3.3_SubscriptionOptions.md)。 92 | 93 | ### 2.2 订阅示例 94 | 95 | 先在 DolphinDB 中创建共享的流数据表,指定进行过滤的列为 sym,并向 5 个 symbol 各插入 2 条记录,共 10 条记录: 96 | 97 | ``` 98 | share streamTable(10000:0,`time`sym`price`id, [TIMESTAMP,SYMBOL,DOUBLE,INT]) as trades 99 | setStreamTableFilterColumn(trades, `sym) 100 | insert into trades values(take(now(), 10), take(`000905`600001`300201`000908`600002, 10), rand(1000,10)/10.0, 1..10) 101 | ``` 102 | 103 | 在 Python API 中,首先指定订阅端口号并启用流订阅( 本例中使用 1.30.x 和 2.00.9 之前版本的 DolphinDB,故需要指定订阅端口号),然后定义回调函数 *handler*。再调用 subscribe 方法开启流订阅,其中设置 `offset=-1`,`filter=np.array(["000905"])`。最后通过 `Event().wait()` 的方式阻塞主线程,保持进程不退出。 104 | 105 | ```python 106 | import dolphindb as ddb 107 | import numpy as np 108 | s = ddb.session() 109 | s.enableStreaming(0) # DolphinDB 的版本为 1.30.x 或小于 2.00.9 时,需指定端口 110 | 111 | def handler(lst): 112 | print(lst) 113 | 114 | s.subscribe("192.168.1.113", 8848, handler, "trades", "action", offset=-1, filter=np.array(["000905"])) 115 | 116 | from threading import Event 117 | Event().wait() # 阻塞主线程,保持进程不退出 118 | ``` 119 | 120 | 执行脚本,发现此时没有流数据被打印出来。该情况是由于 offset 设置为 -1,表示订阅将会从流数据表的当前行开始,因此 DolphinDB `insert into` 命令中包含的数据将不会发送到流订阅客户端。 121 | 122 | 在 DolphinDB 中再次执行同一脚本: 123 | 124 | ``` 125 | insert into trades values(take(now(), 10), take(`000905`600001`300201`000908`600002, 10), rand(1000,10)/10.0, 1..10) 126 | ``` 127 | 128 | 此时 Python 进程开始打印收到的流数据,输出内容如下: 129 | 130 | ``` 131 | [numpy.datetime64('2023-03-17T10:11:19.684'), '000905', 69.3, 1] 132 | [numpy.datetime64('2023-03-17T10:11:19.684'), '000905', 96.5, 6] 133 | ``` 134 | 135 | 在 API 收到的数据中,由于 *filter* 参数生效,故打印结果中仅保留了 `sym="000905"`的数据,其余数据都被过滤掉。 136 | 137 | ## 3. 获取订阅主题 138 | 139 | 通过 `getSubscriptionTopics` 方法可以获取当前 session 中的所有订阅主题。主题的构成方式是:`host/port/tableName/actionName`,每个 session 的主题互不相同。 140 | 141 | 使用示例如下: 142 | 143 | ```python 144 | s.getSubscriptionTopics() 145 | ``` 146 | 147 | 示例的输出结果如下所示: 148 | 149 | ``` 150 | ['192.168.1.113/8848/trades/action'] 151 | ``` 152 | 153 | ## 4. 取消订阅 154 | 155 | 使用 `unsubscribe` 方法可以取消订阅,接口如下: 156 | 157 | ```python 158 | unsubscribe(host, port, tableName, actionName=None) 159 | ``` 160 | 161 | 取消示例中的订阅: 162 | 163 | ```python 164 | s.unsubscribe("192.168.1.113", 8848, "trades", "action") 165 | ``` 166 | -------------------------------------------------------------------------------- /README_CN_NEW/2_BasicOperations/2.5_AsyncWrites/2.5.1_SessionAsyncMode.md: -------------------------------------------------------------------------------- 1 | # session 异步提交 2 | 3 | 在高吞吐率的场景下,尤其是典型的高速小数据写入,使用 API 的异步调用功能可以有效提高 API 的任务吞吐量。异步方式提交有如下几个特点: 4 | 5 | - API 客户端提交任务后,DolphinDB 接到任务后客户端即认为任务已完成。 6 | - API 客户端无法得知任务在 DolphinDB 执行的情况和结果。 7 | - API 客户端的异步任务提交时间取决于提交参数的序列化及其网络传输时间。 8 | 9 | > **注意**:异步方式不适用于前后任务之间有依赖的场景。比如有两个任务,前一个任务向分布式数据库写入数据,后一个任务将新写入的数据结合历史数据做分析。像这类后一个任务对前一任务有依赖的场景,不能使用异步提交的方式。 10 | 11 | Python API 开启 ASYNC(异步)模式的操作可以参照 [2.1.1 Session](../2.1_Session/2.1.1_Constructor.md) 建立 DolphinDB 连接的部分,即设置 session 的 *enableASYNC* 参数为 True。通过这种方式异步写入数据可以节省 API 端检测返回值的时间。 12 | 13 | ```python 14 | s = ddb.session(enableASYNC=True) 15 | ``` 16 | 17 | 以追加数据到分布式表为例,在 Python 中可以参考如下脚本使用异步方式追加数据。 18 | 19 | ```python 20 | import dolphindb as ddb 21 | import dolphindb.settings as keys 22 | import numpy as np 23 | import pandas as pd 24 | 25 | s = ddb.session(enableASYNC=True) # 打开异步模式 26 | s.connect("localhost", 8848, "admin", "123456") 27 | dbPath = "dfs://testDB" 28 | tbName = "tb1" 29 | 30 | 31 | script = """ 32 | dbPath="dfs://testDB" 33 | tbName=`tb1 34 | if(existsDatabase(dbPath)) 35 | dropDatabase(dbPath) 36 | db=database(dbPath, VALUE, ["AAPL", "AMZN", "A"]) 37 | testDictSchema=table(5:0, `id`ticker`price, [INT,SYMBOL,DOUBLE]) 38 | tb1=db.createPartitionedTable(testDictSchema, tbName, `ticker) 39 | """ 40 | s.run(script) #此处脚本可以在服务器端运行 41 | 42 | tb = pd.DataFrame({ 43 | 'id': np.array([1, 2, 2, 3], dtype="int32"), 44 | 'ticker': ['AAPL', 'AMZN', 'AMZN', 'A'], 45 | 'price': [22, 3.5, 21, 26], 46 | }) 47 | 48 | s.run(f"append!{{loadTable('{dbPath}', `{tbName})}}", tb) 49 | ``` 50 | 51 | > **注意**:异步通讯的条件下,只能通过 `session.run()` 方法与 DolphinDB 实现通讯,**并无返回值**。 52 | 53 | ## 示例 1 54 | 55 | 由于在数据吞吐量较高的情况下使用异步的效果更佳,下面给出一个 Python API 写入流数据表的示例。 56 | 57 | ```python 58 | import dolphindb as ddb 59 | 60 | import numpy as np 61 | import pandas as pd 62 | import random 63 | import datetime 64 | 65 | s = ddb.session(enableASYNC=True) 66 | s.connect("localhost", 8848, "admin", "123456") 67 | 68 | n = 100 69 | 70 | script = """ 71 | share streamTable(10000:0,`time`sym`price`id, [TIMESTAMP,SYMBOL,DOUBLE,INT]) as trades 72 | """ 73 | s.run(script) # 此处的脚本可以在服务端直接运行 74 | 75 | # 生成一个 DataFrame 76 | time_list = [np.datetime64(datetime.date(2020, random.randint(1, 12), random.randint(1, 20))) for _ in range(n)] 77 | sym_list = np.random.choice(['IBN', 'GTYU', 'FHU', 'DGT', 'FHU', 'YUG', 'EE', 'ZD', 'FYU'], n) 78 | price_list = [round(np.random.uniform(1, 100), 1) for _ in range(n)] 79 | id_list = np.random.choice([1, 2, 3, 4, 5], n) 80 | 81 | tb = pd.DataFrame({ 82 | 'time': time_list, 83 | 'sym': sym_list, 84 | 'price': price_list, 85 | 'id': id_list, 86 | }) 87 | 88 | for _ in range(50000): 89 | s.run("tableInsert{trades}", tb) 90 | ``` 91 | 92 | Linux 系统中可以使用 time 命令来统计代码执行的时间,Windows 系统中则可以使用 Measure-Command 命令。在本例中使用 time 来统计代码执行时间。 93 | 94 | 同步模式下,即 `enableAsync=False` 时,测试上述代码的执行时间为 8.39 秒;异步模式下,即 `enableAsync=True` 时,测试上述代码的执行之间为 4.89 秒。对比可见,在同步模式下,频繁写入大量小数据,会有较大的网络 IO 开销;而使用异步模式则能解决这一问题,只需要将数据上传至服务端,而不需要返回值。在网络性能较差、任务数量较多的情况下,使用异步模式能够显著提升总体性能,但其缺点是无法保证数据能够正常写入,没有异常报错,因此难以发现问题。 95 | 96 | ## 示例 2 97 | 98 | 由于异步模式的特殊性,在异步追加数据时,如果追加的数据需要进行时间类型的转换,不能直接调用 upload 提交数据到 DolphinDB,然后再在 DolphinDB 用 SQL 脚本进行类型转换,因为异步可能导致数据提交还未完成却已经开始执行 SQL 的情况。为了解决这个问题,建议先在 DolphinDB 中定义好函数视图,后续操作只需要调用该函数视图即可。 99 | 100 | 以下为简单示例。 101 | 102 | 首先,在 DolphihinDB 中定义一个视图函数 appendStreamingData: 103 | 104 | ```txt 105 | login("admin","123456") 106 | share streamTable(10000:0,`time`sym`price`id, [DATE,SYMBOL,DOUBLE,INT]) as trades 107 | def appendStreamingData(mutable data){ 108 | tableInsert(trades, data.replaceColumn!(`time, date(data.time))) 109 | } 110 | addFunctionView(appendStreamingData) 111 | ``` 112 | 113 | 然后在 Python API 异步追加数据: 114 | 115 | ```python 116 | import dolphindb as ddb 117 | import numpy as np 118 | import pandas as pd 119 | import random 120 | import datetime 121 | 122 | s = ddb.session(enableASYNC=True) 123 | s.connect("localhost", 8848, "admin", "123456") 124 | 125 | n = 100 126 | 127 | # 生成一个 DataFrame 128 | time_list = [np.datetime64(datetime.date(2020, random.randint(1, 12), random.randint(1, 20))) for _ in range(n)] 129 | sym_list = np.random.choice(['IBN', 'GTYU', 'FHU', 'DGT', 'FHU', 'YUG', 'EE', 'ZD', 'FYU'], n) 130 | price_list = [round(np.random.uniform(1, 100), 1) for _ in range(n)] 131 | id_list = np.random.choice([1, 2, 3, 4, 5], n) 132 | 133 | tb = pd.DataFrame({ 134 | 'time': time_list, 135 | 'sym': sym_list, 136 | 'price': price_list, 137 | 'id': id_list, 138 | }) 139 | 140 | for _ in range(50000): 141 | s.run("appendStreamingData", tb) 142 | ``` 143 | 144 | 使用函数视图可以将数据作为函数参数传递给 DolphinDB,这也将极大方便 DolphinDB 中的数据清洗、类型转换等任务,结合使用异步模式可以进一步提高总体的性能。 145 | -------------------------------------------------------------------------------- /README_CN_NEW/2_BasicOperations/2.5_AsyncWrites/2.5.3_BatchTableWriter.md: -------------------------------------------------------------------------------- 1 | # BatchTableWriter 2 | 3 | > 注意:BatchTableWriter 现已不再维护,不推荐使用。 4 | 5 | ## 1. 工作原理 6 | 7 | **BatchTableWriter** 以下简称 **BTW**,用于批量写入单条数据。以下为简要的使用说明: 8 | 9 | 1. BTW 提供接口 addTable 方法,用于添加一个写入表,执行该方法后,将会创建一个写入队列和一个 C++ 工作线程。 10 | 11 | 2. 写入数据时,需要调用 insert 方法,并指定写入表的信息和待写入的一行数据。将数据转换为 C++ 对象后放入后台写入队列中。 12 | 13 | 3. 工作线程每隔 100 ms 取出写入队列中的所有数据,一次性将这一批数据全部写入 DolphinDB。 14 | 15 | 4. 如果写入数据时(insert)发生类型转换错误或者列数不匹配等错误,会立刻抛出异常信息,该条数据不会放入写入队列中;如果在工作线程中将数据发送到 DolphinDB 的过程中发生错误,则会将当前写入队列中的数据和发送失败的数据保存起来,调用 getUnwrittenData 即可获得。 16 | 17 | 5. 如果需要查看当前各表的写入状态,可以调用 getAllStatus 方法,该方法将返回一张表,表示各个待写入表的工作状态,包含写入队列长度、发送行数、是否已销毁、是否结束。也可以调用 getStatus,传入表名和数据库名,就可以获得指定表当前的写入状态。 18 | 19 | 6. 不需要再往表中写数据时,可以调用 removeTable。该方法将停止工作线程,然后销毁相关的工作线程、连接、写入队列。 20 | 21 | ## 2. BatchTableWriter 22 | 23 | ```Python 24 | BatchTableWriter(host, port, userid=None, password=None, acquireLock=True) 25 | ``` 26 | 27 | ### 2.1 参数说明 28 | 29 | * **host**:字符串,必填,表示所连接的服务器的 IP 地址。 30 | * **port**:整数,必填,表示所连接的服务器的端口号。 31 | * **userName**:字符串,可选,表示登录时的用户名。 32 | * **password**:字符串,可选,表示登录时用户名对应的密码。 33 | * **acquireLock**: 布尔值,表示在使用过程中是否对 BTW 内部加锁。默认为 True,表示需要加锁。若在并发调用 API 的场景下,建议加锁。 34 | 35 | #### 构造示例 36 | 37 | ```python 38 | writer = ddb.BatchTableWriter("localhost", 8848, "admin", "123456", acquireLock=True) 39 | ``` 40 | 41 | ### 2.2 addTable 42 | 43 | ```Python 44 | writer.addTable(dbPath=None, tableName=None, partitioned=True) 45 | ``` 46 | 47 | #### 功能说明 48 | 49 | 向 BTW 中添加一个待写入的表,如果是分区表则需要设置 `partitioned=True`。 50 | 51 | #### 参数说明 52 | 53 | * **dbName**:表示数据库名。若待写入表为磁盘表时,须填写该参数;若为内存表,则不需要填写该参数。 54 | * **tableName**:表示数据表的表名。 55 | * **partitioned**:表示添加的表是否为分区表。若设置该参数为 True,则表示为分区表。如果添加的表是磁盘未分区表,须设置 `partitioned=False`。 56 | 57 | > **注意:** 58 | > 59 | > * 如果添加的是内存表,则需要共享该表。 60 | > * 表名不可重复添加。如果重复添加,则需要先移除之前添加的表,否则会抛出异常。 61 | 62 | ### 2.3 insert 63 | 64 | ```Python 65 | writer.insert(dbPath=None, tableName=None, *args) 66 | ``` 67 | 68 | #### 功能说明 69 | 70 | 向指定表中插入单行数据。 71 | 72 | #### 参数说明 73 | 74 | * **dbPath**:表示数据库名。若待写入表为磁盘表时,须填写该参数;若为内存表,则不需要填写该参数。 75 | * **tableName**:表示数据表的表名。 76 | * **args**:变长参数,表示插入的一行数据。 77 | 78 | > **注意:** 79 | > 80 | > * 在调用 insert 前须先使用 addTable 添加表,否则会抛出异常。 81 | > 82 | > * 变长参数的个数和数据类型必须与 insert 表的列数及类型匹配。 83 | > 84 | > * 若在插入过程中出现异常进而导致后台线程退出,此时再次调用 insert 将会抛出异常。建议使用 getUnwrittenData 来获取之前所有已经写入缓冲队列但是没有成功写入服务器的数据(不包括本次 insert 的数据),然后使用 removeTable 释放资源。如果需要再次插入数据,则须重新调用 addTable。 85 | > 86 | > * 在移除指定表的过程中调用 insert 仍能成功插入数据,但插入的这部分数据并不会发送到服务器。该操作属于未定义行为,不建议用户进行类似操作。 87 | 88 | ### 2.4 removeTable 89 | 90 | ```Python 91 | writer.removeTable(dbPath=None, tableName=None) 92 | ``` 93 | 94 | #### 功能说明 95 | 96 | 释放由 addTable 添加的表所占用的资源。 97 | 98 | 第一次调用该函数,若该函数返回即表示后台线程已退出。再次写入需要重新调用 addTable。 99 | 100 | ### 2.5 getUnwrittenData 101 | 102 | ```Python 103 | data:pandas.DataFrame = writer.getUnwrittenData(dbPath=None, tableName=None) 104 | ``` 105 | 106 | #### 功能说明 107 | 108 | 获取还未写入的数据。 109 | 110 | 当写入出现错误时,使用该函数可以获取剩余未写入的数据。但是这些未写入的数据不会尝试重写,若需要重新写入,则需要使用 removeTable 后重新调用 addTable,再通过 insert 写入数据。 111 | 112 | ### 2.6 getStatus 113 | 114 | ```Python 115 | res:list = writer.getStatus(dbPath=None, tableName=None) 116 | ``` 117 | 118 | #### 功能说明 119 | 120 | 获取当前的写入状态。详细信息可参考下方返回值说明。 121 | 122 | #### 返回值说明 123 | 124 | 返回值是由一个整型和两个布尔型组合的列表,分别表示当前写入队列的深度、当前表是否被移除(True: 表示正在被移除),以及后台写入线程是否因为出错而退出(True: 表示后台线程因出错而退出)。 125 | 126 | ### 2.7 getAllStatus 127 | 128 | ```Python 129 | res:pandas.DataFrame = writer.getAllStatus() 130 | ``` 131 | 132 | #### 功能说明 133 | 134 | 获取所有当前存在的表的信息,不包含被移除的表。 135 | 136 | #### 返回值说明 137 | 138 | 该函数的返回值是一个 2*6 的表格,包含数据库名(DatabaseName)、数据表名(TableName)、写入队列长度(WriteQueueDepth)、发送行数(sendedRows)、是否已销毁(Removing)和是否结束(Finished)。 139 | 140 | 返回值的示例如下表: 141 | 142 | | DatabaseName | TableName | WriteQueueDepth | sendedRows | Removing | Finished | 143 | | ------------ | --------- | --------------- | ---------- | -------- | -------- | 144 | | 0 | tglobal | 0 | 5 | False | False | 145 | 146 | ## 3. 使用示例 147 | 148 | 本例中,首先定义一个共享表 tglobal,然后构造 BTW,调用 addTable 将 tglobal 加入待写入表。随后调用 insert 方法写入 5 条数据,然后**立刻**调用 getUnwritternData、getStatus、getAllStatus 获取尚未写入的数据、当前表的写入状态和所有表的写入状态。最后,使用 session 查询待写入表中的当前写入数据。 149 | 150 | > **注意:** 示例中,执行 insert 后立刻调用 getUnwrittenData,写入队列中的数据全部被取出,因此,此时 BTW 的写入队列中没有数据,仅有之前已经从写入队列取出、进入工作线程的数据成功写入 DolphinDB。 151 | 152 | ```python 153 | import dolphindb as ddb 154 | import numpy as np 155 | import pandas as pd 156 | 157 | s = ddb.session() 158 | s.connect("localhost", 8848, "admin", "123456") 159 | 160 | script = """ 161 | t = table(1000:0,`id`date`ticker`price, [INT,DATE,SYMBOL,DOUBLE]); 162 | share t as tglobal; 163 | """ 164 | s.run(script) 165 | 166 | writer = ddb.BatchTableWriter("localhost", 8848) 167 | writer.addTable(tableName="tglobal") 168 | writer.insert("","tglobal", 1, np.datetime64("2019-01-01"),'AAPL', 5.6) 169 | writer.insert("","tglobal", 2, np.datetime64("2019-01-01"),'GOOG', 8.3) 170 | writer.insert("","tglobal", 3, np.datetime64("2019-01-02"),'GOOG', 4.2) 171 | writer.insert("","tglobal", 4, np.datetime64("2019-01-03"),'AMZN', 1.4) 172 | writer.insert("","tglobal", 5, np.datetime64("2019-01-05"),'AAPL', 6.9) 173 | 174 | print(writer.getUnwrittenData(dbPath="", tableName="tglobal")) 175 | print(writer.getStatus(tableName="tglobal")) 176 | print(writer.getAllStatus()) 177 | 178 | print("rows:", s.run("tglobal.rows()")) 179 | print(s.run("select * from tglobal")) 180 | ``` 181 | 182 | 输出结果如下: 183 | 184 | ``` 185 | id date ticker price 186 | 0 2 2019-01-01 GOOG 8.3 187 | 1 3 2019-01-02 GOOG 4.2 188 | 2 4 2019-01-03 AMZN 1.4 189 | 3 5 2019-01-05 AAPL 6.9 190 | [0, False, False] 191 | DatabaseName TableName WriteQueueDepth SendedRows Removing Finished 192 | 0 tglobal 0 1 False False 193 | rows: 1 194 | id date ticker price 195 | 0 1 2019-01-01 AAPL 5.6 196 | ``` 197 | -------------------------------------------------------------------------------- /README_CN_NEW/3_AdvancedOperations/3.1_DataTypeCasting/3.1.0_TypeCasting.md: -------------------------------------------------------------------------------- 1 | # 类型转换 2 | 3 | DolphinDB 与 Python API 的交互过程始终遵循 [API交互协议](https://gitee.com/dolphindb/Tutorials_CN/blob/master/api_protocol.md)。该协议规定了通信双方在交互过程中使用的报文信息格式。在 API 与 DolphinDB 的交互过程中,通过指定 *protocol* 参数,即可选择交互过程中使用的传输数据格式协议。 4 | 5 | Python 中包含多套常用类型系统,其与 DolphinDB 的类型系统并非一一对应。为更好地兼容各类型系统与 DolphinDB 之间的数据交互,DolphinDB Python API 自1.30.21.1版本起,session 和 DBConnectionPool 新增 *protocol* 参数。目前已支持的协议包括 PROTOCOL_DDB、PROTOCOL_PICKLE 和 PROTOCOL_ARROW,默认使用 PROTOCOL_PICKLE。此外,对于 UUID、IPADDR、SECOND 等无法直接上传的类型, Python API 支持强制类型转换。 6 | 7 | 下例展示了 session 如何指定使用的传输协议。 8 | 9 | ```python 10 | import dolphindb as ddb 11 | import dolphindb.settings as keys 12 | # 使用协议 PROTOCOL_DDB 13 | s = ddb.session(protocol=keys.PROTOCOL_DDB) 14 | # 使用协议 PROTOCOL_PICKLE 15 | s = ddb.session(protocol=keys.PROTOCOL_PICKLE) 16 | # 使用协议 PROTOCOL_ARROW 17 | s = ddb.session(protocol=keys.PROTOCOL_ARROW) 18 | ``` 19 | 20 | PROTOCOL_DDB、PROTOCOL_PICKLE 和 PROTOCOL_ARROW 协议支持不同的类型系统,提供各自特色的序列化方式以适应不同应用场景,最终实现更高效的数据传输。以下内容为用户选择传输协议提供些许参考建议: 21 | 22 | * PROTOCOL_DDB 协议是 DolphinDB 自定义的一套数据序列化、反序列化方案,被广泛使用于 Python API、C++ API、Java API 等 API 中。其支持的数据形式和数据类型最为全面。 23 | * PROTOCOL_PICKLE 协议基于 Python 原生的 [Pickle 协议](https://python.readthedocs.io/en/latest/library/pickle.html),同时进行了部分适配 DolphinDB 的修改,是 *protocol* 参数默认指定的协议。 24 | * PROTOCOL_ARROW 协议是基于 [Apache Arrow](https://arrow.apache.org/) 通用序列化方案、用于对大型数据集进行序列化和反序列化的协议,可以跨平台、跨语言地进行高效数据传输。 25 | 26 | 从使用场景的角度来看: 27 | 28 | * PROTOCOL_ARROW 协议适用于从上游数据库到下游消费、且全部使用 Apache Arrow 格式作为中间格式的场景,可以方便地在各个组件之间传递数据。而用户只需要付出从数据库取出数据这一次序列化开销,后续不再需要进行序列化和反序列化。此外,使用该协议可以较高效地利用网络带宽,目前有较多的行情服务商使用 Apache Arrow 协议。 29 | * PROTOCOL_PICKLE 协议针对数据传输类的场景,PROTOCOL_DDB 协议支持的数据形式和数据类型最为全面。如果场景中使用 pandas 的 DataFrame 较多,则更推荐使用 PROTOCOL_PICKLE 协议和 PROTOCOL_DDB 协议。由于一般的数据类型用 PROTOCOL_PICKLE 协议的数据传输速度相对更快,因此 *protocol* 参数默认开启 PROTOCOL_PICKLE 协议。 30 | 31 | ## 交互流程 32 | 33 | API 与 DolphinDB 的交互流程可以简化为 session 建立阶段和单次 run 请求阶段。 34 | 35 | ### session 建立阶段 36 | 37 | API 先向 DolphinDB 发起连接请求,发送的报文中包含一组 flag。该 flag 中包含协议参数对应的标志位,表示建立 session 时 API 选用的序列化、反序列化协议。目前版本 Python API 支持的可选协议有 PROTOCOL_DDB、PROTOCOL_PICKLE、PROTOCOL_ARROW。 38 | 39 | 协议参数的生效周期和 session 保持一致。在 session 连接期间,如果上传、查询涉及到的数据形式属于该协议支持的数据形式,则会使用该协议对应的序列化、反序列化方案进行类型转换。如果不属于该协议支持的数据形式,则默认使用 PROTOCOL_DDB 的方式进行处理。举例来说,Python API 中的协议 PROTOCOL_PICKLE 仅支持 Matrix 和 Table 数据形式的反序列化。在下载一个 Vector 数据时,即便指定了使用 PROTOCOL_PICKLE 协议,但由于 PROTOCOL_PICKLE 不支持 Vector 数据形式,因此仍会使用默认的 PROTOCOL_DDB 协议的反序列化流程。 40 | 41 | ### 单次 run 请求阶段 42 | 43 | 与建立 session 类似,单次执行 run 时发送的请求同样附带一组 flag。该 flag 中包含协议参数和附加参数,例如协议参数 *PROTOCOL_PICKLE* 和 *PROTOCOL_DDB* 都具有附加参数 *pickleTableToList*。如果在执行 run 方法时指定 `pickleTableToList=True`,则将改变当次请求的序列化、反序列化流程。 44 | 45 | 但与建立 session 不同的是,单次 run 请求的 flag 参数生效周期和一次查询一致。 46 | -------------------------------------------------------------------------------- /README_CN_NEW/3_AdvancedOperations/3.1_DataTypeCasting/3.1.2_PROTOCOL_PICKLE.md: -------------------------------------------------------------------------------- 1 | # PROTOCOL_PICKLE 2 | 3 | [Pickle 协议](https://python.readthedocs.io/en/latest/library/pickle.html)是一种对 Python 对象进行序列化和反序列化的方式,允许使用者将复杂的 Python 对象转换为可以存储或传输的字节流,再将该字节流转换为原始的 Python 对象。DolphinDB 中提供了基于 Python Pickle 协议特化的反序列化方案 PROTOCOL_PICKLE,该协议仅限在 DolphinDB Python API 中进行使用,其支持的**数据形式**和**数据类型**相对较少。 4 | 5 | **注1:** [数据形式](https://www.dolphindb.cn/cn/help/200/DataTypesandStructures/DataForms/index.html)指 DolphinDB 类型系统中的 DATAFORM,通常包含 Scalar、Vector、Table 等,表示数据结构的形式。 6 | 7 | **注2:** [数据类型](https://www.dolphindb.cn/cn/help/200/DataTypesandStructures/DataTypes/index.html)指 DolphinDB 类型系统中的 DATATYPE,通常包含 INT、DOUBLE、DATETIME 等,表示数据的具体类型。 8 | 9 | **注3:** 以下简称 Python 库 NumPy 为 **np**,pandas 为 **pd**。 10 | 11 | ## 1. 启用 PROTOCOL_PICKLE 12 | 13 | 在以下示例中,session 和 DBConnectionPool 通过设置参数 *protocol* 指定启用 PROTOCOL_PICKLE 协议。在当前版本中 PROTOCOL_DEFUALT 等同于 PROTOCOL_PICKLE,故默认使用 PROTOCOL_PICKLE 作为序列化、反序列化协议。 14 | 15 | ```python 16 | import dolphindb as ddb 17 | import dolphindb.settings as keys 18 | 19 | s = ddb.session(protocol=keys.PROTOCOL_PICKLE) 20 | s.connect("localhost", 8848, "admin", "123456") 21 | 22 | pool = ddb.DBConnectionPool("localhost", 8848, "admin", "123456", 10, protocol=keys.PROTOCOL_PICKLE) 23 | ``` 24 | 25 | ## 2. PROTOCOL_PICKLE 数据形式支持表 26 | 27 | PROTOCOL_PICKLE 支持的数据形式如下表展示: 28 | 29 | | 附加参数 | 数据形式 | 序列化 | 反序列化 | 30 | | :---------------------- | :------- | :----- | :------- | 31 | | pickleTableToList=False | Matrix | 不支持 | 支持 | 32 | | pickleTableToList=False | Table | 不支持 | 支持 | 33 | | pickleTableToList=True | Table | 不支持 | 支持 | 34 | 35 | ## 3. 反序列化 DolphinDB -> Python(设置 pickleTableToList=False) 36 | 37 | ### 3.1 Matrix 38 | 39 | DolphinDB 中的 Matrix 对应 Python 中的 np.ndarray,不同数据类型与 np.dtype 的对应关系如下表所示: 40 | 41 | | DolphinDB类型 | np.dtype | 42 | | :---------------------------------------------------------------------------------------- | :--------------- | 43 | | BOOL(不含空值) | bool | 44 | | CHAR(不含空值) | int8 | 45 | | SHORT(不含空值) | int16 | 46 | | INT(不含空值) | int32 | 47 | | LONG(不含空值) | int64 | 48 | | DATE、MONTH、TIME、TIMESTAMP、MINUTE、SECOND、DATETIME、NANOTIME、NANOTIMESTAMP、DATEHOUR | datetime64[ns] | 49 | | FLOAT | float32 | 50 | | DOUBLE、CHAR(含空值)、SHORT(含空值)、INT(含空值)、LONG(含空值) | float64 | 51 | | BOOL(含空值) | object | 52 | 53 | 和 PROTOCOL_DDB 一致,API 通过 PROTOCOL_PICKLE 协议下载的 Matrix 对应着包含三个元素的 list。list 中的第一个元素为 np.ndarray,表示实际数据;第二、三个元素分别对应 Matrix 的行名和列名,如果未设置行名、列名,则用 None 替代。如下为示例代码: 54 | 55 | ```python 56 | >>> s.run("date([2012.01.02, 2012.02.03])$1:2") 57 | [array([['2012-01-02T00:00:00.000000000', '2012-02-03T00:00:00.000000000']], 58 | dtype='datetime64[ns]'), None, None] 59 | ``` 60 | 61 | > **注意:** 若指定协议为 PROTOCOL_DDB,则下载时间类型 Matrix 对应的 dtype 为 datetime64[D]/datetime64[ms]/datetime64[M]/...;若指定协议为 PROTOCOL_PICKLE,则下载时间类型 Matrix 对应的 dtype 都为 datetime64[ns]。 62 | 63 | ### 3.2 Table 64 | 65 | PROTOCOL_PICKLE 协议中 Table 列类型对应的 np.dtype 如下表所示: 66 | 67 | | DolphinDB 类型 | np.dtype | 68 | | :---------------------------------------------------------------------------------------- | :------------- | 69 | | BOOL(不含空值) | bool | 70 | | CHAR(不含空值) | int8 | 71 | | SHORT(不含空值) | int16 | 72 | | INT(不含空值) | int32 | 73 | | LONG(不含空值) | int64 | 74 | | DATE、MONTH、TIME、TIMESTAMP、MINUTE、SECOND、DATETIME、NANOTIME、NANOTIMESTAMP、DATEHOUR | datetime64[ns] | 75 | | FLOAT | float32 | 76 | | DOUBLE、CHAR(含空值)、SHORT(含空值)、INT(含空值)、LONG(含空值) | float64 | 77 | | BOOL(含空值)、SYMBOL、STRING、UUID、IPADDR、INT128、Array Vector | object | 78 | 79 | **注1:** PROTOCOL_PICKLE 暂不支持 BLOB、DECIMAL32、DECIMAL64 类型的数据列。 80 | 81 | **注2:** PROTOCOL_PICKLE 暂不支持 UUID、IPADDR、INT128 类型的 Array Vector 数据列。 82 | 83 | 下载 Table 型数据的相关代码示例: 84 | 85 | ```python 86 | >>> re = s.run("table([1, NULL] as a, [2012.01.02, 2012.01.05] as b)") 87 | >>> re 88 | a b 89 | 0 1.0 2012-01-02 90 | 1 NaN 2012-01-05 91 | >>> re['a'].dtype 92 | float64 93 | >>> re['b'].dtype 94 | datetime64[ns] 95 | ``` 96 | 97 | ## 4. 反序列化 DolphinDB -> Python(设置 pickleTableToList=True) 98 | 99 | ### Table 100 | 101 | 指定使用 PROTOCOL_PICKLE 协议,在执行 run 方法时,若指定额外参数 `pickleTableToList=True`,则下载 Table 型数据将得到一个 list 数据,且 list 的每个元素都是 np.ndarray。如果下载 Table 型数据的数据列为 Array Vector 列,须确保每个元素的长度一致,其对应数据类型为二维 np.ndarray。 102 | 103 | 本节详细的类型转换规则和 [3.1.1 PROTOCOL_DDB 中 5 小节](./3.1.1_PROTOCOL_DDB.md)一致。 104 | 开启附加参数 pickleTableToList 后,如果执行脚本的返回值数据形式为 Table,则对应的 Python 对象为 list 而非 pd.DataFrame。其中,list 中的每一元素(np.ndarray)都表示 Table 中的一列。 105 | 106 | 和 PROTOCOL_DDB 协议的附加参数稍有不同,PROTOCOL_DDB 协议的附加参数会作为flag的一部分发送至服务端。 107 | -------------------------------------------------------------------------------- /README_CN_NEW/3_AdvancedOperations/3.1_DataTypeCasting/3.1.3_PROTOCOL_ARROW.md: -------------------------------------------------------------------------------- 1 | # PROTOCOL_ARROW 2 | 3 | [Apache Arrow 协议](https://arrow.apache.org/)是一种用于对大型数据集进行序列化和反序列化的协议,可以跨平台、跨语言地进行高效数据传输。DolphinDB 提供的 [formatArrow 插件](https://github.com/dolphindb/DolphinDBPlugin/tree/release200/formatArrow)在 Apache Arrow 协议的基础上进行类型适配,实现 DolphinDB 和 API 之间通过 Apache Arrow 协议进行数据传输。用户在安装 [formatArrow 插件](https://github.com/dolphindb/DolphinDBPlugin/tree/release200/formatArrow)后方可使用 Apache Arrow 协议进行传输。 4 | 5 | > 注意:若用户未安装 [formatArrow 插件](https://github.com/dolphindb/DolphinDBPlugin/tree/release200/formatArrow),即便已指定启用 PROTOCOL_ARROW 协议,API 将默认使用 PROTOCOL_DDB 协议进行传输,并返回 DataFrame。 6 | 7 | ## 1. PROTOCOL_ARROW 数据形式支持表 8 | 9 | 对于 API 而言,PROTOCOL_ARROW 协议目前仅支持 Table 型数据的反序列化,且不支持开启压缩模式。 10 | 11 | | 附加参数 | 数据形式 | 序列化 | 反序列化 | 12 | | :------- | :------- | :----- | :------- | 13 | | 无 | Table | 不支持 | 支持 | 14 | 15 | ## 2. 启用 PROTOCOL_ARROW 16 | 17 | 如果使用 PROTOCOL_ARROW 协议,须安装 [formatArrow 插件](https://github.com/dolphindb/DolphinDBPlugin/tree/release200/formatArrow)和 9.0.0 以上版本的 [pyarrow](https://pypi.org/project/pyarrow/)。 18 | 19 | 在以下示例中,session 和 DBConnectionPool 通过设置参数 *protocol* 指定启用 PROTOCOL_ARROW 协议。 20 | 21 | ```python 22 | import dolphindb as ddb 23 | import dolphindb.settings as keys 24 | 25 | s = ddb.session(protocol=keys.PROTOCOL_ARROW) 26 | s.connect("localhost", 8848, "admin", "123456") 27 | 28 | pool = ddb.DBConnectionPool("localhost", 8848, "admin", "123456", 10, protocol=keys.PROTOCOL_ARROW) 29 | ``` 30 | 31 | ## 3. 反序列化 DolphinDB -> Python 32 | 33 | ### **Table** 34 | 35 | 使用 PROTOCOL_ARROW 协议时,DolphinDB 中的 Table 对应 Python 中的 pyarrow.Table。详细类型转换的对照信息如下表所示: 36 | 37 | | DolphinDB类型 | Arrow类型 | 38 | | :------------ | :---------------------- | 39 | | BOOL | boolean | 40 | | CHAR | int8 | 41 | | SHORT | int16 | 42 | | INT | int32 | 43 | | LONG | int64 | 44 | | DATE | date32 | 45 | | MONTH | date32 | 46 | | TIME | time32(ms) | 47 | | MINUTE | time32(s) | 48 | | SECOND | time32(s) | 49 | | DATETIME | timestamp(s) | 50 | | TIMESTAMP | timestamp(ms) | 51 | | NANOTIME | time64(ns) | 52 | | NANOTIMESTAMP | timestamp(ns) | 53 | | DATEHOUR | timestamp(s) | 54 | | FLOAT | float32 | 55 | | DOUBLE | float64 | 56 | | SYMBOL | dictionary(int32, utf8) | 57 | | STRING | utf8 | 58 | | IPADDR | utf8 | 59 | | UUID | fixed_size_binary(16) | 60 | | INT128 | fixed_size_binary(16) | 61 | | BLOB | large_binary | 62 | | DECIMAL32(X) | decimal128(38, X) | 63 | | DECIMAL64(X) | decimal128(38, X) | 64 | 65 | **注1:** PROTOCOL_ARROW 协议同时支持以上除了 DECIMAL32/DECIMAL64 外的 Array Vector 类型。 66 | 67 | **注2:** 使用 PROTOCOL_ARROW 协议获取 pyarrow.Table 数据后,如果需要将数据转换为 pandas.DataFrame,由于 DolphinDB NANOTIME 数据类型对应 Arrow 的 time64(ns) 类型,因此要求进行转换的小数数值必须为 0.001 的倍数,否则会提示 `Value xxxxxxx has non-zero nanoseconds`。 68 | 69 | 代码示例: 70 | 71 | ```python 72 | >>> s.run("table(1..3 as a)") 73 | pyarrow.Table 74 | a: int32 75 | ---- 76 | a: [[1,2,3]] 77 | ``` 78 | -------------------------------------------------------------------------------- /README_CN_NEW/3_AdvancedOperations/3.1_DataTypeCasting/3.1.4_ForceTypeCasting.md: -------------------------------------------------------------------------------- 1 | # 强制类型转换 2 | 3 | 在使用 upload 接口上传 pandas.DataFrame 时,由于 DolphinDB 类型系统与 Python 类型系统不是一一对应的关系,所以无法直接上传部分类型的数据,例如 UUID、IPADDR、SECOND 等类型。 4 | 5 | 自 1.30.22.1 版本起,Python API 支持强制类型转换。在使用强制类型转换时,需要在待上传的 pandas.DataFrame 上增加属性 \_\_DolphinDB_Type\_\_,该属性是一个 Python 字典对象,键为列名,值为指定的类型。示例如下: 6 | 7 | ```python 8 | import dolphindb as ddb 9 | import pandas as pd 10 | import numpy as np 11 | 12 | s = ddb.session() 13 | s.connect("localhost", 8848, "admin", "123456") 14 | df = pd.DataFrame({ 15 | 'cint': [1, 2, 3], 16 | 'csymbol': ["aaa", "bbb", "aaa"], 17 | 'cblob': ["a1", "a2", "a3"], 18 | }) 19 | 20 | s.upload({"df_wrong": df}) 21 | print(s.run("schema(df_wrong)")['colDefs']) 22 | ``` 23 | 24 | 输出如下: 25 | 26 | ``` 27 | name typeString typeInt extra comment 28 | 0 cint LONG 5 NaN 29 | 1 csymbol STRING 18 NaN 30 | 2 cblob STRING 18 NaN 31 | ``` 32 | 33 | 参考[章节 3.1.1](./3.1.1_PROTOCOL_DDB.md) 可知,如果直接上传 `df`,此时 `cint` 列的 dtype 为 int64,仍会作为 LONG 类型上传;而由于 SYMBOL、BLOB 没有对应的类型,故直接上传的 str 型数据会被视作 STRING 类型。 34 | 35 | 导入 dolphindb.settings,为待上传的 pandas.DataFrame 添加属性,其字典键为需要指定类型的列名。 36 | 37 | ```python 38 | import dolphindb.settings as keys 39 | 40 | df.__DolphinDB_Type__ = { 41 | 'cint': keys.DT_INT, 42 | 'csymbol': keys.DT_SYMBOL, 43 | 'cblob': keys.DT_BLOB, 44 | } 45 | 46 | s.upload({"df_true": df}) 47 | print(s.run("schema(df_true)")['colDefs']) 48 | ``` 49 | 50 | 输出如下: 51 | 52 | ``` 53 | name typeString typeInt extra comment 54 | 0 cint INT 4 NaN 55 | 1 csymbol SYMBOL 17 NaN 56 | 2 cblob BLOB 32 NaN 57 | ``` 58 | 59 | 再次上传后,由输出结果可知 pandas.DataFrame 的各列都被正确转换为指定的类型。 60 | 61 | 自 1.30.22.4 版本起,Python API 支持指定数据类型为 Decimal32 / Decimal64 的精度。使用示例如下: 62 | 63 | ```python 64 | from decimal import Decimal 65 | df = pd.DataFrame({ 66 | 'decimal32': [Decimal("NaN"), Decimal("1.22")], 67 | 'decimal64': [Decimal("1.33355"), Decimal("NaN")], 68 | }) 69 | df.__DolphinDB_Type__ = { 70 | 'decimal32': [keys.DT_DECIMAL32, 2], 71 | 'decimal64': [keys.DT_DECIMAL64, 5], 72 | } 73 | 74 | s.upload({'df': df}) 75 | print(s.run("schema(df)")['colDefs']) 76 | print('-' * 30) 77 | print(s.run("df")) 78 | ``` 79 | 80 | 输出如下: 81 | 82 | ```python 83 | name typeString typeInt extra comment 84 | 0 decimal32 DECIMAL32(2) 37 2 85 | 1 decimal64 DECIMAL64(5) 38 5 86 | ------------------------------ 87 | decimal32 decimal64 88 | 0 NaN 1.33355 89 | 1 1.22 NaN 90 | ``` 91 | -------------------------------------------------------------------------------- /README_CN_NEW/3_AdvancedOperations/3.2_WriteOptions/3.2_WriteOptions.md: -------------------------------------------------------------------------------- 1 | # 多种写入方案 2 | 3 | Python API 提供多种写入方案,可以适配不同场景的写入需求,下面将详细介绍各种写入方案之间的区别。 4 | 5 | | 场景条件 | 建议使用 | 说明 | 6 | |:--------|:------------|:-----| 7 | | 上传变量 | upload | 直接上传变量,适用于所有类型,类型转换更为自由 | 8 | | 执行服务端函数时附带参数 | run | 作为参数上传,同样适用所有类型 | 9 | | 面向对象地在 Python 端操作服务端数据库、数据表 | table 等相关方法 | 便捷地在 Python 端使用服务端数据 | 10 | | 写入批量数据,且数据结构可以较为便捷地转换为 pandas.DataFrame | tableAppender
tableUpsert
PartitionedTableAppender | 自动类型转换,不需要关心类型对应的问题,具体使用请参考本文 2.4| 11 | | 写入流式数据 | MultithreadedTableWriter | 自动类型转换,将流式数据批量发送至服务端 | 12 | | API资源较为紧张,服务端资源较为充裕 | 异步模式 session | 将写入压力转移至服务端,通常不推荐该方式写入 | 13 | 14 | ## 1. upload, table 与 tableInsert 15 | 16 | ### 1.1 upload 17 | 18 | session.upload 方法可以直接上传数据。这种方案适用于上传各种数据形式,例如 TABLE、DICTIONARY、VECTOR 等。在调用 upload 时,须指定数据上传后的变量名。代码示例如下: 19 | 20 | ```python 21 | >>> data = pd.DataFrame({· 22 | ... 'clong': [1, 2, 3], 23 | ... }) 24 | ... 25 | >>> s.upload({'data': data}) 26 | ``` 27 | 28 | 此外,在 upload 上传数据时,因为 DolphinDB 的数据类型和 Python 的原生数据类型、numpy 以及 pandas 的数据类型无法一一对应,因此会出现某些数据类型无法直接上传的情况,例如 UUID、MINUTE 等数据类型。1.30.22.1 及之后版本的 Python API 新增强制类型转换,可以在调用 upload 上传 pd.DataFrame 时,通过添加 \_\_DolphinDB_Type\_\_ 指定待上传列的类型。 29 | 30 | ### 1.2 table 31 | 32 | session.table 方法可以传入一个本地数据对象,例如 pandas.DataFrame/dict/...,将该数据对象作为一个临时表上传到 DolphinDB,其生存周期由 Python API 进行维护。该种方法仅支持上传 TABLE 数据形式的对象。其内部的实现调用了 session.upload,因此也可以通过指定 \_\_DolphinDB_Type\_\_ 以实现强制类型转换。 33 | 34 | 但和 upload 方法稍有不同,upload 方法上传的变量需要手动析构上传变量的生存周期,若处理不当可能会导致 session 占用内存过大。而 table 方法返回一个 Python API 定义的 Table 类对象,在析构时会同时析构 DolphinDB 服务端的该临时表,不需要手动维护生存周期。 35 | 36 | 代码示例如下: 37 | 38 | ```python 39 | >>> data = pd.DataFrame({ 40 | ... 'clong': [1, 2, 3], 41 | ... }) 42 | ... 43 | >>> tb = s.table(data=data) 44 | >>> tb 45 | 46 | ``` 47 | 48 | ### 1.3 tableInsert 49 | 50 | 不同于前两种方法,tableInsert 并非 Python API 提供的方法,而是通过 run 方法上传参数的功能以实现写入的方式。 51 | 52 | * 从数据序列化的角度来看,该方法和 upload, table 没有区别。 53 | * 从使用的角度来说,在某些不需要指定上传变量名的流程中,将数据作为 run 方法的参数上传更为简单直接。例如在写入表时,无需先上传临时表到服务端,然后调用 tableInsert 写入,而是直接作为 tableInsert 的参数上传并写入,以此可简化流程。 54 | 55 | 代码示例如下: 56 | 57 | ```python 58 | >>> data = pd.DataFrame({ 59 | ... 'clong': [1, 2, 3], 60 | ... }) 61 | ... 62 | >>> s.run("t = table(100:0, [`clong], [LONG])") 63 | >>> s.run("tableInsert{t}", data) 64 | 3 65 | ``` 66 | 67 | 此外,如果代码写入部分涉及到访问权限问题,或者写入时有较长步骤,则用户可以将这些内容封装为 functionview,再将需要上传的内容作为 functionview 的参数上传至服务端。 68 | 69 | 参考链接: 70 | 71 | * [DolphinDB 用户手册-部分应用](https://www.dolphindb.cn/cn/help/200/Functionalprogramming/PartialApplication.html) 72 | * [DolphinDB 用户手册-函数视图](https://www.dolphindb.cn/cn/help/200/DatabaseandDistributedComputing/DatabaseOperations/FunctionView.html) 73 | 74 | ### 1.4 upload, table 与 tableInsert 的对比 75 | 76 | 这三种方式本质上都是同一种写入流程,即先判断待上传变量的数据形式、类型,类型转换后作为函数的参数/变量上传至服务端。 77 | 78 | | 方法 | 实现原理 | 适用范围 | 79 | |:-------|:------------|:----------------| 80 | | upload | 直接上传 | 各种数据形式 | 81 | | table | 封装 upload 方法| Table 数据形式对象 | 82 | | run| 通过 run 方法上传参数的功能 | 任何需要传入参数的服务器函数、函数视图 | 83 | 84 | ## 2. tableAppender, tableUpsert 与 PartitionedTableAppender 85 | 86 | ### 2.1 tableAppender 87 | 88 | TableAppender 的内部实现等价于 `run("tableInsert{tableName}", data)`。和直接调用不同的是,tableAppender 在构造时通过获得待写入表的列类型,能够根据列类型实现自动类型转换。 89 | 90 | 代码示例: 91 | 92 | ```python 93 | >>> s.run("t = table(100:0, `csymbol`cvalue, [SYMBOL, LONG])") 94 | >>> tbAppender = ddb.tableAppender(tableName="t", ddbSession=s) 95 | >>> data = pd.DataFrame({ 96 | ... 'csymbol': ["aaa", "bbb", "aaa"], 97 | ... 'cvalue': [1, 2, 3], 98 | ... }) 99 | ... 100 | >>> tbAppender.append(data) 101 | 3 102 | ``` 103 | 104 | ### 2.2 tableUpsert 105 | 106 | tableUpsert 同样会在构造时获取待更新表的列类型,再根据列类型实现自动类型转换。其内部实现等价于 upsert! 方法,故在构造 tableUpsert 时需指定键值列。 107 | 108 | 代码示例: 109 | 110 | ```python 111 | >>> s.run("t = keyedTable(`csymbol, 100:0, `csymbol`cvalue, [SYMBOL, LONG])") 112 | >>> tbUpserter = ddb.tableUpsert(tableName="t", ddbSession=s, keyColNames=["csymbol"]) 113 | >>> data = pd.DataFrame({ 114 | ... 'csymbol': ["aaa", "bbb", "aaa"], 115 | ... 'cvalue': [1, 2, 3], 116 | ... }) 117 | ... 118 | >>> tbUpserter.upsert(data) 119 | ``` 120 | 121 | 参考链接: 122 | 123 | * [DolphinDB 用户手册-upsert!](https://www.dolphindb.cn/cn/help/200/FunctionsandCommands/FunctionReferences/u/upsert!.html) 124 | 125 | ### 2.3 PartitionedTableAppender 126 | 127 | 不同于前两种都是基于 session 写入数据的方法,PartitionedTableAppender 需要在构造时传入 DBConnectionPool 对象,进而将数据并发地写入分区表中。同样的,PartitionedTableAppender 也支持写入数据时的自动类型转换。 128 | 129 | 代码示例: 130 | 131 | ```python 132 | >>> if s.existsDatabase("dfs://test"): 133 | ... s.dropDatabase("dfs://test") 134 | ... 135 | >>> db = s.database(dbPath="dfs://test", partitionType=keys.VALUE, partitions=[1, 2, 3]) 136 | >>> s.run("schema_table = table(100:0, `cindex`cvalue, [INT, DOUBLE]);") 137 | >>> schema_table = s.table(data="schema_table") 138 | >>> tb = db.createPartitionedTable(table=schema_table, tableName="pt", partitionColumns="cindex") 139 | >>> pool = ddb.DBConnectionPool("localhost", 8848, 3, "admin", "123456") 140 | >>> ptableAppender = ddb.PartitionedTableAppender(dbPath="dfs://test", tableName="pt", partitionColName="cindex", dbConnectionPool=pool) 141 | >>> data = pd.DataFrame({ 142 | ... 'cindex': [1, 2, 3, 4, 5], 143 | ... 'cvalue': [1.1, 2.2, 3.3, 4.4, 5.5] 144 | ... }) 145 | ... 146 | >>> ptableAppender.append(data) 147 | 5 148 | ``` 149 | 150 | ### 2.4 tableAppender, tableUpsert 与 PartitionedTableAppender 的对比 151 | 152 | 这三种方式都能够将 pandas.DataFrame 形式的数据自动类型转换后写入到指定表中,但是三者的适用场景有一定区别。 153 | 154 | | 方法 | 实现原理 | 适用范围 | 155 | |:-------|:------------|:----------------| 156 | | tableAppender | 内部实现等价于 `run("tableInsert{tableName}", data)`| 所有表的写入| 157 | | tableUpsert | 内部实现等价于 upsert! 方法| 键值表、索引表、分区表的更新写入| 158 | | PartitionedTableAppender | 在构造时传入 DBConnectionPool 对象,再将数据并发地写入分区表中| 分区表等支持同时写入的表| 159 | 160 | ## 3. MTW, BTW 与 Async tableInsert 161 | 162 | ### 3.1 MultithreadedTableWriter 163 | 164 | MTW 在后台启用多个 C++ 线程,异步地进行数据的类型转换和上传写入。对于每个表,都需要构造一个对应的 MTW 对象进行写入。在写入时,前台调用 insert 后,并不立刻将数据进行转换,而是先将数据放入待转换队列,等待转换线程将数据转换完毕后放入写入队列。最后由多个写入队列向服务端写入数据。 165 | 166 | 代码示例: 167 | 168 | ```python 169 | >>> if s.existsDatabase("dfs://test"): 170 | ... s.dropDatabase("dfs://test") 171 | >>> db = s.database(dbPath="dfs://test", partitionType=keys.VALUE, partitions=[1, 2, 3]) 172 | >>> s.run("schema_table = table(100:0, `cindex`cvalue, [INT, DOUBLE])") 173 | >>> schema_table = s.table(data="schema_table") 174 | >>> pt = db.createPartitionedTable(table=schema_table, tableName="pt", partitionColumns="cindex") 175 | >>> writer = ddb.MultithreadedTableWriter("localhost", 8848, "admin", "123456", dbPath="dfs://test", tableName="pt", threadCount=1) 176 | >>> for i in range(100): 177 | ... writer.insert(i, i*1.1) 178 | >>> writer.waitForThreadCompletion() 179 | >>> res = writer.getStatus() 180 | >>> if res.succeed(): 181 | ... print("Data successfully written.") 182 | ... 183 | Data successfully written. 184 | ``` 185 | 186 | ### 3.2 BatchTableWriter 187 | 188 | BTW 仅为每张表创建一个写入线程,不同于 MTW,BTW 在 insert 时进行类型转换,总体性能较差。 189 | 190 | > **注意: 目前已经停止维护 BTW。** 191 | 192 | ### 3.3 Async tableInsert 193 | 194 | 和 tableInsert 方法类似,Async tableInsert 并非 API 提供的方法,而是在异步模式 session 中调用 run 方法,将待上传数据作为参数上传的一种方式。参考[章节 2.5.1](../../2_BasicOperations/2.5_AsyncWrites/2.5.1_SessionAsyncMode.md),该方法的工作原理是 session 的异步模式执行脚本时,仅需将脚本发送至服务端,方法立刻返回,而无需等待脚本执行完毕再返回。 195 | 196 | 代码示例: 197 | 198 | ```python 199 | >>> s = ddb.session(enableASYNC=True) 200 | >>> s.connect("localhost", 8848, "admin", "123456") 201 | >>> s.run("t = table(100:0, `cindex`cvalue, [INT, DOUBLE]);") 202 | >>> data = pd.DataFrame({ 203 | ... 'cindex': [1, 2, 3, 4, 5], 204 | ... 'cvalue': [1.1, 2.2, 3.3, 4.4, 5.5] 205 | ... }) 206 | ... 207 | >>> for i in range(100): 208 | ... s.run("tableInsert{t}", data) 209 | ... 210 | ``` 211 | 212 | ### 3.4 MTW, BTW 与 Async tableInsert 的对比 213 | 214 | 这三种写入方式都是异步写入,工作原理上稍有不同。 215 | 216 | | 方法名 | 实现原理 | 适用范围 | 优点| 缺点| 217 | |:-------|:------------|:----------------|:-------|:------------| 218 | | MTW | 采用后台 C++ 写入线程的处理方式,提供 *batchSize*, *throttle* 参数用于指定批数据处理的粒度和等待时间| 适用于流式数据场景 | 根据列类型自动转换,将流式数据批量发送至服务端,减少网络影响 | 受制于 Python 本身的全局解释器锁,MTW 在类型转换时难以利用多线程提速。| 219 | | BTW | 采用后台 C++ 写入线程的处理方式,每隔 100 ms 将待写入数据发送至服务端| 适用于流式数据场景 | 批量写入流式数据,减少网络影响 | 无法根据待写入表的列类型进行自动类型转换| 220 | | Async tableInsert | 本质上利用了 session 的异步模式,和 run 方法传入参数| 适用于网络带宽资源紧张的情况| 有效降低网络占用和等待时间| 将写入压力转移至服务端,可能会造成服务端资源占用过多。
如果单次写入仅为一条数据,非批量数据,则可能会占用大量服务器资源。| 221 | 222 | * 在写入阶段,后台多个 C++ 工作线程可以有效进行数据分流和批量上传,降低网络状况带来的影响。 223 | * 当前版本,BTW 已经停止维护,MTW 基本可以替代所有 BTW 的写入场景。 224 | -------------------------------------------------------------------------------- /README_CN_NEW/3_AdvancedOperations/3.3_SubscriptionOptions/3.3_SubscriptionOptions.md: -------------------------------------------------------------------------------- 1 | # 流订阅模式 2 | 3 | 在 Python API 中,共推荐使用四种流订阅模式:单条订阅、批量订阅(设置 `msgAsTable=False`)、批量订阅(设置 `msgAsTable=True`)和异构流表订阅。下面将通过四个示例来分别介绍如何使用这四种订阅模式,以及各种订阅之间的区别。有关流订阅相关参数的介绍,请参考章节 [2.4 流订阅](../../2_BasicOperations/2.4_Subscription/2.4_Subscription.md)。 4 | 5 | ## 1. 单条订阅 6 | 7 | 使用单条订阅模式,不需要指定 *batchSize*,此时 *msgAsTable* 应为 False,*throttle* 参数无效。 8 | 9 | 下例中,首先通过 session.run 执行脚本来构造流表,然后调用 session.enableStreaming 方法启用流订阅,再定义回调函数 handler。开始订阅后,调用 session.run 执行写入脚本,API 立刻收到消息并将结果打印出来。等待3秒后,调用 unsubscribe 取消订阅。 10 | 11 | ```python 12 | import dolphindb as ddb 13 | import numpy as np 14 | import time 15 | 16 | s = ddb.session() 17 | s.connect("192.168.1.113", 8848, "admin", "123456") 18 | 19 | s.run(""" 20 | share streamTable(10000:0,`time`sym`price`id, [TIMESTAMP,SYMBOL,DOUBLE,INT]) as trades 21 | """) 22 | 23 | s.enableStreaming() 24 | 25 | def handler(lst): 26 | print(lst) 27 | 28 | s.subscribe("192.168.1.113", 8848, handler, "trades", "SingleMode", offset=-1) 29 | 30 | s.run("insert into trades values(take(now(), 6), take(`000905`600001`300201`000908`600002, 6), rand(1000,6)/10.0, 1..6)") 31 | 32 | time.sleep(3) 33 | 34 | s.unsubscribe("192.168.1.113", 8848, "trades", "SingleMode") 35 | ``` 36 | 37 | 输出结果如下所示: 38 | 39 | ``` 40 | [numpy.datetime64('2023-03-17T12:06:30.439'), '000905', 36.7, 1] 41 | [numpy.datetime64('2023-03-17T12:06:30.439'), '600001', 80.7, 2] 42 | [numpy.datetime64('2023-03-17T12:06:30.439'), '300201', 68.7, 3] 43 | [numpy.datetime64('2023-03-17T12:06:30.439'), '000908', 52.2, 4] 44 | [numpy.datetime64('2023-03-17T12:06:30.439'), '600002', 45.1, 5] 45 | [numpy.datetime64('2023-03-17T12:06:30.439'), '000905', 55.1, 6] 46 | ``` 47 | 48 | 在流订阅中,API 内部仅使用 PROTOCOL_DDB 协议进行反序列化。在单条订阅模式下,DolphinDB 发送的数据由 API 接收后,每一行数据将从 AnyVector 转换为 list。有关 AnyVector 转换的详细说明,请参考章节 [3.1.1 DDB](../3.1_DataTypeCasting/3.1.1_PROTOCOL_DDB.md)。 49 | 50 | ## 2. 批量订阅(设置 `msgAsTable=False`) 51 | 52 | 若要使用批量订阅模式,则须指定参数 *batchSize* 和 *throttle*,表示当接收到的消息条数超过 *batchSize*,或者处理消息前的等待时间超过 *throttle*,则会触发一次回调,将数据传递给 *handler*,并且按批次来处理数据。当指定 `msgAsTable=False` 时,收到的一批数据将是一个列表 list,其中每一项都是单条数据,结构和单条订阅模式中的一条数据一致。 53 | 54 | 在下例中,分别指定 `batchSize=2`,`throttle=0.1`,表示在 0.1 秒时间内,如果收到了 2 条数据,则立刻调用回调函数传入这2条数据;如果等待的 0.1 秒仅收到 1 条数据,则会在等待结束后调用回调函数传入这 1 条数据。与单条订阅模式相似,批量订阅模式下通过 PROTOCOL_DDB 协议进行数据类型转换,每条数据将从 AnyVector 转为 list。 55 | 56 | ```python 57 | import dolphindb as ddb 58 | import numpy as np 59 | import time 60 | 61 | s = ddb.session() 62 | s.connect("192.168.1.113", 8848, "admin", "123456") 63 | 64 | s.run(""" 65 | share streamTable(10000:0,`time`sym`price`id, [TIMESTAMP,SYMBOL,DOUBLE,INT]) as trades 66 | """) 67 | 68 | s.enableStreaming() 69 | 70 | def handler(lsts): 71 | print(lsts) 72 | 73 | s.subscribe("192.168.1.113", 8848, handler, "trades", "MultiMode1", offset=-1, batchSize=2, throttle=0.1, msgAsTable=False) 74 | 75 | s.run("insert into trades values(take(now(), 6), take(`000905`600001`300201`000908`600002, 6), rand(1000,6)/10.0, 1..6)") 76 | 77 | time.sleep(3) 78 | 79 | s.unsubscribe("192.168.1.113", 8848, "trades", "MultiMode1") 80 | ``` 81 | 82 | 输出结果如下: 83 | 84 | ``` 85 | [[numpy.datetime64('2023-03-17T14:46:27.358'), '000905', 21.2, 1], [numpy.datetime64('2023-03-17T14:46:27.358'), '600001', 39.8, 2]] 86 | [[numpy.datetime64('2023-03-17T14:46:27.358'), '300201', 84.0, 3], [numpy.datetime64('2023-03-17T14:46:27.358'), '000908', 26.2, 4]] 87 | [[numpy.datetime64('2023-03-17T14:46:27.358'), '600002', 25.1, 5], [numpy.datetime64('2023-03-17T14:46:27.358'), '000905', 42.7, 6]] 88 | ``` 89 | 90 | ## 3. 批量订阅(设置 `msgAsTable=True`) 91 | 92 | 开启批量订阅时,如果指定 `msgAsTable=True`,则每一批数据将基于消息块(由 DolphinDB 中的参数 *maxMsgNumPerBlock* 进行配置)处理消息。当收到的记录总数大于等于 *batchSize* 时,*handler* 会对所有达到条件的消息块进行处理。 93 | 94 | 下例中,在开启批量订阅模式后,调用 `session.run` 执行脚本,向流表中写入 1500 条数据,此时 DolphinDB 中的参数 *maxMsgNumPerBlock* 为默认值 1024,因此 API 接收到 1024 条数据后,消息条数恰好超过 `batchSize=1000`,立刻调用回调函数;随后收到剩下的 476 条数据,等待 0.1 秒仍无新数据,再次调用回调函数。因此最后的输出结果为两个长度分别为 1024 和 476 的 DataFrame。 95 | 96 | ```python 97 | import dolphindb as ddb 98 | import numpy as np 99 | import time 100 | 101 | s = ddb.session() 102 | s.connect("192.168.1.113", 8848, "admin", "123456") 103 | 104 | s.run(""" 105 | share streamTable(10000:0,`time`sym`price`id, [TIMESTAMP,SYMBOL,DOUBLE,INT]) as trades 106 | """) 107 | 108 | s.enableStreaming() 109 | 110 | def handler(lsts): 111 | print(lsts) 112 | 113 | s.subscribe("192.168.1.113", 8848, handler, "trades", "MultiMode2", offset=-1, batchSize=1000, throttle=0.1, msgAsTable=True) 114 | 115 | s.run("n=1500;insert into trades values(take(now(), n), take(`000905`600001`300201`000908`600002, n), rand(1000,n)/10.0, 1..n)") 116 | 117 | time.sleep(3) 118 | 119 | s.unsubscribe("192.168.1.113", 8848, "trades", "MultiMode2") 120 | ``` 121 | 122 | 如果修改上述示例中的 *batchSize* 为 1500,发送的数据为 3000 条,服务端发送第一个消息块(长度为 1024)后,不触发回调函数;服务端发送第二个消息块(长度为 1024)后,API 收到的数据条数共为 2048,超过 `batchSize=1500`,立刻触发回调函数,通过 PROTOCOL_DDB 协议将收到的消息从 Table 转换为 pandas.DataFrame;服务端发送第三个消息块(长度为 952)后,经过 0.1 秒,仍没有接收到新数据,此时触发回调函数。在这种情况下,回调函数中收到的数据,长度分别为 2048 和 952。 123 | 124 | ## 4. 异构流表订阅 125 | 126 | DolphinDB 自 1.30.17 及 2.00.5 版本开始,支持通过 [replay](https://www.dolphindb.cn/cn/help/FunctionsandCommands/FunctionReferences/r/replay.html) 函数将多个结构不同的流数据表回放(序列化)到一个流表里,这个流表被称为异构流表。Python API 自 1.30.19 版本开始新增 streamDeserializer 类,用于构造异构流表反序列化器,以实现对异构流表的订阅和反序列化操作。 127 | 128 | ### 4.1 异构流表反序列化器 129 | 130 | Python API 通过 streamDeserializer 类来构造异构流表反序列化器,接口定义如下: 131 | 132 | ```python 133 | streamDeserializer(sym2table, session=None) 134 | ``` 135 | 136 | * *sym2table*:字典对象,其结构与 replay 回放到异构流表的输入表结构保持一致。streamDeserializer 将根据 *sym2table* 指定的结构对注入的数据进行反序列化。 137 | * *session*:已连接 DolphinDB 的 session 对象,默认为 None。如果不指定,将会在订阅时自动获取当前连接。 138 | 139 | 下例构造一个简单的异构流表反序列化器: 140 | 141 | ```python 142 | sd = ddb.streamDeserializer({ 143 | 'msg1': ["dfs://test_StreamDeserializer_pair", "pt1"], 144 | 'msg2': "pt2", 145 | }, session=s) 146 | ``` 147 | 148 | 其中,sym2table 的键为不同输入表的标记,用于区分不同输入表的数据;sym2table 的值为表名,或由分区数据库地址和表名组成的列表(或元组)。订阅时,会通过构造时传入的 session 调用 schema 方法获得 sym2table 键值对应的表的结构,因此并不一定需要填输入表名,只需要和输入表结构一致即可。 149 | 150 | 关于构造 DolphinDB 异构流表的具体脚本,请参照[异构回放示例](https://gitee.com/dolphindb/Tutorials_CN/blob/master/stock_market_replay.md#22-%E5%BC%82%E6%9E%84%E5%9B%9E%E6%94%BE)。 151 | 152 | > **注意**: 153 | > 154 | > 1. 在 DolphinDB 中构造异构流表时,字典中 key 对应的表应为内存表或 replayDS 定义的数据源,请参考 [replay](https://www.dolphindb.cn/cn/help/FunctionsandCommands/FunctionReferences/r/replay.html)。 155 | > 2. API 端构造异构流表反序列化器时,*sym2table* 的值对应的表(可以为分区表、流表或者内存表)结构需要和 DolphinDB 中构造异构流表使用的表结构一致。 156 | > 3. 订阅异构流表时,*msgAsTable* 不能为 True,可以指定 *batchSize* 和 *throttle*。 157 | 158 | ### 4.2 订阅示例 1 (分区表数据源作为输入表) 159 | 160 | 下例中,首先在 DolphinDB 中定义由两个分区表组合而成的异构流表,然后在 Python 客户端定义异构流表反序列化器 sd,再根据 sd 中指定表的结构反序列化数据。在输出结果中,每条数据的末尾都增加了一个字段,用于标识当前数据的 symbol。 161 | 162 | #### 构造异构流表 163 | 164 | 首先在 DolphinDB 中定义输出表,即要订阅的异构流表。 165 | 166 | ``` 167 | try{dropStreamTable(`outTables)}catch(ex){} 168 | share streamTable(100:0, `timestampv`sym`blob`price1,[TIMESTAMP,SYMBOL,BLOB,DOUBLE]) as outTables 169 | ``` 170 | 171 | 然后定义两张输入表,均为分布式分区表。 172 | 173 | ``` 174 | n = 6; 175 | dbName = 'dfs://test_StreamDeserializer_pair' 176 | if(existsDatabase(dbName)){ 177 | dropDB(dbName)} 178 | db = database(dbName,RANGE,2012.01.01 2013.01.01 2014.01.01 2015.01.01 2016.01.01 2017.01.01 2018.01.01 2019.01.01) 179 | table1 = table(100:0, `datetimev`timestampv`sym`price1`price2, [DATETIME, TIMESTAMP, SYMBOL, DOUBLE, DOUBLE]) 180 | table2 = table(100:0, `datetimev`timestampv`sym`price1, [DATETIME, TIMESTAMP, SYMBOL, DOUBLE]) 181 | tableInsert(table1, 2012.01.01T01:21:23 + 1..n, 2018.12.01T01:21:23.000 + 1..n, take(`a`b`c,n), rand(100,n)+rand(1.0, n), rand(100,n)+rand(1.0, n)) 182 | tableInsert(table2, 2012.01.01T01:21:23 + 1..n, 2018.12.01T01:21:23.000 + 1..n, take(`a`b`c,n), rand(100,n)+rand(1.0, n)) 183 | pt1 = db.createPartitionedTable(table1,'pt1',`datetimev).append!(table1) 184 | pt2 = db.createPartitionedTable(table2,'pt2',`datetimev).append!(table2) 185 | ``` 186 | 187 | 将分区表转为数据源后进行回放。 188 | 189 | ``` 190 | re1 = replayDS(sqlObj=, dateColumn=`datetimev, timeColumn=`timestampv) 192 | d = dict(['msg1', 'msg2'], [re1, re2]) 193 | replay(inputTables=d, outputTables=`outTables, dateColumn=`timestampv, timeColumn=`timestampv) 194 | ``` 195 | 196 | #### 订阅异构流表 197 | 198 | ```python 199 | import dolphindb as ddb 200 | 201 | # 异构流表反序列化器返回的数据末尾为异构流表反序列化器中 sym2table 指定的 key 202 | def streamDeserializer_handler(lst): 203 | if lst[-1]=="msg1": 204 | print("Msg1: ", lst) 205 | elif lst[-1]=='msg2': 206 | print("Msg2: ", lst) 207 | else: 208 | print("Error: ", lst) 209 | 210 | s = ddb.session() 211 | s.connect("192.168.1.113", 8848, "admin", "123456") 212 | s.enableStreaming() 213 | 214 | # 填入分区表数据库路径和表名的 list,以获取对应表结构 215 | sd = ddb.streamDeserializer({ 216 | 'msg1': ["dfs://test_StreamDeserializer_pair", "pt1"], 217 | 'msg2': ["dfs://test_StreamDeserializer_pair", "pt2"], 218 | }, session=s) 219 | s.subscribe(host="192.168.1.113", port=8848, handler=streamDeserializer_handler, tableName="outTables", actionName="action", offset=0, resub=False, 220 | msgAsTable=False, streamDeserializer=sd, userName="admin", password="123456") 221 | 222 | from threading import Event 223 | Event().wait() 224 | ``` 225 | 226 | 输出结果如下所示: 227 | 228 | ``` 229 | Msg2: [numpy.datetime64('2012-01-01T01:21:24'), numpy.datetime64('2018-12-01T01:21:23.001'), 'a', 18.43745171907358, 'msg2'] 230 | Msg1: [numpy.datetime64('2012-01-01T01:21:24'), numpy.datetime64('2018-12-01T01:21:23.001'), 'a', 65.69160503265448, 41.17562178615481, 'msg1'] 231 | Msg2: [numpy.datetime64('2012-01-01T01:21:25'), numpy.datetime64('2018-12-01T01:21:23.002'), 'b', 93.68146854126826, 'msg2'] 232 | Msg1: [numpy.datetime64('2012-01-01T01:21:25'), numpy.datetime64('2018-12-01T01:21:23.002'), 'b', 22.181119214976206, 38.162505637388676, 'msg1'] 233 | Msg2: [numpy.datetime64('2012-01-01T01:21:26'), numpy.datetime64('2018-12-01T01:21:23.003'), 'c', 51.19852650281973, 'msg2'] 234 | Msg1: [numpy.datetime64('2012-01-01T01:21:26'), numpy.datetime64('2018-12-01T01:21:23.003'), 'c', 16.937458558939397, 36.79589221812785, 'msg1'] 235 | Msg2: [numpy.datetime64('2012-01-01T01:21:27'), numpy.datetime64('2018-12-01T01:21:23.004'), 'a', 0.812068443512544, 'msg2'] 236 | Msg1: [numpy.datetime64('2012-01-01T01:21:27'), numpy.datetime64('2018-12-01T01:21:23.004'), 'a', 34.11729482654482, 29.094212289899588, 'msg1'] 237 | Msg2: [numpy.datetime64('2012-01-01T01:21:28'), numpy.datetime64('2018-12-01T01:21:23.005'), 'b', 93.43341179518029, 'msg2'] 238 | Msg1: [numpy.datetime64('2012-01-01T01:21:28'), numpy.datetime64('2018-12-01T01:21:23.005'), 'b', 9.413380537647754, 32.449754945002496, 'msg1'] 239 | Msg2: [numpy.datetime64('2012-01-01T01:21:29'), numpy.datetime64('2018-12-01T01:21:23.006'), 'c', 65.18307867064141, 'msg2'] 240 | Msg1: [numpy.datetime64('2012-01-01T01:21:29'), numpy.datetime64('2018-12-01T01:21:23.006'), 'c', 83.58133838768117, 54.27990723075345, 'msg1'] 241 | ``` 242 | 243 | ### 4.3 订阅示例 2 (内存表作为输入表) 244 | 245 | 下例中,在 DolphinDB 中定义了一个由两个内存表构成的异构流表,并在 Python 端使用共享内存表的表名构造反序列化器,最后指定 `batchSize=4` 进行批量订阅。可以看出,在总数据条数为6*2=12的情况下,数据首先按总条数分3批传入回调函数,在每批数据中,每条数据可能来自不同的输入表。因此,共调用回调函数3次,每次输出4条数据构成的一批数据。 246 | 247 | #### 构造异构流表 248 | 249 | ``` 250 | try{dropStreamTable(`outTables)}catch(ex){} 251 | // 构造输出流表 252 | share streamTable(100:0, `timestampv`sym`blob`price1,[TIMESTAMP,SYMBOL,BLOB,DOUBLE]) as outTables 253 | 254 | n = 6; 255 | table1 = table(100:0, `datetimev`timestampv`sym`price1`price2, [DATETIME, TIMESTAMP, SYMBOL, DOUBLE, DOUBLE]) 256 | table2 = table(100:0, `datetimev`timestampv`sym`price1, [DATETIME, TIMESTAMP, SYMBOL, DOUBLE]) 257 | tableInsert(table1, 2012.01.01T01:21:23 + 1..n, 2018.12.01T01:21:23.000 + 1..n, take(`a`b`c,n), rand(100,n)+rand(1.0, n), rand(100,n)+rand(1.0, n)) 258 | tableInsert(table2, 2012.01.01T01:21:23 + 1..n, 2018.12.01T01:21:23.000 + 1..n, take(`a`b`c,n), rand(100,n)+rand(1.0, n)) 259 | share table1 as pt1 260 | share table2 as pt2 261 | 262 | d = dict(['msg1', 'msg2'], [pt1, pt2]) 263 | replay(inputTables=d, outputTables=`outTables, dateColumn=`timestampv, timeColumn=`timestampv) 264 | ``` 265 | 266 | #### 订阅异构流表 267 | 268 | ```python 269 | import dolphindb as ddb 270 | 271 | def streamDeserializer_handler(lsts): 272 | print(lsts) 273 | 274 | s = ddb.session() 275 | s.connect("192.168.1.113", 8848, "admin", "123456") 276 | s.enableStreaming() 277 | 278 | sd = ddb.streamDeserializer({ 279 | 'msg1': "pt1", 280 | 'msg2': "pt2", 281 | }, session=s) 282 | s.subscribe(host="192.168.1.113", port=8848, handler=streamDeserializer_handler, tableName="outTables", actionName="action", offset=0, resub=False, batchSize=4, 283 | msgAsTable=False, streamDeserializer=sd, userName="admin", password="123456") 284 | 285 | from threading import Event 286 | Event().wait() 287 | ``` 288 | 289 | 输出结果如下所示: 290 | 291 | ``` 292 | [[numpy.datetime64('2012-01-01T01:21:24'), numpy.datetime64('2018-12-01T01:21:23.001'), 'a', 87.90784921264276, 'msg2'], [numpy.datetime64('2012-01-01T01:21:24'), numpy.datetime64('2018-12-01T01:21:23.001'), 'a', 14.867915444076061, 92.22166634746827, 'msg1'], [numpy.datetime64('2012-01-01T01:21:25'), numpy.datetime64('2018-12-01T01:21:23.002'), 'b', 80.60459423460998, 'msg2'], [numpy.datetime64('2012-01-01T01:21:25'), numpy.datetime64('2018-12-01T01:21:23.002'), 'b', 10.429520844481885, 29.480175042990595, 'msg1']] 293 | [[numpy.datetime64('2012-01-01T01:21:26'), numpy.datetime64('2018-12-01T01:21:23.003'), 'c', 12.45058359648101, 'msg2'], [numpy.datetime64('2012-01-01T01:21:26'), numpy.datetime64('2018-12-01T01:21:23.003'), 'c', 55.05597074679099, 88.84371786634438, 'msg1'], [numpy.datetime64('2012-01-01T01:21:27'), numpy.datetime64('2018-12-01T01:21:23.004'), 'a', 27.357952459948137, 'msg2'], [numpy.datetime64('2012-01-01T01:21:27'), numpy.datetime64('2018-12-01T01:21:23.004'), 'a', 57.705578718334436, 25.98224212951027, 'msg1']] 294 | [[numpy.datetime64('2012-01-01T01:21:28'), numpy.datetime64('2018-12-01T01:21:23.005'), 'b', 63.73548944480717, 'msg2'], [numpy.datetime64('2012-01-01T01:21:28'), numpy.datetime64('2018-12-01T01:21:23.005'), 'b', 65.34572763741016, 0.6374575316440314, 'msg1'], [numpy.datetime64('2012-01-01T01:21:29'), numpy.datetime64('2018-12-01T01:21:23.006'), 'c', 89.62549424753524, 'msg2'], [numpy.datetime64('2012-01-01T01:21:29'), numpy.datetime64('2018-12-01T01:21:23.006'), 'c', 98.75018240674399, 46.55078419903293, 'msg1']] 295 | ``` 296 | -------------------------------------------------------------------------------- /README_CN_NEW/3_AdvancedOperations/3.4_ObjectOrientedOperationsOnDdbOBjects/3.4.1_Database.md: -------------------------------------------------------------------------------- 1 | # Database 2 | 3 | 在 Python API 中,可以使用 DolphinDB Python API 的原生方法来创建、使用数据库及数据表。本节将介绍如何创建数据库,以及通过数据库创建数据表。 4 | 5 | ## 1. Database, session.database 6 | 7 | Python API 将 DolphinDB 服务端的数据库对象句柄,在 API 包装为 Database 类,封装实现部分功能。通常使用 session.database 方法构造。该方法**部分参数**可以参考 [DolphinDB 用户手册-database](https://www.dolphindb.cn/cn/help/200/FunctionsandCommands/FunctionReferences/d/database.html)。 8 | 9 | 接口如下: 10 | 11 | ```python 12 | session.database(dbName=None, prititionType=None, parititions=None, dbPath=None, engine=None, atomic=None, chunkGranularity=None) 13 | ``` 14 | 15 | - **dbName**: 数据库句柄名称,创建数据库时可以不指定该参数。 16 | - **partitionType**: 分区类型,可选项为 keys.SEQ/keys.VALUE/keys.RANGE/keys.LIST/keys.COMPO/keys.HASH。 17 | - **partitions**: 描述如何进行分区,通常为 list 或者 np.ndarray。 18 | - **dbPath**: 保存数据库的目录的路径。 19 | - **engine**: 数据库存储引擎。 20 | - **atomic**: 写入事务的原子性层级。 21 | - **chunkGranularity**: 分区粒度,可选值为 "Table"/"DATABASE"。 22 | 23 | ### 1.1 数据库句柄 *dbName* 24 | 25 | 当加载已有数据库或创建数据库时,可以指定该参数,表示将数据库加载到内存后的句柄名称。如果不指定该参数,将会自动生成随机字符串作为句柄名称,可以通过 `_getDbName()` 方法获取。 26 | 27 | #### 例1:创建数据库时不指定 dbName 28 | 29 | ```python 30 | dbPath = "dfs://dbName" 31 | if s.existsDatabase(dbPath): 32 | s.dropDatabase(dbPath) 33 | db = s.database(partitionType=keys.VALUE, partitions=[1, 2, 3], dbPath=dbPath) 34 | 35 | dbName = db._getDbName() 36 | print(dbName) 37 | print(s.run(dbName)) 38 | ``` 39 | 40 | 输出结果如下: 41 | 42 | ``` 43 | TMP_DB_15c2bf85DB 44 | DB[dfs://dbName] 45 | ``` 46 | 47 | #### 例2:创建数据库时指定 dbName 48 | 49 | ```python 50 | dbPath = "dfs://dbName" 51 | if s.existsDatabase(dbPath): 52 | s.dropDatabase(dbPath) 53 | db = s.database(dbName="testDB", partitionType=keys.VALUE, partitions=[1, 2, 3], dbPath=dbPath) 54 | 55 | dbName = db._getDbName() 56 | print(dbName) 57 | print(s.run(dbName)) 58 | ``` 59 | 60 | 输出结果如下: 61 | 62 | ``` 63 | testDB 64 | DB[dfs://dbName] 65 | ``` 66 | 67 | ### 1.2 数据库路径 *dbPath* 和 分区参数 *partitionType*/*partitions* 68 | 69 | 调用 session.database 创建数据库时,必须指定分区相关参数 *partitionType* / *partitions*。如果创建的数据库为内存数据库,则不需要指定 dbPath;如果创建的数据库为分区数据库,则必须指定 dbPath。 70 | 71 | #### 各种分区数据库创建方式 72 | 73 | 准备环境: 74 | 75 | ```python 76 | import dolphindb as ddb 77 | import dolphindb.settings as keys 78 | import numpy as np 79 | import pandas as pd 80 | 81 | s = ddb.session() 82 | s.connect("localhost", 8848, "admin", "123456") 83 | ``` 84 | 85 | #### 创建基于 VALUE 分区的数据库 86 | 87 | 按 date 分区: 88 | 89 | ```python 90 | dbPath="dfs://db_value_date" 91 | if s.existsDatabase(dbPath): 92 | s.dropDatabase(dbPath) 93 | dates=np.array(pd.date_range(start='20120101', end='20120110'), dtype="datetime64[D]") 94 | db = s.database(dbName='mydb', partitionType=keys.VALUE, partitions=dates,dbPath=dbPath) 95 | ``` 96 | 97 | 按 month 分区: 98 | 99 | ```python 100 | dbPath="dfs://db_value_month" 101 | if s.existsDatabase(dbPath): 102 | s.dropDatabase(dbPath) 103 | months=np.array(pd.date_range(start='2012-01', end='2012-10', freq="M"), dtype="datetime64[M]") 104 | db = s.database(partitionType=keys.VALUE, partitions=months,dbPath=dbPath) 105 | ``` 106 | 107 | #### 创建基于 RANGE 分区的数据库 108 | 109 | 按 INT 类型分区: 110 | 111 | ```python 112 | dbPath="dfs://db_range_int" 113 | if s.existsDatabase(dbPath): 114 | s.dropDatabase(dbPath) 115 | db = s.database(partitionType=keys.RANGE, partitions=[1, 11, 21], dbPath=dbPath) 116 | ``` 117 | 118 | #### 创建基于 LIST 分区的数据库 119 | 120 | 按 SYMBOL 类型分区: 121 | 122 | ```python 123 | dbPath="dfs://db_list_sym" 124 | if s.existsDatabase(dbPath): 125 | s.dropDatabase(dbPath) 126 | db = s.database(partitionType=keys.LIST, partitions=[['IBM', 'ORCL', 'MSFT'], ['GOOG', 'FB']],dbPath=dbPath) 127 | ``` 128 | 129 | #### 创建基于 HASH 分区的数据库 130 | 131 | 按 INT 类型分区: 132 | 133 | ```python 134 | dbPath="dfs://db_hash_int" 135 | if s.existsDatabase(dbPath): 136 | s.dropDatabase(dbPath) 137 | db = s.database(partitionType=keys.HASH, partitions=[keys.DT_INT, 3], dbPath=dbPath) 138 | ``` 139 | 140 | #### 创建基于 COMPO 分区的数据库 141 | 142 | 以下脚本创建基于 COMPO 分区的数据库及数据表:第一层是基于 VALUE 的 date 类型分区,第二层是基于 RANGE 的 int 类型分区。 143 | 144 | > **注意:** 创建 COMPO 的子分区数据库的 *dbPath* 参数必须设置为空字符串或不设置。 145 | 146 | ```python 147 | db1 = s.database(partitionType=keys.VALUE, partitions=np.array(["2012-01-01", "2012-01-06"], dtype="datetime64[D]")) 148 | db2 = s.database(partitionType=keys.RANGE, partitions=[1, 6, 11]) 149 | dbPath="dfs://db_compo_test" 150 | if s.existsDatabase(dbPath): 151 | s.dropDatabase(dbPath) 152 | db = s.database(partitionType=keys.COMPO, partitions=[db1, db2], dbPath=dbPath) 153 | ``` 154 | 155 | ### 1.3 数据库引擎 *engine* 156 | 157 | 默认使用 OLAP 引擎创建数据库,如果希望使用其他引擎创建数据库,可以指定该参数。 158 | 159 | #### 创建 TSDB 引擎下的数据库 160 | 161 | TSDB 引擎数据库的创建方法和 OLAP 几乎一致,只需要在 database 函数中指定 `engine = "TSDB"`,并在调用建表函数 createTable 和 createPartitionedTable 时指定 sortColumns。 162 | 163 | ```python 164 | dates = np.array(pd.date_range(start='20120101', end='20120110'), dtype="datetime64[D]") 165 | dbPath = "dfs://tsdb" 166 | if s.existsDatabase(dbPath): 167 | s.dropDatabase(dbPath) 168 | db = s.database(partitionType=keys.VALUE, partitions=dates, dbPath=dbPath, engine="TSDB") 169 | ``` 170 | 171 | ### 1.4 事务原子性层级 *atomic* 172 | 173 | 该参数表示写入事务的原子性层级,决定了是否允许并发写入同一分区,可选值为 "TRANS" 和 "CHUNK",默认值为 "TRANS"。 174 | 175 | - 设置为 "TRANS" ,写入事务的原子性层级为事务,即一个事务写入多个分区时,若某个分区被其他写入事务锁定而出现写入冲突,则该事务的写入全部失败。因此,该设置下,不允许并发写入同一个分区。 176 | - 设置为 "CHUNK" ,写入事务的原子性层级为分区。若一个事务写入多个分区时,某分区被其它写入事务锁定而出现冲突,系统会完成其他分区的写入,同时对之前发生冲突的分区不断尝试写入,尝试数分钟后仍冲突才放弃。此设置下,允许并发写入同一个分区,但由于不能完全保证事务的原子性,可能出现部分分区写入成功而部分分区写入失败的情况。同时由于采用了重试机制,写入速度可能较慢。 177 | 178 | ### 1.5 分区粒度 *chunkGranularity* 179 | 180 | 该参数表示分区粒度,可选值为 "TABLE" 和 "DATABASE"。 181 | 182 | - "Table":表级分区,设置后支持同时写入同一分区的不同表。 183 | - "DATABASE":数据库级分区,设置后只支持同时写入不同分区。 184 | 185 | > **注意:** 指定该参数前,需要构造 session 时设置 `enableChunkGranularityConfig=True`,否则该参数无效。 186 | 187 | ## 2. createTable 188 | 189 | 使用 createTable 可以在数据库中创建维度表。其传入参数 *table* 是一个 Table 对象,该对象将作为生成表的结构参考。 190 | 191 | ```python 192 | Database.createTable(table, tableName, sortColumns=None) 193 | ``` 194 | 195 | - **table**:Table 类对象,将根据该表的表结构在数据库中创建一个空的维度表。 196 | - **tableName**:字符串,表示维度表的名称。 197 | - **sortColumns**:字符串或字符串列表,用于指定表的排序列。写入的数据将按照 *sortColumns* 列进行排序。系统默认 *sortColumns* (指定多列时)排序列的最后一列为时间类型,其余列字段作为排序的索引列,称作 sort key。 198 | 199 | 该方法与 DolphinDB 服务器同名函数使用限制一致,请参阅 [DolphinDB 用户手册-createTable](https://www.dolphindb.cn/cn/help/200/FunctionsandCommands/FunctionReferences/c/createTable.html)。 200 | 201 | 下面的代码示例将在 TSDB 引擎数据库中创建一张基于 schema_t 表的结构、按 csymbol 列排序的维度表,表名为 pt。 202 | 203 | ```python 204 | dbPath = "dfs://createTable" 205 | if s.existsDatabase(dbPath): 206 | s.dropDatabase(dbPath) 207 | db = s.database(partitionType=keys.VALUE, partitions=[1, 2, 3], dbPath=dbPath, engine="TSDB") 208 | s.run("schema_t = table(100:0, `ctime`csymbol`price`qty, [TIMESTAMP, SYMBOL, DOUBLE, INT])") 209 | schema_t = s.table(data="schema_t") 210 | pt = db.createTable(schema_t, "pt", ["csymbol"]) 211 | schema = s.run(f'schema(loadTable("{dbPath}", "pt"))') 212 | print(schema["colDefs"]) 213 | ``` 214 | 215 | 输出结果如下: 216 | 217 | ``` 218 | name typeString typeInt extra comment 219 | 0 ctime TIMESTAMP 12 NaN 220 | 1 csymbol SYMBOL 17 NaN 221 | 2 price DOUBLE 16 NaN 222 | 3 qty INT 4 NaN 223 | ``` 224 | 225 | ## 3. createPartitionedTable 226 | 227 | 使用 createPartitionedTable 可以在分布式数据库中创建一个分区表,且返回一个 Table 表对象。该方法也需要传入 Table 对象作为生成表的结构参考。此外,还需要传入一个字符串或者字符串列表,用于表示分区列。 228 | 229 | ```python 230 | Database.createPartitionedTable( 231 | table, tableName, partitionColumns, compressMethods={}, sortColumns=None, 232 | keepDuplicates=None, sortKeyMappingFunction=None 233 | ) 234 | ``` 235 | 236 | - **table**:Table 类对象,将根据该表的表结构在数据库中创建一个空的分区表。 237 | - **tableName**:字符串,表示分区表的名称。 238 | - **partitionColumns**:字符串或字符串列表,表示分区列。 239 | - **compressMethods**:字典,用于指定各列使用的压缩方法,键值分别为列名和压缩方法。 240 | - **sortColumns**:字符串或字符串列表,用于指定表的排序列。写入的数据将按照 sortColumns 列进行排序。系统默认 sortColumns (指定多列时)排序列的最后一列为时间类型,其余列字段作为排序的索引列,称作 sort key。 241 | - **keepDuplicates**:指定在每个分区内如何处理所有 sortColumns 之值皆相同的数据,提供以下选项: 242 | - "ALL":保留所有数据,为默认值。 243 | - "LAST":仅保留最新数据。 244 | - "FIRST":仅保留第一条数据。 245 | - **sortKeyMappingFunction**:DolphinDB 服务端函数名字符串列表,其长度与索引列一致,用于指定各索引列使用的排序方法。 246 | 247 | 该方法与 DolphinDB 服务器同名函数使用限制一致,请参阅 [DolphinDB 用户手册-createPartitionedTable](https://www.dolphindb.cn/cn/help/200/FunctionsandCommands/FunctionReferences/c/createPartitionedTable.html)。 248 | 249 | ### 例 1 250 | 251 | 下面的代码示例将在 TSDB 引擎数据库中根据 schema_t 表的结构创建一张分区列为 TradeDate、索引列为 sortColumns 的分区表,并指定排序列为 SecurityID 和 TradeDate,其中 SecurityID 的排序函数使用 hashBucket{,5},每个分区排序列值相同时的处理策略为 "ALL"。 252 | 253 | ```python 254 | dbPath = "dfs://createPartitionedTable" 255 | if s.existsDatabase(dbPath): 256 | s.dropDatabase(dbPath) 257 | dates = np.array(pd.date_range(start='20220101', end='20220105'), dtype="datetime64[D]") 258 | db = s.database(partitionType=keys.VALUE, partitions=dates, dbPath=dbPath, engine="TSDB") 259 | s.run("schema_t = table(100:0, `SecurityID`TradeDate`TotalVolumeTrade`TotalValueTrade, [SYMBOL, DATE, INT, DOUBLE])") 260 | schema_t = s.table(data="schema_t") 261 | pt = db.createPartitionedTable(schema_t, "pt", partitionColumns="TradeDate", sortColumns=["SecurityID", "TradeDate"], keepDuplicates="ALL", sortKeyMappingFunction=["hashBucket{,5}"]) 262 | schema = s.run(f'schema(loadTable("{dbPath}", "pt"))') 263 | print(schema["colDefs"]) 264 | ``` 265 | 266 | 输出结果如下: 267 | 268 | ``` 269 | name typeString typeInt extra comment 270 | 0 SecurityID SYMBOL 17 NaN 271 | 1 TradeDate DATE 6 NaN 272 | 2 TotalVolumeTrade INT 4 NaN 273 | 3 TotalValueTrade DOUBLE 16 NaN 274 | ``` 275 | 276 | ### 例 2 277 | 278 | 下面的代码示例将在 OLAP 引擎数据库中根据 schema_t 的表结构创建一张分区列为 symbol 的分区表,并指定 timestamp 列压缩方式为 delta。 279 | 280 | ```python 281 | dbPath = "dfs://createPartitionedTable" 282 | if s.existsDatabase(dbPath): 283 | s.dropDatabase(dbPath) 284 | db = s.database(partitionType=keys.VALUE, partitions=["IBM", "MS"], dbPath=dbPath) 285 | s.run("schema_t = table(100:0, `timestamp`symbol`value, [TIMESTAMP, SYMBOL, DOUBLE])") 286 | schema_t = s.table(data="schema_t") 287 | pt = db.createPartitionedTable(schema_t, "pt", partitionColumns="symbol", compressMethods={'timestamp': "delta"}) 288 | schema = s.run(f'schema(loadTable("{dbPath}", "pt"))') 289 | print(schema["colDefs"]) 290 | ``` 291 | 292 | 输出结果如下: 293 | 294 | ``` 295 | name typeString typeInt extra comment 296 | 0 timestamp TIMESTAMP 12 NaN 297 | 1 symbol SYMBOL 17 NaN 298 | 2 value DOUBLE 16 NaN 299 | ``` 300 | -------------------------------------------------------------------------------- /README_CN_NEW/3_AdvancedOperations/3.5_OtherFunctions/3.5_OtherFunctions.md: -------------------------------------------------------------------------------- 1 | # 其他功能 2 | 3 | ## 强制取消任务 4 | 5 | session 对象中提供静态方法 `enableJobCancellation()`,用于开启强制取消任务的功能。此功能默认关闭。开启后,通过 “Ctrl+C” 按键等方式终止 API 进程中时,会同时取消所有 session 提交的正在运行的作业。 6 | 7 | **注意:** 目前该功能仅在 Linux 系统生效。 8 | -------------------------------------------------------------------------------- /README_CN_NEW/Introduction.md: -------------------------------------------------------------------------------- 1 | # 介绍 2 | 3 | **dolphindb** 是 DolphinDB 的官方 Python API,用于连接 DolphinDB 服务端和 Python 客户端,从而实现数据的双向传输和脚本的调用执行。dolphindb 可以方便您在 Python 环境中调用 DolphinDB 进行数据的处理、分析和建模等操作,利用其优秀的计算性能和强大的存储能力来帮助您加速数据的处理和分析。 4 | 5 | 本手册共提供三大章节——快速开始,基本操作和进阶操作。 6 | 7 | * 快速开始章节将介绍 dolphindb 的安装说明、简单示例和常用操作。 8 | * 基本操作章节将介绍使用 dolphindb 的基本操作,如 session(会话)、DBConnectionPool(连接池)、追加数据、流订阅(基本)和异步写入的相关方法、注意事项和使用示例等。 9 | * 进阶操作章节将详细说明类型转换、多种上传和写入数据方法、流订阅(进阶)、面向对象操作数据库的方法,以及其他功能。 10 | 11 | dolphindb 提供了多种接口函数,可用于连接服务器、执行脚本、发送消息等。此外,dolphindb 支持数据的批量处理和异步执行,以及多种数据类型的交互,如 pandas.DataFrame、arrow.Table 等。dolphindb 支持 Linux(x86_64, arm)、Windows、MacOS(arm64, x86_64) 平台和 Python 3.6 - 3.11 版本,其使用 Pybind11 编写 C++ 库,从而优化后台多线程的处理,极大提高了数据交互的性能。 12 | 13 | 若您对本手册有任何宝贵意见,诚邀您通过 [DolphinDB 社区](https://ask.dolphindb.net) 与我们进行反馈交流。 14 | 15 | # 目录 16 | 17 | **第一章 快速开始** 18 | 19 | * 1.1 [安装](./1_QuickStart/1.1_Install.md) 20 | * 1.2 [快速上手](./1_QuickStart/1.2_Demo.md) 21 | 22 | **第二章 基本操作** 23 | 24 | * 2.1 session(会话) 25 | * 2.1.1 [创建](./2_BasicOperations/2.1_Session/2.1.1_Constructor.md) 26 | * 2.1.2 [连接](./2_BasicOperations/2.1_Session/2.1.2_Connect.md) 27 | * 2.1.3 [常用方法](./2_BasicOperations/2.1_Session/2.1.3_OtherParams.md) 28 | * 2.2 DBConnectionPool(连接池) 29 | * 2.2.1 [创建](./2_BasicOperations/2.2_DBConnectionPool/2.2.1_Constructor.md) 30 | * 2.2.2 [方法介绍](./2_BasicOperations/2.2_DBConnectionPool/2.2.2_AsyncMethodsAndOthers.md) 31 | * 2.3 追加数据 32 | * 2.3.1 [tableAppender](./2_BasicOperations/2.3_AutoFitTableAppender/2.3.1_TableAppender.md) 33 | * 2.3.2 [tableUpsert](./2_BasicOperations/2.3_AutoFitTableAppender/2.3.2_TableUpserter.md) 34 | * 2.3.3 [PartitionedTableAppender](./2_BasicOperations/2.3_AutoFitTableAppender/2.3.3_PartitionedTableAppender.md) 35 | * 2.4 [流订阅(基本)](./2_BasicOperations/2.4_Subscription/2.4_Subscription.md) 36 | * 2.5 同步写入 37 | * 2.5.1 [session 异步提交](./2_BasicOperations/2.5_AsyncWrites/2.5.1_SessionAsyncMode.md) 38 | * 2.5.2 [MultithreadedTableWriter](./2_BasicOperations/2.5_AsyncWrites/2.5.2_MultithreadedTableWriter.md) 39 | * 2.5.3 [BatchTableWriter](./2_BasicOperations/2.5_AsyncWrites/2.5.3_BatchTableWriter.md) 40 | 41 | **第三章 进阶操作** 42 | 43 | * 3.1 [类型转换](./3_AdvancedOperations/3.1_DataTypeCasting/3.1.0_TypeCasting.md) 44 | * 3.1.1 [PROTOCOL_DDB 协议](./3_AdvancedOperations/3.1_DataTypeCasting/3.1.1_PROTOCOL_DDB.md) 45 | * 3.1.2 [PROTOCOL_PICKLE 协议](./3_AdvancedOperations/3.1_DataTypeCasting/3.1.2_PROTOCOL_PICKLE.md) 46 | * 3.1.3 [PROTOCOL_ARROW 协议](./3_AdvancedOperations/3.1_DataTypeCasting/3.1.3_PROTOCOL_ARROW.md) 47 | * 3.1.4 [强制转换](./3_AdvancedOperations/3.1_DataTypeCasting/3.1.4_ForceTypeCasting.md) 48 | * 3.2 [多种上传和写入方式的对比](./3_AdvancedOperations/3.2_WriteOptions/3.2_WriteOptions.md) 49 | * 3.3 [流订阅(进阶)](./3_AdvancedOperations/3.3_SubscriptionOptions/3.3_SubscriptionOptions.md) 50 | * 3.4 面向对象操作数据库 51 | * 3.4.1 [数据库](./3_AdvancedOperations/3.4_ObjectOrientedOperationsOnDdbOBjects/3.4.1_Database.md) 52 | * 3.4.2 [数据表](./3_AdvancedOperations/3.4_ObjectOrientedOperationsOnDdbOBjects/3.4.2_Table.md) 53 | * 3.5 [其他](./3_AdvancedOperations/3.5_OtherFunctions/3.5_OtherFunctions.md) 54 | -------------------------------------------------------------------------------- /README_NEW/README.md: -------------------------------------------------------------------------------- 1 | # Python API User Manual 2 | 3 | The [old README file](../README.md) covers DolphinDB Python API version **1.30.21.2** and earlier. It is no longer maintained starting in DolphinDB Python API version **1.30.22.1**. 4 | 5 | > Please refer to the [new DolphinDB Python API manual](https://docs.dolphindb.com/en/pydoc/chap1_quickstart_landingpage.html) going forward for up-to-date documentation. 6 | -------------------------------------------------------------------------------- /ReleaseNotes/1.30.19.1.md: -------------------------------------------------------------------------------- 1 | # 1.30.19.1 2 | 3 | ## 新增功能 4 | 5 | - 新增系统变量 version,通过 dolphindb.\_\_version\_\_ 可以查看 API 的版本号。 6 | - `tableAppender` 支持写入 array vector 类型数据。 7 | - `session.connect` 支持 *reconnect* 参数,实现非高可用场景下,自动重连节点。 8 | - 新增 `streamDeserializer` 类,实现对异构流表的解析,同时,`subscribe` 函数新增 *streamDeserializer* 参数,接收经 `streamDeserializer` 解析后的数据。 9 | - API 端支持返回 `s.run` 的 print 结果。 10 | - (1) 新增 `tableUpsert` 对象,(2) `MultithreadedTableWriter` 新增参数 *mode* 和 *modeOption* ,均可实现对索引内存表、键值内存表,或者 DFS 表通过 `upsert` 方式进行更新。 11 | - 支持上传或读取 INT128, UUID, IP 类型的数组向量,但上传或读取这些类型的数组向量时需设置 *enablePickle* =false。 12 | 13 | ## 功能优化 14 | 15 | - 规范 API 空值处理方式。 16 | - `session` 对象 *enableASYN* 参数名调整为 enableASYNC。 17 | - 通过 API 连接集群服务器时,实现请求的负载均衡。 18 | - `MultithreadedTableWriter` 对象写入内存表时,参数 dbPath 和 tableName 的设置发生改变: dbPath 需设置为空,tableName 需为内存表表名。 19 | 20 | ## 故障修复 21 | 22 | - 解决通过 API 查询到的数据存在乱码时,无法下载数据的问题。 23 | - 解决 session 关闭后,端口没有及时释放的问题。 24 | -------------------------------------------------------------------------------- /ReleaseNotes/1.30.19.2.md: -------------------------------------------------------------------------------- 1 | # 1.30.19.2 2 | 3 | ## 新增功能 4 | 5 | - 为函数添加注解,支持在调用函数时提示函数用法。 6 | - Windows 系统下,Python API 新增支持官网 Python3.8, Python3.9。 7 | - DBConnectionPool 的 runTaskAsync 函数支持上传数据。 8 | - session 增加 enableJobCancellation 方法,仅支持 Linux 系统,通过 Ctrl+C 取消进程中所有正在执行的 session.run() 的任务。 9 | - Linux aarch64 系统下,Python API 支持 conda 环境的 Python3.7-Python3.9。 10 | 11 | ## 故障修复 12 | 13 | - 解决了 Table 对象被删除后,服务器端不会自动释放资源的问题。 14 | -------------------------------------------------------------------------------- /ReleaseNotes/1.30.19.3.md: -------------------------------------------------------------------------------- 1 | # 1.30.19.3 2 | 3 | ## 新增功能 4 | 5 | - `session` 类新增 `setTimeOut` 方法,用于设置 TCP 连接的 TCP_USER_TIMEOUT 选项。仅 Linux 系统生效。 6 | - `createPartitionedTable` 新增参数 *sortKeyMappingFunction* ,支持对 sortKey 降维。 7 | 8 | ## 功能优化 9 | 10 | - DataFrame 在指定 `__DolphinDB_Type__` 属性后,可以按照指定类型上传。 11 | 12 | ## 故障修复 13 | 14 | - 修复 Python API 上传 object 类型的 Bool 数据时出现数值错误的问题。 15 | -------------------------------------------------------------------------------- /ReleaseNotes/1.30.19.4.md: -------------------------------------------------------------------------------- 1 | # 1.30.19.4 2 | 3 | ## 新增功能 4 | 5 | - 流订阅指定 *batchSize* 为小数时增加报错提示。 6 | 7 | ## 功能优化 8 | 9 | - 流订阅指定 *msgAsTable* = True 且指定 *batchSize* 为正整数时,将基于消息块处理记录。 10 | - python API 最高支持 NumPy 1.23.4 和 pandas 1.5.2。 11 | - 优化上传数据报错信息。 12 | - 优化 Mac python API 报错信息。 13 | 14 | ## 故障修复 15 | 16 | - 修复下载的数据中时间戳小于1970时,会报错的问题。 17 | - 修复通过 `tableAppender`, `tableUpsert`, `PartitionedTableAppender` 写入包含 INT128, IPADDR, UUID, BLOB 类型列时,写入失败的问题。 18 | - 修复通过 `s.dropPartition` 删除分区,或通过 `s.loadTable` 加载表时,由于创建的临时 database handle 和 table handle 未销毁而造成 server 内存泄漏的问题。 19 | -------------------------------------------------------------------------------- /ReleaseNotes/1.30.21.1.md: -------------------------------------------------------------------------------- 1 | # 1.30.21.1 2 | 3 | ## 新增功能 4 | 5 | - 新增支持 Python3.10。(**1.30.21.1**) 6 | - `Session` 和 `DBConnectionPool` 新增 `protocol` 参数,在构建函数时进行使用,可指定数据格式的传输协议。(**1.30.21.1**) 7 | - 支持流订阅通过 API 发起的连接接收数据。(**1.30.21.1**) 8 | - `DBConnectionPool.addTask` 新增 `args` 参数,可以接收已定义的对象。(**1.30.21.1**) 9 | - 支持 `tableAppender`, `tableUpsert` 和 `PartitionedTableAppender` 上传 IPADDR, UUID 和 INT128 类型的数据。(**1.30.21.1**) 10 | - 支持基于 Apache Arrow 协议下载数据。(**1.30.21.1**) 11 | - 支持使用 DolphinDB 自定义的数据报文格式(简称 DDB 协议)下载和上传 DECIMAL 类型数据。(**1.30.21.1**) 12 | 13 | ## 功能优化 14 | 15 | - 优化了报错信息。(**1.30.21.1**) 16 | 17 | ## 故障修复 18 | 19 | - 修复 macOS 重复创建 MultithreadedTableWriter 后提示创建信号量失败的问题。(**1.30.21.1**) 20 | - 修复开启 pickle 后下载包含 STRING 类型列的空表提示 "unmarshall failed"的问题。(**1.30.21.1**) 21 | - 修复流订阅中包含 array vector 数据时发生 API Abort的问题。(**1.30.21.1**) 22 | - 修复在 uWSGI 中调用 Python API 执行 SQL,API 发生段错误的问题。(**1.30.21.1**) 23 | - 修复上传数据中包含空值 np.nan 时,服务器结果产生字符 NaN 而非空值的问题。(**1.30.21.1**) 24 | -------------------------------------------------------------------------------- /ReleaseNotes/1.30.21.2.md: -------------------------------------------------------------------------------- 1 | # 1.30.21.2 2 | 3 | ## 功能优化 4 | 5 | - 调整 Python API 依赖库pandas 的版本为不小于1.0.0。 6 | 7 | ## 故障修复 8 | 9 | - 修复当 MultithreadedTableWriter 写入失败时,调用 getUnwrittenData 方法会导致段错误的问题。 10 | - 修复无法下载超长 BLOB 数据(超过 64K长度)的问题。 11 | - 修复 Mac ARM 版本中在订阅 1.30.21、2.00.9及之后版本的 DolphinDB 时出现内存越界的问题。 12 | - 修复上传 np.datetime64 类型的空值数据被识别为错误类型的问题。 13 | - 修复上传第一个元素为 Decimal(“NaN“) 的 Vector 时发生数值溢出的问题。 14 | - 修复通过 PROTOCOL_DDB 协议下载 BLOB 类型的集合出现段错误的问题。 15 | - 修复调用 loadTableBySQL 方法时会覆盖当前 session 中变量”db”值的问题。 16 | - 修复 DBConnectionPool 调用 addTask 添加任务后若不取出数据则会导致进程卡住的问题。 17 | -------------------------------------------------------------------------------- /ReleaseNotes/1.30.22.1.md: -------------------------------------------------------------------------------- 1 | # 1.30.22.1 2 | 3 | ## 新增功能 4 | 5 | - Session 和 DBConnectionPool 均新增参数 *show_output* ,其用于指定是否在 Python 客户端展示脚本的输出内容。 6 | - TableAppender(原类名 tableAppender), TableUpserter(原类名 tableUpsert)和 PartitionedTableAppender 新增支持写入数据时根据表结构自动进行类型转换。 7 | - 新增支持 Numpy 的 C order 模式。 8 | - 新增支持在上传 DataFrame 时,通过设置属性 \_\_DolphinDB_Type\_\_ 指定列类型以实现强制类型转换。 9 | - 新增支持 MultithreadedTableWriter 在写入流表时,若连接断开将自动进行重连。 10 | 11 | ## 功能优化 12 | 13 | - 更新 Python API 用户手册。 14 | - 调整类名 tableUpsert 为 TableUpserter,与原有类名兼容。 15 | - 调整类名 tableAppender 为 TableAppender,与原有类名兼容。 16 | - 调整类名 session 为 Session,与原有类名兼容。 17 | - 优化了部分报错信息。 18 | - 优化下载乱码字符串时的处理逻辑。 19 | - 删除了 Table 类在析构时的打印信息。 20 | - 若流订阅中 handler 发生错误将报错并打印异常信息。 21 | 22 | ## 故障修复 23 | 24 | - 修复查询表时若添加多个 where 条件执行优先级异常的问题。 25 | - 修复在调用 TableAppender(原类名 tableAppender), TableUpserter(原类名 tableUpsert)或 PartitionTableAppender 上传 BLOB, INT128, UUID 和 IPADDR 对应的 arrayVector 型的数据时提示警告信息的问题。 26 | - 修复流订阅中偶现提示解析消息失败的问题。 27 | - 修复 DBConnectionPool 在析构时未调用 shutDown 导致进程卡住的问题。 28 | - 修复了 TableAppender(原类名 tableAppender), TableUpserter(原类名 tableUpsert) 和 PartitionedTableAppender 在引用 Session 或 DBConnectionPool 时,由于 Session 或 DBConnectionPool 提前析构导致无法使用的问题。 29 | -------------------------------------------------------------------------------- /ReleaseNotes/1.30.22.2.md: -------------------------------------------------------------------------------- 1 | # 1.30.22.2 2 | 3 | ## 新增功能 4 | 5 | - Session 和 DBConnectionPool 中 run 方法新增支持指定任务的并行度和优先级。 6 | - Session 新增支持使用锁以保证线程安全。 7 | 8 | ## 功能优化 9 | 10 | - 调整 numpy 依赖版本为1.18.0~1.24.4。 11 | - 调整构造 Table 类时,传入参数 *dbPath*, *data* 时加载数据表的逻辑与Session.loadTable 的逻辑一致。 12 | - 使用 where 方法只添加一个筛选条件时生成语句将不包含括号。 13 | 14 | ## 故障修复 15 | 16 | - 修复使用 where 方法拼接多个筛选条件后生成语句不符合预期逻辑的问题。 17 | - 修复 Table 类 drop 方法在某些情况下不执行的问题。 18 | - 修复 TableUpdate、TableDelete 对象使用 where 方法,对其使用 showSQL 方法后返回错误 SQL 语句的问题。 19 | - 修复使用 upload 方法上传非 Table 对象时错误进行压缩上传的问题。 20 | - 修复 Table 类对象拼接 SQL 字符串时出现不合理书写方式的问题。 21 | - 修复构造 Table 类时内部参数设置有误导致使用 showSQl 后输出逻辑不正常的问题。 22 | -------------------------------------------------------------------------------- /ReleaseNotes/1.30.22.3.md: -------------------------------------------------------------------------------- 1 | # 1.30.22.3 2 | 3 | ## 新功能 4 | 5 | * 传输协议 PROTOCOL_DDB 新增支持 Decimal32/64 ArrayVector 类型数据的上传或下载。 6 | -------------------------------------------------------------------------------- /ReleaseNotes/1.30.22.4.md: -------------------------------------------------------------------------------- 1 | # 1.30.22.4 2 | 3 | ## 新功能 4 | 5 | * 全平台支持使用 PROTOCOL_ARROW 协议。 6 | * 支持上传 Pandas 2.0 PyArrow 后端的数据。 7 | * 强制类型转换支持指定数据类型为 Decimal32 / Decimal64 的精度。 8 | -------------------------------------------------------------------------------- /ReleaseNotes/1.30.22.5.md: -------------------------------------------------------------------------------- 1 | # 1.30.22.5 2 | 3 | ## 故障修复 4 | 5 | * 修复 Numpy ndarray 在指定不同内存布局时,上传数据出现乱序的问题。 6 | * 修复 TableAppender/TableUpserter/PartitionedTableAppender 类向低版本的 DolphinDB 追加数据时出现的兼容性问题。 7 | * 修复特定情况下上传 ArrayVector 列丢失空值数据的问题。 8 | -------------------------------------------------------------------------------- /ReleaseNotes/1.30.22.6.md: -------------------------------------------------------------------------------- 1 | # 1.30.22.6 2 | 3 | ## 新增功能 4 | 5 | - 新增支持 Python 3.11。 6 | - PROTOCOL_DDB 协议新增支持数据类型 DECIMAL128。 7 | - 新增上传字符串不能超过 64KB 的限制。 8 | 9 | ## 功能优化 10 | 11 | - drop 函数调整对于 STRING 型和不同长度的 list 型的输入值的处理逻辑。 12 | 13 | ## 故障修复 14 | 15 | - 修复下载字典中若包含 UUID/IPADDR/INT128 等类型数据时出现段错误的问题。 16 | - 修复 `MultithreadedTableWriter` 类成员未正常析构的问题。 17 | - 修复 `MultithreadedTableWriter` 在部分场景下插入错误类型的数据导致 Abort 的问题。 18 | - 修复上传 DataFrame 时指定列类型为 DECIMAL32/64/128,若该列第一个数据为空值则导致段错误的问题。 19 | - 修复上传 DataFrame 时若某列 dtype=datetime64[us] 则出现列长不匹配或报错的问题。 20 | -------------------------------------------------------------------------------- /ReleaseNotes/README_EN.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | The release notes for DolphinDB Python API version 1.30.19.1 and later can be found in the [Python API Reference Guide - Release Notes](https://docs.dolphindb.com/en/pydoc/release_notes_landingpage.html) on the official DolphinDB website. 4 | 5 | For release notes of earlier DolphinDB Python API versions, refer to the [DolphinDB Release Notes - Python API](https://github.com/dolphindb/release/blob/master/1.30/README.md#python) on GitHub. 6 | -------------------------------------------------------------------------------- /data/US.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dolphindb/api_python3/59de148d3e9febe234b10cde81aa5d215491fb77/data/US.rar -------------------------------------------------------------------------------- /images/K-line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dolphindb/api_python3/59de148d3e9febe234b10cde81aa5d215491fb77/images/K-line.png -------------------------------------------------------------------------------- /sample.py: -------------------------------------------------------------------------------- 1 | import dolphindb as ddb 2 | import datetime as date 3 | import pandas as pd 4 | import numpy as np 5 | s = ddb.session() 6 | s.connect("localhost",8081,"admin","123456") 7 | dates = '2019.08.01..2019.08.10' 8 | syms = '`IBM`AAPL`MSFT`GS`YHOO`TS`TSL`C`MC`HA' 9 | script='data = table(1:0,`date`sym`price, [DATE,SYMBOL,DOUBLE])' 10 | print(script) 11 | t = s.run(script) 12 | ## Create a partitioned DFS table 13 | userName = 'admin' 14 | pwd = '123456' 15 | symRange = 'cutPoints({a},3)'.format(a=syms) 16 | dbPath = 'dfs://demo' 17 | tableName = 'testTableName' 18 | if(s.existsDatabase(dbPath)): 19 | s.dropDatabase(dbPath) 20 | lgnScript = 'login("{a}","{b}")'.format(a=userName,b=pwd) 21 | dbDateScript='dbDate=database("",VALUE,{a})'.format(a=dates) 22 | dbSymScript='dbSym=database("",RANGE,{b})'.format(b=symRange) 23 | script= '{login};{db1};{db2};db = database("{db}",COMPO,[dbDate,dbSym])'.format(login=lgnScript, db1=dbDateScript, db2=dbSymScript, db=dbPath) 24 | s.run(script) 25 | ## Write to the DFS table 26 | script='db.createPartitionedTable(data,`{a}, `date`sym)'.format(a=tableName) 27 | print(script) 28 | s.run(script) 29 | 30 | loadScript = 'tb = loadTable("{a}","{b}")'.format(a=dbPath,b=tableName) 31 | s.run(loadScript) 32 | script = 'tableInsert{tb}' 33 | print(script) 34 | dataTable = pd.DataFrame({'date':np.array(['2019-08-01', '2019-08-02'], dtype="datetime64[D]"),'sym':['MSFT','GS'],'price':[90,89]}) 35 | s.run(script, dataTable) 36 | 37 | script='select * from loadTable("{a}","{b}")'.format(a=dbPath,b=tableName) 38 | re = s.run(script) 39 | print(re) 40 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import dolphindb as ddb 2 | import datetime as date 3 | import pandas as pd 4 | import numpy as np 5 | 6 | caseCount = 0 7 | failedCount = 0 8 | succCount = 0 9 | 10 | def AssertTrue(exp, title, testingCase): 11 | global caseCount 12 | global failedCount 13 | global succCount 14 | caseCount += 1 15 | if exp: 16 | print(testingCase, ' ' , title, ': success.') 17 | succCount += 1 18 | else: 19 | print(testingCase, title, ': failed.' ) 20 | failedCount += 1 21 | 22 | def printResult(): 23 | print("testing finished: failed cases/all cases is ", failedCount, '/', caseCount) 24 | 25 | s = ddb.session() 26 | s.connect("localhost",8081,"admin","123456") 27 | 28 | case = "testStringVector" 29 | vector = s.run("`IBM`GOOG`YHOO"); 30 | AssertTrue((vector==['IBM','GOOG','YHOO']).all(), '1', case) 31 | 32 | case = "testFunctionDef" 33 | obj = s.run("def(a,b){return a+b}") 34 | AssertTrue(len(obj)>0,"1",case) 35 | 36 | case = "testSymbolVector" 37 | vector = s.run("rand(`IBM`MSFT`GOOG`BIDU,10)") 38 | AssertTrue(len(['IBM','GOOG','YHOO'])>0, '1', case) 39 | 40 | case = "testIntegerVector" 41 | vector = s.run("2938 2920 54938 1999 2333") 42 | AssertTrue((vector==[2938,2920,54938,1999,2333]).all(), '1', case) 43 | 44 | case = "testDoubleVector" 45 | vector = s.run("rand(10.0,10)") 46 | AssertTrue(len(vector)==10, '1', case) 47 | 48 | case = "testDateVector" 49 | vector = s.run("2012.10.01 +1..3") 50 | AssertTrue((vector==[np.datetime64('2012-10-02', dtype='datetime64[D]'), np.datetime64('2012-10-03', dtype='datetime64[D]'), np.datetime64('2012-10-04', dtype='datetime64[D]')]).all(), '1', case) 51 | 52 | case = "testDateVector" 53 | vector = s.run("2012.10.01T15:00:04 + 2009..2011") 54 | AssertTrue((vector==[np.datetime64('2012-10-01T15:33:33', dtype='datetime64[s]'), np.datetime64('2012-10-01T15:33:34', dtype='datetime64[s]'), np.datetime64('2012-10-01T15:33:35', dtype='datetime64[s]')]).all(), '1', case) 55 | 56 | case = "testDateTimeVector" 57 | vector = s.run("2012.10.01T15:00:04 + 2009..2011") 58 | AssertTrue((vector==[np.datetime64('2012-10-01T15:33:33', dtype='datetime64[s]'), np.datetime64('2012-10-01T15:33:34', dtype='datetime64[s]'), np.datetime64('2012-10-01T15:33:35', dtype='datetime64[s]')]).all(), '1', case) 59 | 60 | case = "testIntMatrix" 61 | matx = s.run("1..6$2:3") 62 | # print(matx[0]) 63 | # print(np.array([[1, 3, 5],[2, 4, 6]])) 64 | AssertTrue((matx[0]==np.array([[1, 3, 5],[2, 4, 6]])).all(), '1', case) 65 | 66 | case = "testIntMatrixWithLabel" 67 | matx = s.run("cross(add,1..5,1..10)") 68 | # print(matx) 69 | # print(np.array([[1, 3, 5],[2, 4, 6]])) 70 | AssertTrue((matx[0]==np.array([[ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],[ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],[ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],[ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]])).all(), '1', case) 71 | 72 | case = "testTable" 73 | script = '''n=20; 74 | syms=`IBM`C`MS`MSFT`JPM`ORCL`BIDU`SOHU`GE`EBAY`GOOG`FORD`GS`PEP`USO`GLD`GDX`EEM`FXI`SLV`SINA`BAC`AAPL`PALL`YHOO`KOH`TSLA`CS`CISO`SUN; 75 | mytrades=table(09:30:00+rand(18000,n) as timestamp,rand(syms,n) as sym, 10*(1+rand(100,n)) as qty,5.0+rand(100.0,n) as price); 76 | select qty,price from mytrades where sym==`IBM;''' 77 | 78 | table = s.run(script); 79 | AssertTrue(table.shape[1]==2 , "1", case) 80 | 81 | case = "testDictionary" 82 | script = '''dict(1 2 3,`IBM`MSFT`GOOG)''' 83 | dic = s.run(script); 84 | # print(dic) 85 | AssertTrue(dic[2]=='MSFT' , "1", case) 86 | AssertTrue(dic[1]=='IBM' , "2", case) 87 | AssertTrue(dic[3]=='GOOG' , "3", case) 88 | 89 | # case = "testFunction" 90 | # array = {1.5, 2.5, 7}; 91 | # result = s.run("sum", array); 92 | # print(result) 93 | 94 | case = "testFunction1" 95 | s.upload({'a':[1.5,2.5,7]}) 96 | result = s.run("accumulate(+,a)"); 97 | AssertTrue((result==[1.5,4.0,11.0]).all(),'1',case) 98 | 99 | case = "testFunction2" 100 | s.run("login('admin','123456')"); 101 | s.run("def Foo5(a,b){ f=file('testsharp.csv','w');f.writeLine(string(a)); }"); 102 | args = [1.3,1.4]; 103 | result = s.run("Foo5",1.3,1.4); 104 | 105 | case = "testAnyVector" 106 | obj = s.run("[1, 2, [1,3, 5],[0.9, 0.8]]") 107 | AssertTrue((obj[2]==[1,3,5]).all(),"1",case) 108 | 109 | 110 | case ="testSet" 111 | obj = s.run("set(1+3*1..3)") 112 | AssertTrue(obj=={10, 4, 7},"1", case) 113 | 114 | case="testMatrixUpload" 115 | a = s.run("cross(+, 1..5, 1..5)") 116 | b = s.run("1..25$5:5") 117 | s.upload({'a':a,'b':b}); 118 | mtx = s.run('a+b') 119 | print(mtx[0]) 120 | print(mtx[1]) 121 | print(mtx[2]) 122 | printResult() -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | 2 | Please config the following paramaters in test/setup/settings.py: 3 | 4 | ``` 5 | HOST="192.168.1.14" 6 | PORT=20932 7 | WORK_DIR='/hdd/hdd5/hzy/python_api_test_server/WORK_DIR' 8 | DATA_DIR='/hdd/hdd1/jenkins/jenkins/workspace/python_api_test/data' 9 | SUBPORT=20936 10 | ``` 11 | -------------------------------------------------------------------------------- /test/setup/settings.py: -------------------------------------------------------------------------------- 1 | HOST="192.168.1.14" 2 | PORT=20932 3 | WORK_DIR='/hdd/hdd5/hzy/python_api_test_server/WORK_DIR' 4 | DATA_DIR='/hdd/hdd1/jenkins/jenkins/workspace/python_api_test/data' 5 | SUBPORT=20936 6 | -------------------------------------------------------------------------------- /test/testAll.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | 4 | if __name__ == "__main__": 5 | test_dir = os.path.join(os.path.dirname(os.path.realpath(__file__))) # Get the current working directory 6 | discover = unittest.defaultTestLoader.discover(test_dir, pattern="test_*.py") 7 | with open(test_dir+"/reports/unit_report.txt", "w") as f: 8 | runner = unittest.TextTestRunner(stream=f, verbosity=2) 9 | # verbosity = 0 Return traceback info about error and failure 10 | # verbosity = 1 Return traceback info about error and failure, and the execution result of each test case. ".": success, F: failure, E: error, S: skip. 11 | # such as: .EEEEEEEEEEEEEE.EE.E.EEE.....F......F.. 12 | # verbosity = 2 Return all test results 13 | runner.run(discover) 14 | -------------------------------------------------------------------------------- /test/test_dropDatabase.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import dolphindb as ddb 3 | from os import path 4 | from setup import HOST, PORT, WORK_DIR 5 | 6 | 7 | class DBInfo: 8 | dfsDBName = 'dfs://testDropDatabase' 9 | diskDBName = WORK_DIR + '/testDropDatabase' 10 | table = 'tb1' 11 | 12 | 13 | def create_dfs_dimension_db(): 14 | s = ddb.session() 15 | s.connect(HOST, PORT, "admin", "123456") 16 | ddb_script = ''' 17 | login('admin','123456') 18 | dbPath='{db}' 19 | if(existsDatabase(dbPath)) 20 | dropDatabase(dbPath) 21 | db=database(dbPath,RANGE,1..10) 22 | n=100000 23 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 24 | db.createTable(tdata,`{tb}).append!(tdata) 25 | '''.format(db=DBInfo.dfsDBName, tb=DBInfo.table) 26 | s.run(ddb_script) 27 | s.close() 28 | 29 | 30 | def create_dfs_range_db(): 31 | s = ddb.session() 32 | s.connect(HOST, PORT, "admin", "123456") 33 | ddb_script = ''' 34 | login('admin','123456') 35 | dbPath='{db}' 36 | if(existsDatabase(dbPath)) 37 | dropDatabase(dbPath) 38 | db=database(dbPath,RANGE,0..10*10000+1) 39 | n=100000 40 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, 1..n as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 41 | db.createPartitionedTable(tdata,`{tb},`id).append!(tdata) 42 | '''.format(db=DBInfo.dfsDBName, tb=DBInfo.table) 43 | s.run(ddb_script) 44 | s.close() 45 | 46 | 47 | def create_dfs_hash_db(): 48 | s = ddb.session() 49 | s.connect(HOST, PORT, "admin", "123456") 50 | ddb_script = ''' 51 | login('admin','123456') 52 | dbPath='{db}' 53 | if(existsDatabase(dbPath)) 54 | dropDatabase(dbPath) 55 | db=database(dbPath,HASH,[INT,10]) 56 | n=100000 57 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 58 | db.createPartitionedTable(tdata,`{tb},`id).append!(tdata) 59 | '''.format(db=DBInfo.dfsDBName, tb=DBInfo.table) 60 | s.run(ddb_script) 61 | s.close() 62 | 63 | 64 | def create_dfs_value_db(): 65 | s = ddb.session() 66 | s.connect(HOST, PORT, "admin", "123456") 67 | ddb_script = ''' 68 | login('admin','123456') 69 | dbPath='{db}' 70 | if(existsDatabase(dbPath)) 71 | dropDatabase(dbPath) 72 | db=database(dbPath,VALUE,2010.01.01..2010.01.30) 73 | n=100000 74 | tdata=table(sort(take(2010.01.01..2010.01.30, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 75 | db.createPartitionedTable(tdata,`{tb},`date).append!(tdata) 76 | '''.format(db=DBInfo.dfsDBName, tb=DBInfo.table) 77 | s.run(ddb_script) 78 | s.close() 79 | 80 | 81 | def create_dfs_list_db(): 82 | s = ddb.session() 83 | s.connect(HOST, PORT, "admin", "123456") 84 | ddb_script = ''' 85 | login('admin','123456') 86 | dbPath='{db}' 87 | if(existsDatabase(dbPath)) 88 | dropDatabase(dbPath) 89 | db=database(dbPath,LIST,[`AMD`QWE`CES,`DOP`ASZ,`FSD`BBVC,`AWQ`DS]) 90 | n=100000 91 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 92 | db.createPartitionedTable(tdata,`{tb},`sym).append!(tdata) 93 | '''.format(db=DBInfo.dfsDBName, tb=DBInfo.table) 94 | s.run(ddb_script) 95 | s.close() 96 | 97 | 98 | def create_dfs_compo_range_range_db(): 99 | s = ddb.session() 100 | s.connect(HOST, PORT, "admin", "123456") 101 | ddb_script = ''' 102 | login('admin','123456') 103 | dbPath='{db}' 104 | if(existsDatabase(dbPath)) 105 | dropDatabase(dbPath) 106 | db1=database('',RANGE,2010.01M+0..12) 107 | db2=database('',RANGE,1 3 5 7 9 11) 108 | db=database(dbPath,COMPO,[db1,db2]) 109 | n=100000 110 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 111 | db.createPartitionedTable(tdata,`{tb},`date`id).append!(tdata) 112 | '''.format(db=DBInfo.dfsDBName, tb=DBInfo.table) 113 | s.run(ddb_script) 114 | s.close() 115 | 116 | 117 | def create_disk_unpartitioned_db(): 118 | s = ddb.session() 119 | s.connect(HOST, PORT, "admin", "123456") 120 | ddb_script = ''' 121 | login('admin','123456') 122 | dbPath='{db}' 123 | if(exists(dbPath)) 124 | dropDatabase(dbPath) 125 | db=database(dbPath) 126 | n=100000 127 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, 1..n as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 128 | saveTable(db,tdata) 129 | '''.format(db=DBInfo.diskDBName) 130 | s.run(ddb_script) 131 | s.close() 132 | 133 | 134 | def create_disk_range_db(): 135 | s = ddb.session() 136 | s.connect(HOST, PORT, "admin", "123456") 137 | ddb_script = ''' 138 | login('admin','123456') 139 | dbPath='{db}' 140 | if(exists(dbPath)) 141 | dropDatabase(dbPath) 142 | db=database(dbPath,RANGE,0..10*10000+1) 143 | n=100000 144 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, 1..n as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 145 | db.createPartitionedTable(tdata,`{tb},`id).append!(tdata) 146 | '''.format(db=DBInfo.diskDBName, tb=DBInfo.table) 147 | s.run(ddb_script) 148 | s.close() 149 | 150 | 151 | def create_disk_hash_db(): 152 | s = ddb.session() 153 | s.connect(HOST, PORT, "admin", "123456") 154 | ddb_script = ''' 155 | login('admin','123456') 156 | dbPath='{db}' 157 | if(exists(dbPath)) 158 | dropDatabase(dbPath) 159 | db=database(dbPath,HASH,[INT,10]) 160 | n=100000 161 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 162 | db.createPartitionedTable(tdata,`{tb},`id).append!(tdata) 163 | '''.format(db=DBInfo.diskDBName, tb=DBInfo.table) 164 | s.run(ddb_script) 165 | s.close() 166 | 167 | 168 | def create_disk_value_db(): 169 | s = ddb.session() 170 | s.connect(HOST, PORT, "admin", "123456") 171 | ddb_script = ''' 172 | login('admin','123456') 173 | dbPath='{db}' 174 | if(exists(dbPath)) 175 | dropDatabase(dbPath) 176 | db=database(dbPath,VALUE,2010.01.01..2010.01.30) 177 | n=100000 178 | tdata=table(sort(take(2010.01.01..2010.01.30, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 179 | db.createPartitionedTable(tdata,`{tb},`date).append!(tdata) 180 | '''.format(db=DBInfo.diskDBName, tb=DBInfo.table) 181 | s.run(ddb_script) 182 | s.close() 183 | 184 | 185 | def create_disk_list_db(): 186 | s = ddb.session() 187 | s.connect(HOST, PORT, "admin", "123456") 188 | ddb_script = ''' 189 | login('admin','123456') 190 | dbPath='{db}' 191 | if(exists(dbPath)) 192 | dropDatabase(dbPath) 193 | db=database(dbPath,LIST,[`AMD`QWE`CES,`DOP`ASZ,`FSD`BBVC,`AWQ`DS]) 194 | n=100000 195 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 196 | db.createPartitionedTable(tdata,`{tb},`sym).append!(tdata) 197 | '''.format(db=DBInfo.diskDBName, tb=DBInfo.table) 198 | s.run(ddb_script) 199 | s.close() 200 | 201 | 202 | def create_disk_compo_range_range_db(): 203 | s = ddb.session() 204 | s.connect(HOST, PORT, "admin", "123456") 205 | ddb_script = ''' 206 | login('admin','123456') 207 | dbPath='{db}' 208 | if(exists(dbPath)) 209 | //dropDatabase(dbPath) 210 | rmdir(dbPath, true) 211 | db1=database('',RANGE,2010.01M+0..12) 212 | db2=database('',RANGE,1 3 5 7 9 11) 213 | db=database(dbPath,COMPO,[db1,db2]) 214 | n=100000 215 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 216 | db.createPartitionedTable(tdata,`{tb},`date`id).append!(tdata) 217 | '''.format(db=DBInfo.diskDBName, tb=DBInfo.table) 218 | s.run(ddb_script) 219 | s.close() 220 | 221 | 222 | def existsDB(dbName): 223 | s = ddb.session() 224 | s.connect(HOST, PORT, "admin", "123456") 225 | return s.run("existsDatabase('{db}')".format(db=dbName)) 226 | 227 | 228 | class DropDatabaseTest(unittest.TestCase): 229 | @classmethod 230 | def setUp(cls): 231 | cls.s = ddb.session() 232 | cls.s.connect(HOST, PORT, "admin", "123456") 233 | dbPaths = [DBInfo.dfsDBName, DBInfo.diskDBName] 234 | for dbPath in dbPaths: 235 | script = """ 236 | if(existsDatabase('{dbPath}')) 237 | dropDatabase('{dbPath}') 238 | if(exists('{dbPath}')) 239 | rmdir('{dbPath}', true) 240 | """.format(dbPath=dbPath) 241 | cls.s.run(script) 242 | 243 | @classmethod 244 | def tearDown(cls): 245 | cls.s = ddb.session() 246 | cls.s.connect(HOST, PORT, "admin", "123456") 247 | dbPaths = [DBInfo.dfsDBName, DBInfo.diskDBName] 248 | for dbPath in dbPaths: 249 | script = """ 250 | if(existsDatabase('{dbPath}')) 251 | dropDatabase('{dbPath}') 252 | if(exists('{dbPath}')) 253 | rmdir('{dbPath}', true) 254 | """.format(dbPath=dbPath) 255 | cls.s.run(script) 256 | 257 | def test_dropDatabase_dfs_dimension(self): 258 | dbName = DBInfo.dfsDBName 259 | self.assertEqual(existsDB(dbName), False) 260 | create_dfs_dimension_db() 261 | self.assertEqual(existsDB(dbName), True) 262 | self.s.dropDatabase(dbName) 263 | self.assertEqual(existsDB(dbName), False) 264 | 265 | def test_dropDatabase_dfs_range(self): 266 | dbName = DBInfo.dfsDBName 267 | self.assertEqual(existsDB(dbName), False) 268 | create_dfs_range_db() 269 | self.assertEqual(existsDB(dbName), True) 270 | self.s.dropDatabase(dbName) 271 | self.assertEqual(existsDB(dbName), False) 272 | 273 | def test_dropDatabase_dfs_hash(self): 274 | dbName = DBInfo.dfsDBName 275 | self.assertEqual(existsDB(dbName), False) 276 | create_dfs_hash_db() 277 | self.assertEqual(existsDB(dbName), True) 278 | self.s.dropDatabase(dbName) 279 | self.assertEqual(existsDB(dbName), False) 280 | 281 | def test_dropDatabase_dfs_value(self): 282 | dbName = DBInfo.dfsDBName 283 | self.assertEqual(existsDB(dbName), False) 284 | create_dfs_value_db() 285 | self.assertEqual(existsDB(dbName), True) 286 | self.s.dropDatabase(dbName) 287 | self.assertEqual(existsDB(dbName), False) 288 | 289 | def test_dropDatabase_dfs_list(self): 290 | dbName = DBInfo.dfsDBName 291 | self.assertEqual(existsDB(dbName), False) 292 | create_dfs_list_db() 293 | self.assertEqual(existsDB(dbName), True) 294 | self.s.dropDatabase(dbName) 295 | self.assertEqual(existsDB(dbName), False) 296 | 297 | def test_dropDatabase_dfs_compo_range_range(self): 298 | dbName = DBInfo.dfsDBName 299 | self.assertEqual(existsDB(dbName), False) 300 | create_dfs_compo_range_range_db() 301 | self.assertEqual(existsDB(dbName), True) 302 | self.s.dropDatabase(dbName) 303 | self.assertEqual(existsDB(dbName), False) 304 | 305 | def test_dropDatabase_disk_unpartitioned(self): 306 | dbName = DBInfo.diskDBName 307 | self.assertEqual(path.exists(dbName), False) 308 | create_disk_unpartitioned_db() 309 | self.assertEqual(path.exists(dbName), True) 310 | self.s.dropDatabase(dbName) 311 | self.assertEqual(path.exists(dbName), False) 312 | 313 | def test_dropDatabase_disk_range(self): 314 | dbName = DBInfo.diskDBName 315 | self.assertEqual(existsDB(dbName), False) 316 | create_disk_range_db() 317 | self.assertEqual(existsDB(dbName), True) 318 | self.s.dropDatabase(dbName) 319 | self.assertEqual(existsDB(dbName), False) 320 | 321 | def test_dropDatabase_disk_hash(self): 322 | dbName = DBInfo.diskDBName 323 | self.assertEqual(existsDB(dbName), False) 324 | create_disk_hash_db() 325 | self.assertEqual(existsDB(dbName), True) 326 | self.s.dropDatabase(dbName) 327 | self.assertEqual(existsDB(dbName), False) 328 | 329 | def test_dropDatabase_disk_value(self): 330 | dbName = DBInfo.diskDBName 331 | self.assertEqual(existsDB(dbName), False) 332 | create_disk_value_db() 333 | self.assertEqual(existsDB(dbName), True) 334 | self.s.dropDatabase(dbName) 335 | self.assertEqual(existsDB(dbName), False) 336 | 337 | def test_dropDatabase_disk_list(self): 338 | dbName = DBInfo.diskDBName 339 | self.assertEqual(existsDB(dbName), False) 340 | create_disk_list_db() 341 | self.assertEqual(existsDB(dbName), True) 342 | self.s.dropDatabase(dbName) 343 | self.assertEqual(existsDB(dbName), False) 344 | 345 | def test_dropDatabase_disk_compo_range_range(self): 346 | dbName = DBInfo.diskDBName 347 | self.assertEqual(existsDB(dbName), False) 348 | create_disk_compo_range_range_db() 349 | self.assertEqual(existsDB(dbName), True) 350 | self.s.dropDatabase(dbName) 351 | self.assertEqual(existsDB(dbName), False) 352 | 353 | def test_dropDatabase_paramete(self): 354 | dbName = DBInfo.diskDBName 355 | self.assertEqual(existsDB(dbName), False) 356 | create_disk_value_db() 357 | self.assertEqual(existsDB(dbName), True) 358 | with self.assertRaises(TypeError): 359 | self.s.dropDatabase(dbPath_error=dbName) 360 | self.s.dropDatabase(dbPath= dbName) 361 | self.assertEqual(existsDB(dbName), False) 362 | 363 | 364 | 365 | 366 | if __name__ == '__main__': 367 | unittest.main() 368 | -------------------------------------------------------------------------------- /test/test_enableStreaming.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import dolphindb as ddb 3 | import numpy as np 4 | from setup import * 5 | 6 | 7 | class TestEanbleStreaming(unittest.TestCase): 8 | @classmethod 9 | def setUp(cls): 10 | cls.s = ddb.session() 11 | cls.s.connect(HOST, PORT, "admin", "123456") 12 | cls.NODE1 = cls.s.run("getNodeAlias()") 13 | cls.NODE2 = cls.s.run("(exec name from rpc(getControllerAlias(), getClusterPerf) where mode = 0 and name != getNodeAlias())[0]") 14 | cls.NODE3 = cls.s.run("(exec name from rpc(getControllerAlias(), getClusterPerf) where mode = 0 and name != getNodeAlias())[1]") 15 | 16 | @classmethod 17 | def tearDownClass(cls): 18 | pass 19 | 20 | def test_enableStreaming_port_string(self): 21 | self.assertRaises(TypeError, self.s.enableStreaming, "aaa") 22 | 23 | def test_enableStream_port_missing(self): 24 | self.assertRaises(TypeError, self.s.enableStreaming) 25 | 26 | def test_enableStream_port_nan(self): 27 | self.assertRaises(TypeError, self.s.enableStreaming, np.nan) 28 | 29 | def test_enableStream_port_float(self): 30 | self.assertRaises(TypeError, self.s.enableStreaming, 25.48) 31 | 32 | def test_enableStream_port_negative_integer(self): 33 | self.assertRaises(RuntimeError, self.s.enableStreaming, -2) 34 | 35 | def test_enableStream_port_zero(self): 36 | self.assertRaises(RuntimeError, self.s.enableStreaming, 0) 37 | 38 | def test_enableStream_port_zero(self): 39 | self.assertRaises(RuntimeError, self.s.enableStreaming, 0) 40 | 41 | 42 | if __name__ == '__main__': 43 | unittest.main() -------------------------------------------------------------------------------- /test/test_existsDatabase.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import dolphindb as ddb 3 | from os import path 4 | from setup import HOST, PORT, WORK_DIR 5 | 6 | 7 | class DBInfo: 8 | dfsDBName = 'dfs://testExistsDatabase' 9 | diskDBName = WORK_DIR + '/testExistsDatabase' 10 | table = 'tb1' 11 | 12 | 13 | def create_dfs_dimension_db(): 14 | s = ddb.session() 15 | s.connect(HOST, PORT, "admin", "123456") 16 | ddb_script = ''' 17 | login('admin','123456') 18 | dbPath='{db}' 19 | if(existsDatabase(dbPath)) 20 | dropDatabase(dbPath) 21 | db=database(dbPath,RANGE,1..10) 22 | n=100000 23 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 24 | db.createTable(tdata,`{tb}).append!(tdata) 25 | '''.format(db=DBInfo.dfsDBName, tb=DBInfo.table) 26 | s.run(ddb_script) 27 | s.close() 28 | 29 | 30 | def create_dfs_range_db(): 31 | s = ddb.session() 32 | s.connect(HOST, PORT, "admin", "123456") 33 | ddb_script = ''' 34 | login('admin','123456') 35 | dbPath='{db}' 36 | if(existsDatabase(dbPath)) 37 | dropDatabase(dbPath) 38 | db=database(dbPath,RANGE,0..10*10000+1) 39 | n=100000 40 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, 1..n as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 41 | db.createPartitionedTable(tdata,`{tb},`id).append!(tdata) 42 | '''.format(db=DBInfo.dfsDBName, tb=DBInfo.table) 43 | s.run(ddb_script) 44 | s.close() 45 | 46 | 47 | def create_dfs_hash_db(): 48 | s = ddb.session() 49 | s.connect(HOST, PORT, "admin", "123456") 50 | ddb_script = ''' 51 | login('admin','123456') 52 | dbPath='{db}' 53 | if(existsDatabase(dbPath)) 54 | dropDatabase(dbPath) 55 | db=database(dbPath,HASH,[INT,10]) 56 | n=100000 57 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 58 | db.createPartitionedTable(tdata,`{tb},`id).append!(tdata) 59 | '''.format(db=DBInfo.dfsDBName, tb=DBInfo.table) 60 | s.run(ddb_script) 61 | s.close() 62 | 63 | 64 | def create_dfs_value_db(): 65 | s = ddb.session() 66 | s.connect(HOST, PORT, "admin", "123456") 67 | ddb_script = ''' 68 | login('admin','123456') 69 | dbPath='{db}' 70 | if(existsDatabase(dbPath)) 71 | dropDatabase(dbPath) 72 | db=database(dbPath,VALUE,2010.01.01..2010.01.30) 73 | n=100000 74 | tdata=table(sort(take(2010.01.01..2010.01.30, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 75 | db.createPartitionedTable(tdata,`{tb},`date).append!(tdata) 76 | '''.format(db=DBInfo.dfsDBName, tb=DBInfo.table) 77 | s.run(ddb_script) 78 | s.close() 79 | 80 | 81 | def create_dfs_list_db(): 82 | s = ddb.session() 83 | s.connect(HOST, PORT, "admin", "123456") 84 | ddb_script = ''' 85 | login('admin','123456') 86 | dbPath='{db}' 87 | if(existsDatabase(dbPath)) 88 | dropDatabase(dbPath) 89 | db=database(dbPath,LIST,[`AMD`QWE`CES,`DOP`ASZ,`FSD`BBVC,`AWQ`DS]) 90 | n=100000 91 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 92 | db.createPartitionedTable(tdata,`{tb},`sym).append!(tdata) 93 | '''.format(db=DBInfo.dfsDBName, tb=DBInfo.table) 94 | s.run(ddb_script) 95 | s.close() 96 | 97 | 98 | def create_dfs_compo_range_range_db(): 99 | s = ddb.session() 100 | s.connect(HOST, PORT, "admin", "123456") 101 | ddb_script = ''' 102 | login('admin','123456') 103 | dbPath='{db}' 104 | if(existsDatabase(dbPath)) 105 | dropDatabase(dbPath) 106 | db1=database('',RANGE,2010.01M+0..12) 107 | db2=database('',RANGE,1 3 5 7 9 11) 108 | db=database(dbPath,COMPO,[db1,db2]) 109 | n=100000 110 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 111 | db.createPartitionedTable(tdata,`{tb},`date`id).append!(tdata) 112 | '''.format(db=DBInfo.dfsDBName, tb=DBInfo.table) 113 | s.run(ddb_script) 114 | s.close() 115 | 116 | 117 | def create_disk_unpartitioned_db(): 118 | s = ddb.session() 119 | s.connect(HOST, PORT, "admin", "123456") 120 | ddb_script = ''' 121 | login('admin','123456') 122 | dbPath='{db}' 123 | if(exists(dbPath)) 124 | dropDatabase(dbPath) 125 | db=database(dbPath) 126 | n=100000 127 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, 1..n as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 128 | saveTable(db,tdata) 129 | '''.format(db=DBInfo.diskDBName) 130 | s.run(ddb_script) 131 | s.close() 132 | 133 | 134 | def create_disk_range_db(): 135 | s = ddb.session() 136 | s.connect(HOST, PORT, "admin", "123456") 137 | ddb_script = ''' 138 | login('admin','123456') 139 | dbPath='{db}' 140 | if(exists(dbPath)) 141 | dropDatabase(dbPath) 142 | db=database(dbPath,RANGE,0..10*10000+1) 143 | n=100000 144 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, 1..n as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 145 | db.createPartitionedTable(tdata,`{tb},`id).append!(tdata) 146 | '''.format(db=DBInfo.diskDBName, tb=DBInfo.table) 147 | s.run(ddb_script) 148 | s.close() 149 | 150 | 151 | def create_disk_hash_db(): 152 | s = ddb.session() 153 | s.connect(HOST, PORT, "admin", "123456") 154 | ddb_script = ''' 155 | login('admin','123456') 156 | dbPath='{db}' 157 | if(exists(dbPath)) 158 | dropDatabase(dbPath) 159 | db=database(dbPath,HASH,[INT,10]) 160 | n=100000 161 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 162 | db.createPartitionedTable(tdata,`{tb},`id).append!(tdata) 163 | '''.format(db=DBInfo.diskDBName, tb=DBInfo.table) 164 | s.run(ddb_script) 165 | s.close() 166 | 167 | 168 | def create_disk_value_db(): 169 | s = ddb.session() 170 | s.connect(HOST, PORT, "admin", "123456") 171 | ddb_script = ''' 172 | login('admin','123456') 173 | dbPath='{db}' 174 | if(exists(dbPath)) 175 | dropDatabase(dbPath) 176 | db=database(dbPath,VALUE,2010.01.01..2010.01.30) 177 | n=100000 178 | tdata=table(sort(take(2010.01.01..2010.01.30, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 179 | db.createPartitionedTable(tdata,`{tb},`date).append!(tdata) 180 | '''.format(db=DBInfo.diskDBName, tb=DBInfo.table) 181 | s.run(ddb_script) 182 | s.close() 183 | 184 | 185 | def create_disk_list_db(): 186 | s = ddb.session() 187 | s.connect(HOST, PORT, "admin", "123456") 188 | ddb_script = ''' 189 | login('admin','123456') 190 | dbPath='{db}' 191 | if(exists(dbPath)) 192 | dropDatabase(dbPath) 193 | db=database(dbPath,LIST,[`AMD`QWE`CES,`DOP`ASZ,`FSD`BBVC,`AWQ`DS]) 194 | n=100000 195 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 196 | db.createPartitionedTable(tdata,`{tb},`sym).append!(tdata) 197 | '''.format(db=DBInfo.diskDBName, tb=DBInfo.table) 198 | s.run(ddb_script) 199 | s.close() 200 | 201 | 202 | def create_disk_compo_range_range_db(): 203 | s = ddb.session() 204 | s.connect(HOST, PORT, "admin", "123456") 205 | ddb_script = ''' 206 | login('admin','123456') 207 | dbPath='{db}' 208 | if(exists(dbPath)) 209 | //dropDatabase(dbPath) 210 | rmdir(dbPath, true) 211 | db1=database('',RANGE,2010.01M+0..12) 212 | db2=database('',RANGE,1 3 5 7 9 11) 213 | db=database(dbPath,COMPO,[db1,db2]) 214 | n=100000 215 | tdata=table(sort(take(2010.01.01..2010.12.31, n)) as date, take(1..10,n) as id,take(`AMD`QWE`CES`DOP`ASZ`FSD`BBVC`AWQ`DS, n) as sym,rand(100,n) as val) 216 | db.createPartitionedTable(tdata,`{tb},`date`id).append!(tdata) 217 | '''.format(db=DBInfo.diskDBName, tb=DBInfo.table) 218 | s.run(ddb_script) 219 | s.close() 220 | 221 | 222 | def dropDB(dbName): 223 | s = ddb.session() 224 | s.connect(HOST, PORT, "admin", "123456") 225 | s.run("dropDatabase('{db}')".format(db=dbName)) 226 | 227 | 228 | class ExistsDatabaseTest(unittest.TestCase): 229 | @classmethod 230 | def setUp(cls): 231 | cls.s = ddb.session() 232 | cls.s.connect(HOST, PORT, "admin", "123456") 233 | dbPaths = [DBInfo.dfsDBName, DBInfo.diskDBName] 234 | for dbPath in dbPaths: 235 | script = """ 236 | if(existsDatabase('{dbPath}')) 237 | dropDatabase('{dbPath}') 238 | if(exists('{dbPath}')) 239 | rmdir('{dbPath}', true) 240 | """.format(dbPath=dbPath) 241 | cls.s.run(script) 242 | 243 | @classmethod 244 | def tearDown(cls): 245 | cls.s = ddb.session() 246 | cls.s.connect(HOST, PORT, "admin", "123456") 247 | dbPaths = [DBInfo.dfsDBName, DBInfo.diskDBName] 248 | for dbPath in dbPaths: 249 | script = """ 250 | if(existsDatabase('{dbPath}')) 251 | dropDatabase('{dbPath}') 252 | if(exists('{dbPath}')) 253 | rmdir('{dbPath}', true) 254 | """.format(dbPath=dbPath) 255 | cls.s.run(script) 256 | 257 | def test_existsDatabase_dfs_dimension(self): 258 | dbName = DBInfo.dfsDBName 259 | self.assertEqual(self.s.existsDatabase(dbName), False) 260 | create_dfs_dimension_db() 261 | self.assertEqual(self.s.existsDatabase(dbName), True) 262 | dropDB(dbName) 263 | self.assertEqual(self.s.existsDatabase(dbName), False) 264 | 265 | def test_existsDatabase_dfs_range(self): 266 | dbName = DBInfo.dfsDBName 267 | self.assertEqual(self.s.existsDatabase(dbName), False) 268 | create_dfs_range_db() 269 | self.assertEqual(self.s.existsDatabase(dbName), True) 270 | dropDB(dbName) 271 | self.assertEqual(self.s.existsDatabase(dbName), False) 272 | 273 | def test_existsDatabase_dfs_hash(self): 274 | dbName = DBInfo.dfsDBName 275 | self.assertEqual(self.s.existsDatabase(dbName), False) 276 | create_dfs_hash_db() 277 | self.assertEqual(self.s.existsDatabase(dbName), True) 278 | dropDB(dbName) 279 | self.assertEqual(self.s.existsDatabase(dbName), False) 280 | 281 | def test_existsDatabase_dfs_value(self): 282 | dbName = DBInfo.dfsDBName 283 | self.assertEqual(self.s.existsDatabase(dbName), False) 284 | create_dfs_value_db() 285 | self.assertEqual(self.s.existsDatabase(dbName), True) 286 | dropDB(dbName) 287 | self.assertEqual(self.s.existsDatabase(dbName), False) 288 | 289 | def test_existsDatabase_dfs_list(self): 290 | dbName = DBInfo.dfsDBName 291 | self.assertEqual(self.s.existsDatabase(dbName), False) 292 | create_dfs_list_db() 293 | self.assertEqual(self.s.existsDatabase(dbName), True) 294 | dropDB(dbName) 295 | self.assertEqual(self.s.existsDatabase(dbName), False) 296 | 297 | def test_existsDatabase_dfs_compo_range_range(self): 298 | dbName = DBInfo.dfsDBName 299 | self.assertEqual(self.s.existsDatabase(dbName), False) 300 | create_dfs_compo_range_range_db() 301 | self.assertEqual(self.s.existsDatabase(dbName), True) 302 | dropDB(dbName) 303 | self.assertEqual(self.s.existsDatabase(dbName), False) 304 | 305 | def test_existsDatabase_disk_unpartitioned(self): 306 | dbName = DBInfo.diskDBName 307 | self.assertEqual(path.exists(dbName), False) 308 | create_disk_unpartitioned_db() 309 | self.assertEqual(path.exists(dbName), True) 310 | dropDB(dbName) 311 | self.assertEqual(path.exists(dbName), False) 312 | 313 | def test_existsDatabase_disk_range(self): 314 | dbName = DBInfo.diskDBName 315 | self.assertEqual(self.s.existsDatabase(dbName), False) 316 | create_disk_range_db() 317 | self.assertEqual(self.s.existsDatabase(dbName), True) 318 | dropDB(dbName) 319 | self.assertEqual(self.s.existsDatabase(dbName), False) 320 | 321 | def test_existsDatabase_disk_hash(self): 322 | dbName = DBInfo.diskDBName 323 | self.assertEqual(self.s.existsDatabase(dbName), False) 324 | create_disk_hash_db() 325 | self.assertEqual(self.s.existsDatabase(dbName), True) 326 | dropDB(dbName) 327 | self.assertEqual(self.s.existsDatabase(dbName), False) 328 | 329 | def test_existsDatabase_disk_value(self): 330 | dbName = DBInfo.diskDBName 331 | self.assertEqual(self.s.existsDatabase(dbName), False) 332 | create_disk_value_db() 333 | self.assertEqual(self.s.existsDatabase(dbName), True) 334 | dropDB(dbName) 335 | self.assertEqual(self.s.existsDatabase(dbName), False) 336 | 337 | def test_existsDatabase_disk_list(self): 338 | dbName = DBInfo.diskDBName 339 | self.assertEqual(self.s.existsDatabase(dbName), False) 340 | create_disk_list_db() 341 | self.assertEqual(self.s.existsDatabase(dbName), True) 342 | dropDB(dbName) 343 | self.assertEqual(self.s.existsDatabase(dbName), False) 344 | 345 | def test_existsDatabase_disk_compo_range_range(self): 346 | dbName = DBInfo.diskDBName 347 | self.assertEqual(self.s.existsDatabase(dbName), False) 348 | create_disk_compo_range_range_db() 349 | self.assertEqual(self.s.existsDatabase(dbName), True) 350 | dropDB(dbName) 351 | self.assertEqual(self.s.existsDatabase(dbName), False) 352 | 353 | def test_existsDatabase_peremata(self): 354 | dbName = DBInfo.diskDBName 355 | create_disk_compo_range_range_db() 356 | with self.assertRaises(TypeError): 357 | self.s.existsDatabase(dbUrl_ERROR=dbName) 358 | self.s.existsDatabase(dbUrl=dbName) 359 | dropDB(dbName) 360 | if __name__ == '__main__': 361 | unittest.main() 362 | -------------------------------------------------------------------------------- /test/test_hashBucket.py: -------------------------------------------------------------------------------- 1 | 2 | import unittest 3 | import dolphindb as ddb 4 | import numpy as np 5 | import pandas as pd 6 | from setup import HOST, PORT, WORK_DIR, DATA_DIR 7 | from numpy.testing import assert_array_equal, assert_array_almost_equal 8 | 9 | class TestFunctionHashBucket(unittest.TestCase): 10 | @classmethod 11 | def setUp(cls): 12 | cls.s = ddb.session() 13 | cls.s.connect(HOST, PORT, "admin", "123456") 14 | @classmethod 15 | def tearDownClass(cls): 16 | pass 17 | 18 | def test_hashBucket_bucketNum_double(self): 19 | with self.assertRaises(ValueError): 20 | self.s.hashBucket("a", 3.5) 21 | 22 | def test_hashBucket_bucketNum_negative(self): 23 | pass 24 | # print(self.s.hashBucket("a", -3)) 25 | 26 | def test_hashBucket_bucketNum_zero(self): 27 | pass 28 | # print(self.s.hashBucket("a", 0)) 29 | 30 | def test_hashBucket_input_bool(self): 31 | pass 32 | # print(self.s.hashBucket([True, False], 3)) 33 | 34 | def test_hashBucket_input_int(self): 35 | re = self.s.hashBucket(np.arange(1, 51), 7) 36 | expected = self.s.run("hashBucket(1..50, 7)") 37 | assert_array_equal(re, expected) 38 | re = self.s.hashBucket([11, 58, 23, 59, np.nan, np.nan], 5) 39 | expected = self.s.run("hashBucket([11, 58, 23, 59, NULL, NULL], 5)") 40 | assert_array_equal(re, expected) 41 | re = self.s.hashBucket(1, 3) 42 | expected = self.s.run("hashBucket(1, 3)") 43 | self.assertEqual(re, expected) 44 | # re = self.s.hashBucket(np.nan, 3) 45 | # expected = self.s.run("hashBucket(int(), 3)") 46 | # self.assertEqual(re, expected) 47 | 48 | def test_hashBucket_input_string(self): 49 | re = self.s.hashBucket(["aaa", "", "ccc"], 3) 50 | expected = self.s.run('hashBucket(["aaa", "", "ccc"], 3)') 51 | assert_array_equal(re, expected) 52 | re = self.s.hashBucket(["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"], 7) 53 | expected = self.s.run('hashBucket(["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"], 7)') 54 | assert_array_equal(re, expected) 55 | re = self.s.hashBucket("AAPL", 3) 56 | expected = self.s.run('hashBucket("AAPL", 3)') 57 | self.assertEqual(re, expected) 58 | re = self.s.hashBucket("", 5) 59 | expected = self.s.run('hashBucket("", 5)') 60 | self.assertEqual(re, expected) 61 | 62 | def test_hashBucket_input_date(self): 63 | # re = self.s.hashBucket(np.array(pd.date_range(start='2012-1-1', end='2012-12-31', freq='D'), dtype='datetime64[D]'), 50) 64 | re = self.s.hashBucket(np.array(pd.date_range(start='1/1/2012', end='12/31/2012', freq='D'), dtype='datetime64[D]'), 50) 65 | expected = self.s.run('hashBucket(2012.01.01..2012.12.31, 50)') 66 | assert_array_equal(re, expected) 67 | # re = self.s.hashBucket([np.datetime64('2012-06-08', dtype='datetime64[D]'), np.datetime64('NaT', dtype='datetime64[D]'), np.datetime64('2012-06-11', dtype='datetime64[D]')], 5) 68 | # expected = self.s.run("hashBucket([2012.06.08, NULL, 2012.06.11], 5)") 69 | # assert_array_equal(re, expected) 70 | re = self.s.hashBucket(np.datetime64('2013-01-01'), 5) 71 | # re = self.s.hashBucket(np.datetime64('2013-01-01', dtype='datetime64[D]'), 5) 72 | expected = self.s.run('hashBucket(2013.01.01, 5)') 73 | self.assertEqual(re, expected) 74 | # re = self.s.hashBucket(np.datetime64('NaT', dtype='datetime64[D]'), 5) 75 | # expected = self.s.run('hashBucket(date(), 5)') 76 | # self.assertEqual(re, expected) 77 | 78 | def test_hashBucket_input_month(self): 79 | re = self.s.hashBucket(np.array(pd.date_range(start='2005-1', end='2007-01', freq='M'), dtype='datetime64[M]'), 13) 80 | expected = self.s.run('hashBucket(2005.01M..2006.12M, 13)') 81 | assert_array_equal(re, expected) 82 | # re = self.s.hashBucket(np.array(['2012-06', 'NaT'], dtype='datetime64[M]'), 3) 83 | # expected = self.s.run('hashBucket([2012.06M, month()], 3)') 84 | # assert_array_equal(re, expected) 85 | re = self.s.hashBucket(np.datetime64('2012-06'), 5) 86 | expected = self.s.run('hashBucket(2012.06M, 5)') 87 | self.assertEqual(re, expected) 88 | # re = self.s.hashBucket(np.datetime64('NaT', dtype='datetime64[M]'), 3) 89 | # expected = self.s.run('hashBucket(month(), 3)') 90 | # self.assertEqual(re, expected) 91 | 92 | def test_hashBucket_input_second(self): 93 | re = self.s.hashBucket(np.array(pd.date_range(start='2002-12-01T00:25:36', end='2002-12-02T00:25:36', freq='h'), dtype='datetime64[s]'), 7) 94 | expected = self.s.run("hashBucket(temporalAdd(2002.12.01T00:25:36, 0..24, 'h'), 7)") 95 | assert_array_equal(re, expected) 96 | # re = self.s.hashBucket(np.array(['2002-12-01T00:25:36', 'NaT', '2002-12-02T00:25:36'], dtype='datetime64[s]'), 7) 97 | # expected = self.s.run("hashBucket([2002.12.01T00:25:36, NULL, 2002.12.02T00:25:36], 7)") 98 | # assert_array_equal(re, expected) 99 | re = self.s.hashBucket(np.datetime64('2002-12-02T00:25:36'), 3) 100 | expected = self.s.run("hashBucket(2002.12.02T00:25:36, 3)") 101 | self.assertEqual(re, expected) 102 | # re = self.s.hashBucket(np.datetime64('NaT', dtype='datetime64[s]'), 11) 103 | # expected = self.s.run("hashBucket(datetime(), 11)") 104 | # self.assertEqual(re, expected) 105 | 106 | def test_hashBucket_input_millisecond(self): 107 | re = self.s.hashBucket(np.array(pd.date_range(start='2002-12-01T00:25:36.009', end='2002-12-02T00:25:36.009', freq='h'), dtype='datetime64[ms]'), 7) 108 | expected = self.s.run("hashBucket(temporalAdd(2002.12.01T00:25:36.009, 0..24, 'h'), 7)") 109 | assert_array_equal(re, expected) 110 | # re = self.s.hashBucket(np.array(['2002-12-01T00:25:36.009', 'NaT', '2002-12-02T00:25:36.825'], dtype='datetime64[ms]'), 3) 111 | # expected = self.s.run("hashBucket([2002.12.01T00:25:36.009, NULL, 2002.12.02T00:25:36.825], 3)") 112 | # assert_array_equal(re, expected) 113 | re = self.s.hashBucket(np.datetime64('2002-12-01T00:25:36.009'), 7) 114 | expected = self.s.run("hashBucket(2002.12.01T00:25:36.009, 7)") 115 | self.assertEqual(re, expected) 116 | # re = self.s.hashBucket(np.datetime64('NaT', dtype='datetime64[ms]'), 5) 117 | # expected = self.s.run('hashBucket(timestamp(), 5)') 118 | # self.assertEqual(re, expected) 119 | 120 | def test_hashBucket_input_nanosecond(self): 121 | re = self.s.hashBucket(np.array(pd.date_range(start='2002-12-01T00:25:36.009008007', end='2002-12-02T00:25:36.009008007', freq='h'), dtype='datetime64[ns]'), 7) 122 | expected = self.s.run("hashBucket(temporalAdd(2002.12.01T00:25:36.009008007, 0..24, 'h'), 7)") 123 | assert_array_equal(re, expected) 124 | # re = self.s.hashBucket(np.array(['2002-12-01T00:25:36.009008007', 'NaT', '2002-12-02T00:25:36.825008007'], dtype='datetime64[ns]'), 3) 125 | # expected = self.s.run("hashBucket([2002.12.01T00:25:36.009008007, NULL, 2002.12.02T00:25:36.825008007], 3)") 126 | # assert_array_equal(re, expected) 127 | re = self.s.hashBucket(np.datetime64('2002-12-01T00:25:36.009485695'), 7) 128 | expected = self.s.run("hashBucket(2002.12.01T00:25:36.009485695, 7)") 129 | self.assertEqual(re, expected) 130 | # re = self.s.hashBucket(np.datetime64('NaT', dtype='datetime64[ns]'), 5) 131 | # expected = self.s.run('hashBucket(nanotimestamp(), 5)') 132 | # self.assertEqual(re, expected) 133 | 134 | def test_hashBucket_peremata(self): 135 | with self.assertRaises(TypeError): 136 | self.s.hashBucket(obj_ERROR=["aaa", "", "ccc"], nBucket=3) 137 | with self.assertRaises(TypeError): 138 | self.s.hashBucket(obj=["aaa", "", "ccc"], nBucket_ERROR=3) 139 | self.s.hashBucket(obj=["aaa", "", "ccc"], nBucket=3) 140 | if __name__ == '__main__': 141 | unittest.main() 142 | -------------------------------------------------------------------------------- /test/test_loadText.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import dolphindb as ddb 3 | from pandas.testing import assert_frame_equal 4 | from setup import HOST, PORT, WORK_DIR, DATA_DIR 5 | 6 | 7 | class LoadTextTest(unittest.TestCase): 8 | @classmethod 9 | def setUp(cls): 10 | cls.s = ddb.session() 11 | cls.s.connect(HOST, PORT, "admin", "123456") 12 | 13 | @classmethod 14 | def tearDown(cls): 15 | cls.s = ddb.session() 16 | cls.s.connect(HOST, PORT, "admin", "123456") 17 | 18 | def test_loadText_param_fileName(self): 19 | data = DATA_DIR + "/trades.csv" 20 | tb = self.s.loadText(data) 21 | rs = self.s.run("select * from loadText('{data}')".format(data=data)) 22 | assert_frame_equal(tb.toDF(), rs) 23 | 24 | def test_loadText_param_delimiter(self): 25 | data = DATA_DIR + "/trades.csv" 26 | tb = self.s.loadText(data, ";") 27 | rs = self.s.run("select * from loadText('{data}', ';')".format(data=data)) 28 | assert_frame_equal(tb.toDF(), rs) 29 | 30 | def test_loadText_param_delimiter(self): 31 | data = DATA_DIR + "/trades.csv" 32 | with self.assertRaises(TypeError): 33 | self.s.loadText(remoteFilePath_ERROR=data, delimiter=";") 34 | with self.assertRaises(TypeError): 35 | self.s.loadText(remoteFilePath=data, delimiter_ERROR=";") 36 | self.s.loadText(remoteFilePath=data, delimiter=";") 37 | if __name__ == '__main__': 38 | unittest.main() 39 | -------------------------------------------------------------------------------- /test/test_saveTable.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import dolphindb as ddb 3 | import pandas as pd 4 | import numpy as np 5 | from pandas.testing import assert_frame_equal 6 | from setup import HOST, PORT, WORK_DIR 7 | 8 | 9 | class DBInfo: 10 | diskDBName = WORK_DIR + '/testSaveTable' 11 | table = 'tb' 12 | 13 | 14 | class SaveTableTest(unittest.TestCase): 15 | @classmethod 16 | def setUp(cls): 17 | cls.s = ddb.session() 18 | cls.s.connect(HOST, PORT, "admin", "123456") 19 | dbPath = DBInfo.diskDBName 20 | script = """ 21 | if(existsDatabase('{dbPath}')) 22 | dropDatabase('{dbPath}') 23 | if(exists('{dbPath}')) 24 | rmdir('{dbPath}', true) 25 | """.format(dbPath=dbPath) 26 | cls.s.run(script) 27 | 28 | @classmethod 29 | def tearDown(cls): 30 | cls.s = ddb.session() 31 | cls.s.connect(HOST, PORT, "admin", "123456") 32 | 33 | dbPath = DBInfo.diskDBName 34 | script = """ 35 | if(existsDatabase('{dbPath}')) 36 | dropDatabase('{dbPath}') 37 | if(exists('{dbPath}')) 38 | rmdir('{dbPath}', true) 39 | """.format(dbPath=dbPath) 40 | cls.s.run(script) 41 | 42 | def test_saveTable_disk_unpartitioned(self): 43 | dbPath = DBInfo.diskDBName 44 | tbName = DBInfo.table 45 | n = 1000 46 | df = pd.DataFrame({"id": np.arange(1, n + 1, 1), 47 | "date": pd.date_range("2020.01.01", periods=n), 48 | "sym": np.random.choice(["ASA", "SADSA", "WQE"], n), 49 | "val": np.random.rand(n) * 100}) 50 | data = self.s.table(data=df, tableAliasName=tbName) 51 | self.s.saveTable(data, dbPath) 52 | rs = self.s.loadTable(tbName, dbPath) 53 | assert_frame_equal(rs.toDF(), df) 54 | 55 | def test_saveTable_paramete(self): 56 | dbPath = DBInfo.diskDBName 57 | tbName = DBInfo.table 58 | n = 1000 59 | df = pd.DataFrame({"id": np.arange(1, n + 1, 1), 60 | "date": pd.date_range("2020.01.01", periods=n), 61 | "sym": np.random.choice(["ASA", "SADSA", "WQE"], n), 62 | "val": np.random.rand(n) * 100}) 63 | data = self.s.table(data=df, tableAliasName=tbName) 64 | with self.assertRaises(TypeError): 65 | self.s.saveTable(tbl_ERROR = data, dbPath = dbPath) 66 | with self.assertRaises(TypeError): 67 | self.s.saveTable(tbl = data, dbPath_ERROR = dbPath) 68 | self.s.saveTable(tbl = data, dbPath = dbPath) 69 | 70 | if __name__ == '__main__': 71 | unittest.main() 72 | -------------------------------------------------------------------------------- /test/test_subscribe.py: -------------------------------------------------------------------------------- 1 | # import unittest 2 | # import dolphindb as ddb 3 | # import numpy as np 4 | # import pandas as pd 5 | # from setup import * 6 | # from functools import partial 7 | # import time 8 | # from threading import Event 9 | 10 | # def prepareLocalPublisher(tableName): 11 | # s = ddb.session() 12 | # s.connect(HOST, PORT, "admin", "123456") 13 | # ddb_script = ''' 14 | # n=1000 15 | # share streamTable(n:0, `sym`date`price1`price2`vol1`vol2, [SYMBOL, DATE, DOUBLE, DOUBLE, INT, INT]) as {stName} 16 | # syms = rand(`A`B`C`D`E, n) 17 | # dates = rand(2012.06.01..2012.06.10, n) 18 | # prices1 = rand(100.0, n) 19 | # prices2 = rand(100.0, n) 20 | # vol1 = rand(100, n) 21 | # vol2 = rand(100, n) 22 | # {stName}.append!(table(syms, dates, prices1, prices2, vol1, vol2)) 23 | # '''.format(stName=tableName) 24 | # s.run(ddb_script) 25 | # s.close() 26 | 27 | # def prepareLocalPublisherFilter(tableName, colName): 28 | # s = ddb.session() 29 | # s.connect(HOST, PORT, "admin", "123456") 30 | # ddb_script = ''' 31 | # n=1000 32 | # share streamTable(n:0, `sym`date`price1`price2`vol1`vol2, [SYMBOL, DATE, DOUBLE, DOUBLE, INT, INT]) as {stName} 33 | # setStreamTableFilterColumn({stName}, '{filterCol}') 34 | # syms = rand(`A`B`C`D`E, n) 35 | # dates = rand(2012.06.01..2012.06.10, n) 36 | # prices1 = rand(100.0, n) 37 | # prices2 = rand(100.0, n) 38 | # vol1 = rand(100, n) 39 | # vol2 = rand(100, n) 40 | # {stName}.append!(table(syms, dates, prices1, prices2, vol1, vol2)) 41 | # '''.format(stName=tableName, filterCol=colName) 42 | # s.run(ddb_script) 43 | # s.close() 44 | 45 | # def writePublisher(tableName): 46 | # s = ddb.session() 47 | # s.connect(HOST, PORT, "admin", "123456") 48 | # ddb_script = ''' 49 | # n=1000 50 | # syms = rand(`A`B`C`D`E, n) 51 | # dates = rand(2012.06.01..2012.06.10, n) 52 | # prices1 = rand(100.0, n) 53 | # prices2 = rand(100.0, n) 54 | # vol1 = rand(100, n) 55 | # vol2 = rand(100, n) 56 | # objByName('{stName}').append!(table(syms, dates, prices1, prices2, vol1, vol2)) 57 | # '''.format(stName=tableName) 58 | # s.run(ddb_script) 59 | # s.close() 60 | 61 | # def saveListToDf(df, msg): 62 | # df.loc[len(df)] = msg 63 | 64 | # def saveTableToDf(df, msg): 65 | # df['sym'] = msg[0] 66 | # df['date'] = msg[1] 67 | # df['price1'] = msg[2] 68 | # df['price2'] = msg[3] 69 | # df['vol1'] = msg[4] 70 | # df['vol2'] = msg[5] 71 | 72 | # class TestSubscribe(unittest.TestCase): 73 | # @classmethod 74 | # def setUp(cls): 75 | # cls.s = ddb.session() 76 | # cls.s.connect(HOST, PORT, "admin", "123456") 77 | # cls.NODE1 = cls.s.run("getNodeAlias()") 78 | # cls.NODE2 = cls.s.run("(exec name from rpc(getControllerAlias(), getClusterPerf) where mode = 0 and name != getNodeAlias() order by name)[0]") 79 | # cls.NODE3 = cls.s.run("(exec name from rpc(getControllerAlias(), getClusterPerf) where mode = 0 and name != getNodeAlias() order by name)[1]") 80 | # cls.PORT1 = cls.s.run("(exec port from rpc(getControllerAlias(), getClusterPerf) where mode = 0 and name = '{nodeName}')[0]".format(nodeName=cls.NODE1)) 81 | # cls.PORT2 = cls.s.run("(exec port from rpc(getControllerAlias(), getClusterPerf) where mode = 0 and name = '{nodeName}')[0]".format(nodeName=cls.NODE2)) 82 | # cls.PORT3 = cls.s.run("(exec port from rpc(getControllerAlias(), getClusterPerf) where mode = 0 and name = '{nodeName}')[0]".format(nodeName=cls.NODE3)) 83 | # cls.HOST1 = cls.s.run("(exec host from rpc(getControllerAlias(), getClusterPerf) where mode = 0 and name = '{nodeName}')[0]".format(nodeName=cls.NODE1)) 84 | # cls.HOST2 = cls.s.run("(exec host from rpc(getControllerAlias(), getClusterPerf) where mode = 0 and name = '{nodeName}')[0]".format(nodeName=cls.NODE2)) 85 | # cls.HOST3 = cls.s.run("(exec host from rpc(getControllerAlias(), getClusterPerf) where mode = 0 and name = '{nodeName}')[0]".format(nodeName=cls.NODE3)) 86 | 87 | # @classmethod 88 | # def tearDownClass(cls): 89 | # Event().wait(1) 90 | # cls.s.close() 91 | 92 | # def test_subscribe_host_missing(self): 93 | # self.s.enableStreaming(SUBPORT) 94 | # prepareLocalPublisher("st") 95 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 96 | # saveListToDf_partial = partial(saveListToDf, subDf) 97 | # self.assertRaises(TypeError, self.s.subscribe, self.PORT1, saveListToDf_partial, "st", "st_slave", 0) 98 | 99 | # def test_subscribe_host_integer(self): 100 | # self.s.enableStreaming(SUBPORT) 101 | # prepareLocalPublisher("st") 102 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 103 | # saveListToDf_partial = partial(saveListToDf, subDf) 104 | # self.assertRaises(TypeError, self.s.subscribe, 1, self.PORT1, saveListToDf_partial, "st", "st_slave", 0) 105 | 106 | # def test_subscribe_host_float(self): 107 | # self.s.enableStreaming(SUBPORT) 108 | # prepareLocalPublisher("st") 109 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 110 | # saveListToDf_partial = partial(saveListToDf, subDf) 111 | # self.assertRaises(TypeError, self.s.subscribe, np.nan, self.PORT1, saveListToDf_partial, "st", "st_slave", 0) 112 | 113 | # def test_subscribe_host_invalid(self): 114 | # self.s.enableStreaming(SUBPORT) 115 | # prepareLocalPublisher("st") 116 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 117 | # saveListToDf_partial = partial(saveListToDf, subDf) 118 | # self.assertRaises(RuntimeError, self.s.subscribe, "aaa", self.PORT1, saveListToDf_partial, "st", "st_slave", 0) 119 | 120 | # def test_subscribe_port_float(self): 121 | # self.s.enableStreaming(SUBPORT) 122 | # prepareLocalPublisher("st") 123 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 124 | # saveListToDf_partial = partial(saveListToDf, subDf) 125 | # self.assertRaises(TypeError, self.s.subscribe, self.HOST1, np.nan, saveListToDf_partial, "st", "st_slave", 0) 126 | 127 | 128 | # def test_subscribe_port_string(self): 129 | # self.s.enableStreaming(SUBPORT) 130 | # prepareLocalPublisher("st") 131 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 132 | # saveListToDf_partial = partial(saveListToDf, subDf) 133 | # self.assertRaises(TypeError, self.s.subscribe, self.HOST1, "aaa", saveListToDf_partial, "st", "st_slave", 0) 134 | 135 | # def test_subscribe_tableName_integer(self): 136 | # self.s.enableStreaming(SUBPORT) 137 | # prepareLocalPublisher("st") 138 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 139 | # saveListToDf_partial = partial(saveListToDf, subDf) 140 | # self.assertRaises(TypeError, self.s.subscribe, self.HOST1, self.PORT1, saveListToDf_partial, 1, "st_slave", 0) 141 | 142 | # def test_subscribe_tableName_float(self): 143 | # self.s.enableStreaming(SUBPORT) 144 | # prepareLocalPublisher("st") 145 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 146 | # saveListToDf_partial = partial(saveListToDf, subDf) 147 | # self.assertRaises(TypeError, self.s.subscribe, self.HOST1, self.PORT1, saveListToDf_partial, np.nan, "st_slave", 0) 148 | # def test_subscribe_table_not_exist(self): 149 | # self.s.enableStreaming(SUBPORT) 150 | # prepareLocalPublisher("st") 151 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 152 | # saveListToDf_partial = partial(saveListToDf, subDf) 153 | # self.assertRaises(RuntimeError, self.s.subscribe, self.HOST1, self.PORT1, saveListToDf_partial, "not_exist", "st_slave", 0) 154 | 155 | # def test_subscribe_actionName_integer(self): 156 | # self.s.enableStreaming(SUBPORT) 157 | # prepareLocalPublisher("st") 158 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 159 | # saveListToDf_partial = partial(saveListToDf, subDf) 160 | # self.assertRaises(TypeError, self.s.subscribe, self.HOST1, self.PORT1, saveListToDf_partial, "st", 1, 0) 161 | 162 | # def test_subscribe_actionName_float(self): 163 | # self.s.enableStreaming(SUBPORT) 164 | # prepareLocalPublisher("st") 165 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 166 | # saveListToDf_partial = partial(saveListToDf, subDf) 167 | # self.assertRaises(TypeError, self.s.subscribe, self.HOST1, self.PORT1, saveListToDf_partial, "st", 25.485, 0) 168 | 169 | # def test_subscribe_offset_string(self): 170 | # self.s.enableStreaming(SUBPORT) 171 | # prepareLocalPublisher("st") 172 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 173 | # saveListToDf_partial = partial(saveListToDf, subDf) 174 | # self.assertRaises(TypeError, self.s.subscribe, self.HOST1, self.PORT1, saveListToDf_partial, "st", "st_slave", "aaa") 175 | 176 | # def test_subscribe_resub_string(self): 177 | # self.s.enableStreaming(SUBPORT) 178 | # prepareLocalPublisher("st") 179 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 180 | # saveListToDf_partial = partial(saveListToDf, subDf) 181 | # self.assertRaises(TypeError, self.s.subscribe, self.HOST1, self.PORT1, saveListToDf_partial, "st", "st_slave", 0, "False") 182 | 183 | # def test_subscribe_filter_datatype_incompatible(self): 184 | # self.s.enableStreaming(SUBPORT) 185 | # prepareLocalPublisherFilter("st", "sym") 186 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 187 | # saveListToDf_partial = partial(saveListToDf, subDf) 188 | # self.assertRaises(RuntimeError, self.s.subscribe, self.HOST1, self.PORT1, saveListToDf_partial, "st", "st_slave", 0, False, np.array([1, 2, 3])) 189 | 190 | # def test_subscribe_offset_0(self): 191 | # self.s.enableStreaming(SUBPORT) 192 | # prepareLocalPublisher("st") 193 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 194 | # saveListToDf_partial = partial(saveListToDf, subDf) 195 | # self.s.subscribe(self.HOST1, self.PORT1, saveListToDf_partial, "st", "st_slave", 0) 196 | # time.sleep(10) 197 | # self.assertEqual(subDf.__len__(), 1000) 198 | # self.s.unsubscribe(self.HOST1, self.PORT1, "st", "st_slave") 199 | 200 | # def test_subscribe_offset_negative_1(self): 201 | # self.s.enableStreaming(SUBPORT) 202 | # prepareLocalPublisher("st") 203 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 204 | # saveListToDf_partial = partial(saveListToDf, subDf) 205 | # self.s.subscribe(self.HOST1, self.PORT1, saveListToDf_partial, "st", "st_slave", -1) 206 | # writePublisher("st") 207 | # time.sleep(10) 208 | # self.assertEqual(subDf.__len__(), 1000) 209 | # self.s.unsubscribe(self.HOST1, self.PORT1, "st", "st_slave") 210 | 211 | # def test_subscribe_offset_99(self): 212 | # self.s.enableStreaming(SUBPORT) 213 | # prepareLocalPublisher("st") 214 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 215 | # saveListToDf_partial = partial(saveListToDf, subDf) 216 | # self.s.subscribe(self.HOST1, self.PORT1, saveListToDf_partial, "st", "st_slave", 99) 217 | # time.sleep(10) 218 | # self.assertEqual(subDf.__len__(), 901) 219 | # self.s.unsubscribe(self.HOST1, self.PORT1, "st", "st_slave") 220 | 221 | # def test_subscribe_filter_string(self): 222 | # self.s.enableStreaming(SUBPORT) 223 | # prepareLocalPublisherFilter("st", "sym") 224 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 225 | # saveListToDf_partial = partial(saveListToDf, subDf) 226 | # self.s.subscribe(self.HOST1, self.PORT1, saveListToDf_partial, "st", "st_slave", 0, False, np.array(["B","C"])) 227 | # time.sleep(10) 228 | # expected = self.s.run("exec count(*) from st where sym in `B`C") 229 | # self.assertEqual(subDf.__len__(), expected) 230 | # self.s.unsubscribe(self.HOST1, self.PORT1, "st", "st_slave") 231 | 232 | # def test_subscribe_filter_date(self): 233 | # self.s.enableStreaming(SUBPORT) 234 | # prepareLocalPublisherFilter("st", "date") 235 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 236 | # saveListToDf_partial = partial(saveListToDf, subDf) 237 | # self.s.subscribe(self.HOST1, self.PORT1, saveListToDf_partial, "st", "st_slave", 0, False, np.array(['2012-06-01','2012-06-02'], dtype="datetime64[D]")) 238 | # time.sleep(10) 239 | # expected = self.s.run("exec count(*) from st where date in [2012.06.01, 2012.06.02]") 240 | # self.assertEqual(subDf.__len__(), expected) 241 | # self.s.unsubscribe(self.HOST1, self.PORT1, "st", "st_slave") 242 | 243 | # def test_subscribe_msgAsTable_true(self): 244 | # self.s.enableStreaming(SUBPORT) 245 | # prepareLocalPublisher("st") 246 | # subDf = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 247 | # saveTableToDf_partial = partial(saveTableToDf, subDf) 248 | # self.s.subscribe(self.HOST1, self.PORT1, saveTableToDf_partial, "st", "st_slave", offset = 0, resub = False, msgAsTable = True) 249 | # time.sleep(10) 250 | # self.assertEqual(subDf.__len__(), 1000) 251 | # self.s.unsubscribe(self.HOST1, self.PORT1, "st", "st_slave") 252 | 253 | # def test_multi_subscribe_one_local_table(self): 254 | # self.s.enableStreaming(SUBPORT) 255 | # prepareLocalPublisher("st") 256 | # subDf1 = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 257 | # subDf2 = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 258 | # saveListToDf_partial1 = partial(saveListToDf, subDf1) 259 | # saveListToDf_partial2 = partial(saveListToDf, subDf2) 260 | # self.s.subscribe(self.HOST1, self.PORT1, saveListToDf_partial1, "st", "st_slave1", 0) 261 | # self.s.subscribe(self.HOST1, self.PORT1, saveListToDf_partial2, "st", "st_slave2", 0) 262 | # time.sleep(10) 263 | # self.assertEqual(subDf1.__len__(), 1000) 264 | # self.assertEqual(subDf2.__len__(), 1000) 265 | # self.s.unsubscribe(self.HOST1, self.PORT1, "st", "st_slave1") 266 | # self.s.unsubscribe(self.HOST1, self.PORT1, "st", "st_slave2") 267 | 268 | # def test_multi_subscribe_one_local_table_unsubscribe_one(self): 269 | # self.s.enableStreaming(SUBPORT) 270 | # prepareLocalPublisher("st") 271 | # subDf1 = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 272 | # subDf2 = pd.DataFrame(columns=('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 273 | # saveListToDf_partial1 = partial(saveListToDf, subDf1) 274 | # saveListToDf_partial2 = partial(saveListToDf, subDf2) 275 | # self.s.subscribe(self.HOST1, self.PORT1, saveListToDf_partial1, "st", "st_slave1", 0) 276 | # self.s.subscribe(self.HOST1, self.PORT1, saveListToDf_partial2, "st", "st_slave2", 0) 277 | # time.sleep(10) 278 | # self.assertEqual(subDf1.__len__(), 1000) 279 | # self.assertEqual(subDf2.__len__(), 1000) 280 | # # unsunscribe one of subscription 281 | # self.s.unsubscribe(self.HOST1, self.PORT1, "st", "st_slave1") 282 | # # continue writing data to publisher 283 | # writePublisher("st") 284 | # time.sleep(10) 285 | # self.assertEqual(subDf1.__len__(), 1000) 286 | # self.assertEqual(subDf2.__len__(), 2000) 287 | # self.s.unsubscribe(self.HOST1, self.PORT1, "st", "st_slave2") 288 | 289 | # def test_one_resubscribe_one_local_table(self): 290 | # pass 291 | # # self.s.enableStreaming(SUBPORT) 292 | # # prepareLocalPublisher("st") 293 | # # subDf = pd.DataFrame(columns = ('sym', 'date', 'price1', 'price2', 'vol1', 'vol2')) 294 | # # saveListToDf_partial = partial(saveListToDf, subDf) 295 | # # self.s.subscribe(self.HOST1, self.PORT1, saveListToDf_partial, "st", "st_slave", 0) 296 | # # time.sleep(10) 297 | # # self.assertEqual(subDf.__len__(), 1000) 298 | # # self.s.unsubscribe(self.HOST1, self.PORT1, "st", "st_slave") 299 | # # time.sleep(5) 300 | # # self.s.subscribe(self.HOST1, self.PORT1, saveListToDf_partial, "st", "st_slave", -1) 301 | # # writePublisher("st") 302 | # # time.sleep(10) 303 | # # self.assertEqual(subDf.__len__(), 2000) 304 | # # self.s.unsubscribe(self.HOST1, self.PORT1, "st", "st_slave") 305 | # # time.sleep(5) 306 | # # self.s.subscribe(self.HOST1, self.PORT1, saveListToDf_partial, "st", "st_slave", -1) 307 | # # writePublisher("st") 308 | # # time.sleep(10) 309 | # # self.assertEqual(subDf.__len__(), 3000) 310 | # # self.s.unsubscribe(self.HOST1, self.PORT1, "st", "st_slave") 311 | 312 | 313 | # if __name__ == '__main__': 314 | # unittest.main() 315 | --------------------------------------------------------------------------------