Cloud Storage を使用して FHIR リソースのインポートとエクスポートを行う

このページでは、projects.locations.datasets.fhirStores.import メソッドと projects.locations.datasets.fhirStores.export メソッドを使用して Cloud Storage との間で FHIR リソースをエクスポートおよびインポートする方法について説明します。

FHIR データの形式に応じて、FHIR ストアにデータを読み込むには、projects.locations.datasets.fhirStores.import メソッドまたは projects.locations.datasets.fhirStores.fhir.executeBundle メソッドを使用します。メソッドの選択については、FHIR のインポートをご覧ください。

Cloud Storage 権限を設定する

Cloud Storage との間で FHIR リソースをエクスポートおよびインポートする前に、Cloud Healthcare サービス エージェントサービス アカウントに追加の権限を付与する必要があります。詳細については、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 バケット内の 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 バケット内の 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)が 1 行以上含まれます。各行は 1 つ以上のリソースを含むバンドルです。ContentStructure を指定しない場合、デフォルトで BUNDLE になります。
  • RESOURCE: ソースファイルには、改行区切りの JSON(ndjson)が 1 行以上含まれます。各行は単一のリソースです。
  • BUNDLE_PRETTY: ソースファイル全体が 1 つの JSON バンドルです。JSON は複数の行にまたがることがあります。
  • RESOURCE_PRETTY: ソースファイル全体が 1 つの 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"}

このファイルには、2 つの Encounter リソースと 1 つの Patient リソースが含まれています。各リソースは別々の行にあるため、ContentStructureRESOURCE に設定します。

ContentStructure がデータの形式と一致しない場合は、データが正しくインポートされないか、まったくインポートされない可能性があります。たとえば、インポート リクエストで ContentStructureRESOURCE に設定されていない場合、上記のサンプル ファイルは正しくインポートされません。

以下のサンプルは、Cloud Storage バケットから FHIR リソースをインポートする方法を示しています。

Console

Cloud Storage バケットから FHIR リソースをインポートするには、次の手順を行います。

  1. Google Cloud コンソールで、[データセット] ページに移動します。
    データセットに移動
  2. FHIR リソースのインポート先の FHIR ストアを含むデータセットをクリックします。
  3. データストアのリストで、FHIR ストアの [アクション] リストから [インポート] を選択ます。

    FHIR ストアへのインポート ページが表示されます。
  4. [プロジェクト] リストで、Cloud Storage プロジェクトを選択します。
  5. [ロケーション] リストで、Cloud Storage バケットを選択します。
  6. [FHIR インポート設定] で、関連するコンテンツ ストラクチャを選択します。
  7. [インポート] をクリックして FHIR リソースをインポートします。
  8. オペレーションのステータスを追跡するには、[オペレーション] タブをクリックします。オペレーションが完了すると、次の状況が表示されます。
    • [長時間実行オペレーションのステータス] セクションでは、[OK] の見出しの下に、緑色のチェックマークが表示されます。
    • [概要] セクションでは、オペレーション ID と同じ行に緑色のチェックマークと [OK] インジケーターが表示されます。
    エラーが発生した場合は、[アクション] をクリックしてから、[Cloud Logging で詳細を表示] をクリックします。

gcloud

FHIR リソースを FHIR ストアにインポートするには、gcloud healthcare fhir-stores import gcs コマンドを使用します。以下の情報を指定します。

  • 親データセットの名前。
  • FHIR ストアの名前。
  • Cloud Storage バケット内のオブジェクトの場所。バケット内のファイルの場所は任意であり、次のサンプルで指定されている形式を厳密に遵守する必要はありません。Cloud Storage 内の FHIR リソースの場所を指定するときは、ワイルドカードを使用して 1 つ以上のディレクトリから複数のファイルをインポートできます。次のワイルドカードがサポートされています。
    • * は、0 個以上の区切り文字でない文字に一致します。たとえば、gs://BUCKET/DIRECTORY/Example*.ndjsonDIRECTORY の Example.ndjson と Example22.ndjson に一致します。
    • ** は 0 個以上の文字(区切り文字を含む)と一致します。パスの末尾で使用する必要があり、パスには他のワイルドカードを使用しないでください。ファイル名の拡張子(.ndjson など)でも使用できます。この場合、指定したディレクトリとそのサブディレクトリに、このファイル名拡張子を持つすべてのファイルをインポートできます。 たとえば、gs://BUCKET/DIRECTORY/**.ndjson では .ndjson というファイル名拡張子を持つすべてのファイルを DIRECTORYとそのサブディレクトリにインポートします。
    • ? は 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 メソッドを使用します。

  • バケット内のファイルの場所は任意であり、次のサンプルで指定されている形式を厳密に遵守する必要はありません。
  • Cloud Storage 内の FHIR リソースの場所を指定するときは、ワイルドカードを使用して 1 つ以上のディレクトリから複数のファイルをインポートできます。次のワイルドカードがサポートされています。
    • * は、0 個以上の区切り文字でない文字に一致します。たとえば、gs://BUCKET/DIRECTORY/Example*.ndjsonDIRECTORY の Example.ndjson と Example22.ndjson に一致します。
    • ** は 0 個以上の文字(区切り文字を含む)と一致します。パスの末尾で使用する必要があり、パスには他のワイルドカードを使用しないでください。ファイル名の拡張子(.ndjson など)でも使用できます。この場合、指定したディレクトリとそのサブディレクトリに、このファイル名拡張子を持つすべてのファイルをインポートできます。 たとえば、gs://BUCKET/DIRECTORY/**.ndjson では .ndjson というファイル名拡張子を持つすべてのファイルを DIRECTORYとそのサブディレクトリにインポートします。
    • ? は 1 つの文字に一致します。たとえば、gs://BUCKET/DIRECTORY/Example?.ndjson は Example1.ndjson と一致しますが、Example.ndjson や Example01.ndjson とは一致しません。

curl

FHIR リソースを FHIR ストアにインポートするには、POST リクエストを行い、次の情報を指定します。

  • 親データセットの名前。
  • FHIR ストアの名前。
  • Cloud Storage バケット内のオブジェクトの場所。
  • アクセス トークン

次のサンプルは、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"
}

レスポンスにはオペレーション名が含まれています。オペレーションのステータスを追跡するには、オペレーションの 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 バケット内のオブジェクトの場所。
  • アクセス トークン

次のサンプルは、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"
}

レスポンスにはオペレーション名が含まれています。オペレーションのステータスを追跡するには、オペレーションの 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 リソースをエクスポートする場合は、データを増分エクスポートすることを検討してください。手順については、増分エクスポートをご覧ください。

エクスポート中、Cloud Healthcare API は FHIR ストアからリソースタイプごとに 1 つのファイルを作成します。ファイル名は、アンダースコアで区切られたオペレーション ID とリソースタイプで構成されます。各ファイルは、改行区切りの JSON で構成され、各行は、ファイル名のリソースタイプに対応する FHIR リソースです。たとえば、複数の患者レコードをエクスポートする場合、出力ファイルは 1264567891234567_Patient のような名前で、FHIR ストアからの各患者のリソースの各行が含まれます。

Console

FHIR リソースを Cloud Storage にエクスポートするには、次の手順を実行します。

  1. Google Cloud コンソールで、[データセット] ページに移動します。
    データセットに移動
  2. FHIR リソースのエクスポート元の FHIR ストアを含むデータセットをクリックします。
  3. データストアのリストで、FHIR ストアの [アクション] リストから [エクスポート] を選択します。

    FHIR リソースのエクスポート ページが表示されます。
  4. [Google Cloud Storage バケット] を選択します。
  5. [プロジェクト] リストで、Cloud Storage プロジェクトを選択します。
  6. [ロケーション] リストで、Cloud Storage バケットを選択します。
  7. [エクスポート] をクリックして、FHIR リソースを Cloud Storage 内の定義された場所にエクスポートします。
  8. オペレーションのステータスを追跡するには、[オペレーション] タブをクリックします。オペレーションが完了すると、次の状況が表示されます。
    • [長時間実行オペレーションのステータス] セクションでは、[OK] の見出しの下に、緑色のチェックマークが表示されます。
    • [概要] セクションでは、オペレーション ID と同じ行に緑色のチェックマークと [OK] インジケーターが表示されます。
    エラーが発生した場合は、[アクション] をクリックしてから、[Cloud Logging で詳細を表示] をクリックします。

gcloud

FHIR リソースを Cloud Storage バケットにエクスポートするには、gcloud healthcare fhir-stores export gcs コマンドを使用します。以下の情報を指定します。

  • 親データセットの名前。
  • FHIR ストアの名前
  • 親プロジェクトの名前
  • 宛先の Cloud Storage バケットまたはディレクトリ。Cloud Healthcare API はリソースタイプごとに 1 つのオブジェクトを作成するため、オブジェクトではなく Cloud Storage のバケットまたはディレクトリに書き込みます。各オブジェクトは、改行区切りの JSON で構成され、各行は FHIR リソースです。存在しないディレクトリを指定した場合には、作成されます。
  • 1 つ以上の FHIR リソースタイプのカンマ区切りのリストとして定義された特定のリソースタイプのみをエクスポートする省略可能なフラグ --resource-type
  • YYYY-MM-DDThh:mm:ss.sss+zz:zz として定義された特定の時間の経過後に更新されたリソースのみをエクスポートする、省略可能なフラグ --since

次のサンプルは、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 バケットまたはディレクトリに書き込みます。これは、Cloud Healthcare API がリソースタイプごとに 1 つの改行区切りの JSON ファイルを作成するためです。各 JSON ファイルの各行は FHIR リソースです。
  • コマンドに存在しないディレクトリが指定されている場合は、そのディレクトリが作成されます。

curl

FHIR リソースをエクスポートするには、POST リクエストを行い、次の情報を指定します。

  • 親データセットの名前。
  • FHIR ストアの名前。
  • 宛先の Cloud Storage バケット。
  • アクセス トークン
  • 1 つ以上の FHIR リソースタイプのカンマ区切りリストとして定義された特定のリソースタイプのみをエクスポートする省略可能なフィールド _type
  • YYYY-MM-DDThh:mm:ss.sss+zz:zz として定義された特定の時間の経過後に更新されたリソースのみをエクスポートする、省略可能なフィールド _since

次のサンプルは、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"
}

レスポンスにはオペレーション名が含まれています。オペレーションのステータスを追跡するには、オペレーションの 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 Healthcare API はリソースタイプごとに 1 つのオブジェクトを作成するため、オブジェクトではなく Cloud Storage のバケットまたはディレクトリに書き込みます。各オブジェクトは、改行区切りの JSON で構成され、各行は FHIR リソースです。
  • アクセス トークン
  • 1 つ以上の FHIR リソースタイプのカンマ区切りリストとして定義された特定のリソースタイプのみをエクスポートする省略可能なフィールド _type
  • YYYY-MM-DDThh:mm:ss.sss+zz:zz として定義された特定の時間の経過後に更新されたリソースのみをエクスポートする、省略可能なフィールド _since

次のサンプルは、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"
}

レスポンスにはオペレーション名が含まれています。オペレーションのステータスを追跡するには、オペレーションの 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 でのエラーログの表示をご覧ください。

オペレーション全体がエラーで返る場合は、長時間実行オペレーションのトラブルシューティングをご覧ください。

次のステップ