deepfos 1.1.66__tar.gz → 1.1.68__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {deepfos-1.1.66 → deepfos-1.1.68}/CHANGELOG.md +21 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/PKG-INFO +1 -1
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/_version.py +3 -3
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/deepmodel.py +192 -187
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/dimension.py +2 -2
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/finmodel.py +25 -6
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/journal.py +19 -8
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/options.py +20 -3
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos.egg-info/PKG-INFO +1 -1
- {deepfos-1.1.66 → deepfos-1.1.68}/.gitattributes +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/.gitee/ISSUE_GUIDELINES.md +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/.gitee/ISSUE_TEMPLATE.md +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/.gitignore +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/MANIFEST.in +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/README.md +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/algo/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/algo/graph.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/V1_1/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/V1_1/business_model.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/V1_1/dimension.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/V1_1/models/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/V1_1/models/business_model.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/V1_1/models/dimension.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/V1_2/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/V1_2/dimension.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/V1_2/models/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/V1_2/models/dimension.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/account.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/accounting_engines.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/app.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/approval_process.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/base.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/business_model.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/consolidation.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/consolidation_process.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/datatable.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/deep_pipeline.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/deepconnector.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/deepfos_task.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/deepmodel.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/dimension.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/financial_model.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/journal_model.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/journal_template.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/memory_financial_model.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/account.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/accounting_engines.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/app.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/approval_process.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/base.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/business_model.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/consolidation.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/consolidation_process.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/datatable_mysql.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/deep_pipeline.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/deepconnector.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/deepfos_task.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/deepmodel.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/dimension.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/financial_model.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/journal_model.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/journal_template.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/memory_financial_model.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/platform.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/python.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/reconciliation_engine.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/reconciliation_report.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/role_strategy.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/smartlist.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/space.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/system.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/variable.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/models/workflow.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/platform.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/python.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/reconciliation_engine.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/reconciliation_report.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/role_strategy.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/smartlist.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/space.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/system.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/variable.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/api/workflow.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/boost/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/boost/jstream.c +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/boost/jstream.pyx +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/boost/pandas.c +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/boost/pandas.pyx +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/boost/py_jstream.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/boost/py_pandas.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/cache.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/config.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/cube/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/cube/_base.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/cube/constants.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/cube/cube.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/cube/formula.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/cube/syscube.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/cube/typing.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/cube/utils.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/dimension/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/dimension/_base.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/dimension/dimcreator.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/dimension/dimension.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/dimension/dimexpr.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/dimension/dimmember.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/dimension/eledimension.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/dimension/filters.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/dimension/sysdimension.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/logictable/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/logictable/_cache.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/logictable/_operator.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/logictable/nodemixin.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/logictable/sqlcondition.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/core/logictable/tablemodel.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/cipher.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/clickhouse.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/connector.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/daclickhouse.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/dameng.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/damysql.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/dbkits.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/deepengine.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/deepmodel.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/deepmodel_kingbase.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/edb.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/gauss.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/kingbase.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/mysql.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/oracle.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/postgresql.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/sqlserver.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/db/utils.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/accounting.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/apvlprocess.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/base.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/bizmodel.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/datatable.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/deep_pipeline.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/deepconnector.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/fact_table.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/journal_template.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/pyscript.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/reconciliation.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/rolestrategy.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/smartlist.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/variable.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/element/workflow.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/exceptions/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/exceptions/hook.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lazy.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/__init__.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/_javaobj.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/asynchronous.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/concurrency.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/constant.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/decorator.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/deepchart.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/deepux.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/discovery.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/edb_lexer.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/eureka.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/filterparser.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/httpcli.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/jsonstreamer.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/msg.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/nacos.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/patch.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/redis.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/serutils.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/stopwatch.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/subtask.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/sysutils.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/lib/utils.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/local.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos/translation.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos.egg-info/SOURCES.txt +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos.egg-info/dependency_links.txt +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos.egg-info/not-zip-safe +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos.egg-info/requires.txt +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/deepfos.egg-info/top_level.txt +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/requirements.txt +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/setup.cfg +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/setup.py +0 -0
- {deepfos-1.1.66 → deepfos-1.1.68}/versioneer.py +0 -0
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
## [1.1.68] - 2025-07-29
|
|
2
|
+
|
|
3
|
+
### 更新
|
|
4
|
+
|
|
5
|
+
* DeepModel元素insert_df和update_df接受chunksize=None
|
|
6
|
+
* DeepModel元素insert_df和update_df组织的bulk语句优化
|
|
7
|
+
* 修复DataFrame.replace会引发类型推断导致的bug
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## [1.1.67] - 2025-07-22
|
|
11
|
+
|
|
12
|
+
### 新增
|
|
13
|
+
|
|
14
|
+
* 财务模型元素查询支持指定透视成员列表
|
|
15
|
+
* 凭证模型数据更新接受指定头行操作类型
|
|
16
|
+
|
|
17
|
+
### 更新
|
|
18
|
+
|
|
19
|
+
* 兼容维度load_dataframe依据共享成员列分数据时共享成员列非bool列的处理
|
|
20
|
+
|
|
21
|
+
|
|
1
22
|
## [1.1.66] - 2025-07-10
|
|
2
23
|
|
|
3
24
|
### 更新
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-
|
|
11
|
+
"date": "2025-08-07T03:10:50+0000",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "1.1.
|
|
14
|
+
"full-revisionid": "e1b02f36c62e37f03965c5219dc9426d19af95a8",
|
|
15
|
+
"version": "1.1.68"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import re
|
|
2
|
+
import textwrap
|
|
2
3
|
import threading
|
|
3
4
|
|
|
4
5
|
import numpy as np
|
|
@@ -7,7 +8,7 @@ import json
|
|
|
7
8
|
import uuid
|
|
8
9
|
from contextlib import asynccontextmanager, contextmanager
|
|
9
10
|
from contextvars import ContextVar
|
|
10
|
-
from itertools import count
|
|
11
|
+
from itertools import count, chain
|
|
11
12
|
from typing import (
|
|
12
13
|
List, TYPE_CHECKING, Any, Dict, Union, NamedTuple,
|
|
13
14
|
Iterable, Optional, Literal
|
|
@@ -37,7 +38,7 @@ from deepfos.lib import serutils
|
|
|
37
38
|
from deepfos.lib.asynchronous import future_property, evloop
|
|
38
39
|
from deepfos.lib.decorator import flagmethod, cached_property, lru_cache
|
|
39
40
|
from deepfos.lib.utils import (
|
|
40
|
-
AliasGenerator, to_version_tuple,
|
|
41
|
+
AliasGenerator, to_version_tuple,
|
|
41
42
|
)
|
|
42
43
|
|
|
43
44
|
__all__ = ['AsyncDeepModel', 'DeepModel', 'to_fields', 'QueryWithArgs']
|
|
@@ -121,6 +122,7 @@ dm_type_to_edb_scalar = {
|
|
|
121
122
|
'uuid': 'std::str',
|
|
122
123
|
'json': 'std::json',
|
|
123
124
|
}
|
|
125
|
+
TAB = ' ' * 4
|
|
124
126
|
|
|
125
127
|
|
|
126
128
|
class ObjectElement(ObjectParam):
|
|
@@ -485,19 +487,30 @@ def _iter_single_assign(
|
|
|
485
487
|
field: PtrInfo,
|
|
486
488
|
cast_type: str,
|
|
487
489
|
target_main_field: Dict[str, MainField]
|
|
488
|
-
):
|
|
489
|
-
|
|
490
|
+
) -> str:
|
|
491
|
+
"""
|
|
492
|
+
生成单字段赋值语句
|
|
493
|
+
|
|
494
|
+
Args:
|
|
495
|
+
field: 字段信息
|
|
496
|
+
cast_type: 字段类型
|
|
497
|
+
target_main_field: 目标字段信息
|
|
498
|
+
|
|
499
|
+
Returns:
|
|
500
|
+
赋值语句
|
|
501
|
+
"""
|
|
502
|
+
assign = f"\n{field.name} := "
|
|
490
503
|
# 设置标量值
|
|
491
504
|
if field.name not in target_main_field:
|
|
492
505
|
if field.is_multi:
|
|
493
|
-
return
|
|
506
|
+
return assign + f"json_array_unpack(item['{field.name}'])"
|
|
494
507
|
|
|
495
|
-
|
|
508
|
+
assign += f"<{cast_type}>"
|
|
496
509
|
|
|
497
510
|
if cast_type in NEED_CAST_STR:
|
|
498
|
-
|
|
511
|
+
assign += '<std::str>'
|
|
499
512
|
|
|
500
|
-
return
|
|
513
|
+
return assign + f"item['{field.name}']"
|
|
501
514
|
|
|
502
515
|
# 设置link target值
|
|
503
516
|
link = field.name
|
|
@@ -516,20 +529,20 @@ def _iter_single_assign(
|
|
|
516
529
|
target = cast_type
|
|
517
530
|
|
|
518
531
|
if main_field.is_multi:
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
)"""
|
|
532
|
+
assign += 'distinct (\n' + textwrap.indent(textwrap.dedent(f"""\
|
|
533
|
+
for each_{link} in json_array_unpack(json_get(item, '{link}', 'target'))
|
|
534
|
+
union (
|
|
535
|
+
select detached {target}
|
|
536
|
+
filter .{main_field.business_key} = <{main_field.type}>each_{link}
|
|
537
|
+
)"""), TAB) + '\n)'
|
|
526
538
|
else:
|
|
527
|
-
|
|
539
|
+
assign += textwrap.dedent(f"""\
|
|
540
|
+
assert_single((
|
|
528
541
|
select detached {target}
|
|
529
542
|
filter .{main_field.business_key} = <{main_field.type}>(json_get(item, '{link}'))
|
|
530
|
-
))"""
|
|
543
|
+
))""")
|
|
531
544
|
|
|
532
|
-
return
|
|
545
|
+
return assign
|
|
533
546
|
|
|
534
547
|
|
|
535
548
|
def bulk_insert_by_fields(
|
|
@@ -537,21 +550,16 @@ def bulk_insert_by_fields(
|
|
|
537
550
|
field_type: List[PtrInfo],
|
|
538
551
|
target_main_field: Dict[str, MainField],
|
|
539
552
|
):
|
|
540
|
-
insert_assign_body = ','.join(
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
insert {object_name} {{
|
|
551
|
-
{insert_assign_body}
|
|
552
|
-
}}
|
|
553
|
-
)
|
|
554
|
-
"""
|
|
553
|
+
insert_assign_body = ','.join([
|
|
554
|
+
_iter_single_assign(field, field.type, target_main_field)
|
|
555
|
+
for field in field_type
|
|
556
|
+
])
|
|
557
|
+
return textwrap.dedent(f"""
|
|
558
|
+
with raw_data := <json>to_json(<std::str>${BATCH_INSERT_KW}),
|
|
559
|
+
for item in json_array_unpack(raw_data) union (
|
|
560
|
+
insert {object_name} {{{textwrap.indent(insert_assign_body, TAB * 4)}
|
|
561
|
+
}}
|
|
562
|
+
)""")
|
|
555
563
|
|
|
556
564
|
|
|
557
565
|
def bulk_upsert_by_fields(
|
|
@@ -562,34 +570,27 @@ def bulk_upsert_by_fields(
|
|
|
562
570
|
update_fields: Iterable[str]
|
|
563
571
|
):
|
|
564
572
|
conflict_on_fields = map(lambda n: f'.{n}', exclusive_fields)
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
for field in field_type
|
|
570
|
-
]
|
|
571
|
-
)
|
|
573
|
+
insert_assign_body = ','.join([
|
|
574
|
+
_iter_single_assign(field, field.type, target_main_field)
|
|
575
|
+
for field in field_type
|
|
576
|
+
])
|
|
572
577
|
update_assign_body = ','.join(
|
|
573
578
|
[
|
|
574
579
|
_iter_single_assign(field, field.type, target_main_field)
|
|
575
580
|
for field in field_type if field.name in update_fields
|
|
576
581
|
]
|
|
577
582
|
)
|
|
578
|
-
|
|
579
|
-
return f"""
|
|
583
|
+
return textwrap.dedent(f"""
|
|
580
584
|
with raw_data := <json>to_json(<std::str>${BATCH_INSERT_KW}),
|
|
581
585
|
for item in json_array_unpack(raw_data) union (
|
|
582
|
-
insert {object_name} {{
|
|
583
|
-
{insert_assign_body}
|
|
586
|
+
insert {object_name} {{{textwrap.indent(insert_assign_body, TAB * 4)}
|
|
584
587
|
}}
|
|
585
588
|
unless conflict on ({','.join(conflict_on_fields)})
|
|
586
589
|
else (
|
|
587
|
-
update {object_name} set {{
|
|
588
|
-
{update_assign_body}
|
|
590
|
+
update {object_name} set {{{textwrap.indent(update_assign_body, TAB * 5)}
|
|
589
591
|
}}
|
|
590
592
|
)
|
|
591
|
-
)
|
|
592
|
-
"""
|
|
593
|
+
)""")
|
|
593
594
|
|
|
594
595
|
|
|
595
596
|
def bulk_update_by_fields(
|
|
@@ -599,32 +600,27 @@ def bulk_update_by_fields(
|
|
|
599
600
|
match_fields: Iterable[str],
|
|
600
601
|
update_fields: Iterable[str],
|
|
601
602
|
):
|
|
602
|
-
update_assign_body = ','.join(
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
]
|
|
607
|
-
)
|
|
603
|
+
update_assign_body = ','.join([
|
|
604
|
+
_iter_single_assign(field, field.type, target_main_field)
|
|
605
|
+
for field in field_type if field.name in update_fields
|
|
606
|
+
])
|
|
608
607
|
|
|
609
608
|
field_type_map = {field.name: field.type for field in field_type}
|
|
610
|
-
|
|
611
609
|
match_str = " and ".join(
|
|
612
610
|
[
|
|
613
611
|
f".{name} = <{field_type_map.get(name, 'std::str')}>item['{name}']"
|
|
614
612
|
for name in match_fields
|
|
615
613
|
]
|
|
616
614
|
)
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
)
|
|
627
|
-
"""
|
|
615
|
+
return textwrap.dedent(f"""
|
|
616
|
+
with raw_data := <json>to_json(<std::str>${BATCH_INSERT_KW}),
|
|
617
|
+
for item in json_array_unpack(raw_data) union (
|
|
618
|
+
update {object_name}
|
|
619
|
+
filter {match_str}
|
|
620
|
+
set {{{textwrap.indent(update_assign_body, TAB * 3)}
|
|
621
|
+
}}
|
|
622
|
+
)
|
|
623
|
+
""")
|
|
628
624
|
|
|
629
625
|
|
|
630
626
|
def format_obj(obj: edgedb.Object) -> ObjectTypeFrame:
|
|
@@ -793,8 +789,7 @@ class AsyncDeepModel(ElementBase[DeepModelAPI]):
|
|
|
793
789
|
self,
|
|
794
790
|
direct_access: bool = True,
|
|
795
791
|
pg_dsn: str = None,
|
|
796
|
-
|
|
797
|
-
after_chunk: ChunkAlert = None,
|
|
792
|
+
**kwargs
|
|
798
793
|
):
|
|
799
794
|
self._txn_ = ContextVar('QLTXN')
|
|
800
795
|
self.appmodule = f"app{OPTION.api.header['app']}"
|
|
@@ -805,8 +800,6 @@ class AsyncDeepModel(ElementBase[DeepModelAPI]):
|
|
|
805
800
|
self.alias = AliasGenerator()
|
|
806
801
|
self.pg_dsn = pg_dsn
|
|
807
802
|
self._globals = None
|
|
808
|
-
self.before_chunk = before_chunk
|
|
809
|
-
self.after_chunk = after_chunk
|
|
810
803
|
self._clients = threading.local()
|
|
811
804
|
|
|
812
805
|
@future_property
|
|
@@ -1099,6 +1092,10 @@ class AsyncDeepModel(ElementBase[DeepModelAPI]):
|
|
|
1099
1092
|
会自动用作所有string形式ql的参数
|
|
1100
1093
|
|
|
1101
1094
|
"""
|
|
1095
|
+
qls_with_args = self._collect_execute_qls(qls, kwargs)
|
|
1096
|
+
return await self._maybe_exec_qls(qls_with_args)
|
|
1097
|
+
|
|
1098
|
+
def _collect_execute_qls(self, qls, kwargs):
|
|
1102
1099
|
self._ensure_client()
|
|
1103
1100
|
if isinstance(qls, str):
|
|
1104
1101
|
qls_with_args = [QueryWithArgs(
|
|
@@ -1126,8 +1123,7 @@ class AsyncDeepModel(ElementBase[DeepModelAPI]):
|
|
|
1126
1123
|
))
|
|
1127
1124
|
else:
|
|
1128
1125
|
raise TypeError(f'qls参数中出现类型非法成员:{type(ql)}')
|
|
1129
|
-
|
|
1130
|
-
return await self._maybe_exec_qls(qls_with_args)
|
|
1126
|
+
return qls_with_args
|
|
1131
1127
|
|
|
1132
1128
|
execute.__doc__ = execute.__doc__ + DOC_ARGS_KWARGS
|
|
1133
1129
|
|
|
@@ -1302,6 +1298,10 @@ class AsyncDeepModel(ElementBase[DeepModelAPI]):
|
|
|
1302
1298
|
name = field.name
|
|
1303
1299
|
# 链接至其他对象,记录目标对象信息
|
|
1304
1300
|
if is_multi:
|
|
1301
|
+
if name not in relation:
|
|
1302
|
+
raise ValueError(
|
|
1303
|
+
f'对象[{object_name}]的多选链接:[{name}]未定义在relation中'
|
|
1304
|
+
)
|
|
1305
1305
|
link_props = set(relation[name].columns).intersection(field.props)
|
|
1306
1306
|
else:
|
|
1307
1307
|
link_props = set(
|
|
@@ -1312,30 +1312,17 @@ class AsyncDeepModel(ElementBase[DeepModelAPI]):
|
|
|
1312
1312
|
tgt_main_field[name] = MainField(tgt_bkey, is_multi, link_props)
|
|
1313
1313
|
return field_info, tgt_main_field
|
|
1314
1314
|
|
|
1315
|
-
def
|
|
1316
|
-
self,
|
|
1317
|
-
data: pd.DataFrame,
|
|
1318
|
-
ql: str,
|
|
1319
|
-
chunksize: int,
|
|
1320
|
-
qls: List[QueryWithArgs]
|
|
1321
|
-
):
|
|
1315
|
+
def _ql_payload(self, data: pd.DataFrame, ql: str,):
|
|
1322
1316
|
self._ensure_client()
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
),
|
|
1333
|
-
kwargs={kw_name: part.to_json(
|
|
1334
|
-
orient='records', double_precision=15,
|
|
1335
|
-
force_ascii=False, default_handler=str
|
|
1336
|
-
)},
|
|
1337
|
-
globals=self._globals
|
|
1338
|
-
))
|
|
1317
|
+
kw_name = self.alias.get(BATCH_INSERT_KW)
|
|
1318
|
+
return QueryWithArgs(
|
|
1319
|
+
commands=ql.replace(f'${BATCH_INSERT_KW}', f'${kw_name}'),
|
|
1320
|
+
kwargs={kw_name: data.to_json(
|
|
1321
|
+
orient='records', double_precision=15,
|
|
1322
|
+
force_ascii=False, default_handler=str
|
|
1323
|
+
)},
|
|
1324
|
+
globals=self._globals,
|
|
1325
|
+
)
|
|
1339
1326
|
|
|
1340
1327
|
@staticmethod
|
|
1341
1328
|
def _split_self_link(data, relation, structure, bkey):
|
|
@@ -1391,6 +1378,89 @@ class AsyncDeepModel(ElementBase[DeepModelAPI]):
|
|
|
1391
1378
|
data = data.join(link.to_frame(name), on=bkey)
|
|
1392
1379
|
return data
|
|
1393
1380
|
|
|
1381
|
+
async def _collect_bulk_qls(
|
|
1382
|
+
self,
|
|
1383
|
+
object_name: str,
|
|
1384
|
+
data: pd.DataFrame,
|
|
1385
|
+
relation: Dict[str, pd.DataFrame] = None,
|
|
1386
|
+
chunk_size: int = 500,
|
|
1387
|
+
enable_upsert: bool = False,
|
|
1388
|
+
update_fields: Iterable[str] = None,
|
|
1389
|
+
exclusive_fields: Iterable[str] = None,
|
|
1390
|
+
match_fields: Iterable[str] = None,
|
|
1391
|
+
insert: bool = True
|
|
1392
|
+
) -> List[List[QueryWithArgs]]:
|
|
1393
|
+
if object_name in self.objects:
|
|
1394
|
+
obj = self.objects[object_name]
|
|
1395
|
+
else:
|
|
1396
|
+
raise ObjectNotExist(
|
|
1397
|
+
f'DeepModel对象[{object_name}]在当前应用不存在,无法插入数据'
|
|
1398
|
+
)
|
|
1399
|
+
if obj.external:
|
|
1400
|
+
raise ExternalObjectReadOnly('外部对象只可读')
|
|
1401
|
+
|
|
1402
|
+
structure = ObjectStructure(name=obj.name, structure=obj.fields.values())
|
|
1403
|
+
self._valid_data(data, object_name, relation, structure, check_required=insert)
|
|
1404
|
+
|
|
1405
|
+
relation = relation or {}
|
|
1406
|
+
bkey = await self._get_bkey(obj)
|
|
1407
|
+
if bkey not in data.columns:
|
|
1408
|
+
raise RequiredFieldUnfilled(f'缺少业务主键[{bkey}]')
|
|
1409
|
+
|
|
1410
|
+
# data拼接relation df
|
|
1411
|
+
data = self._merge_relation(data, relation, structure, bkey)
|
|
1412
|
+
# 从data中分离出self-link更新信息
|
|
1413
|
+
data, self_link_dfs = self._split_self_link(data, relation, structure, bkey)
|
|
1414
|
+
field_info, tgt_main_field = await self._collect_bulk_field_info(
|
|
1415
|
+
object_name, structure, data, relation
|
|
1416
|
+
)
|
|
1417
|
+
field_names = set(map(lambda f: f.name, field_info))
|
|
1418
|
+
if insert:
|
|
1419
|
+
if enable_upsert:
|
|
1420
|
+
self._valid_upsert(obj, field_names, bkey, exclusive_fields, update_fields)
|
|
1421
|
+
|
|
1422
|
+
exclusive_fields = set(exclusive_fields or {bkey}) & set(field_names)
|
|
1423
|
+
update_fields = set(update_fields or (field_names - {bkey})) & set(field_names)
|
|
1424
|
+
if enable_upsert and update_fields:
|
|
1425
|
+
bulk_ql = bulk_upsert_by_fields(
|
|
1426
|
+
object_name, field_info, tgt_main_field,
|
|
1427
|
+
exclusive_fields, update_fields
|
|
1428
|
+
)
|
|
1429
|
+
else:
|
|
1430
|
+
bulk_ql = bulk_insert_by_fields(object_name, field_info, tgt_main_field)
|
|
1431
|
+
else:
|
|
1432
|
+
if missing := (set(match_fields or [bkey]) - set(field_names)):
|
|
1433
|
+
raise ValueError(f"match fields: {missing} 不在提供的数据中")
|
|
1434
|
+
|
|
1435
|
+
match_fields = set(match_fields or [bkey]) & set(field_names)
|
|
1436
|
+
if to_upd := (field_names - match_fields):
|
|
1437
|
+
bulk_ql = bulk_update_by_fields(
|
|
1438
|
+
object_name, field_info, tgt_main_field,
|
|
1439
|
+
match_fields, to_upd
|
|
1440
|
+
)
|
|
1441
|
+
else:
|
|
1442
|
+
bulk_ql = None
|
|
1443
|
+
qls = []
|
|
1444
|
+
self._ensure_client()
|
|
1445
|
+
if chunk_size is None:
|
|
1446
|
+
chunk_size = len(data)
|
|
1447
|
+
for i in range(0, len(data), chunk_size):
|
|
1448
|
+
part = structure.fit(data.iloc[i: i + chunk_size])
|
|
1449
|
+
ql_chunk = []
|
|
1450
|
+
# Ignore bulk_ql when only update multi links
|
|
1451
|
+
if bulk_ql is not None:
|
|
1452
|
+
ql_chunk = [self._ql_payload(part, bulk_ql)]
|
|
1453
|
+
for update_field, (update_df, main_field) in self_link_dfs.items():
|
|
1454
|
+
field = structure.fields[update_field]
|
|
1455
|
+
update_ql = bulk_update_by_fields(
|
|
1456
|
+
object_name, [field], {update_field: main_field},
|
|
1457
|
+
[bkey], [update_field]
|
|
1458
|
+
)
|
|
1459
|
+
update_part = update_df.iloc[i: i + chunk_size]
|
|
1460
|
+
ql_chunk.append(self._ql_payload(update_part, update_ql))
|
|
1461
|
+
qls.append(ql_chunk)
|
|
1462
|
+
return qls
|
|
1463
|
+
|
|
1394
1464
|
@txn_support
|
|
1395
1465
|
async def insert_df(
|
|
1396
1466
|
self,
|
|
@@ -1401,6 +1471,7 @@ class AsyncDeepModel(ElementBase[DeepModelAPI]):
|
|
|
1401
1471
|
enable_upsert: bool = False,
|
|
1402
1472
|
update_fields: Iterable[str] = None,
|
|
1403
1473
|
exclusive_fields: Iterable[str] = None,
|
|
1474
|
+
commit_per_chunk: bool = False,
|
|
1404
1475
|
) -> None:
|
|
1405
1476
|
"""以事务执行基于DataFrame字段信息的批量插入数据
|
|
1406
1477
|
|
|
@@ -1421,6 +1492,9 @@ class AsyncDeepModel(ElementBase[DeepModelAPI]):
|
|
|
1421
1492
|
exclusive_fields: upsert句式下update的exclusive fields列表,
|
|
1422
1493
|
涉及的fields需出现在data或relation中,
|
|
1423
1494
|
默认为业务主键
|
|
1495
|
+
commit_per_chunk: 每次插入后是否提交事务,
|
|
1496
|
+
默认为False,即所有数据插入后再提交事务
|
|
1497
|
+
该参数仅在非start transaction上下文中生效
|
|
1424
1498
|
|
|
1425
1499
|
Notes:
|
|
1426
1500
|
|
|
@@ -1493,54 +1567,16 @@ class AsyncDeepModel(ElementBase[DeepModelAPI]):
|
|
|
1493
1567
|
logger.info("data为空,无DML执行")
|
|
1494
1568
|
return
|
|
1495
1569
|
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
f'DeepModel对象[{object_name}]在当前应用不存在,无法插入数据'
|
|
1501
|
-
)
|
|
1502
|
-
if obj.external:
|
|
1503
|
-
raise ExternalObjectReadOnly('外部对象只可读')
|
|
1504
|
-
|
|
1505
|
-
structure = ObjectStructure(name=obj.name, structure=obj.fields.values())
|
|
1506
|
-
|
|
1507
|
-
self._valid_data(data, object_name, relation, structure)
|
|
1508
|
-
|
|
1509
|
-
relation = relation or {}
|
|
1510
|
-
bkey = await self._get_bkey(obj)
|
|
1511
|
-
# data拼接relation df
|
|
1512
|
-
data = self._merge_relation(data, relation, structure, bkey)
|
|
1513
|
-
# 从data中分离出self-link更新信息
|
|
1514
|
-
data, self_link_dfs = self._split_self_link(data, relation, structure, bkey)
|
|
1515
|
-
field_info, tgt_main_field = await self._collect_bulk_field_info(
|
|
1516
|
-
object_name, structure, data, relation
|
|
1570
|
+
qls = await self._collect_bulk_qls(
|
|
1571
|
+
object_name, data, relation, chunksize,
|
|
1572
|
+
enable_upsert, update_fields, exclusive_fields,
|
|
1573
|
+
insert=True
|
|
1517
1574
|
)
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
exclusive_fields = set(exclusive_fields or {bkey}) & set(field_names)
|
|
1523
|
-
update_fields = set(update_fields or (field_names - {bkey})) & set(field_names)
|
|
1524
|
-
if enable_upsert and update_fields:
|
|
1525
|
-
insert_ql = bulk_upsert_by_fields(
|
|
1526
|
-
object_name, field_info, tgt_main_field,
|
|
1527
|
-
exclusive_fields, update_fields
|
|
1528
|
-
)
|
|
1575
|
+
if commit_per_chunk:
|
|
1576
|
+
for ql_chunk in qls:
|
|
1577
|
+
await self.execute(ql_chunk)
|
|
1529
1578
|
else:
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
qls = []
|
|
1533
|
-
self._collect_qls(structure.fit(data), insert_ql, chunksize, qls)
|
|
1534
|
-
if self_link_dfs:
|
|
1535
|
-
for update_field, (update_df, main_field) in self_link_dfs.items():
|
|
1536
|
-
field = structure.fields[update_field]
|
|
1537
|
-
update_ql = bulk_update_by_fields(
|
|
1538
|
-
object_name, [field], {update_field: main_field},
|
|
1539
|
-
[bkey], [update_field]
|
|
1540
|
-
)
|
|
1541
|
-
self._collect_qls(update_df, update_ql, chunksize, qls)
|
|
1542
|
-
|
|
1543
|
-
await self.execute(qls)
|
|
1579
|
+
await self.execute(list(chain(*qls)))
|
|
1544
1580
|
|
|
1545
1581
|
async def get_object(
|
|
1546
1582
|
self,
|
|
@@ -1693,6 +1729,7 @@ class AsyncDeepModel(ElementBase[DeepModelAPI]):
|
|
|
1693
1729
|
relation: Dict[str, pd.DataFrame] = None,
|
|
1694
1730
|
chunksize: int = 500,
|
|
1695
1731
|
match_fields: Iterable[str] = None,
|
|
1732
|
+
commit_per_chunk: bool = False,
|
|
1696
1733
|
) -> None:
|
|
1697
1734
|
"""以事务执行基于DataFrame字段信息的批量更新数据
|
|
1698
1735
|
|
|
@@ -1709,57 +1746,23 @@ class AsyncDeepModel(ElementBase[DeepModelAPI]):
|
|
|
1709
1746
|
若有link property,则以property名为列名,提供在除source和target的列中
|
|
1710
1747
|
chunksize: 单次最大行数
|
|
1711
1748
|
match_fields: update的匹配列表,涉及的fields需出现在data或relation中,默认为业务主键
|
|
1749
|
+
commit_per_chunk: 每次插入后是否提交事务,
|
|
1750
|
+
默认为False,即所有数据插入后再提交事务
|
|
1751
|
+
该参数仅在非start transaction上下文中生效
|
|
1712
1752
|
"""
|
|
1713
1753
|
if data.empty:
|
|
1714
1754
|
logger.info("data为空,无DML执行")
|
|
1715
1755
|
return
|
|
1716
1756
|
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
raise ObjectNotExist(
|
|
1721
|
-
f'DeepModel对象[{object_name}]在当前应用不存在,无法更新数据'
|
|
1722
|
-
)
|
|
1723
|
-
if obj.external:
|
|
1724
|
-
raise ExternalObjectReadOnly('外部对象只可读')
|
|
1725
|
-
|
|
1726
|
-
structure = ObjectStructure(name=obj.name, structure=obj.fields.values())
|
|
1727
|
-
self._valid_data(data, object_name, relation, structure, check_required=False)
|
|
1728
|
-
relation = relation or {}
|
|
1729
|
-
bkey = await self._get_bkey(obj)
|
|
1730
|
-
if bkey not in data.columns:
|
|
1731
|
-
raise RequiredFieldUnfilled(f'缺少业务主键[{bkey}]')
|
|
1732
|
-
# data拼接relation df
|
|
1733
|
-
data = self._merge_relation(data, relation, structure, bkey)
|
|
1734
|
-
# 从data中分离出self-link更新信息
|
|
1735
|
-
data, self_link_dfs = self._split_self_link(data, relation, structure, bkey)
|
|
1736
|
-
field_info, tgt_main_field = await self._collect_bulk_field_info(
|
|
1737
|
-
object_name, structure, data, relation
|
|
1738
|
-
)
|
|
1739
|
-
field_names = set(map(lambda f: f.name, field_info))
|
|
1740
|
-
|
|
1741
|
-
if missing := (set(match_fields or [bkey]) - set(field_names)):
|
|
1742
|
-
raise ValueError(f"match fields: {missing} 不在提供的数据中")
|
|
1743
|
-
|
|
1744
|
-
match_fields = set(match_fields or [bkey]) & set(field_names)
|
|
1745
|
-
update_ql = bulk_update_by_fields(
|
|
1746
|
-
object_name, field_info, tgt_main_field,
|
|
1747
|
-
match_fields, field_names - match_fields
|
|
1757
|
+
qls = await self._collect_bulk_qls(
|
|
1758
|
+
object_name, data, relation, chunksize,
|
|
1759
|
+
match_fields=match_fields, insert=False
|
|
1748
1760
|
)
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
update_ql = bulk_update_by_fields(
|
|
1755
|
-
object_name, [field],
|
|
1756
|
-
{update_field: main_field},
|
|
1757
|
-
[bkey],
|
|
1758
|
-
[update_field]
|
|
1759
|
-
)
|
|
1760
|
-
self._collect_qls(update_df, update_ql, chunksize, qls)
|
|
1761
|
-
|
|
1762
|
-
await self.execute(qls)
|
|
1761
|
+
if commit_per_chunk:
|
|
1762
|
+
for ql_chunk in qls:
|
|
1763
|
+
await self.execute(ql_chunk)
|
|
1764
|
+
else:
|
|
1765
|
+
await self.execute(list(chain(*qls)))
|
|
1763
1766
|
|
|
1764
1767
|
@asynccontextmanager
|
|
1765
1768
|
async def start_transaction(self, flatten: bool = False):
|
|
@@ -1799,7 +1802,7 @@ class AsyncDeepModel(ElementBase[DeepModelAPI]):
|
|
|
1799
1802
|
|
|
1800
1803
|
Important:
|
|
1801
1804
|
|
|
1802
|
-
仅 :func:`insert_df` :func:`execute` 方法支持在事务中执行
|
|
1805
|
+
仅 :func:`insert_df` :func:`update_df` :func:`execute` 方法支持在事务中执行
|
|
1803
1806
|
|
|
1804
1807
|
"""
|
|
1805
1808
|
try:
|
|
@@ -1913,6 +1916,7 @@ class DeepModel(AsyncDeepModel, metaclass=SyncMeta):
|
|
|
1913
1916
|
enable_upsert: bool = False,
|
|
1914
1917
|
update_fields: Iterable[str] = None,
|
|
1915
1918
|
exclusive_fields: Iterable[str] = None,
|
|
1919
|
+
commit_per_chunk: bool = False,
|
|
1916
1920
|
) -> None:
|
|
1917
1921
|
...
|
|
1918
1922
|
|
|
@@ -1934,6 +1938,7 @@ class DeepModel(AsyncDeepModel, metaclass=SyncMeta):
|
|
|
1934
1938
|
relation: Dict[str, pd.DataFrame] = None,
|
|
1935
1939
|
chunksize: int = 500,
|
|
1936
1940
|
match_fields: Iterable[str] = None,
|
|
1941
|
+
commit_per_chunk: bool = False,
|
|
1937
1942
|
) -> None:
|
|
1938
1943
|
...
|
|
1939
1944
|
|
|
@@ -956,7 +956,7 @@ class AsyncDimension(ElementBase[DimensionAPI]):
|
|
|
956
956
|
|
|
957
957
|
# -----------------------------------------------------------------------------
|
|
958
958
|
# has shared member
|
|
959
|
-
df_shared: pd.DataFrame = df[df[shared_mbr_col]]
|
|
959
|
+
df_shared: pd.DataFrame = df[df[shared_mbr_col].astype(bool)]
|
|
960
960
|
if self._strict:
|
|
961
961
|
existed_mbrs = self._member_memo
|
|
962
962
|
|
|
@@ -989,7 +989,7 @@ class AsyncDimension(ElementBase[DimensionAPI]):
|
|
|
989
989
|
# 在df中丢弃系统中已存在的共享节点
|
|
990
990
|
df_shared_remain = df_shared[~df_shared.index.isin(dup_sharedmbrs)]
|
|
991
991
|
|
|
992
|
-
df_not_shared = df[~df[shared_mbr_col]]
|
|
992
|
+
df_not_shared = df[~df[shared_mbr_col].astype(bool)]
|
|
993
993
|
existed_idx = df_not_shared[DFLT_NAME_COLUMN].isin(existed_mbrs)
|
|
994
994
|
return df_not_shared.loc[existed_idx], \
|
|
995
995
|
pd.concat([df_not_shared.loc[~existed_idx], df_shared_remain])
|