使用 Cloud Storage 导入和导出 FHIR 资源

本页面介绍如何使用 projects.locations.datasets.fhirStores.importprojects.locations.datasets.fhirStores.export 方法将 FHIR 资源导出和导入 Cloud Storage。

根据 FHIR 数据的格式,您可以使用 projects.locations.datasets.fhirStores.import 方法或 projects.locations.datasets.fhirStores.fhir.executeBundle 方法将数据加载到 FHIR 存储区。有关选择方法的指导,请参阅 FHIR 导入

设置 Cloud Storage 权限

在将 FHIR 资源导出到 Cloud Storage 以及从 Cloud Storage 导入 FHIR 资源之前,您必须向 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 生成模拟患者数据

默认情况下,生成的合成数据会使用 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 目录运行以下 gsutil cp命令:

    gsutil -m 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 目录运行以下 gsutil cp命令:

    gsutil -m 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 资源和一个患者资源。每个资源都在单独的行上,因此您将 ContentStructure 设置为 RESOURCE

如果 ContentStructure 与您的数据格式不匹配,您的数据可能会导入不正确或根本无法导入。例如,除非在导入请求中将 ContentStructure 设置为 RESOURCE,否则上述示例文件无法正确导入。

以下示例展示了如何从 Cloud Storage 存储桶导入 FHIR 资源。

控制台

如需从 Cloud Storage 存储桶导入 FHIR 资源,请完成以下步骤:

  1. 在 Google Cloud 控制台中,进入数据集页面。
    进入“数据集”
  2. 点击包含要导入的 FHIR 资源的 FHIR 数据集。
  3. 在数据存储区列表中,选择导入操作FHIR 存储区的列表。

    导入到 FHIR 存储区页面会出现。
  4. 项目列表中,选择一个 Cloud Storage 项目。
  5. 位置列表中,选择一个 Cloud Storage 存储桶。
  6. FHIR 导入设置中,选择相关的内容结构。
  7. 点击导入以导入 FHIR 资源。
  8. 要跟踪操作的状态,请点击操作标签页。操作完成后,系统会显示以下指示:
    • 长时间运行的操作状态部分下方的确定标题下会显示一个绿色的对勾标记。
    • 概览部分在操作 ID 的同一行中显示一个绿色对勾标记和一个确定指示符。
    如果您遇到任何错误,请点击操作,然后点击在 Cloud Logging 中查看详细信息

gcloud

要将 FHIR 资源导入 FHIR 存储区,请使用 gcloud healthcare fhir-stores import gcs 命令。请指定以下信息:

  • 父数据集的名称
  • FHIR 存储区的名称
  • 对象在 Cloud Storage 存储桶中的位置。文件在存储桶中的位置是任意的,无需严格遵循以下示例中指定的格式。在 Cloud Storage 中指定 FHIR 资源的位置时,您可以使用通配符从一个或多个目录导入多个文件。支持以下通配符:
    • 使用 * 可以匹配零个或更多的非分隔符字符。例如,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 方法。

  • 文件在存储桶中的位置是任意的,无需严格遵循以下示例中指定的格式。
  • 在 Cloud Storage 中指定 FHIR 资源的位置时,您可以使用通配符从一个或多个目录导入多个文件。支持以下通配符:
    • 使用 * 可以匹配零个或更多的非分隔符字符。例如,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 存储桶中的位置
  • 访问令牌

以下示例展示了如何使用 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 存储桶中的位置
  • 访问令牌

以下示例展示了使用 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 存储区中的所有资源。

在导出期间,Cloud Healthcare API 会为 FHIR 存储区中的每种资源类型创建一个文件。文件名由操作 ID 和资源类型组成,并以下划线分隔。每个文件由以换行符分隔的 JSON 组成,其中每行都是与文件名中的资源类型对应的 FHIR 资源。例如,如果您导出多个患者记录,则输出文件将类似于 1264567891234567_Patient,并且将包含来自 FHIR 存储区的每个患者资源的一行。

控制台

如需将 FHIR 资源导出到 Cloud Storage,请完成以下步骤:

  1. 在 Google Cloud 控制台中,进入数据集页面。
    进入“数据集”
  2. 点击要从中导出 FHIR 资源的 FHIR 存储区的数据集。
  3. 在数据存储区列表中,从 FHIR 存储区的操作列表中选择导出。则会显示

    Export FHIR 资源页面。
  4. 选择 Google Cloud Storage 存储分区
  5. 项目列表中,选择一个 Cloud Storage 项目。
  6. 位置列表中,选择一个 Cloud Storage 存储桶。
  7. 点击导出,将 FHIR 资源导出到 Cloud Storage 中的指定位置。
  8. 要跟踪操作的状态,请点击操作标签页。操作完成后,系统会显示以下指示:
    • 长时间运行的操作状态部分下方的确定标题下会显示一个绿色的对勾标记。
    • 概览部分在操作 ID 的同一行中显示一个绿色对勾标记和一个确定指示符。
    如果您遇到任何错误,请点击操作,然后点击在 Cloud Logging 中查看详细信息

gcloud

如需将 FHIR 资源导出到 Cloud Storage 存储桶,请使用 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 存储桶或目录(而非对象),因为 Cloud Healthcare API 会为每个资源类型创建一个以换行符分隔的 JSON 文件。在每个 JSON 文件中,每行都是一个 FHIR 资源。
  • 如果该命令指定的目录不存在,则会创建该目录。

curl

要导出 FHIR 资源,请发出 POST 请求并指定以下信息:

  • 父数据集的名称
  • FHIR 存储区的名称
  • 目标 Cloud Storage 存储桶
  • 访问令牌
  • 可选字段 _type,仅导出特定资源类型,定义为一个或多个 FHIR 资源类型的逗号分隔列表
  • 可选字段 _since,仅导出在特定时间之后更新的资源(定义为 YYYY-MM-DDThh:mm:ss.sss+zz:zz

以下示例展示了使用 curlPOST 请求。

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 导入或导出请求期间发生错误,则错误会记录到 Cloud Logging。如需了解详情,请参阅在 Cloud Logging 中查看错误日志

如果整个操作返回错误,请参阅排查长时间运行的操作问题

后续步骤