顯示具有 HL7 FHIR Server 標籤的文章。 顯示所有文章
顯示具有 HL7 FHIR Server 標籤的文章。 顯示所有文章

2018年12月22日 星期六

HL7 FHIR Server初體驗 - 查詢篇 - 實戰之2

有了上一篇實戰的經驗,這篇應該就容易多了。本篇是強調搜尋後的結果要如何處理。
==========

  • _sort

將查詢所得進行特定欄位之排序,此特定欄位,當然是只有支援之查詢參數。預設是遞增,要遞減就在欄位前面加減號。這邊排序的結果,並不會影響到Server上的排序。
  1. 確認是正確的URI。
  2. 可惜,Server沒有支援。

  • _count

標準文件與Server支援度有差異,不知能否實作。(我猜啦)
應該這麼解釋,Bundle回來的資料非常多,所以,應該要支援Page的機制。若依據範例應該長這樣子:

<Bundle xmlns="http://hl7.org/fhir">
  <!-- snip metadata -->
  <!-- This Search url starts with base search, and adds the effective
    parameters, and additional parameters for search state. All searches
    SHALL return this value.

   In this case, the search continuation method is that the server
    maintains a state, with page references into the stateful list.
 -->
  <link>
    <relation value="self">
    <url value="http://example.org/Patient?name=peter&stateid=23&page=3"/>
  </link>
  <!-- 4 links for navigation in the search. All of these are optional, but recommended -->

  <link>
    <relation value="first"/>
    <url value="http://example.org/Patient?name=peter&stateid=23&page=1"/>
  </link>
  <link>
    <relation value="previous"/>
    <url value="http://example.org/Patient?name=peter&stateid=23&page=2"/>
  </link>
  <link>
    <relation value="next"/>
    <url value="http://example.org/Patient?name=peter&stateid=23&page=4"/>
  </link>
  <link>
    <relation value="last"/>
    <url value="http://example.org/Patient?name=peter&stateid=23&page=26"/>
  </link>

  <!-- then the search results... -->
</Bundle>
也就是說,在<link>下應該要提供URI,讓Client程式能夠實作上一頁,下一頁的機制。
回頭看看,之前查詢的結果。這台伺服器所提供的URI卻是

  "link": [
        {
            "relation": "self",
            "url": "/Patient"
        },
        {
            "relation": "last",
            "url": "/Patient?_skip=230"
        },
        {
            "relation": "next",
            "url": "/Patient?_skip=10"
        }
    ],
而我查FHIR文件,是沒有這個_skip的參數說明,但這是RESTful API所支援。也就是說,這個Server提供三個跳頁連接,last是直接到最後一筆,next是跳到第10筆。
上機實作吧。
沒加

有加
看出差異了嗎?
預設值是每一頁10筆資料。當加上了_count=20,表示每一頁要有20筆資料。所以,Next就會是_skip=20,因為第一頁有20筆,第二頁當然要先跳掉20筆。

  • _include與_revinclude

沒有_include
_include是說主體Resource有參照到的其他Resource也一併用Bundle帶回來。
我們先看這個沒有加_include的查詢

  1. 確認URI,沒有加任何查詢參數。
  2. 總共有40筆。
  3. 這一頁帶10筆回來。
再看看有帶_include的話。
有_include
  1. 確認URI有_include查詢參數
  2. 總共有40筆。
  3. 這次帶回11筆?喔!原來,這10筆MedicationRequest(主體Resoource),有參照的patient查詢參數者,也會帶回來。(原本這10筆的subject,符合參照規範者,只有ID為part1的這一筆。)
那_revinclude又是怎麼一回事呢?(好不容易找到適合案例)

  1. 確認URI有_revinclude查詢參數
  2. 總共有234筆,每次傳10筆,終於來到第四頁(也就是省略30筆)。終於找到案例了。
  3. 除了帶回Patient資料外。還要帶回Encounter,但,這個Encounter的patient查詢參數,是參照於本頁中的某一筆Patient。
能夠理解這兩者差異嗎?
_include是本體內容有參照element,被參照的Resource放進來。
_revinclude是本體內容有被指定之Resourc參照到的話,則把參照到這個本體的外部Resource放進來。

  • _summary
只想看重要欄位。

取完整 _summary=false
取摘要_summary=true


  • _total

這個Server不會實作。因為,在Bundle Resource有Total這個element。

  • _element

這是用來指定要回傳哪些element。必要欄位不用寫入,他一定會回傳。
  1. 確認URI所加入的查詢參數與值。
  2. 回傳我們所要求的element。

  • _contained

要搞懂這個,得先對Resource有很熟悉。大部分的Resource都繼承於DomainResource。而他有一個element叫contained。這個做什麼用的呢?跟參照有關。參照的形式有很多種,我們大部分看到的都是Resource/ID。有需要時,再點選連結取得資料,當然前面加上URL就是向外部要資料。但是,還有一種情境是乾脆把這些外部參照的資料,就放在本體中。所置放的位置就在contained裡面,每一個放在裡面的Resource會有自己的ID,此時參照的寫法就變成"#ID"了。
所以,_countained設成false就是不要把這些參照resource放進來。true就是要。至於還有個both就不好理解,那就實作看看了。
先看正常的查詢,也就是會帶Resource到contained位置。
  1. 確認URI的查詢內容
  2. 這個還是解釋一下。MedicationRequest有medication的查詢參數。其實他是指向Medication。所以,我們去看Medication有一個查詢參數ingredient-code,他是token,所以可以用code去查詢了。
  3. 因為上述查詢是並在一起,Medicaiton就會被放到Contained裡面。
  4. 此時,會有一個ID,提供給參照欄位之參照點。

不過,我還是沒有實作成功。還是回傳無支援。
再想想吧。

=========
===後記===
=========
記得,實務操作不會如此。反而是系統分析師要去定義設計適當流程與畫面,產生適當的查詢字串然後跟後台要資料,再處理之,

2018年12月21日 星期五

HL7 FHIR Server初體驗 - 查詢篇 - 實戰之1

講了一些查詢的文法,最終還是要上線玩一玩才有感覺。
==========
為了讓查詢更加豐富,endpoint轉到官方提供的平台:https://vonk.fire.ly/前端工具仍然使用Postman
(R4也快公告了。此篇以後,都會採用R4的版本)
==========

暖身

  1. 抓Patient資源。因為沒有設定ID,所以取回全部。
  2. 目前這台FHIR Server上有199筆Patient Resource。用Bundle Resource打包多個Patient Resource。
別忘了,3.1.1查詢文件中,有3.1.1.1 Summary Table,裡面可以使用的參數。還要問問Patient Resource,可有哪些查詢參數。
(補充說明:這是R4的版本。Patient Resource已經是Normative,但是,查詢參數卻還是Trial Use)

  • ID
現能理解/ID與查詢參數是一樣的東西。前者是被預設成路徑的一部分。

  • _lastUpdated
_lastUpdate處理的是日期格式。那我們想知道2018年上半年有異動過的Resource。
  1. 輸入查詢參數。他是name=value的結構。value可以有prefix。
  2. 回傳用Bundle打包。總計30筆。
  3. 這些Patient Resource都會落在這個日期區間。

  • _tag, _profile, _security
Server上的所有Patient Resource,都沒有這三個欄位,無法進行實戰展示。

  • _text, _content
這兩個也不容易測試。兩者差異應是_text只會全文檢索,DomainResource.text的部分。而_content則是全部資源內容皆會。
這個功能不好測試,目前所使用https://vonk.fire.ly/的伺服器好像沒有支援。等有適當的案例時,再來補充。

  • _list

這個要跟List資源一起看。可是呢,在List那邊又說盡量用_list查詢。
目前,也還沒有實作成功,並沒有找出適當的展示案例,待補充之。

  • _has

這也是有點難理解的查詢條件,引入了Reverse Chaining的觀念。用以下這個範例來說明:

GET [base]/Patient?_has:Observation:patient:code=1234-5

目標是要取得Patient,但是這個Patient是被用在Observation中,真正去搜尋的是Observation。也就是說,去搜尋Observation,然後使用其查詢參數patient,來回傳所得到之Patient。但是,這邊對Observation又進一步的過濾,也就是,只需要code等於1234-5的Observation,然後傳給我這個Patient是誰。
為了實作這個,我們得先去找適當的Observation來測試。
首先我們要知道Observation有什麼查詢參數可用。上述範例用的是code。一查確實有。
code TUtokenThe code of the observation typeObservation.code13 Resources
<!--
當然,也有patient。
patient TUreferenceThe subject that the observation is about (if patient)Observation.subject.where(resolve() is Patient)
(Patient)
33 Resources
-->
等等,他是資料型態是token。那就要去看看在name=value的value部分,應該要長什麼樣子。其中有一個格式最簡單:
  • [parameter]=[code]: the value of [code] matches a Coding.code or Identifier.value irrespective of the value of the system property
也就是範例中所呈現的樣子。
接著,我們先來看看Server有哪些Observation。
  1. 這個URI會取回所有Observation Resource。
  2. 我們找任意一筆。有個code的編碼85354-9。待會要用這個當查詢值。
  3. 順便看一下,這個Observation所參照的病患是誰。他的ID是example。等等,這只能說這一筆。說不一定其他筆就不是這個patient。查詢完再來驗證。
有這些資料後,我們就可以開始下_has的查詢條件了。(補充說明,這些流程是為了解說查詢,實務上不是如此)

  1. 再用查詢code=85354-9來看看。
  2. 總共有6筆。
這六筆的subject(Patient)分別是(用剪貼,順便比較不同的表達方式):
"subject": {
                    "reference": "https://vonk.fire.ly/Patient/example"
                },
"subject": {
                    "identifier": {
                        "value": "547741699393"
                    }
                },
"subject": {
                    "reference": "https://vonk.fire.ly/547741699393"
                },
 "subject": {
                    "reference": "https://vonk.fire.ly/Patient/example"
                },
"subject": {
                    "reference": "https://vonk.fire.ly/Patient/example"
                },
"subject": {
                    "reference": "https://vonk.fire.ly/Patient/example"
                },
所以,實際上病患只有三個。所以,待會下_has的查詢時,應該會回傳三筆Patient資料。(希望能成功)
  1. URI的長相。
  2. 注意參數設定方式。
  3. 可惜,只有找回一筆。
  4. 此病患的ID是example。
為什麼只有一個呢?哈~回去看那六筆資料,其實只有一筆才是符合規範。


  • _type

這傢伙是用來調出指定之Resource。若只有指定單一Resource,那個標準URI格式所得是一樣的。我們來試試除了Patient也要Observation。(應該要很多)
  1. URI。注意,各資源只有逗號,不可有空白。
  2. 總共取回2305筆。其中Patient是232筆,Observation是2073筆。
==========
這是實作查詢,對終端使用者來說,不會是這樣的流程。但是,如果你要發展Client端或Server端的程式,我想這個基礎功應該不可避免。


2018年12月15日 星期六

HL7 FHIR Server初體驗 - 查詢篇 - 戰技之3

戰技之1戰技之2講的是共同性查詢參數架構。但,實際上的重點卻是在各Resource能支援哪些查詢參數。
趕快再來戰一篇。
==========
每個Resource所支援的查詢參數不盡相同,但是配合共同性查詢參數之應用是一樣的。本篇就以資源對象Patient為主。先來看看,除了有前傳所提到的查詢參數外,Patient資源還有額外的查詢參數。抱歉,偷懶直接複製部分官方文件,完整內容還是要參考官方文件。看了之後....。

Name
Type
Description
active
token
Whether the patient record is active
address
string
A server defined search that may match any of the string fields in the Address, including line, city, state, country, postalCode, and/or text
address-city
string
A city specified in an address
address-country
string
A country specified in an address
address-postalcode
string
A postalCode specified in an address
address-state
string
A state specified in an address
address-use
token
A use code specified in an address
animal-breed
token
The breed for animal patients
animal-species
token
The species for animal patients
birthdate
date
The patient's date of birth
death-date
date
The date of death has been provided and satisfies this search value
deceased
token
This patient has been marked as deceased, or as a death date entered
email
token
A value in an email contact
family
string
A portion of the family name of the patient
gender
token
Gender of the patient
general-practitioner
reference
Patient's nominated general practitioner, not the organization that manages the record



given
string
A portion of the given name of the patient
identifier
token
A patient identifier
language
token
Language code (irrespective of use value)
link
reference
All patients linked to the given patient



name
string
A server defined a search that may match any of the string fields in the HumanName, including family, give, prefix, suffix, suffix, and/or text
organization
reference
The organization at which this person is a patient



phone
token
A value in a phone contact
phonetic
string
A portion of either family or given name using some kind of phonetic matching algorithm
telecom
token
The value in any kind of telecom details of the patient

其實,真正問題在於你的實作情境,更精確說法,要看你的Client端程式,要提供哪些查詢條件。情境就是要看Name這個欄位。如何組出正確的查詢語法,就是得看Type這個欄位。
至於範例,都在前面出現過了。

HL7 FHIR Server初體驗 - 查詢篇 - 戰技之2

戰技之1只討論了查詢參數的資料型態。另外,共用性查詢參數也得要進行討論。
因為,還沒有講解Resource結構,在這邊的理解度上,可能會有所不足。
得說聲抱歉,有結構性的系列文件,還在思考中。真的,文件太多了。
==========

共用性查詢參數

這部份又可以分成兩塊,查詢前與查詢後。

  1. 查詢前
    • _id
    • _lastUpdated
    • _tag
    • _profile
    • _security
    • _text
    • _content
    • _list
    • _has
    • _type
    • _query
  2. 查詢後
    • _sort
    • _count
    • _include
    • _revinclude
    • _summary
    • _elements
    • _contained
    • _containedType

=====
查詢前
=====

  • _id

就是查詢id,只不過利用Router的設定,可以變成uri的一部分。也就是說,下兩個查詢模式是一樣的。

GET [base]/Patient?_id=23


GET [base]/Patient/23

  • _lastUpdated, _tag, _profile, _security
每一個資源都是繼承於DomainResource,而DomainResource又繼承於Resource
Resource有一個element叫meta,他的資料型態是Meta。Meta的element結構為:
Structure

NameFlagsCard.TypeDescription & Constraints
.. MetaΣElementMetadata about a resource
Elements defined in Ancestors: id, extension
... versionIdΣ0..1idVersion specific identifier
... lastUpdatedΣ0..1instantWhen the resource version last changed
... profileΣ0..*uriProfiles this resource claims to conform to
... securityΣ0..*CodingSecurity Labels applied to this resource
All Security Labels (Extensible)
... tagΣ0..*CodingTags applied to this resource
Common Tags (Example)

是的,所有資源都會有,這四個查詢參數,就是來設定這四個欄位的。注意,這四個要用相對應的查詢資料型態:
  • lastUpdated -> Date/DateTime
  • profile -> Token, URI
  • servurity -> Token
  • tag -> Token
範例
GET [base]/Observation?_lastUpdated=gt2010-10-01
GET [base]/Condition?_tag=http://acme.org/codes|needs-review
GET [base]/DiagnosticReport?_profile=http://hl7.org/fhir/StructureDefinition/lipid
GET [base]/DiagnosticReport?_profile=Profile/lipid

  • _text, _content

這兩個傢伙是用來查詢narrative的部分。前段說,所有資源都繼承於DomainResource。而DomainResource有個屬性text,就是攜帶Human Readable的部分。可以想像,這就是全文檢索之意。至於_text與_content之差異,抱歉,我看不出來也理解不出來。
Structure
NameFlagsCard.TypeDescription & Constraintsdoco
.. DomainResourceIResourceA resource with narrative, extensions, and contained resources
+ If the resource is contained in another resource, it SHALL NOT contain nested Resources
+ If the resource is contained in another resource, it SHALL NOT contain any narrative
+ If a resource is contained in another resource, it SHALL NOT have a meta.versionId or a meta.lastUpdated
+ If the resource is contained in another resource, it SHALL be referred to from elsewhere in the resource
Elements defined in Ancestors: id, meta, implicitRules, language
... textI0..1NarrativeText summary of the resource, for human interpretation
... contained0..*ResourceContained, inline Resources
... extension0..*ExtensionAdditional Content defined by implementations
... modifierExtension?!0..*ExtensionExtensions that cannot be ignored
範例
GET [base]/Condition?_text=(bone OR liver) AND metastases  --> 原文是and,我改成AND。

  • _list

用在List資源打包回資源,他是有順序性的。用這個可以取出特定的那一個。
範例
GET [base]/Patient?_list=42

  • _has

這是個限定搜尋的概念。可是他又引入一個新觀念稱之為Reverse Chaining。看範例先。
ET [base]/Patient?_has:Observation:patient:code=1234-5
這個若看得懂,那HL7 FHIR的功力應該超人一等。
他的意思是說,我要找Patient這個資源,但是,用了_has,也就是說,這個Pateint必須要有Observation。那我怎麼知道這個Observation是哪個Patient呢?那就是利用了Observation的查詢參數patient,而且呀,要求這個Observation的code這個欄位必須等於1234-5。
再看這範例(看看什麼叫做Chain)
GET [base]/Patient?_has:Observation:patient:_has:AuditEvent:entity:user=MyUserId
抓Patient,但需要有Observation,透過patient查詢參數連結。而這個Observation需要有AuditEvent,透過entity查詢參數連結,而且AuditEvent的查詢參數user必須等於MyUserId。

  • _type
一般而言,我們的查詢的URI是長這樣子:GET [base]/Observation?params... 也就是說,每次只能針對一種Resource來處理。如果,我們想一次抓好幾種Resource時,就要用這個查詢參數。
GET [base]/?_type=Observation,Condition&other params...

  • _query
這個也不是很懂。總之,就是預先定義好查詢條件,並且設定名稱。然後就可以直接使用。語法結構如下。照文件說是要與OperationDefinition這個Resource配合。


 GET [base]/Patient?_query=name&parameters...
=====
查詢後
=====

  • _sort

把查詢所得進行排序。其中前置符號有-,表示遞減排序。
GET [base]/Observation?_sort=status,-date,category

  • _count
這個不是只有多少筆數,而是頁數。什麼意思呢?例如透過Bundle回來的資源筆數非常多,我們可以切成批次,也就是分成數頁,每一次只傳回固定的項目。那_count就是告訴我們,調回來的資源被切成了幾頁是要求Server每一頁要有多少筆。(實作後之認知)

  • _include, _revinclude

Resource常常會參照到其他的Resource。如何在調閱時,也希望把相關參照到的資源也一塊調回來,就可以用_include。至於要找反過來被參考到的,則是用_revinclude。(沒實作過,不太清楚運作結果,已實作)

  • _summary

這個是非常重要的查詢參數,可以決定回傳資料包含哪些。範例
 GET [base]/ValueSet?_summary=true

value可以設定下列值:
    • true:Resource定義中,欄位被設定要放入summary者。
    • text:只傳回text, id, meta等Top-level的必要element。
    • data:不包含text element。也就是Human Readable那塊。
    • count:只傳回合乎條件的資源筆數,Resource沒傳回。
    • false:什麼都要傳。

有點要注意,當設_summary=text時,就不能合併_include與_revinclude。這個應該可以想得通吧。
  • _elements


如果覺得_summary所指定的element還是不適用,那就可以利用這個查詢參數來指定要哪些element回來。不過,那些必要的element,還是會一起回來。

  • _contained, _containedType

這個是講到串連條件的問題,誰包含誰。抱歉,我還是看不懂。