使用 Cloud Storage 匯入及匯出 FHIR 資源

本頁說明如何使用 projects.locations.datasets.fhirStores.importprojects.locations.datasets.fhirStores.export 方法,將 FHIR 資源匯出及匯入 Cloud Storage。

視 FHIR 資料的格式而定,如要將資料載入 FHIR 儲存庫,您可以使用 projects.locations.datasets.fhirStores.import 方法或 projects.locations.datasets.fhirStores.fhir.executeBundle 方法。如需選擇方法的指引,請參閱「FHIR 匯入」。

設定 Cloud Storage 權限

如要將 FHIR 資源匯出及匯入 Cloud Storage,您必須先授予 Cloud Healthcare Service Agent 服務帳戶額外權限。詳情請參閱「FHIR 存放區 Cloud Storage 權限」。

產生模擬病患資料

Synthea™ 是一種模擬器,可產生病患群體資料。如果未使用 Synthea™ 產生病患族群資料,請跳至「匯入 FHIR 資源」或「匯出 FHIR 資源」。

您只能匯入 FHIR 儲存庫設定接受的版本資料。

如要下載及安裝 Synthea™,請完成下列步驟:

  1. 從 GitHub 複製 Synthea™ 工具存放區:

    git clone https://github.com/synthetichealth/synthea.git
    
  2. 完成安裝步驟

請繼續前往下列其中一個章節,為特定 FHIR 版本產生資料:

為 R4 產生模擬病患資料

根據預設,產生的 Synthea™ 資料會使用 FHIR R4 JSON 表示法來表示資源。如要產生 Synthea™ FHIR R4 資料並匯入 Cloud Healthcare API FHIR 儲存庫,請完成下列步驟:

  1. 按照操作說明產生合成病患資料。產生的資料會輸出至 FHIR R4 的 synthea/output/fhir_r4

  2. 將產生的資料複製到 Cloud Storage 值區,以便匯入 Cloud Healthcare API FHIR 儲存庫。舉例來說,如要將資料複製到現有 Cloud Storage bucket 中名為 synthea-data 的目錄,請從 synthea 目錄執行下列 gcloud storage cp 指令:

    gcloud storage cp output/fhir_r4/* gs://BUCKET/synthea-data
  3. 按照匯入 FHIR 資源的操作說明進行。

為 DSTU2 或 STU3 產生模擬病患資料

如要產生 Synthea™ FHIR DSTU2 或 STU3 資料,並匯入 Cloud Healthcare API FHIR 儲存庫,請完成下列步驟:

  1. synthea 目錄中,使用文字編輯器開啟 src/main/resources/synthea.properties 檔案,然後視您要產生 DSTU2 或 STU3 資料,進行下列變更。

    如要產生 FHIR STU3 資料,請按照下列步驟操作:

    • 將所有 *.fhir.export*.fhir_dstu2.export 值設為 false
    • 將所有 *.fhir_stu3.export 值設為 true

    如要產生 FHIR DSTU2 資料,請按照下列步驟操作:

    • 將所有 *.fhir.export*.fhir_stu3.export 值設為 false
    • 將所有 *.fhir_dstu2.export 值設為 true

    舉例來說,如要產生 FHIR STU3 資料,請執行下列指令:

    exporter.fhir.export = false
    exporter.fhir_stu3.export = true
    exporter.fhir_dstu2.export = false
    
    exporter.hospital.fhir.export = false
    exporter.hospital.fhir_stu3.export = true
    exporter.hospital.fhir_dstu2.export = false
    
    exporter.practitioner.fhir.export = false
    exporter.practitioner.fhir_stu3.export = true
    exporter.practitioner.fhir_dstu2.export = false
  2. 按照操作說明產生合成病患資料。產生的資料會輸出至 FHIR STU3 的 synthea/output/fhir_stu3 或 FHIR DSTU2 的 synthea/output/fhir_dstu2 目錄。

  3. 將產生的資料複製到 Cloud Storage 值區,以便匯入 Cloud Healthcare API FHIR 儲存庫。舉例來說,如要將資料複製到現有 Cloud Storage bucket 中名為 synthea-data 的目錄,請從 synthea 目錄執行下列 gcloud storage cp 指令:

    gcloud storage cp output/fhir_stu3/* gs://BUCKET/synthea-data
  4. 按照匯入 FHIR 資源的操作說明進行。

匯入 FHIR 資源

設定匯入要求主體時,請將 ContentStructure 設為下列任一值:

  • CONTENT_STRUCTURE_UNSPECIFIED
  • BUNDLE:來源檔案包含一或多行以換行符號分隔的 JSON (ndjson)。每一行都是一個套件,內含一或多個資源。如未指定 ContentStructure,則預設為 BUNDLE
  • RESOURCE:來源檔案包含一或多行以換行符號分隔的 JSON (ndjson)。每一行都是單一資源。
  • BUNDLE_PRETTY:整個來源檔案就是一個 JSON 組合包。JSON 可以跨越多行。
  • RESOURCE_PRETTY:整個來源檔案就是一個 JSON 資源。JSON 可以跨越多行。

舉例來說,假設您要匯入名為 resources.ndjson 的檔案,其中包含下列內容:

{"class":{"code":"IMP","display":"inpatient encounter","system":"http://hl7.org/fhir/v3/ActCode"},"id":"6090e773-3e91-40a7-8fce-1e22f6774c29","reason":[{"text":"The patient had an abnormal heart rate. She was concerned about this."}],"resourceType":"Encounter","status":"finished","subject":{"reference":"Patient/2938bb9e-1f16-429e-8d44-9508ab0e4151"}}
{"class":{"code":"IMP","display":"inpatient encounter","system":"http://hl7.org/fhir/v3/ActCode"},"id":"7101f884-4f02-51b8-9gdf-2f33g7885d30","reason":[{"text":"The patient was experiencing recurrent fevers."}],"resourceType":"Encounter","status":"finished","subject":{"reference":"Patient/3049cc0f-2g27-530f-9e55-0619bc1f5262"}}
{"birthDate":"1970-01-01","gender":"female","id":"2938bb9e-1f16-429e-8d44-9508ab0e4151","name":[{"family":"Smith","given":["Darcy"],"use":"official"}],"resourceType":"Patient"}

這個檔案包含兩個 Encounter 資源和一個 Patient 資源。每個資源都位於不同行,因此請將 ContentStructure 設為 RESOURCE

如果 ContentStructure 與資料格式不符,資料可能無法正確匯入,甚至無法匯入。舉例來說,除非在匯入要求中將 ContentStructure 設為 RESOURCE,否則上述範例檔案不會正確匯入。

下列範例說明如何從 Cloud Storage 值區匯入 FHIR 資源。

控制台

如要從 Cloud Storage 值區匯入 FHIR 資源,請完成下列步驟:

  1. 在 Google Cloud 控制台中,前往「資料集」頁面。
    前往「資料集」
  2. 按一下包含 FHIR 儲存庫的資料集,您要將 FHIR 資源匯入該儲存庫。
  3. 在資料儲存庫清單中,從 FHIR 儲存庫的「Actions」(動作)清單中選擇「Import」(匯入)

    系統會顯示「Import to FHIR store」(匯入至 FHIR 儲存庫) 頁面。
  4. 在「Project」(專案) 清單中,選取 Cloud Storage 專案。
  5. 在「Location」(位置) 清單中,選取 Cloud Storage bucket。
  6. 在「FHIR import settings」(FHIR 匯入設定) 中,選取相關內容結構。
  7. 按一下「匯入」即可匯入 FHIR 資源。
  8. 如要追蹤作業狀態,請按一下「Operations」(作業) 分頁標籤。作業完成後,會顯示下列指標:
    • 「長時間執行的作業狀態」部分在「OK」標題下方顯示綠色勾號。
    • 「總覽」部分會顯示綠色勾號和「OK」指標,與作業 ID 位於同一列。
    如果發生任何錯誤,請按一下「動作」,然後點選「在 Cloud Logging 中查看詳細資料」

gcloud

如要將 FHIR 資源匯入 FHIR 儲存庫,請使用 gcloud healthcare fhir-stores import gcs 指令。指定下列資訊:

  • 父項資料集的名稱
  • FHIR 儲存庫名稱
  • Cloud Storage bucket 中的物件位置。bucket 內的檔案位置是任意的,不一定要完全符合下列範例指定的格式。在 Cloud Storage 中指定 FHIR 資源的位置時,可以使用萬用字元從一或多個目錄匯入多個檔案。 支援的萬用字元如下:
    • 使用 * 可比對 0 個以上的非分隔符字元。舉例來說,gs://BUCKET/DIRECTORY/Example*.ndjson 會與 DIRECTORY 中的 Example.ndjson 和 Example22.ndjson 相符。
    • 使用 ** 來比對 0 個以上的字元 (包括分隔符)。必須用於路徑結尾,且路徑中不得有其他萬用字元。也可以搭配檔案名稱副檔名 (例如 .ndjson) 使用,匯入指定目錄及其子目錄中,副檔名為該名稱的所有檔案。舉例來說,gs://BUCKET/DIRECTORY/**.ndjson 會匯入 DIRECTORY 及其子目錄中副檔名為 .ndjson 的所有檔案。
    • 使用「?」比對 1 個字元。舉例來說,gs://BUCKET/DIRECTORY/Example?.ndjson 與 Example1.ndjson 相符,但與 Example.ndjson 或 Example01.ndjson 不相符。

下列範例顯示 gcloud healthcare fhir-stores import gcs 指令。

gcloud healthcare fhir-stores import gcs FHIR_STORE_ID \
  --dataset=DATASET_ID \
  --location=LOCATION \
  --gcs-uri=gs://BUCKET/DIRECTORY/FHIR_RESOURCE_NAME.ndjson

如要指定 FHIR 來源檔案的結構,請使用 --content-structure 標記。

指令列會顯示作業 ID,作業完成後則會顯示 done

Request issued for: [FHIR_STORE_ID]
Waiting for operation [OPERATION_ID] to complete...done.
name: projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID

如要查看作業的詳細資料,請執行 gcloud healthcare operations describe 指令,並提供回應中的 OPERATION_ID

gcloud healthcare operations describe OPERATION_ID \
  --dataset=DATASET_ID

回覆中會包含 done: true

done: true
metadata:
'@type': type.googleapis.com/google.cloud.healthcare.v1.OperationMetadata
apiMethodName: google.cloud.healthcare.v1.fhir.FhirService.ImportResources
createTime: 'CREATE_TIME'
endTime: 'END_TIME'
logsUrl: https://console.cloud.google.com/logs/query/CLOUD_LOGGING_URL,
counter:
  success: 'SUCCESS_COUNT' 
name: projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID
response:
'@type': type.googleapis.com/google.cloud.healthcare.v1.fhir.rest.ImportResourcesResponse
fhirStore: projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID

API

如要將 FHIR 資源匯入 FHIR 儲存庫,請使用 projects.locations.datasets.fhirStores.import 方法。

  • Bucket 中的檔案位置是任意的,不一定要完全符合下列範例中指定的格式。
  • 在 Cloud Storage 中指定 FHIR 資源的位置時,可以使用萬用字元從一或多個目錄匯入多個檔案。 支援的萬用字元如下:
    • 使用 * 可比對 0 個以上的非分隔符字元。舉例來說,gs://BUCKET/DIRECTORY/Example*.ndjson 會與 DIRECTORY 中的 Example.ndjson 和 Example22.ndjson 相符。
    • 使用 ** 來比對 0 個以上的字元 (包括分隔符)。必須用於路徑結尾,且路徑中不得有其他萬用字元。也可以搭配檔案名稱副檔名 (例如 .ndjson) 使用,匯入指定目錄及其子目錄中,副檔名為該名稱的所有檔案。舉例來說,gs://BUCKET/DIRECTORY/**.ndjson 會匯入 DIRECTORY 及其子目錄中副檔名為 .ndjson 的所有檔案。
    • 使用「?」比對 1 個字元。舉例來說,gs://BUCKET/DIRECTORY/Example?.ndjson 與 Example1.ndjson 相符,但與 Example.ndjson 或 Example01.ndjson 不相符。

curl

如要將 FHIR 資源匯入 FHIR 儲存庫,請提出 POST 要求,並指定下列資訊:

  • 父項資料集的名稱
  • FHIR 儲存庫名稱
  • Cloud Storage bucket 中物件的位置
  • 存取權杖

以下範例說明如何使用 curl,透過 POST 要求匯入單一檔案。

curl -X POST \
    -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
    -H "Content-Type: application/json; charset=utf-8" \
    --data "{
      'contentStructure': 'CONTENT_STRUCTURE',
      'gcsSource': {
        'uri': 'gs://BUCKET/DIRECTORY/FHIR_RESOURCE_FILE'
      }
    }" "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID:import"

如果要求成功,伺服器會以 JSON 格式傳回回應:

{
  "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID"
}

回應會包含作業名稱。如要追蹤作業狀態,可以使用 Operation get 方法

curl -X GET \
    -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
    "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID"

如果要求成功,伺服器會以 JSON 格式傳回作業狀態的回應:

{
  "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID",
  "metadata": {
    "@type": "type.googleapis.com/google.cloud.healthcare.v1.OperationMetadata",
    "apiMethodName": "google.cloud.healthcare.v1.fhir.FhirService.ImportResources",
    "createTime": "CREATE_TIME",
    "endTime": "END_TIME",
    "logsUrl": "https://console.cloud.google.com/logs/query/CLOUD_LOGGING_URL",
    "counter": {
      "success": "SUCCESS_COUNT"
    }
  },
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.cloud.healthcare.v1.fhir.rest.ImportResourcesResponse",
  }
}

PowerShell

如要將 FHIR 資源匯入 FHIR 儲存庫,請提出 POST 要求,並指定下列資訊:

  • 父項資料集的名稱
  • FHIR 儲存庫名稱
  • Cloud Storage bucket 中物件的位置
  • 存取權杖

下列範例顯示如何使用 Windows PowerShell 提出 POST 要求。

$cred = gcloud auth application-default print-access-token
$headers = @{ Authorization = "Bearer $cred" }

Invoke-WebRequest `
  -Method Post `
  -Headers $headers `
  -ContentType: "application/json; charset=utf-8" `
  -Body "{
    'contentStructure': 'CONTENT_STRUCTURE',
    'gcsSource': {
      'uri': 'gs://BUCKET/DIRECTORY/FHIR_RESOURCE_FILE'
    }
  }" `
  -Uri "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID:import" | Select-Object -Expand Content

如果要求成功,伺服器會以 JSON 格式傳回回應:

{
  "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID"
}

回應會包含作業名稱。如要追蹤作業狀態,可以使用 Operation get 方法

$cred = gcloud auth application-default print-access-token
$headers = @{ Authorization = "Bearer $cred" }

Invoke-WebRequest `
  -Method Get `
  -Headers $headers `
  -Uri "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID" | Select-Object -Expand Content

如果要求成功,伺服器會以 JSON 格式傳回作業狀態的回應:

{
  "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID",
  "metadata": {
    "@type": "type.googleapis.com/google.cloud.healthcare.v1.OperationMetadata",
    "apiMethodName": "google.cloud.healthcare.v1.fhir.FhirService.ImportResources",
    "createTime": "CREATE_TIME",
    "endTime": "END_TIME",
    "logsUrl": "https://console.cloud.google.com/logs/query/CLOUD_LOGGING_URL",
    "counter": {
      "success": "SUCCESS_COUNT"
    }
  },
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.cloud.healthcare.v1.fhir.rest.ImportResourcesResponse",
  }
}

Go

import (
	"context"
	"fmt"
	"io"
	"time"

	healthcare "google.golang.org/api/healthcare/v1"
)

// importsFHIRResource imports an FHIR resource.
func importFHIRResource(w io.Writer, projectID, location, datasetID, fhirStoreID, gcsURI string) error {
	ctx := context.Background()

	healthcareService, err := healthcare.NewService(ctx)
	if err != nil {
		return fmt.Errorf("healthcare.NewService: %w", err)
	}

	storesService := healthcareService.Projects.Locations.Datasets.FhirStores

	name := fmt.Sprintf("projects/%s/locations/%s/datasets/%s/fhirStores/%s", projectID, location, datasetID, fhirStoreID)
	req := &healthcare.ImportResourcesRequest{
		ContentStructure: "RESOURCE",
		GcsSource: &healthcare.GoogleCloudHealthcareV1FhirGcsSource{
			Uri: gcsURI,
		},
	}

	op, err := storesService.Import(name, req).Do()
	if err != nil {
		return fmt.Errorf("Import: %w", err)
	}

	operationsService := healthcareService.Projects.Locations.Datasets.Operations
	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-ticker.C:
			newOp, err := operationsService.Get(op.Name).Do()
			if err != nil {
				return fmt.Errorf("operationsService.Get(%q): %v", op.Name, err)
			}
			if newOp.Done {
				if newOp.Error != nil {
					return fmt.Errorf("import operation %q completed with error: %s", op.Name, newOp.Error.Details)
				}
				return nil
			}
		}
	}
}

Java

import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.healthcare.v1.CloudHealthcare;
import com.google.api.services.healthcare.v1.CloudHealthcare.Projects.Locations.Datasets.FhirStores;
import com.google.api.services.healthcare.v1.CloudHealthcareScopes;
import com.google.api.services.healthcare.v1.model.GoogleCloudHealthcareV1FhirGcsSource;
import com.google.api.services.healthcare.v1.model.ImportResourcesRequest;
import com.google.api.services.healthcare.v1.model.Operation;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.IOException;
import java.util.Collections;

public class FhirStoreImport {
  private static final String FHIR_NAME = "projects/%s/locations/%s/datasets/%s/fhirStores/%s";
  private static final JsonFactory JSON_FACTORY = new GsonFactory();
  private static final NetHttpTransport HTTP_TRANSPORT = new NetHttpTransport();

  public static void fhirStoreImport(String fhirStoreName, String gcsUri) throws IOException {
    // String fhirStoreName =
    //    String.format(
    //        FHIR_NAME, "your-project-id", "your-region-id", "your-dataset-id", "your-fhir-id");
    // String gcsUri = "gs://your-bucket-id/path/to/destination/dir"

    // Initialize the client, which will be used to interact with the service.
    CloudHealthcare client = createClient();

    // Configure where the store should be imported from.
    GoogleCloudHealthcareV1FhirGcsSource gcsSource =
        new GoogleCloudHealthcareV1FhirGcsSource().setUri(gcsUri);
    ImportResourcesRequest importRequest = new ImportResourcesRequest().setGcsSource(gcsSource);

    // Create request and configure any parameters.
    FhirStores.CloudHealthcareImport request =
        client
            .projects()
            .locations()
            .datasets()
            .fhirStores()
            .healthcareImport(fhirStoreName, importRequest);

    // Execute the request, wait for the operation to complete, and process the results.
    try {
      Operation operation = request.execute();
      while (operation.getDone() == null || !operation.getDone()) {
        // Update the status of the operation with another request.
        Thread.sleep(500); // Pause for 500ms between requests.
        operation =
            client
                .projects()
                .locations()
                .datasets()
                .operations()
                .get(operation.getName())
                .execute();
      }
      System.out.println("FHIR store import complete: " + operation.getResponse());
    } catch (Exception ex) {
      System.out.printf("Error during request execution: %s", ex.toString());
      ex.printStackTrace(System.out);
    }
  }

  private static CloudHealthcare createClient() throws IOException {
    // Use Application Default Credentials (ADC) to authenticate the requests
    // For more information see https://cloud.google.com/docs/authentication/production
    GoogleCredentials credential =
        GoogleCredentials.getApplicationDefault()
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));

    // Create a HttpRequestInitializer, which will provide a baseline configuration to all requests.
    HttpRequestInitializer requestInitializer =
        request -> {
          new HttpCredentialsAdapter(credential).initialize(request);
          request.setConnectTimeout(60000); // 1 minute connect timeout
          request.setReadTimeout(60000); // 1 minute read timeout
        };

    // Build the client for interacting with the service.
    return new CloudHealthcare.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer)
        .setApplicationName("your-application-name")
        .build();
  }
}

Node.js

const google = require('@googleapis/healthcare');
const healthcare = google.healthcare({
  version: 'v1',
  auth: new google.auth.GoogleAuth({
    scopes: ['https://www.googleapis.com/auth/cloud-platform'],
  }),
});
const sleep = ms => {
  return new Promise(resolve => setTimeout(resolve, ms));
};

const importFhirResources = async () => {
  // TODO(developer): uncomment these lines before running the sample
  // const cloudRegion = 'us-central1';
  // const projectId = 'adjective-noun-123';
  // const datasetId = 'my-dataset';
  // const fhirStoreId = 'my-fhir-store';
  // const gcsUri = 'my-bucket/my-directory/*.json'
  const name = `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/fhirStores/${fhirStoreId}`;
  const request = {
    name,
    resource: {
      contentStructure: 'RESOURCE',
      gcsSource: {
        uri: `gs://${gcsUri}`,
      },
    },
  };

  const operation =
    await healthcare.projects.locations.datasets.fhirStores.import(request);
  const operationName = operation.data.name;

  const operationRequest = {name: operationName};

  // Wait twenty seconds for the LRO to finish.
  await sleep(20000);

  // Check the LRO's status
  const operationStatus =
    await healthcare.projects.locations.datasets.operations.get(
      operationRequest
    );

  const success = operationStatus.data.metadata.counter.success;

  if (typeof success !== 'undefined') {
    console.log(
      `Import FHIR resources succeeded. ${success} resources imported.`
    );
  } else {
    console.log(
      'Imported FHIR resources failed. Details available in Cloud Logging at the following URL:\n',
      operationStatus.data.metadata.logsUrl
    );
  }
};

importFhirResources();

Python

def import_fhir_resources(project_id, location, dataset_id, fhir_store_id, gcs_uri):
    """Import resources into the FHIR store by copying them from the
    specified source.

    See https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/healthcare/api-client/v1/fhir
    before running the sample."""
    # Imports the Google API Discovery Service.
    from googleapiclient import discovery

    api_version = "v1"
    service_name = "healthcare"
    # Instantiates an authorized API client by discovering the Healthcare API
    # and using GOOGLE_APPLICATION_CREDENTIALS environment variable.
    client = discovery.build(service_name, api_version)

    # TODO(developer): Uncomment these lines and replace with your values.
    # project_id = 'my-project'  # replace with your GCP project ID
    # location = 'us-central1'  # replace with the parent dataset's location
    # dataset_id = 'my-dataset'  # replace with the parent dataset's ID
    # fhir_store_id = 'my-fhir-store'  # replace with the FHIR store ID
    # gcs_uri = 'my-bucket'  # replace with a Cloud Storage bucket
    fhir_store_parent = "projects/{}/locations/{}/datasets/{}".format(
        project_id, location, dataset_id
    )
    fhir_store_name = f"{fhir_store_parent}/fhirStores/{fhir_store_id}"

    body = {
        "contentStructure": "CONTENT_STRUCTURE_UNSPECIFIED",
        "gcsSource": {"uri": f"gs://{gcs_uri}"},
    }

    # Escape "import()" method keyword because "import"
    # is a reserved keyword in Python
    request = (
        client.projects()
        .locations()
        .datasets()
        .fhirStores()
        .import_(name=fhir_store_name, body=body)
    )

    response = request.execute()
    print(f"Imported FHIR resources: {gcs_uri}")

    return response

匯出 FHIR 資源

下列範例說明如何將 FHIR 資源匯出至 Cloud Storage 值區。從 FHIR 儲存庫匯出 FHIR 資源時,系統會匯出 FHIR 儲存庫中的所有資源。

如果您定期匯出 FHIR 資源,建議您以遞增方式匯出資料。如需操作說明,請參閱「增量匯出」。

匯出期間,Cloud Healthcare API 會為 FHIR 儲存庫中的每個資源類型建立一個檔案。檔案名稱包含作業 ID 和資源類型,並以底線分隔。每個檔案都包含以換行符分隔的 JSON,其中每行都是 FHIR 資源,對應於檔案名稱中的資源類型。舉例來說,如果您匯出多筆病患記錄,輸出檔案的名稱會類似 1264567891234567_Patient,且 FHIR 存放區中的每個病患資源都會佔一行。

控制台

如要將 FHIR 資源匯出至 Cloud Storage,請完成下列步驟:

  1. 在 Google Cloud 控制台中,前往「資料集」頁面。
    前往「資料集」
  2. 按一下包含 FHIR 儲存庫的資料集,您要從該儲存庫匯出 FHIR 資源。
  3. 在資料儲存庫清單中,從 FHIR 儲存庫的「Actions」(動作)清單中選擇「Export」(匯出)

    系統會顯示「Export FHIR resources」(匯出 FHIR 資源) 頁面。
  4. 選取「Google Cloud Storage Bucket」
  5. 在「Project」(專案) 清單中,選取 Cloud Storage 專案。
  6. 在「Location」(位置) 清單中,選取 Cloud Storage bucket。
  7. 按一下「匯出」,將 FHIR 資源匯出至 Cloud Storage 中定義的位置。
  8. 如要追蹤作業狀態,請按一下「Operations」(作業) 分頁標籤。作業完成後,會顯示下列指標:
    • 「長時間執行的作業狀態」部分在「OK」標題下方顯示綠色勾號。
    • 「總覽」部分會顯示綠色勾號和「OK」指標,與作業 ID 位於同一列。
    如果發生任何錯誤,請按一下「動作」,然後點選「在 Cloud Logging 中查看詳細資料」

gcloud

如要將 FHIR 資源匯出至 Cloud Storage bucket,請使用 gcloud healthcare fhir-stores export gcs 指令。指定下列資訊:

  • 父項資料集的名稱
  • FHIR 儲存庫名稱
  • 父項專案名稱
  • 目的地 Cloud Storage 值區或目錄。請寫入 Cloud Storage 值區或目錄,而非物件,因為 Cloud Healthcare API 會為每個資源類型建立一個物件。每個物件都是以換行符號分隔的 JSON,每行都是 FHIR 資源。如果您指定的目錄不存在,系統會自動建立。
  • 選用標記 --resource-type,只匯出特定資源類型,定義為一或多個 FHIR 資源類型 (以半形逗號分隔)
  • 選用旗標 --since,只匯出在特定時間後更新的資源,定義為 YYYY-MM-DDThh:mm:ss.sss+zz:zz

下列範例顯示 gcloud healthcare fhir-stores export gcs 指令。

gcloud healthcare fhir-stores export gcs FHIR_STORE_ID \
  --dataset=DATASET_ID \
  --location=LOCATION \
  --project=PROJECT_ID
  --gcs-uri=gs://BUCKET/DIRECTORY

指令列會顯示作業 ID:

Waiting for operation [OPERATION_ID] to complete...done.
name: projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID

如要查看作業狀態,請執行 gcloud healthcare operations describe 指令,並提供回應中的 OPERATION_ID

gcloud healthcare operations describe OPERATION_ID \
  --dataset=DATASET_ID

指令完成後,回應中會包含 done

metadata:
'@type': type.googleapis.com/google.cloud.healthcare.v1.OperationMetadata
apiMethodName: google.cloud.healthcare.v1.fhir.FhirService.ExportFhirData
createTime: "CREATE_TIME"
endTime: "END_TIME"
name: projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID
response:
'@type': type.googleapis.com/google.cloud.healthcare.v1.fhir.rest.ExportResourcesResponse
fhirStore: projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID
resourceCount: 'RESOURCE_COUNT'

API

如要匯出 FHIR 資源,請使用 projects.locations.datasets.fhirStores.export 方法。

  • 請寫入 Cloud Storage bucket 或目錄,而非物件,因為 Cloud Healthcare API 會為每個資源類型建立一個以換行符號分隔的 JSON 檔案。在每個 JSON 檔案中,每一行都是 FHIR 資源。
  • 如果指令指定的目錄不存在,系統會建立該目錄。

curl

如要匯出 FHIR 資源,請發出 POST 要求並指定下列資訊:

  • 父項資料集的名稱
  • FHIR 儲存庫名稱
  • 目標 Cloud Storage 值區
  • 存取權杖
  • 選用欄位 _type,僅匯出特定資源類型,定義為以逗號分隔的一或多個 FHIR 資源類型清單
  • 選用欄位 _since,只會匯出在特定時間後更新的資源,該時間定義為 YYYY-MM-DDThh:mm:ss.sss+zz:zz

以下範例顯示如何使用 curl 發出 POST 要求。

curl -X POST \
    -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
    -H "Content-Type: application/json; charset=utf-8" \
    --data "{
      'gcsDestination': {
        'uriPrefix': 'gs://BUCKET/DIRECTORY'
      },
    }" "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID:export"

如果要求成功,伺服器會以 JSON 格式傳回回應:

{
  "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID"
}

回應會包含作業名稱。如要追蹤作業狀態,可以使用 Operation get 方法

curl -X GET \
    -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
    "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID"

如果要求成功,伺服器會以 JSON 格式傳回作業狀態的回應:

{
  "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID",
  "metadata": {
    "@type": "type.googleapis.com/google.cloud.healthcare.v1.OperationMetadata",
    "apiMethodName": "google.cloud.healthcare.v1.fhir.FhirService.ExportResources",
    "createTime": "CREATE_TIME",
    "endTime": "END_TIME",
    "logsUrl": "https://console.cloud.google.com/logs/query/CLOUD_LOGGING_URL",
    "counter": {
      "success": "SUCCESS_COUNT"
    }
  },
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.cloud.healthcare.v1.fhir.rest.ExportResourcesResponse",
  }
}

PowerShell

如要匯出 FHIR 資源,請發出 POST 要求並指定下列資訊:

  • 父項資料集的名稱
  • FHIR 儲存庫名稱
  • 目的地 Cloud Storage 值區或目錄。請寫入 Cloud Storage 值區或目錄,而非物件,因為 Cloud Healthcare API 會為每個資源類型建立一個物件。每個物件都是以換行符號分隔的 JSON,每行都是 FHIR 資源。
  • 存取權杖
  • 選用欄位 _type,僅匯出特定資源類型,定義為以逗號分隔的一或多個 FHIR 資源類型清單
  • 選用欄位 _since,只會匯出在特定時間後更新的資源,該時間定義為 YYYY-MM-DDThh:mm:ss.sss+zz:zz

下列範例顯示如何使用 Windows PowerShell 提出 POST 要求。

$cred = gcloud auth application-default print-access-token
$headers = @{ Authorization = "Bearer $cred" }

Invoke-WebRequest `
  -Method Post `
  -Headers $headers `
  -ContentType: "application/json; charset=utf-8" `
  -Body "{
    'gcsDestination': {
      'uriPrefix': 'gs://BUCKET/DIRECTORY'
    },
  }" `
  -Uri "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID:export" | Select-Object -Expand Content

如果要求成功,伺服器會以 JSON 格式傳回回應:

{
  "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID"
}

回應會包含作業名稱。如要追蹤作業狀態,可以使用 Operation get 方法

$cred = gcloud auth application-default print-access-token
$headers = @{ Authorization = "Bearer $cred" }

Invoke-WebRequest `
  -Method Get `
  -Headers $headers `
  -Uri "https://healthcare.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID" | Select-Object -Expand Content

如果要求成功,伺服器會以 JSON 格式傳回作業狀態的回應:

{
  "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID",
  "metadata": {
    "@type": "type.googleapis.com/google.cloud.healthcare.v1.OperationMetadata",
    "apiMethodName": "google.cloud.healthcare.v1.fhir.FhirService.ExportResources",
    "createTime": "CREATE_TIME",
    "endTime": "END_TIME",
    "logsUrl": "https://console.cloud.google.com/logs/query/CLOUD_LOGGING_URL",
    "counter": {
      "success": "SUCCESS_COUNT"
    }
  },
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.cloud.healthcare.v1.fhir.rest.ExportResourcesResponse",
  }
}

Go

import (
	"context"
	"fmt"
	"io"
	"time"

	healthcare "google.golang.org/api/healthcare/v1"
)

// exportFHIRResource exports the resources in the FHIR store.
func exportFHIRResource(w io.Writer, projectID, location, datasetID, fhirStoreID, gcsURIPrefix string) error {
	ctx := context.Background()

	healthcareService, err := healthcare.NewService(ctx)
	if err != nil {
		return fmt.Errorf("healthcare.NewService: %w", err)
	}

	storesService := healthcareService.Projects.Locations.Datasets.FhirStores

	name := fmt.Sprintf("projects/%s/locations/%s/datasets/%s/fhirStores/%s", projectID, location, datasetID, fhirStoreID)
	req := &healthcare.ExportResourcesRequest{
		GcsDestination: &healthcare.GoogleCloudHealthcareV1FhirGcsDestination{
			UriPrefix: gcsURIPrefix,
		},
	}

	op, err := storesService.Export(name, req).Do()
	if err != nil {
		return fmt.Errorf("Export: %w", err)
	}

	operationsService := healthcareService.Projects.Locations.Datasets.Operations
	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-ticker.C:
			newOp, err := operationsService.Get(op.Name).Do()
			if err != nil {
				return fmt.Errorf("operationsService.Get(%q): %v", op.Name, err)
			}
			if newOp.Done {
				if newOp.Error != nil {
					return fmt.Errorf("export operation %q completed with error: %v", op.Name, newOp.Error)
				}
				return nil
			}
		}
	}
}

Java

import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.healthcare.v1.CloudHealthcare;
import com.google.api.services.healthcare.v1.CloudHealthcare.Projects.Locations.Datasets.FhirStores;
import com.google.api.services.healthcare.v1.CloudHealthcareScopes;
import com.google.api.services.healthcare.v1.model.ExportResourcesRequest;
import com.google.api.services.healthcare.v1.model.GoogleCloudHealthcareV1FhirGcsDestination;
import com.google.api.services.healthcare.v1.model.Operation;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.IOException;
import java.util.Collections;

public class FhirStoreExport {
  private static final String FHIR_NAME = "projects/%s/locations/%s/datasets/%s/fhirStores/%s";
  private static final JsonFactory JSON_FACTORY = new GsonFactory();
  private static final NetHttpTransport HTTP_TRANSPORT = new NetHttpTransport();

  public static void fhirStoreExport(String fhirStoreName, String gcsUri) throws IOException {
    // String fhirStoreName =
    //    String.format(
    //        FHIR_NAME, "your-project-id", "your-region-id", "your-dataset-id", "your-fhir-id");
    // String gcsUri = "gs://your-bucket-id/path/to/destination/dir"

    // Initialize the client, which will be used to interact with the service.
    CloudHealthcare client = createClient();

    // Configure where the store will be exported too.
    GoogleCloudHealthcareV1FhirGcsDestination gcsDestination =
        new GoogleCloudHealthcareV1FhirGcsDestination().setUriPrefix(gcsUri);
    ExportResourcesRequest exportRequest =
        new ExportResourcesRequest().setGcsDestination(gcsDestination);

    // Create request and configure any parameters.
    FhirStores.Export request =
        client.projects().locations().datasets().fhirStores().export(fhirStoreName, exportRequest);

    // Execute the request, wait for the operation to complete, and process the results.
    try {
      Operation operation = request.execute();
      while (operation.getDone() == null || !operation.getDone()) {
        // Update the status of the operation with another request.
        Thread.sleep(500); // Pause for 500ms between requests.
        operation =
            client
                .projects()
                .locations()
                .datasets()
                .operations()
                .get(operation.getName())
                .execute();
      }
      System.out.println("Fhir store export complete." + operation.getResponse());
    } catch (Exception ex) {
      System.out.printf("Error during request execution: %s", ex.toString());
      ex.printStackTrace(System.out);
    }
  }

  private static CloudHealthcare createClient() throws IOException {
    // Use Application Default Credentials (ADC) to authenticate the requests
    // For more information see https://cloud.google.com/docs/authentication/production
    GoogleCredentials credential =
        GoogleCredentials.getApplicationDefault()
            .createScoped(Collections.singleton(CloudHealthcareScopes.CLOUD_PLATFORM));

    // Create a HttpRequestInitializer, which will provide a baseline configuration to all requests.
    HttpRequestInitializer requestInitializer =
        request -> {
          new HttpCredentialsAdapter(credential).initialize(request);
          request.setConnectTimeout(60000); // 1 minute connect timeout
          request.setReadTimeout(60000); // 1 minute read timeout
        };

    // Build the client for interacting with the service.
    return new CloudHealthcare.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer)
        .setApplicationName("your-application-name")
        .build();
  }
}

Node.js

const google = require('@googleapis/healthcare');
const healthcare = google.healthcare({
  version: 'v1',
  auth: new google.auth.GoogleAuth({
    scopes: ['https://www.googleapis.com/auth/cloud-platform'],
  }),
});
const sleep = ms => {
  return new Promise(resolve => setTimeout(resolve, ms));
};

const exportFhirResourcesGcs = async () => {
  // TODO(developer): uncomment these lines before running the sample
  // const cloudRegion = 'us-central1';
  // const projectId = 'adjective-noun-123';
  // const datasetId = 'my-dataset';
  // const fhirStoreId = 'my-fhir-store';
  // const gcsUri = 'my-bucket/my-directory'
  const name = `projects/${projectId}/locations/${cloudRegion}/datasets/${datasetId}/fhirStores/${fhirStoreId}`;
  const request = {
    name,
    resource: {
      gcsDestination: {
        // The destination location in Cloud Storage for the FHIR resources
        uriPrefix: `gs://${gcsUri}`,
      },
    },
  };

  const operation =
    await healthcare.projects.locations.datasets.fhirStores.export(request);
  const operationName = operation.data.name;

  // Wait ten seconds for the LRO to finish
  await sleep(10000);

  // Check the LRO's status
  const operationStatus =
    await healthcare.projects.locations.datasets.operations.get({
      name: operationName,
    });

  if (typeof operationStatus.data.metadata.counter !== 'undefined') {
    console.log('Exported FHIR resources successfully');
  } else {
    console.log('Export failed');
  }
};

exportFhirResourcesGcs();

Python

def export_fhir_store_gcs(project_id, location, dataset_id, fhir_store_id, gcs_uri):
    """Export resources to a Google Cloud Storage bucket by copying
    them from the FHIR store.

    See https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/healthcare/api-client/v1/fhir
    before running the sample."""
    # Imports the Google API Discovery Service.
    from googleapiclient import discovery

    api_version = "v1"
    service_name = "healthcare"
    # Instantiates an authorized API client by discovering the Healthcare API
    # and using GOOGLE_APPLICATION_CREDENTIALS environment variable.
    client = discovery.build(service_name, api_version)

    # TODO(developer): Uncomment these lines and replace with your values.
    # project_id = 'my-project'  # replace with your GCP project ID
    # location = 'us-central1'  # replace with the parent dataset's location
    # dataset_id = 'my-dataset'  # replace with the parent dataset's ID
    # fhir_store_id = 'my-fhir-store' # replace with the FHIR store ID
    # gcs_uri = 'my-bucket' # replace with a Cloud Storage bucket
    fhir_store_parent = "projects/{}/locations/{}/datasets/{}".format(
        project_id, location, dataset_id
    )
    fhir_store_name = f"{fhir_store_parent}/fhirStores/{fhir_store_id}"

    body = {"gcsDestination": {"uriPrefix": f"gs://{gcs_uri}/fhir_export"}}

    request = (
        client.projects()
        .locations()
        .datasets()
        .fhirStores()
        .export(name=fhir_store_name, body=body)
    )

    response = request.execute()
    print(f"Exported FHIR resources to bucket: gs://{gcs_uri}")

    return response

增量匯出

您可以指定時間戳記,只匯出先前成功匯出後新增至 FHIR 儲存庫的 FHIR 資源。這樣一來,您就能提升效能,避免重新匯出整個 FHIR 存放區的成本,同時確保匯出的資料一律為最新狀態。

呼叫 fhirStores.export 時,請在 _since 欄位中指定時間戳記。

排解 FHIR 匯入和匯出要求問題

如果 FHIR 匯入或匯出要求發生錯誤,系統會將錯誤記錄到 Cloud Logging。詳情請參閱「查看 Cloud Logging 中的錯誤記錄檔」。

如果整個作業傳回錯誤,請參閱「排解長期執行的作業問題」。

後續步驟