搜索​​​​

清除过滤器
文章
Jingwei Wang · 九月 1, 2023

JWT - JSON Web Token Authentication

JWT Authentication 原理及验证流程 原理 JWT 是一种结构紧凑、URL 安全的身份验证、授权或信息交换方式。在身份验证的情况下,服务器会向已通过身份验证的客户端提供一个 JWT,这样客户端在 JWT 过期之前就无需再提供密码来访问服务器上受保护的资源。 验证流程: 客户端发送Login到服务端 服务端返回 JWT给客户端 客户端校验JWT签名 客户端发送带有JWT签名的request到服务端 服务端检查JWT签名的有效期,在有效期内,则返回response给客户端,不在有效期内,返回error JWT 配置步骤 创建REST服务 配置Web Application 安全配置 客户端发送Login,从服务器获取JWT 配置成功,发送带有JWT的request 1. 创建REST服务 在InterSystems IRIS中,可以使用/api/mgmnt自动创建REST的 .disp .impl 和 .spec 类,本篇文章不介绍具体创建REST服务的流程,具体内容请参考社区文章创建REST 服务。 2. 配置Web Application IRIS管理门户:系统管理 -> 安全 -> 应用程序 -> Web 应用程序 选中 ‘Use JWT Authentication’ 复选框,并设置‘JWT Access Token Timeout’ 和‘JWT Refresh Token Timeout’, 其中,JWT Access Token Timeout 为JWT 令牌超时秒数,如果你打算长时间测试 API,建议将此值设为 1 小时(3600 秒),JWT Refresh Token Timeout(令牌刷新超时的秒数)设为 900 秒。 3. 安全配置 IRIS管理门户:系统管理 -> 安全 -> 系统安全 -> 身份验证/Web 会话选项 ‘JWT Issuer field’用于签名和验证 JWT 的签名算法。‘JWT Issuer field’将出现在 JWT 的声明部分,其目的是告知是谁给了你这个令牌。您可以将其设置为 "InterSystems"。 4. 客户端发送Login,从服务器获取JWT 使用基本 HTTP 身份验证或在请求正文中使用有效凭证发送POST请求到http://<api>/login端点,服务器端会返回一个访问令牌和一个刷新令牌,可在后续请求中使用。 body样例: { "user": "superuser", "password": "iris" } 5.配置成功,发送带有JWT的request 到此为止,您已经成功配置JWT,可以发送带有JWT的请求了。只需要在Header中添加Authorization,如下所示: -H "Authorization: Bearer {access_token}"
公告
Claire Zheng · 七月 21

更新漏洞处理策略

在 InterSystems,我们相信负责任地披露最近发现的安全漏洞。我们向客户提供及时的信息,同时防止信息落入可能滥用信息的人之手。我们还了解每个客户在解决安全问题方面都有不同的要求。 从 2023 年开始,我们对安全漏洞修复方法进行了两项重大更改,我想强调一下: 安全漏洞补丁将包含在每个版本中 改进客户通知 每个版本中的安全漏洞补丁现在,每个版本都可能包含针对安全漏洞的补丁,而不是等待在安全版本中提供补丁。我们改进的发布节奏将及时向现场提供补丁。 改进客户通知中低影响项目(通常包括侦察攻击或跨站点脚本攻击等漏洞)将包含在每个版本中,并在产品发布说明中进行描述。 更高严重性项目的修复也将包含在每个版本中,因为它们已准备就绪,但有关修复的信息将被禁运,直到补丁包含在所有受支持的版本中。 当所有受支持的版本中都已修复这些问题时,将针对高严重性和严重性问题发布安全警报。 为什么 InterSystems 做出这些改变?我们相信这些改进将: 更快地为我们的客户获取安全补丁 帮助专注于最严重的修复 在某些情况下,可以将安全修复程序作为补丁而不是完整的工具包提供 通过按安全影响对漏洞进行分组,提高安全漏洞管理方式的透明度 允许系统管理员根据他们的需要和要求应用更多修复 当然,随着维护版本和持续交付版本的概念,这个过程变得更加复杂。为了帮助客户了解他们何时以及如何获得安全修复,我们发布了包含更详细信息的漏洞处理政策。 我期待到 2023 年,我们可以继续改善您与我们的安全和漏洞管理计划相关的体验。
文章
Hao Ma · 一月 10, 2021

完整性检查_ 加速还是减速

虽然 Caché 和 InterSystems IRIS 数据库的[完整性](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GCDI_integrity)完全不会受到系统故障的影响,但物理存储设备故障确实会损坏其存储的数据。 因此,许多站点选择运行定期数据库完整性检查,尤其要与备份配合,以验证在发生灾难时是否可以依赖给定的备份。 系统管理员在应对涉及存储损坏的灾难时,也可能强烈需要完整性检查。 完整性检查必须读取所检查的 global 的每个块(如果尚未在缓冲区中),并且按照 global 结构指示的顺序读取。 这会花费大量时间,**但完整性检查能够以存储子系统可以承受的最快速度进行读取**。 在某些情况下,需要以这种方式运行以尽快获得结果。 在其他情况下,完整性检查需要更加保守,以避免消耗过多的存储子系统带宽。 ## 行动计划 以下概述适合大多数情况。 本文其余部分中的详细讨论提供了采取其中任一行动或得出其他行动方案所需的信息。 1. 如果使用 Linux 并且完整性检查很慢,请参阅下面有关启用异步 I/O 的信息。 2. 如果完整性检查必须尽快完成,则在隔离的环境中运行;或者如果迫切需要结果,则使用多进程完整性检查来并行检查多个 global 或数据库。 进程数乘以每个进程将执行的并发异步读取数(默认为 8,如果使用 Linux 并且禁用异步 I/O 则为 1)是实时并发读取数的限制。 假定平均数是限制数量的一半,然后与存储子系统的能力进行比较。 例如,存储由 20 个驱动器条带化,每个进程的默认并发读取数为 8,则可能需要 5 个或更多进程才能利用存储子系统的全部能力 (5*8/2=20)。 3. 在平衡完整性检查速度与对生产的影响时,首先调整多进程完整性检查的进程数,然后如果需要的话,查看可调参数 SetAsyncReadBuffers。 对于长期解决方案(以及为消除误报),请参见下面的隔离完整性检查。 4. 如果已经被限制为一个进程(例如有一个极大的 global 或存在其他外部约束),并且完整性检查的速度需要上下调整,则查看下面的可调参数 SetAsyncReadBuffers。 ## 多进程完整性检查 让完整性检查更快完成(以更高的速度使用系统资源)的一般解决方案是将工作分给多个并行进程。 一些完整性检查用户界面和 API 会这样做,而其他一些则使用单个进程。 对进程的分配按 global 进行,因此对单个 global 的检查始终由一个进程执行(Caché 2018.1 之前的版本按数据库而不是按 global 分配工作)。 多进程完整性检查的主要 API 是 **CheckLIst^Integrity**(有关详细信息,请参阅[文档](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GCDI_integrity#GCDI_integrity_verify_utility))。 它将结果收集在一个临时的 global 中,通过 Display^Integrity 来显示。 以下是使用 5 个进程检查 3 个数据库的示例。 这里如省略数据库列表参数,将检查所有数据库。 set dblist=$listbuild(“/data/db1/”,”/data/db2/”,”/data/db3/”) set sc=$$CheckList^Integrity(,dblist,,,5) do Display^Integrity() kill ^IRIS.TempIntegrityOutput(+$job) /* Note: evaluating ‘sc’ above isn’t needed just to display the results, but...    $system.Status.IsOK(sc) - ran successfully and found no errors    $system.Status.GetErrorCodes(sc)=$$$ERRORCODE($$$IntegrityCheckErrors) // 267                            - ran successfully, but found errors.    Else - a problem may have prevented some portion from running, ‘sc’ may have            multiple error codes, one of which may be $$$IntegrityCheckErrors. */ 像这样使用 CheckLIst^Integrity 是实现我们感兴趣的控制水平的最直接方法。 管理门户接口和完整性检查任务(内置但未安排)使用多个进程,但可能无法为我们的用途提供足够的控制。* 其他完整性检查接口,尤其是终端用户接口 ^INTEGRIT 或 ^Integrity 以及 Silent^Integrity,在单个进程中执行完整性检查。 因此,这些接口不能以最快的速度完成检查,并且它们使用的资源也较少。 但一个优点是,它们的结果是可见的,可以记录到文件或输出到终端,因为每个 global 都会被检查,而且顺序明确。 ## 异步 I/O 完整性检查进程会排查 global 的每个指针块,一次检查一个,根据它指向的数据块的内容来进行验证。 数据块以异步 I/O 的方式读取,以确保每时每刻都有一定数量的读取请求供存储子系统处理,并且每次读取完成后都进行验证。 在 Linux 上,异步 I/O 只有与直接 I/O 结合时才有效,而 InterSystems IRIS 2020.3 之前的版本默认不启用直接 I/O。 这解释了大量 Linux 上完整性检查时间过长的情况。 幸运的是,可以在 Cache 2018.1、IRIS 2019.1 及以后的版本上启用直接 I/O,方法是在 .cpf 文件的 [config] 部分中设置 **wduseasyncio=1**,然后重新启动。 通常建议设置此参数,以在繁忙系统上实现 I/O 可伸缩性,并且自 Caché 2015.2 起,在非 Linux 平台上默认设置此参数。 在启用之前,确保已经为数据库缓存(global 缓冲区)配置了足够的内存,因为启用直接 I/O 后,数据库将不再被 Linux(冗余)缓存。 未启用时,完整性检查执行的读取会同步完成,不能有效利用存储。 在所有平台上,完整性检查进程一次执行的读取数默认设置为 8。 如果必须更改单个完整性检查进程从磁盘读取的速率,可以调整此参数 – 向上调会使单个进程更快完成,向下调则使用更少的存储带宽。 请记住: * 此参数应用于每个完整性检查进程。 当使用多个进程时,进程数会使实时读取数增加。更改并行完整性检查进程数会产生较大影响,因此这通常是先做的事情。 每个进程还受到计算时间的限制(除其他限制外),因此增加此参数的值所获得的收益也有限。 * 这只在存储子系统处理并发读取的能力范围内有效。 如果数据库存储在单个本地驱动器上,再高的数值也没有用处,而在几十个驱动器上条带化的存储阵列可以并发处理几十个读取。 要从 %SYS 命名空间调整此参数,则 **do SetAsyncReadBuffers^Integrity(**value**)**。 要查看当前值,则 **write $$GetAsyncReadBuffers^Integrity()**。 更改在检查下一个 global 时生效。 目前,该设置在系统重启后不会保持,虽然可以将其添加到 SYSTEM^%ZSTART 中。 有一个相似的参数用于在磁盘上的块是连续(或接近连续)分布时控制每次读取的最大大小。 此参数很少需要用到,尽管具有高存储延迟的系统或具有较大块大小的数据库可能会从微调中受益。 该值的单位为 64KB,因此值 1 表示 64KB,4 表示 256KB 等等。0(默认值)表示让系统选择,当前选择 1 (64KB)。 此参数的 ^Integrity 函数(类似于上面提及的函数)为 **SetAsyncReadBufferSize** 和 **GetAsyncReadBufferSize**。 ## 隔离完整性检查 许多站点直接在生产系统上运行定期完整性检查。 这当然是最简单的配置,但并不理想。 除了完整性检查对存储带宽的影响,并发数据库更新活动有时还可能导致误报错误(尽管检查算法内置了缓解措施)。 因此,在生产系统上运行的完整性检查所报告的错误,需要由管理员进行评估和/或重新检查。 很多时候,存在更好的选择。 可以将存储快照或备份映像挂载到另一台主机上,在那里由隔离的 Caché 或 IRIS 实例运行完整性检查。 这样不仅可以防止任何误报,而且如果存储也与生产隔离,运行完整性检查可以充分利用存储带宽并更快完成。 这种方法非常适合使用完整性检查来验证备份的模型;经过验证的备份可以有效验证截至生成备份前的生产情况。 还可以通过云和虚拟化平台更容易地从快照建立可用的隔离环境。   * * * * 管理门户接口、完整性检查任务和 SYS.Database 的 IntegrityCheck 方法会选择相当多的进程(等于 CPU 内核数),在很多情况下缺少所需的控制。 管理门户和任务还会对任何报告错误的 global 执行完整的重新检查,以识别可能因并发更新而出现的误报。 除了完整性检查算法内置的误报缓解措施,也可能进行这种重新检查;在某些情况下,由于会花费额外的时间(重新检查在单个进程中运行,并检查整个 global),可能并不需要重新检查。 此行为将来可能会更改。
文章
姚 鑫 · 七月 4, 2021

第二十七章 定制SAX解析器的执行自定义实体解析

# 第二十七章 定制SAX解析器的执行自定义实体解析 # 执行自定义实体解析 XML文档可能包含对外部DTD或其他实体的引用。默认情况下,InterSystems IRIS尝试查找这些实体的源文档并解析它们。要控制InterSystems IRIS解析外部实体的方式,请使用以下步骤: 1. 定义实体解析程序类。 此类必须在扩展`%XML.SAX.EntityResolver`,并且必须实现 `resolveEntity()`方法,该方法具有以下签名: ```java method resolveEntity(publicID As %Library.String, systemID As %Library.String) as %Library.Integer ``` 每当XML处理器找到对外部实体(如DTD)的引用时,就会调用该方法;这里的public ID和systemID是该实体的Public和系统标识符字符串。 该方法应获取实体或文档,将其作为流返回,然后在将流包装在`%XML.SAX.StreamAdapter`的实例中。此类提供了用于确定流特征的必要方法。 如果无法解析该实体,则该方法应返回`$$$NULLOREF` ,以向SAX解析器指示该实体无法解析)。 尽管方法签名指示返回值为`%Library.Integer`,但该方法应返回`%XML.SAX.StreamAdapter`的实例或该类的子类。 此外,引用外部实体的标识符始终传递给文档中指定的`resolveEntity()`方法。具体地说,如果这样的标识符使用相对URL,则该标识符将作为相对URL传递,这意味着引用文档的实际位置不会传递给`resolveEntity()`方法,并且无法解析该实体。在这种情况下,请使用默认实体解析器,而不是自定义实体解析器。 2. 读取XML文档时,请执行以下操作: a. 创建实体解析程序类的实例。 b. 读取XML文档时使用该实例,如本章前面的“指定解析器选项”中所述。 ## 示例 例如,以下XML文档: ```xml Some < xhtml-content > with custom entities &entity1; and &entity2;. Here is another paragraph with &entity1; again. ``` 本文档使用以下DTD: ```xml ``` 要阅读本文档,需要如下所示的自定义实体解析器: ```java Class CustomResolver.Resolver Extends %XML.SAX.EntityResolver { Method resolveEntity(publicID As %Library.String, systemID As %Library.String) As %Library.Integer { Try { Set res=##class(%Stream.TmpBinary).%New() //check if we are here to resolve a custom entity If systemID="http://www.intersystems.com/xml/entities/entity1" { Do res.Write("Value for entity1") Set return=##class(%XML.SAX.StreamAdapter).%New(res) } Elseif systemID="http://www.intersystems.com/xml/entities/entity2" { Do res.Write("Value for entity2") Set return=##class(%XML.SAX.StreamAdapter).%New(res) } Else //otherwise call the default resolver { Set res=##class(%XML.SAX.EntityResolver).%New() Set return=res.resolveEntity(publicID,systemID) } } Catch { Set return=$$$NULLOREF } Quit return } } ``` 下面的类包含一个demo方法,该方法解析前面显示的文件并使用此自定义解析器: ```java Include (%occInclude, %occSAX) Class CustomResolver.ParseFileDemo { ClassMethod ParseFile() { Set res= ##class(CustomResolver.Resolver).%New() Set file="c:/temp/html.xml" Set parsemask=$$$SAXALLEVENTS+$$$SAXERROR Set status=##class(%XML.TextReader).ParseFile(file,.textreader,res,,parsemask,,0) If $$$ISERR(status) {Do $system.OBJ.DisplayError(status) Quit } Write !,"Parsing the file ",file,! Write "Custom entities in this file:" While textreader.Read() { If textreader.NodeType="entity"{ Write !, "Node:", textreader.seq Write !," name: ", textreader.Name Write !," value: ", textreader.Value } } } } ``` 下面显示了此方法在终端会话中的输出: ```java GXML>d ##class(CustomResolver.ParseFileDemo).ParseFile() Parsing the file c:/temp/html.xml Custom entities in this file: Node:13 name: entity1 value: Value for entity1 Node:15 name: entity2 value: Value for entity2 Node:21 name: entity1 value: Value for entity1 ``` ## 示例2 例如,读取包含以下内容的XML文档: ```xml ``` 在本例中,将在`publicId`设置为 `-//OASIS//DTD DocBook XML V4.1.2//EN`并将`systemId`设置为`c:\test\doctypes\docbook\docbookx.dtd.`的情况下调用`resolveEntity`方法。 `resolveEntity`方法确定外部实体的正确源,将其作为流返回,并将其包装在`%XML.StreamAdaptor`的实例中。XML解析器从这个专用流中读取实体定义。 例如,请参考InterSystems IRIS库中包含的`%XML.Catalog`和`%XML.CatalogResolverclass。%XML.Catalog`类定义一个简单的数据库,该数据库将公共和系统标识符与URL相关联。`%XML.CatalogResolver`类是一个实体解析器类,它使用此数据库查找给定标识符的URL。`%XML.Catalogclass`可以从SGML样式的编录文件加载其数据库;该文件将标识符映射到标准格式的URL。
文章
姚 鑫 · 九月 12, 2022

第三十章 管理许可(三)

# 第三十章 管理许可(三) # 确定许可证容量和使用情况 如何知道已使用了多少许可证以及由谁使用?类中的 `%SYSTEM.License` 提供了到 `IRIS` 许可证应用程序编程接口 (`API`) 的接口,并提供了许多方法和相关查询,可以使用这些方法和相关查询来查询许可证容量和当前使用情况。 可以使用 `%Library.%ResultSet` 类的 `RunQuery` 方法运行多个许可查询。例如: ``` USER>do ##class(%ResultSet).RunQuery("%SYSTEM.License","Summary") LicenseUnitUse:Local:Distributed: 当前使用的软件许可单元 :2:2: 使用的最大软件许可单元数 :3:2: 授权的软件许可单元 :25:25: 当前连接 :2:2: 最大连接数 :6:6: ``` 可以从管理门户的许可证使用页面(系统操作 > 许可证使用)查看这些查询的输出,详细信息如下表所示: 许可证使用页面上的链接| `License Query` ---|--- `Summary` |`Summary()` — 返回许可证使用摘要,如 `$System.License.ShowSummary` 所示。 `Usage by Process`| `ProcessList()` — 返回操作系统进程标识符 (`PID`) 使用的许可证,如 `$System.License.DumpLocalPID` 所示。 `Usage by User`| `UserList()` —按用户 ID 返回许可证使用。 `Distributed License Usage`| `AllKeyConnectionList()` — 返回按用户排序的当前分布式许可证使用情况。 (当没有连接许可服务器时禁用此功能。) 还可以使用 `%SYSTEM.License` 中的以下类方法来显示信息,或将许可证数据库转储到文件中: `$System.License.CKEY` 显示密钥。该子例程由 `^CKEY` 程序调用,该程序为保持兼容性而保留: ```java USER>Do $System.License.CKEY() InterSystems IRIS Key display: Based on the active key file 'c:\intersystems\irishealth\mgr\iris.key' LicenseCapacity = InterSystems IRIS 2021.2 Enterprise - Concurrent Users for x86-64 (Microsoft Windows):25, Natural Language Processing (NLP), En CustomerName = ISC DC Moderators - Xin Yao OrderNumber = 202224285 ExpirationDate = 7/15/2023 AuthorizationKey = 4125500002500002500000XXXXXXXXXXXXXXXXX01 MachineID = 当前可用 = 23 最小可用 = 22 最大可用 = 25 ``` `$System.License.ShowCounts` 总结了在本地系统共享内存中跟踪的许可证使用情况: ```java USER> Do $System.License.ShowCounts() 本地软件许可使用视图. 25 授权的总数量 LU 23 当前可用 LU 22 最小可用 LU 2 当前用户处于活动状态 3 处于活动状态的最大用户数 1 当前 CSP 用户处于活动状态 1 处于活动状态的最大 CSP 用户数 0 当前 CSP 会话处于宽限期 0 处于宽限期的最大 CSP 会话数 ``` `.License.ShowServer` 显示活动的许可证服务器地址和端口: ```java USER> Do $System.License.ShowServer() 活动软件许可服务器地址 = 127.0.0.1 端口 = 4002 ``` 如果开发了基于 `REST` 的应用程序,许可证将随着使用而消耗。为防止这种情况发生,请配置可以建立的 `Web Gateway` 连接数。从 `Web Gateway` 管理部分的管理门户: 1. 导航到服务器访问。 2. 选择无状态参数。 3. 将最大值设置为比许可证小 `2` 或 `3` 的数字,以允许服务器端登录。 **注意:根据应用程序的服务器端需求,需要对此进行调整。** 通过在所有可用连接都忙时执行此操作,新请求将排队而不是被拒绝。由于超出许可计数,不会看到拒绝。随着数量的增长,客户端的响应时间会减慢。这表明需要购买更多许可证。 如果开发了基于 REST 的应用程序,许可证将随着使用而消耗。为防止这种情况发生,请配置可以建立的 Web Gateway 连接数。从 Web Gateway 管理部分的管理门户 姚老师,上面描述的这段是从哪里进入的啊,可以给与相应的导航图吗
文章
姚 鑫 · 三月 10, 2022

第七十七章 SQL函数 LENGTH

# 第七十七章 SQL函数 LENGTH 返回字符串表达式中字符数的字符串函数。 # 大纲 ```java LENGTH(string-expression) {fn LENGTH(string-expression)} ``` # 参数 - `string-expression` - 字符串表达式,可以是列名、字符串文字或另一个标量函数的结果,其中基础数据类型可以表示为任何字符类型(例如 `CHAR` 或 `VARCHAR`)。 `LENGTH` 返回 `INTEGER` 数据类型。 # 描述 `LENGTH` 返回一个整数,表示给定字符串表达式的字符数,而不是字节数。字符串表达式可以是字符串(从中删除尾随空格)或数字( IRIS 将其转换为规范形式)。 请注意,`LENGTH` 可用作 ODBC 标量函数(使用花括号语法)或 SQL 通用函数。 `LENGTH` 和其他长度函数(`$LENGTH`、`CHARACTER_LENGTH`、`CHAR_LENGTH` 和 `DATALENGTH`)都执行以下操作: - `LENGTH` 返回字段的逻辑(内部数据存储)值的长度,而不是显示值,无论 `SelectMode` 设置如何。所有 SQL 函数始终使用字段的内部存储值。 - `LENGTH` 返回数字的规范形式的长度。规范形式的数字不包括前导零和尾随零、前导符号(单个减号除外)和尾随小数分隔符。 `LENGTH` 返回数字字符串的字符串长度。数字字符串不会转换为规范形式。 - `LENGTH` 不排除字符串中的前导空格。可以使用 `LTRIM` 函数从字符串中删除前导空格。 在执行以下操作时,`LENGTH` 与其他长度函数(`$LENGTH`、`CHARACTER_LENGTH`、`CHAR_LENGTH` 和 `DATALENGTH`)不同: - `LENGTH` 不包括尾随空格和字符串终止字符。 `$LENGTH`、`CHARACTER_LENGTH`、`CHAR_LENGTH` 和 `DATALENGTH` 不排除尾随空格和终止符。 - 如果传递一个 `NULL` 值,`LENGTH` 返回 `NULL`,如果传递一个空字符串,则返回 `0`。 如果传递 `NULL` 值,`CHARACTER_LENGTH`、`CHAR_LENGTH` 和 `DATALENGTH` 也返回 `NULL`,如果传递空字符串,则返回 `0`。如果传递一个 `NULL` 值,则 `$LENGTH` 返回 `0`,如果传递一个空字符串,则返回 `0`。 - `LENGTH` 不支持数据流字段。为字符串表达式指定流字段会导致 `SQLCODE -37`。 `$LENGTH` 也不支持流字段。 `CHARACTER_LENGTH`、`CHAR_LENGTH` 和 `DATALENGTH` 函数确实支持数据流字段。 # 示例 在以下示例中, IRIS 首先将每个数字转换为规范形式(删除前导零和尾随零,解析前导符号,并删除尾随小数分隔符)。每个 `LENGTH` 返回长度为 `1`: ```sql SELECT {fn LENGTH(7.00)} AS CharCount, {fn LENGTH(+007)} AS CharCount, {fn LENGTH(007.)} AS CharCount, {fn LENGTH(00000.00)} AS CharCount, {fn LENGTH(-0)} AS CharCount 1 1 1 1 1 ``` 在以下示例中,第一个 `LENGTH` 删除前导零,返回长度值 `2`;第二个 `LENGTH` 将数值视为字符串,并且不删除前导零,返回长度值 `3`: ```sql SELECT LENGTH(0.7) AS CharCount, LENGTH('0.7') AS CharCount 2 3 ``` 以下示例返回值 `12`: ```sql SELECT LENGTH('INTERSYSTEMS') AS CharCount 12 ``` 以下示例显示了 `LENGTH` 如何处理前导和尾随空格。第一个 `LENGTH `返回 `15`,因为 `LENGTH` 不包括尾随空格,但不包括前导空格。第二个 `LENGTH` 返回 `12`,因为 `LTRIM` 排除了前导空格: ```sql SELECT LENGTH(' INTERSYSTEMS ') AS CharCount, LENGTH(LTRIM(' INTERSYSTEMS ')) AS CharCount 15 12 ``` 以下示例返回 `Sample.Person` 表中每个 `Name` 值中的字符数: ```sql SELECT Name,{fn LENGTH(Name)} AS CharCount FROM Sample.Person ORDER BY CharCount ``` 以下示例返回 `DOB`(出生日期)字段中的字符数。请注意,返回的长度(由 `LENGTH`、`CHAR_LENGTH` 和 `CHARACTER_LENGTH`)是日期的内部 (`$HOROLOG`) 格式,而不是显示格式。 `DOB`的显示长度为十个字符;所有三个长度函数都返回 5 的内部长度: ```sql SELECT DOB,{fn LENGTH(DOB)} AS LenCount, CHAR_LENGTH(DOB) AS CCount, CHARACTER_LENGTH(DOB) AS CtrCount FROM Sample.Person ``` 以下嵌入式 SQL 示例给出了 `Unicode` 字符字符串的长度。返回的长度是字符数 (7),而不是字节数。 ```java /// d ##class(PHA.TEST.SQLCommand).Length() ClassMethod Length() { s a = $CHAR(920,913,923,913,931,931,913) &sql(SELECT LENGTH(:a) INTO :b ) if SQLCODE'=0 { w !,"Error code ",SQLCODE } else { w !,"The Greek Sea: ",a,!,$LENGTH(a),!,b } } ``` ```java DHC-APP>d ##class(PHA.TEST.SQLCommand).Length() The Greek Sea: ΘΑΛΑΣΣΑ 7 7 ```
文章
Michael Lei · 三月 21, 2023

参赛文章选编- 使用嵌入式 Python教程

InterSystems IRIS 是一个高性能、可靠且可扩展的数据平台,用于为医疗保健、金融服务和其他行业构建和部署关键任务应用程序。它提供了广泛的功能,包括数据管理、集成、分析等。 IRIS 提供的功能之一是能够将 Python 代码嵌入到 ObjectScript 代码中。这意味着您可以在 IRIS 应用程序中使用 Python 库和函数,让您可以访问大量的工具和资源。在本文中,我们将了解如何在 InterSystems IRIS 中使用嵌入式 Python。 设置嵌入式 Python 在 IRIS 中开始使用嵌入式 Python 之前,您需要设置环境。这涉及安装 Python 解释器和配置 IRIS 以识别它。 第一步是安装 Python。您可以从官方网站 ( https://www.python.org/downloads/ ) 下载最新版本的 Python。安装 Python 后,需要将其添加到系统的 PATH 环境变量中。这允许 IRIS 找到 Python 解释器。 接下来,您需要配置 IRIS 以识别 Python。为此,您需要创建一个 Python 网关。网关是一个在 IRIS 之外运行的进程,充当 IRIS 和 Python 之间的桥梁。 要创建网关,请打开一个终端窗口并导航到 Python 安装目录。然后运行以下命令: python -m irisnative 此命令启动 Python 网关并创建到 IRIS 的连接。您应该看到类似于以下内容的输出: Python Gateway Version: 0.7 Python Version: 3.8.5 (default, Jan 27 2021, 15:41:15) [GCC 9.3.0] Connected to: IRIS 这表明 Python 网关正在运行并连接到 IRIS。 在 IRIS 中使用嵌入式 Python 现在您已经设置了环境,您可以开始在 IRIS 中使用 Python。为此,您需要使用 ##class(%Net.Remote.Gateway).%New() 方法创建到 Python 网关的连接。此方法返回对可用于执行 Python 代码的 Python 对象的引用。 以下是如何使用嵌入式 Python 计算两个数字之和的示例: Set gateway = ## class (%Net.Remote.Gateway).% New () Set result = gateway.% Execute ( "2 + 3" ) Write "Result: " , result , ! 在此示例中,我们使用 %New() 方法创建到 Python 网关的连接。然后我们执行Python代码"2 + 3",返回结果5。我们将结果存储在result-变量中并输出到控制台。 在 IRIS 中使用 Python 库 在 IRIS 中使用嵌入式 Python 的好处之一是能够使用 Python 库。 Python 拥有庞大的库生态系统,用于数据处理、机器学习等。通过使用这些库,您可以扩展 IRIS 应用程序的功能。 要在 IRIS 中使用 Python 库,您首先需要使用 pip包管理器安装该库。例如,要安装 numpy 库,请运行以下命令: pip install numpy 安装该库后,您可以通过使用 Python 网关导入它来在 IRIS 应用程序中使用它。以下是如何使用 numpy库创建矩阵的示例: vbnet Set gateway = ##class(%Net.Remote.Gateway).%New() Set np = gateway.%Get("numpy") Set matrix = np.%New("array", 10, 10) 在这个例子中, 我们使用 %New()方法创建到 Python 网关的连接。然后我们使用 -%Get() 方法检索对 numpy 库的引用。我们使用 numpy 库中的 array 方法创建一个新矩阵,大小为 10 x 10。我们将矩阵存储在matrix 变量,然后我们可以在我们的 IRIS 应用程序中使用它。 结论 在本文中,我们了解了如何在 InterSystems IRIS 中使用嵌入式 Python。我们已经了解了如何在 IRIS 中设置环境、执行 Python 代码和使用 Python 库。使用嵌入式 Python 可以扩展 IRIS 应用程序的功能,使您能够访问大量的工具和资源。通过将 Python 的强大功能与 IRIS 的性能和可靠性相结合,您可以构建满足组织需求的关键任务应用程序。
文章
姚 鑫 · 八月 4, 2022

第十七章 源代码文件 REST API 教程(二)

# 第十七章 源代码文件 REST API 教程(二) # 获取在命名空间中定义的源代码文件 要获取有关命名空间中源代码文件的信息: - 首先,使用 `GetDocNames` 方法获取文件的名称。 - 然后用`GetDoc` 方法获取一个文件的内容,也可以用`GetDocs` 方法获取多个文件的内容。 - 如果要提高应用程序的网络效率,可以保留源代码文件的名称和内容的本地缓存,并使用 `GetModifiedDocNames` 方法仅获取内容发生变化的源代码文件的名称或使用带有 `If-None-Match HTTP` 标头的 `GetDoc` 方法。 `GetDocNames` 方法返回映射到命名空间的所有数据库中的所有源代码文件的名称。 ```json { "status": { "errors": [], "summary": "" }, "console": [], "result": { "content": [ { "name": "%Api.DocDB.cls", "cat": "CLS", "ts": "2016-08-03 20:01:42.000", "upd": true, "db": "IRISLIB", "gen": false }, ... { "name": "EnsProfile.mac", "cat": "RTN", "ts": "2003-09-19 13:53:31.000", "upd": true, "db": "INVENTORYR", "gen": false }, ... { "name": "xyz.mac", "cat": "RTN", "ts": "2016-08-11 15:05:02.167", "upd": false, "db": "INVENTORYR", "gen": false } ] } } ``` 以下 `GetDoc` 调用返回 `xyz.mac` 文件的内容: ```java http://localhost:52773/api/atelier/v1/INVENTORY/doc/xyz.mac ``` 此调用返回: ```json { "status": { "errors": [], "summary": "" }, "console": [], "result": { "name": "xyz.mac", "db": "INVENTORYR", "ts": "2016-09-14 14:10:16.540", "upd": false, "cat": "RTN", "status": "", "enc": false, "flags": 0, "content": [ "ROUTINE xyz", "xyz ;", " w \"hello\"" ] } } ``` # 在命名空间中创建新文件或更新现有文件 要在命名空间中创建新文件或更新现有文件,请使用 `PutDoc` 方法。例如,以下 `REST` 调用在 `INVENTORY` 命名空间中创建一个新的 `xyz.mac` 源代码文件,或者,如果 `xyz.mac` 文件存在,则此调用将文件的原始定义替换为新定义。如果要更新新文件,则必须指定 `HTTP` 标头 `If-None-Match` 以标识文件的当前版本,或指定 `?ignoreConflict=1 URL` 参数以绕过版本检查。有关详细信息,请参阅参考部分中的 `PutDoc`。 ```java PUT http://localhost:52773/api/atelier/v1/INVENTORY/doc/xyz.mac ``` 应该指定 `Content-Type application/json` 和以下 `JSON` 消息: ```json { "enc": false, "content": [ "ROUTINE xyz", "xyz ;", " w \"hello\"" ] } ``` 该调用返回以下 `JSON` 消息。它显示源代码文件已在 `INVENTORYR` 数据库中创建,该数据库是 `INVENTORY` 命名空间中例程的默认数据库。 ```json { "status": { "errors": [], "summary": "" }, "console": [], "result": { "name": "xyz.mac", "db": "INVENTORYR", "ts": "2016-09-14 14:10:16.540", "upd": false, "cat": "RTN", "status": "", "enc": false, "flags": 0, "content": [] } } ``` 如果要更新或创建二进制文件,请为 `enc` 指定一个真值,并将二进制内容作为二进制值的 `base64` 编码的块数组包含在内。
文章
姚 鑫 · 六月 21, 2022

第六章 操作位和位串(二)

# 第六章 操作位和位串(二) # 将位序列存储为整数 如果要将一系列布尔参数传递给方法,一种常见的方法是将它们作为编码为单个整数的位序列传递。 例如,`Security.System.ExportAll()` 方法用于从 IRIS 实例中导出安全设置。如果查看此方法的类引用,将看到它的定义如下: ```java classmethod ExportAll(FileName As %String = "SecurityExport.xml", ByRef NumExported As %String, Flags As %Integer = -1) as %Status ``` 第三个参数 `Flags` 是一个整数,其中每个位代表一种可以导出的安全记录。 ```java Flags - What type of records to export to the file, -1 = ALL Bit 0 - System Bit 1 - Events Bit 2 - Services Bit 4 - Resources Bit 5 - Roles Bit 6 - Users Bit 7 - Applications Bit 8 - SSL Configs Bit 9 - PhoneProvider Bit 10 - X509Credential Bit 11 - OpenAMIdentityService Bit 12 - SQL privileges Bit 13 - X509Users Bit 14 - DocDBs Bit 15 - LDAPConfig Bit 16 - KMIPServer ``` 存储为整数的位串中的位 `0` 表示 `20`,位 `1` 表示 `2^1`,依此类推。如果要导出与位 `5`、`6`、`7`、`8`、`10`、`11` 和 `13` 对应的类型的安全记录,可以通过将 `Flags` 设置为 `2^5` +`2^6` + `2^7`+ `2^8` + `2^10` + `2^11` + `2^13` = `11744` 来完成. 在 ObjectScript 中,这可能看起来像: ```java set flags = (2**5) + (2**6) + (2**7) + (2**8) + (2**10) + (2**11) + (2**13) set status = ##class(Security.System).ExportAll("SecurityExport.xml", .numExported, flags) ``` 一些 API 定义了宏以使代码更易于阅读。一种这样的情况是在 `DataMove` 实用程序中,其中 `DataMove` 对象是使用 `DataMove.Data.CreateFromMapEdits()` 方法创建的。无需过多介绍细节,该方法在类参考中定义如下: ```java classmethod CreateFromMapEdits(Name As %String, ByRef Properties As %String, ByRef Warnings As %String, ByRef Errors As %String) as %Status ``` 它有以下参数: - `Name` - 要创建的 `DataMove` 对象的名称。 - `Properties` - 用于创建对象的属性数组。可以选择指定以下属性。 - `Properties("Description")`- 数据移动操作的描述,默认 =`""`。 - `Properties("Flags")` - 描述操作的标志,默认 = `0`。 - `Properties("LogFile")`- 日志文件的目录和文件名,默认 = `\iris\mgr\DataMovename.log`。 为了使 Properties("Flags") 更容易定义,这些宏可供使用: 控制数据移动的位标志。 - `$$$BitNoSrcJournal` - 允许不记录源数据库 - `$$$BitNoWorkerJobs` - 在复制数据期间不要使用“worker”作业 - `$$$BitBatchMode` - 在“批处理”模式下运行复制作业 - `$$$BitCheckActivate` - 在 `Activate()` 期间调用 `$$CheckActivate^ZDATAMOVE()` 这些宏定义为特定位的计算值,允许设置正确的位,而无需记住哪个位代表哪个标志: `#;DataMove` 标志属性的定义 - `#define BitNoSrcJournal 1` - `#define BitNoWorkerJobs 512` - `#define BitBatchMode 2048` - `#define BitCheckActivate 4096` 在代码中,可以使用此代码片段来设置标志并创建一个 `DataMove` 对象: ```java // Set properties("Flags") to 6657 set properties("Flags") = $$$BitNoSrcJournal + $$$BitNoWorkerJobs + $$$BitBatchMode + $$$BitCheckActivate set status = ##class(DataMove.Data).CreateFromMapEdits("dm", .properties, .warnings, .errors) ```
文章
姚 鑫 · 六月 29, 2022

第十三章 信号(三)- 示例演示

# 第十三章 信号(三)- 示例演示 ## 运行示例 `Main`、`Producer` 和 `Consumer` 这三个类中的每一个都有自己的 `Run` 方法,最好在各自的终端窗口中运行它们。每次运行时,它都会显示它为日志生成的消息。一旦用户通过提供它正在等待的输入来响应 `Main` 类,`Main` 的 `Run` 方法将终止删除信号量。然后,用户可以通过键入命令查看所有进程的合并日志文件的显示 ```java Do ##class(Semaphore.Util).ShowLog() ``` 注意:以下所有示例都假定所有类都已在`“USER”`命名空间中编译。 ### 示例 1 - 创建和删除信号量 最简单的例子演示了信号量的创建和销毁。它使用 `Semaphore.Main` 类。请执行下列操作: 1. 打开一个终端窗口。 2. 输入命令—— ```java Do ##class(Semaphore.Main).Run() ``` 3. 该方法创建信号量。如果成功,将看到消息“输入任何字符以终止运行方法”。按下 `Enter` 键。该方法显示信号量的初始化值,将其删除,然后退出。 4. 通过发出命令显示日志文件 ```java Do ##class(Semaphore.Util).ShowLog() ``` 按照上述步骤在终端窗口中显示的消息示例如下 ```java 消息示例如下 DHC-APP>Do ##class(Semaphore.Main).Run() (1) Main Started (2) New semaphore (3) Created: "Counter"; Value = 0; Id = 0x0x10000 (4) Semaphore create result: 1 (5) Enter any character to terminate Run method (6) Final value = 0 (7) Semaphore delete status: 1 (8) Main Finished (9) Closing Semaphore: Id = 0x10000 日志输出如下所示: DHC-APP> Do ##class(Semaphore.Util).ShowLog() Message Log: Entries = 9 # $JOB Sender Message 1) 24888 Main: Main Started 2) 24888 Counter: New semaphore 3) 24888 Counter: Created: "Counter"; Value = 0; Id = 0x0x10000 4) 24888 Main: Semaphore create result: 1 5) 24888 Main: Enter any character to terminate Run method 6) 24888 Main: Final value = 0 7) 24888 Main: Semaphore delete status: 1 8) 24888 Main: Main Finished 9) 24888 Counter: Closing Semaphore: Id = 0x10000 ``` ### 示例 2——创建信号量并连续递增它 这个例子展示了生产者在工作,以及从两个进程中捕获日志消息。 1. 打开两个单独的终端窗口。称它们为`“A”`和`“B”`。 2. 在窗口 `A` 中,键入以下命令,但不要在末尾键入 ENTER 键 - ```java Do ##class(Semaphore.Main).Run() ``` 3. 在窗口 `B` 中,键入以下命令,但同样,不要在命令末尾键入 `ENTER` 键 - ```java Do ##class(Semaphore.Producer).Run() ``` 4. 现在,在窗口 `A` 中,按 `ENTER` 键。然后在窗口 `B` 中,按 `ENTER` 键。这将启动两个类并行执行。他们各自的消息显示在他们自己的窗口中。 5. `Producer` 进程完成后,关闭 `B` 窗口。 6. 在 `A` 窗口中,按 `ENTER` 键以完成 `Main` 类。然后,使用以下命令显示日志 - ```java Do ##class(Semaphore.Util).ShowLog() ``` 对于此示例,以下是输出 `A` 窗口 ```java DHC-APP>Do ##class(Semaphore.Main).Run() (1) Main Started (2) New semaphore (3) Created: "Counter"; Value = 0; Id = 0x0x20001 (4) Semaphore create result: 1 (5) Enter any character to terminate Run method (17) Final value = 30 (18) Semaphore delete status: 1 (19) Main Finished (20) Closing Semaphore: Id = 0x20001 ``` `B` 窗口 ```java DHC-APP>Do ##class(Semaphore.Producer).Run() (6) Producer.1 Started (7) New semaphore (8) Open Id = 0x20001 (9) Increment 0x20001 = 0 by 5 wait 10 sec (10) Increment 0x20001 = 5 by 5 wait 4 sec (11) Increment 0x20001 = 10 by 3 wait 1 sec (12) Increment 0x20001 = 13 by 5 wait 9 sec (13) Increment 0x20001 = 18 by 5 wait 8 sec (14) Increment 0x20001 = 23 by 4 wait 2 sec (15) Increment 0x20001 = 27 by 1 wait 8 sec (16) Increment 0x20001 = 28 by 2 wait 5 sec (21) Producer.1 Finished (22) Closing Semaphore: Id = 0x20001 ``` 日志显示 ```java DHC-APP>Do ##class(Semaphore.Util).ShowLog() Message Log: Entries = 22 # $JOB Sender Message 1) 21080 Main: Main Started 2) 21080 Counter: New semaphore 3) 21080 Counter: Created: "Counter"; Value = 0; Id = 0x0x20001 4) 21080 Main: Semaphore create result: 1 5) 21080 Main: Enter any character to terminate Run method 6) 27724 Producer.1: Producer.1 Started 7) 27724 Counter: New semaphore 8) 27724 Producer.1: Open Id = 0x20001 9) 27724 Producer.1: Increment 0x20001 = 0 by 5 wait 10 sec 10) 27724 Producer.1: Increment 0x20001 = 5 by 5 wait 4 sec 11) 27724 Producer.1: Increment 0x20001 = 10 by 3 wait 1 sec 12) 27724 Producer.1: Increment 0x20001 = 13 by 5 wait 9 sec 13) 27724 Producer.1: Increment 0x20001 = 18 by 5 wait 8 sec 14) 27724 Producer.1: Increment 0x20001 = 23 by 4 wait 2 sec 15) 27724 Producer.1: Increment 0x20001 = 27 by 1 wait 8 sec 16) 27724 Producer.1: Increment 0x20001 = 28 by 2 wait 5 sec 17) 21080 Main: Final value = 30 18) 21080 Main: Semaphore delete status: 1 19) 21080 Main: Main Finished 20) 21080 Counter: Closing Semaphore: Id = 0x20001 21) 27724 Producer.1: Producer.1 Finished 22) 27724 Counter: Closing Semaphore: Id = 0x20001 ``` ### 示例 3 - 同时运行所有三个进程 此示例显示尝试以连贯的方式增加和减少相同的信号量。它使用所有三个主要类。 1. 打开三个单独的终端窗口。称它们为`“A”`、`“B”`和`“C”`。 2. 在窗口 `A` 中,键入以下命令,但最后不要按 `ENTER` 键 ```java Do ##class(Semaphore.Main).Run() ``` 3. 在窗口 `B` 中,键入以下命令,但同样,不要在命令末尾按 `ENTER` 键 - ```java Do ##class(Semaphore.Producer).Run() ``` 4. 在窗口 `C` 中,键入以下命令,但同样,不要在命令末尾按 `ENTER` 键 - ```java Do ##class(Semaphore.Consumer).Run() ``` 5. 从窗口 `A` 开始,访问每个窗口并键入 `ENTER` 键。这将启动 `Main` 类,然后是其他两个类。如前所述,每个进程都会在自己的窗口中显示其日志消息。 6. 当`Producer` 和`Consumer` 进程都完成后,关闭`B` 窗口和`C` 窗口。 7. 在 `A` 窗口中,按 `ENTER` 键以完成 `Main` 类。然后,使用以下命令显示日志 ```java Do ##class(Semaphore.Util).ShowLog() ``` 运行此示例会产生类似于以下内容的输出: 窗口 `A` ```java DHC-APP>Do ##class(Semaphore.Main).Run() (1) Main Started (2) New semaphore (3) Created: "Counter"; Value = 0; Id = 0x0x40003 (4) Semaphore create result: 1 (5) Enter any character to terminate Run method (64) Final value = 0 (65) Semaphore delete status: 1 (66) Main Finished (67) Closing Semaphore: Id = 0x40003 ``` 窗口`B` ```java DHC-APP>Do ##class(Semaphore.Producer).Run() (6) Producer.1 Started (7) New semaphore (8) Open Id = 0x40003 (9) Increment 0x40003 = 0 by 5 wait 8 sec (20) Increment 0x40003 = 0 by 4 wait 4 sec (25) Increment 0x40003 = 0 by 3 wait 1 sec (29) Increment 0x40003 = 0 by 2 wait 10 sec (36) Increment 0x40003 = 0 by 4 wait 3 sec (40) Increment 0x40003 = 0 by 5 wait 5 sec (52) Increment 0x40003 = 0 by 5 wait 6 sec (58) Increment 0x40003 = 0 by 2 wait 2 sec (62) Producer.1 Finished (63) Closing Semaphore: Id = 0x40003 ``` 窗口`C` ```java DHC-APP>Do ##class(Semaphore.Consumer).Run() (10) Consumer.1 Started (11) New semaphore (12) Consumer: Open Id = 0x40003 (13) Decrement 0x40003 = 5 by 1 wait 10 sec (14) WaitCompleted: 0x40003; Amt = 1 (15) Granted (16) Decrement 0x40003 = 4 by 5 wait 2 sec (17) WaitCompleted: 0x40003; Amt = 4 (18) Granted (19) Decrement 0x40003 = 0 by 5 wait 8 sec (21) WaitCompleted: 0x40003; Amt = 4 (22) Granted (23) Decrement 0x40003 = 0 by 5 wait 6 sec (25) WaitCompleted: 0x40003; Amt = 3 (26) Granted (27) Decrement 0x40003 = 0 by 3 wait 1 sec (28) Timeout (30) Decrement 0x40003 = 0 by 4 wait 4 sec (31) WaitCompleted: 0x40003; Amt = 2 (32) Granted (33) Decrement 0x40003 = 0 by 2 wait 7 sec (34) Timeout (35) Decrement 0x40003 = 0 by 4 wait 9 sec (37) WaitCompleted: 0x40003; Amt = 4 (38) Granted (39) Decrement 0x40003 = 0 by 2 wait 5 sec (41) WaitCompleted: 0x40003; Amt = 2 (42) Granted (43) Decrement 0x40003 = 3 by 1 wait 3 sec (44) WaitCompleted: 0x40003; Amt = 1 (45) Granted (46) Decrement 0x40003 = 2 by 2 wait 10 sec (47) WaitCompleted: 0x40003; Amt = 2 (48) Granted (49) Decrement 0x40003 = 0 by 2 wait 4 sec (50) Timeout (51) Decrement 0x40003 = 0 by 3 wait 4 sec (53) WaitCompleted: 0x40003; Amt = 5 (54) Granted (55) Decrement 0x40003 = 0 by 1 wait 1 sec (56) Timeout (57) Decrement 0x40003 = 0 by 3 wait 7 sec (59) WaitCompleted: 0x40003; Amt = 2 (60) Granted (61) Consumer.1 Finished ``` 日志显示 ```java DHC-APP>Do ##class(Semaphore.Util).ShowLog() Message Log: Entries = 67 # $JOB Sender Message 1) 6412 Main: Main Started 2) 6412 Counter: New semaphore 3) 6412 Counter: Created: "Counter"; Value = 0; Id = 0x0x40003 4) 6412 Main: Semaphore create result: 1 5) 6412 Main: Enter any character to terminate Run method 6) 22236 Producer.1: Producer.1 Started 7) 22236 Counter: New semaphore 8) 22236 Producer.1: Open Id = 0x40003 9) 22236 Producer.1: Increment 0x40003 = 0 by 5 wait 8 sec 10) 20224 Consumer.1: Consumer.1 Started 11) 20224 Counter: New semaphore 12) 20224 Consumer.1: Consumer: Open Id = 0x40003 13) 20224 Consumer.1: Decrement 0x40003 = 5 by 1 wait 10 sec 14) 20224 Counter: WaitCompleted: 0x40003; Amt = 1 15) 20224 Consumer.1: Granted 16) 20224 Consumer.1: Decrement 0x40003 = 4 by 5 wait 2 sec 17) 20224 Counter: WaitCompleted: 0x40003; Amt = 4 18) 20224 Consumer.1: Granted 19) 20224 Consumer.1: Decrement 0x40003 = 0 by 5 wait 8 sec 20) 22236 Producer.1: Increment 0x40003 = 0 by 4 wait 4 sec 21) 20224 Counter: WaitCompleted: 0x40003; Amt = 4 22) 20224 Consumer.1: Granted 23) 20224 Consumer.1: Decrement 0x40003 = 0 by 5 wait 6 sec 24) 22236 Producer.1: Increment 0x40003 = 0 by 3 wait 1 sec 25) 20224 Counter: WaitCompleted: 0x40003; Amt = 3 26) 20224 Consumer.1: Granted 27) 20224 Consumer.1: Decrement 0x40003 = 0 by 3 wait 1 sec 28) 20224 Consumer.1: Timeout 29) 22236 Producer.1: Increment 0x40003 = 0 by 2 wait 10 sec 30) 20224 Consumer.1: Decrement 0x40003 = 0 by 4 wait 4 sec 31) 20224 Counter: WaitCompleted: 0x40003; Amt = 2 32) 20224 Consumer.1: Granted 33) 20224 Consumer.1: Decrement 0x40003 = 0 by 2 wait 7 sec 34) 20224 Consumer.1: Timeout 35) 20224 Consumer.1: Decrement 0x40003 = 0 by 4 wait 9 sec 36) 22236 Producer.1: Increment 0x40003 = 0 by 4 wait 3 sec 37) 20224 Counter: WaitCompleted: 0x40003; Amt = 4 38) 20224 Consumer.1: Granted 39) 20224 Consumer.1: Decrement 0x40003 = 0 by 2 wait 5 sec 40) 22236 Producer.1: Increment 0x40003 = 0 by 5 wait 5 sec 41) 20224 Counter: WaitCompleted: 0x40003; Amt = 2 42) 20224 Consumer.1: Granted 43) 20224 Consumer.1: Decrement 0x40003 = 3 by 1 wait 3 sec 44) 20224 Counter: WaitCompleted: 0x40003; Amt = 1 45) 20224 Consumer.1: Granted 46) 20224 Consumer.1: Decrement 0x40003 = 2 by 2 wait 10 sec 47) 20224 Counter: WaitCompleted: 0x40003; Amt = 2 48) 20224 Consumer.1: Granted 49) 20224 Consumer.1: Decrement 0x40003 = 0 by 2 wait 4 sec 50) 20224 Consumer.1: Timeout 51) 20224 Consumer.1: Decrement 0x40003 = 0 by 3 wait 4 sec 52) 22236 Producer.1: Increment 0x40003 = 0 by 5 wait 6 sec 53) 20224 Counter: WaitCompleted: 0x40003; Amt = 5 54) 20224 Consumer.1: Granted 55) 20224 Consumer.1: Decrement 0x40003 = 0 by 1 wait 1 sec 56) 20224 Consumer.1: Timeout 57) 20224 Consumer.1: Decrement 0x40003 = 0 by 3 wait 7 sec 58) 22236 Producer.1: Increment 0x40003 = 0 by 2 wait 2 sec 59) 20224 Counter: WaitCompleted: 0x40003; Amt = 2 60) 20224 Consumer.1: Granted 61) 20224 Consumer.1: Consumer.1 Finished 62) 22236 Producer.1: Producer.1 Finished 63) 22236 Counter: Closing Semaphore: Id = 0x40003 64) 6412 Main: Final value = 0 65) 6412 Main: Semaphore delete status: 1 66) 6412 Main: Main Finished 67) 6412 Counter: Closing Semaphore: Id = 0x40003 ``` ## 其他变量 此示例的其他变量是可能的。虽然一次只能运行一个 `Semaphore.Main`,但在其他窗口中执行的生产者或消费者的数量没有限制。鼓励用户在各种场景中尝试不同数量的消费者和生产者,例如 - 运行三个消费者和一个生产者,这样信号量就会有更大的“竞争”。运气好的话,日志会显示两个或多个消费者发出了减少信号量的请求,并且都成功了,因为信号量值大到足以满足两个请求的部分或全部。 - 还可以使用这些类来演示删除信号量时其他进程中发生的情况。为此,在 `Producers` 或 `Consumers` 运行时,切换到 `Main` 类正在运行的窗口,然后按 `ENTER`。在完成处理过程中,`Main` 类将删除信号量,`Producer` 或 `Consumer` 的 `OREF` 将不再有效。下次尝试使用将产生错误。 - 通过将信号量的名称更改为看起来像全局名称的名称,可以将信号量映射到例如 `ECP` 系统上的不同实例。
文章
姚 鑫 · 五月 7, 2021

第三章 使用多维存储(全局变量)(三)

# 第三章 使用多维存储(全局变量)(三) # 在全局变量中复制数据 若要将全局变量(全部或部分)的内容复制到另一个全局变量(或局部数组)中,请使用ObjectScript `Merge`命令。 下面的示例演示如何使用`Merge`命令将`OldData`全局变量的全部内容复制到`NewData`全局变量中: ``` Merge ^NewData = ^OldData ``` 如果合并命令的`source`参数有下标,则复制该节点及其后代中的所有数据。如果`Destination`参数有下标,则使用目标地址作为顶级节点复制数据。例如,以下代码: ```java Merge ^NewData(1,2) = ^OldData(5,6,7) ``` 将`^OldData(5,6,7)`及其下的所有数据复制到`^NewData(1,2)`。 # 维护全局变量内的共享计数器 大规模事务处理应用程序的一个主要并发瓶颈可能是创建唯一标识符值。例如,考虑一个订单处理应用程序,在该应用程序中,必须为每一张新发票指定一个唯一的标识号。传统的方法是维护某种计数器表。每个创建新发票的进程都会等待获取此计数器上的锁,递增其值,然后将其解锁。这可能会导致对此单个记录的激烈资源争用。 为了解决此问题,InterSystems IRIS提供了ObjectScript `$INCREMENT`函数。`$INCREMENT`自动递增全局节点的值(如果该节点没有值,则设置为1)。`$INCREMENT`的原子性意味着不需要锁;该函数保证返回一个新的增量值,不会受到任何其他进程的干扰。 可以使用`$INCREMENT`,如下所示。首先,必须决定在其中存放计数器的全局节点。接下来,无论何时需要新的计数器值,只需调用`$INCREMENT`: ```java SET counter = $INCREMENT(^MyCounter) ``` InterSystems IRIS对象和SQL使用的默认存储结构使用`$INCREMENT`来分配唯一的对象(行)标识符值。 # 对全局变量中的数据进行排序 存储在全局变量中的数据会根据下标的值自动排序。例如,下面的ObjectScript代码定义了一组全局变量(按随机顺序),然后遍历它们以演示全局节点按下标自动排序: ```java /// w ##class(PHA.TEST.Global).GlobalSort() ClassMethod GlobalSort() { Kill ^Data Set ^Data("Cambridge") = "" Set ^Data("New York") = "" Set ^Data("Boston") = "" Set ^Data("London") = "" Set ^Data("Athens") = "" Set key = $Order(^Data("")) While (key '= "") { Write key,! Set key = $Order(^Data(key)) } q "" } ``` ```java DHC-APP> w ##class(PHA.TEST.Global).GlobalSort() Athens Boston Cambridge London New York ``` 应用程序可以利用全局函数提供的自动排序来执行排序操作或维护对某些值的有序、交叉引用的索引。 InterSystems SQL和ObjectScript使用全局变量自动执行这些任务。 # 全局变量节点排序规则 全局变量节点的排序顺序(称为排序)在两个级别上进行控制:全局变量本身内部和使用全局变量的应用程序。 在应用程序级别,可以通过对用作下标的值执行数据转换来控制全局节点的排序方式(InterSystems SQL和对象通过用户指定的排序函数来执行此操作)。 例如,如果创建一个按字母顺序排序但忽略大小写的名称列表,那么通常你会使用名称的大写版本作为下标: ```java /// w ##class(PHA.TEST.Global).GlobalSortAlpha() ClassMethod GlobalSortAlpha() { Kill ^Data For name = "Cobra","jackal","zebra","AARDVark" { Set ^Data($ZCONVERT(name,"U")) = name } Set key = $Order(^Data("")) While (key '= "") { Write ^Data(key),! Set key = $Order(^Data(key)) } q "" } ``` ``` DHC-APP>w ##class(PHA.TEST.Global).GlobalSortAlpha() AARDVark Cobra jackal zebra ``` 此示例将每个名称转换为大写(使用`$ZCONVERT`函数),以便对下标进行排序,而不考虑大小写。每个节点都包含未转换的值,以便可以显示原始值。 ## 数值和字符串值下标 **数字值在字符串值之前进行排序;也就是说,值`1`在值`“a”`之前。如果对给定的下标同时使用数值和字符串值,则需要注意这一点。如果将全局变量用于索引(即根据值对数据进行排序),则最常见的是将值排序为数字(如薪水`salaries`)或字符串(如邮政编码`postal codes`)。** **对于按数字排序的节点,典型的解决方案是使用一元`+`运算符将下标值强制为数字值。例如,如果要构建按年龄对`id`值进行排序的索引,则可以强制年龄始终为数字:** ```java Set ^Data(+age,id) = "" ``` **如果希望将值排序为字符串(如`“0022”`、`“0342”`、`“1584”`),则可以通过添加空格(`“”`)字符来强制下标值始终为字符串。例如,如果正在构建一个按邮政编码对`id`值进行排序的索引,则可以强制`zipcode`始终为字符串:** ```java Set ^Data(" "_zipcode,id) = "" ``` 这确保带有前导零的值(如`“0022”`)始终被视为字符串。 ## `$SORTBEGIN`和`$SORTEND`函数 通常,不必担心在InterSystems IRIS中对数据进行排序。无论使用SQL还是直接全局访问,排序都是自动处理的。 然而,在某些情况下,可以更有效地进行排序。 具体来说,在以下情况下(`1`)需要设置大量随机(即未排序)的全局节点,(`2`)生成的全局节点的总大小接近InterSystems IRIS缓冲池的很大一部分,那么性能可能会受到不利影响- 因为很多SET操作涉及到磁盘操作(因为数据不适合缓存)。 这种情况通常出现在涉及创建索引全局函数的情况下,例如批量数据加载、索引填充或对临时全局函数中的未索引值进行排序 为了有效地处理这些情况,ObjectScript提供了`$SORTBEGIN`和`$SORTEND`函数。 `$SORTBEGIN`函数为全局变量(或其中的一部分)启动了一种特殊模式,在这种模式中,进入全局变量的数据集被写入一个特殊的临时缓冲区,并在内存(或临时磁盘存储)中进行排序。 当在操作结束时调用`$SORTEND`函数时,数据将按顺序写入实际的全局存储中。 总体操作效率更高,因为实际的写操作是按照要求更少磁盘操作的顺序完成的。 `$SORTBEGIN`函数很容易使用; 在开始排序操作之前,用你想要排序的全局变量的名称调用它,并在操作完成时调用`$SORTEND`: ```java /// w ##class(PHA.TEST.Global).GlobalSortBeginEnd() ClassMethod GlobalSortBeginEnd() { Kill ^Data // 为^Data全局初始化排序模式 Set ret = $SortBegin(^Data) For i = 1:1:10000 { Set ^Data($Random(1000000)) = "" } Set ret = $SortEnd(^Data) // ^Data现在已经设置和排序 Set start = $ZH // 现在迭代并显示(按顺序) Set key = $Order(^Data("")) While (key '= "") { Write key,! Set key = $Order(^Data(key)) } Set elap = $ZH - start Write "Time (seconds): ",elap q "" } ``` `$SORTBEGIN`函数是为全局变量创建的特殊情况而设计的,在使用时必须小心。 特别地,在`$SORTBEGIN`模式下,不能从正在写入的全局变量中读取数据; 由于数据没有写入,读取将是不正确的。 InterSystems SQL自动使用这些函数创建临时全局索引(例如对未索引的字段进行排序)。 ## 在全局变量中使用间接 通过间接方式,ObjectScript提供了一种在运行时创建全局变量引用的方法。 这对于在程序编译时不知道全局变量结构或名称的应用程序非常有用。 间接操作符@支持间接操作,它解除了对包含表达式的字符串的引用。 根据@操作符的使用方式,有几种间接类型。 下面的代码提供了一个名称间接引用的示例,在这个示例中,使用`@`操作符对包含全局引用的字符串进行解引用: ```java /// w ##class(PHA.TEST.Global).GlobalIndirect() ClassMethod GlobalIndirect() { Kill ^Data Set var = "^Data(100)" // 现在使用间接设置^Data(100) Set @var = "This data was set indirectly." // 现在直接显示值: Write "Value: ",^Data(100) q "" } ``` ```java DHC-APP> w ##class(PHA.TEST.Global).GlobalIndirect() Value: This data was set indirectly. ``` 也可以使用下标间接在间接语句中混合表达式(变量或文字值): ```java /// w ##class(PHA.TEST.Global).GlobalIndirect1() ClassMethod GlobalIndirect1() { Kill ^Data Set glvn = "^Data" For i = 1:1:10 { Set @glvn@(i) = "This data was set indirectly." } Set key = $Order(^Data("")) While (key '= "") { Write "Value ",key, ": ", ^Data(key),! Set key = $Order(^Data(key)) } q "" } ``` ```javan DHC-APP>w ##class(PHA.TEST.Global).GlobalIndirect1() Value 1: This data was set indirectly. Value 2: This data was set indirectly. Value 3: This data was set indirectly. Value 4: This data was set indirectly. Value 5: This data was set indirectly. Value 6: This data was set indirectly. Value 7: This data was set indirectly. Value 8: This data was set indirectly. Value 9: This data was set indirectly. Value 10: This data was set indirectly. ``` 间接是ObjectScript的一个基本特性; 它并不局限于全局引用。
文章
Claire Zheng · 三月 3, 2021

投票必读:如何成为社区活跃用户(Active user)

亲爱的社区用户: InterSystems社区会举办丰富的竞赛活动,项目提交后最重要的环节就是“投票”! 一般来说社区新注册用户在投票时会发现无法投票并收到以下提示: 如果遇到这种情况,解决方案分两步: 第一步:登陆社区(cn.community.intersystems.com)后进行发帖、回复、回答问题等操作; 第二步:如果您在社区已经完成了上述第一步的操作,那么接下来只需要……等一天。因为我们的系统会每天进行一次active user更新。 成为Active user后,就可以继续给你心仪的项目投票啦! 建议中国用户登录社区中文版(cn.community.intersystems.com)进行操作,以便获得更快的支持! 请再等一天 @Sheng.Li @Jianjun.Miao @gu.jingguo , Thx! 明白了,十分感谢! 明白了,十分感谢! 原来有中文社区,好不容易才找到,,, 通过学习期望也能参加到竞赛!加油! 碰到了这个问题,完美解决,谢谢! 注册满一天留言才能投票
文章
姚 鑫 · 六月 9, 2021

第二章 从对象写入XML输出

# 第二章 从对象写入XML输出 本章介绍如何从InterSystems IRIS对象生成XML输出。 # 创建XML编写器概述 InterSystems IRIS提供了用于为InterSystems IRIS对象生成`XML`输出的工具。可以指定XML投影的详细信息,如将对象投影到`XML`中所述。然后创建一个`Writer`方法,该方法指定`XML`输出的整体结构:字符编码、对象的显示顺序、是否包括处理指令等。 基本要求如下: - 如果需要特定对象的输出,则该对象的类定义必须扩展`%XML.Adaptor`。除了少数例外,该对象引用的类还必须扩展`%XML.Adaptor`。 - 输出方法必须创建`%XML.Writer`的实例,然后使用该实例的方法。 下面的终端会话显示了一个简单的示例,在该示例中,我们访问启用了XML的对象并为其生成输出: ```java /// d ##class(Sample.Person).Populate(100) /// w ##class(PHA.TEST.Xml).Obj2Xml(1) ClassMethod Obj2Xml(ID) { s obj = ##class(Sample.Person).%OpenId(ID) s xml = ##class(%XML.Writer).%New() s xml.Indent=1 s status = xml.RootObject(obj) q "" } ``` ```java DHC-APP>w ##class(PHA.TEST.Xml).Obj2Xml(1) yaoxin 111-11-1117 1990-04-25 889 Clinton Drive St Louis WI 78672 9619 Ash Avenue Ukiah AL 56589 濮氶懌 111-11-1115 Red Orange Yellow Green Red Orange Yellow 31 ``` # 创建输出方法 输出方法按照指定的顺序逐段构造一个XML文档。输出方法的整体结构取决于需要输出完整的XML文档,还是仅仅输出一个片段。 ## 输出方法的整体结构 方法应按以下顺序执行以下部分或全部操作: 1. 如果使用的对象可能无效,请调用该对象的`%ValidateObject()`方法并检查返回的状态。如果对象无效,则XML也将无效。 **`%XML.Writer` 在导出对象之前不会对其进行验证。这意味着,如果刚刚创建了一个对象,但尚未对其进行验证,则该对象(以及XML)可能是无效的(例如,因为缺少必需的属性)。** 2. 创建`%XML.Writer`类的实例,并根据需要设置其属性。 特别是,需要设置以下属性: - `Indent` 缩进-控制输出是在缩进和换行中生成(如果缩进等于1),还是作为单个长行生成(如果缩进等于0)。后者是默认设置。 - `IndentChars` 缩进字符-指定用于缩进的字符。默认值为两个空格的字符串。如果缩进为0,则此属性无效。 - `Charset` 字符集-指定要使用的字符集。 为了提高可读性,本文档中的示例使用缩进等于1。 3. 指定输出目标。 默认情况下,输出写入当前设备。要指定输出目标,请在开始编写文档之前调用以下方法之一: - `OutputToDevice()`-将输出定向到当前设备。 - `OutputToFile()`-将输出定向到指定文件。可以指定绝对路径或相对路径。请注意,该目录路径必须已经存在。 - `OutputToString()`-将输出定向到字符串。稍后,可以使用另一种方法来检索此字符串。 - `OutputToStream()`-将输出定向到指定的流。 4. 启动文档。可以使用`StartDocument()`方法。请注意,如果尚未通过`StartDocument()`启动文档,则以下方法会隐式启动文档:`Write()`、`WriteDocType()`、`RootElement()`、`WriteComment()`和`WriteProcessingInstruction()`。 5. 可以选择写入文档的序言行。可以使用以下方法: - `WriteDocType()` - 编写DOCTYPE声明。 - `WriteProcessingInstructions()`-编写处理指令。 6. 可以选择指定默认命名空间。编写器将其用于没有定义的XML命名空间的类。 7. 可以选择将命名空间声明添加到根元素。为此,可以在启动根元素之前调用几个实用程序方法。 8. 启动文档的根元素。详细信息取决于该文档的根元素是否对应于InterSystems IRIS对象。有两种可能性: - 根元素可能直接对应于InterSystems IRIS对象。如果要为单个对象生成输出,通常会出现这种情况。 在本例中,使用`RootObject()`方法,该方法将指定的启用XML的对象作为根元素写入。 - 根元素可能只是一组元素的包装器,而这些元素是InterSystems IRIS对象。 在本例中,使用`RootElement()`方法,该方法插入具有指定名称的根级元素。 9. 如果使用`RootElement()`方法,请调用方法来为根元素内的一个或多个元素生成输出。可以按照选择的任何顺序或逻辑在根元素中编写任何元素。有几种方法可以编写单个元素,并且可以结合使用这些技术: - 可以使用`object()`方法,该方法写入启用XML的对象。可以指定此元素的名称,也可以使用由对象定义的默认值。 - 可以使用`element()`方法,该方法使用提供的名称写入元素的开始标记。然后,可以使用`WriteAttribute()`、`WriteChars()`、`WriteCData()`等方法编写内容、属性和子元素。子元素可以是另一个`Element()`,也可以是`Object()`。使用`EndElement()`方法指示元素的结束。 - 可以使用`%XML.Element`并手动构造元素。 10. 如果使用的是`RootElement()`方法,请调用`EndRootElement()`方法。此方法关闭文档的根元素,并根据需要减少缩进(如果有)。 11. 如果文档是从`StartDocument()`开始的,请调用`EndDocument()`方法关闭文档。 12. 如果将输出定向到字符串,请使用`GetXMLString()`方法检索该字符串。 还有许多其他可能的组织,但请注意,某些方法只能在某些上下文中调用。具体地说,一旦开始一个文档,在结束第一个文档之前,不能开始另一个文档。如果这样做,`Writer`方法将返回以下状态: ```java #6275: Cannot output a new XML document or change %XML.Writer properties until the current document is completed. #6275:在当前文档完成之前,无法输出新的XML文档或更改%XML。Writer属性。 ``` `StartDocument()`方法的作用是:显式启动文档。如前所述,其他方法隐式启动文档:`write()`、`WriteDocType()`、`RootElement()`、`WriteComment()`和`WriteProcessingInstruction()`。 注意:这里描述的方法旨在使够向XML文档写入特定的单元,但在某些情况下,可能需要更多的控制。在`%XML.Writer`提供了一个额外的方法`Write()`,可以使用该方法将任意字符串写入输出中的任何位置。 此外,还可以使用`Reset()`方法重新初始化编写器属性和输出方法。如果已经生成了一个XML文档,并且希望在不创建新的编写器实例的情况下生成另一个文档,这将非常有用。 ## 错误检查 **%XML.Writer的大多数方法都会返回状态。应该在每个步骤之后检查状态,并在适当的情况下退出。** ## 插入注释行 如前所述,使用`WriteComment()`方法插入注释行。可以在文档中的任何位置使用此方法。如果尚未启动XML文档,此方法将隐式启动文档。 ## 示例 ```java /// w ##class(PHA.TEST.Xml).Write() ClassMethod Write() As %Status { s obj = ##class(Sample.Person).%OpenId(1) set writer=##class(%XML.Writer).%New() set writer.Indent=1 set status=writer.OutputToDevice() if $$$ISERR(status) { do $System.Status.DisplayError(status) quit $$$ERROR($$$GeneralError, "输出目标无效") } set status=writer.RootObject(obj) if $$$ISERR(status) { do $System.Status.DisplayError(status) quit $$$ERROR($$$GeneralError, "写入根对象时出错") } quit status } ``` 请注意,此方法使用`OutputToDevice()`方法将输出定向到当前设备(默认目标)。这不是必需的,但仅用于演示目的。 当然,输出取决于所使用的类,但可能如下所示: ```java DHC-APP>w ##class(PHA.TEST.Xml).Write() xiaoli 111-11-1111 test 2662 1 ``` ## 有关缩进选项的详细信息 如前所述,可以使用编写器的缩进属性来获取包含附加换行符的输出,以获得更好的可读性。此格式没有正式规范。本节介绍`%XML.Writer`使用的规则。如果缩进等于`1`: - 任何只包含空格字符的元素都会转换为空元素。 - 每个元素都放在自己的行上。 - 如果某个元素是前一个元素的子元素,则该元素相对于该父元素缩进。缩进由`IndentChars`属性确定,该属性默认为两个空格。
文章
Michael Lei · 十二月 27, 2022

FHIRDrop、FHIRBox - 到 FHIR® 的托管文件传输

一个简单的生产配置,使 FHIR 交易捆绑包能够通过 Box 和 Dropbox 加载到 InterSystems® FHIR® 服务器中。使用包含的 MFT 连接组件和 14 行自定义业务流程,此生产配置会将您的交易捆绑包处理到 FHIR 资源,以便立即使用,就像哈利·波特的魔法一样。 我首先会展示该生产配置的短视频导览、MFT 连接以及 IRIS 上 Box 和 Dropbox 的 Oauth2 应用配置,接下来循序渐进地展示一些步骤,让您使用您喜欢的任何 MFT 供应商以及您选择的任何工作流程、桌面、API 或 Web 控制台拖放操作。 一些陷阱: OAUTH2 回调需要使用 IRIS 的 SSL 端点来提供重定向… 最好在 Health Connect Cloud 上尝试一下! Dropbox for Business 在基于团队的令牌方面面临挑战,个人 Dropbox 则运行良好。 这不是无法忍受的情形,但需要一些耐心。 配置 MFT 连接时,注意 Dropbox 的 baseurl 上的“/”(确保它存在)。 对于 Box 和 DropBox 的路径,MFT 出站适配器都需要具有结尾“/”。 现在,鉴于以上获奖的 OBS 支持的内容可能有不足之处,如果 InterSytems 文档还不够,下面是需要遵循的步骤。 步骤概览: 将 FHIRDrop 或 FHIRBox 应用创建到一个点,然后停止! (协作和倾听) 在您的 InterSystems FHIR 服务器、HealthConnect 或 I4H 上配置 MFT 连接。 完成您的 FHIRDrop 或 FHIRBox 应用,提供来自 MFT 连接的重定向 URL。 授权您的 MFT 连接。 构建您的生产配置。 正常拖放 创建 FHIRDrop 或 FHIRBox 应用 这里的想法是在每个 Box 和 Dropbox 开发者控制台中“开始”您的应用配置,这会让您看到客户端 ID 和客户端密码,然后让选项卡挂起并移动到 IRIS MFT 连接。 (协作和倾听)只需收集您的客户端 ID 和客户端密码,挂起浏览器选项卡,然后继续: 配置 MFT 连接 基 URL:https://api.box.com/2.0 基 URL:https://api.dropboxapi.com/2/(注意结尾斜杠) 完成应用注册 现在,回到应用注册并完成应用。确保插入上述步骤中的重定向 URL,并添加与 file.read、file.write 有关的范围。 授权您的托管文件传输连接 回到您的托管文件传输连接并通过调用“Get Access Token”(获取访问令牌)来“授权”连接。 构建您的生产配置 生产配置 以下是自定义业务流程,生产配置的源代码:https://gitlab.com/isc_cloud/fhir-drop-fhir-box-mft-2-fhir 正常拖放! 现在,获取 FHIR! ![图片](/sites/default/files/inline/images/gar.png)
文章
Frank Ma · 一月 5, 2022

在Hadoop大数据存储库库中使用SQL (Apache Hive)

本文译自 https://community.intersystems.com/post/using-sql-apache-hive-hadoop-big-data-repositories 大家好, 在使用Spark做Hadoop时,InterSystems IRIS有一个很好的连接器。但市场上也提供了大数据Hadoop访问的其他优秀替代方案-Aparche Hive。请看区别: HIVE SPARK Hive是一个数据库,用类似于RDBMS数据库的表格形式存储数据。 Spark不是一个数据库,它是一个数据分析框架,可以在内存中对大至PB字节的大容量数据进行复杂的数据分析。 使用称作HiveQL的自己的SQL引擎,数据可以从Hive中抽取出来。只能使用SQLs来抽取数据。 Spark既能使用复杂SQLs(Complex SQLs)也能使用MapReduce机制进行数据分析。它支持Java, Scala 和Python写的分析框架。 Hive在Hadoop之上运行。 Spark没有自己专用的存储。实际上,它是从外部的分布式数据存储如运行在Hadoop和MongoDB上的Hive、HBase中抽取数据。 Hive是一个基于数据仓库技术的数据库 Spark更适合在内存中进行复杂和快速的数据分析以及对数据进行流式处理。 对于那些需要在可横向扩展的RDBMS数据库上运行数据仓库操作的应用来说,Hive是最适合的。 Spark最适合于那些要求比MapReduce机制更快地进行大数据分析的应用。 来源: https://dzone.com/articles/comparing-apache-hive-vs-spark 我做了一个PEX互操作性服务,可以让你在你的InterSystems IRIS应用内部使用Apache Hive。请试用如下步骤: 1. 在iris-hive-adapter 项目上做一个Git Clone: $ git clone https://github.com/yurimarx/iris-hive-adapter.git 2. 在这个目录内打开terminal 并运行: $ docker-compose build 3. 运行IRIS容器: $ docker-compose up 4. 打开项目中的Hive Prouction,运行一个Hello样例): http://localhost:52773/csp/irisapp/EnsPortal.ProductionConfig.zen?PRODUCTION=dc.irishiveadapter.HiveProduction 5. 点击“开始”运行Production. 6. 现在我们来测试应用! 7. 运行你的REST客户端应用程序(比如Postman),在body部分使用项目的URLS和命令(使用POST请求): 7.1 在大数据中生成一个新的表:POST http://localhost:9980/?Type=DDL. 在BODY中: CREATE TABLE helloworld (消息字符串) 7.2 在表中插入: POST http://localhost:9980/?Type=DDL. 在BODY中: INSERT INTO helloworld VALUES ("hello") 7.3 T从表中得到结果清单: POST http://localhost:9980/?Type=DML. 在BODY中: SELECT * FROM helloworld (注意:这里的类型是) 现在,你有了2个在IRIS中使用大数据的选项:Hive 或者Spark。希望你喜欢。