Spanner Graph を設定してクエリを実行する

このドキュメントでは、 Google Cloud コンソールとクライアント ライブラリを使用して、Spanner Graph データベースを設定してクエリし、複雑なデータ関係をモデル化、保存、分析する方法について説明します。

次のトピックでは、以下の方法について説明します。

Spanner の料金の詳細については、Spanner の料金をご覧ください。

Codelab を試すには、Spanner Graph を使ってみるをご覧ください。

始める前に

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  5. Verify that billing is enabled for your Google Cloud project.

  6. Spanner API を有効にします。
    Spanner API を有効にするに移動
    1. インスタンスとデータベースを作成するために必要な権限を取得するには、プロジェクトに対する Cloud Spanner 管理者 roles/spanner.admin)IAM ロールを付与するよう管理者に依頼してください。

    1. Cloud Spanner 管理者ロールが付与されていない場合、Spanner グラフに対してクエリを実行するために必要な権限を取得するには、プロジェクトに対する Cloud Spanner データベース リーダー roles/spanner.databaseReader)IAM ロールを付与するよう管理者に依頼してください。

    Spanner インスタンスの作成

    Spanner を初めて使用する際は、インスタンスを作成する必要があります。インスタンスは、Spanner データベースのリソースを割り当てます。このセクションでは、 Google Cloud コンソールを使用してインスタンスを作成する方法について説明します。

    1. Google Cloud コンソールで、[Spanner] ページに移動します。

      Spanner に移動

    2. Google Cloud プロジェクトを選択するか、まだ作成していない場合は新しく作成します。

    3. 次のいずれかを行います。

      1. まだ Spanner インスタンスを作成していない場合は、[Spanner へようこそ] ページで [プロビジョニングされたインスタンスを作成] をクリックします。

      2. Spanner インスタンスを作成している場合は、[インスタンス] ページで [インスタンスを作成] をクリックします。

    4. [エディションを選択] ページで、[Enterprise Plus] または [Enterprise] を選択します。

      Spanner Graph は、Enterprise エディションまたは Enterprise Plus エディションでのみ使用できます。異なるエディションを比較するには、[エディションを比較] をクリックします。詳細については、Spanner エディションの概要をご覧ください。

    5. [続行] をクリックします。

    6. [インスタンス名] にインスタンス名(test-instance など)を入力します。

    7. [インスタンス ID] で、インスタンス ID をそのままにしておくか、変更します。デフォルトでインスタンス名がインスタンス ID になりますが、変更できます。インスタンス名とインスタンス ID は同じでも異なってもかまいません。

    8. [続行] をクリックします。

    9. [構成を選択] で、次の操作を行います。

      1. [リージョン] を選択したままにします。

      2. [構成を選択] でリージョンを選択します。Spanner は、選択したリージョンにインスタンスを保存して複製します。

      3. [続行] をクリックします。

    10. [コンピューティング容量を構成する] で、次の操作を行います。

      1. [ユニットを選択] で、[処理ユニット(PU)] を選択します。

      2. [スケーリング モードを選択] で [手動で割り当てる] を選択し、[数量] は 1,000 処理ユニットのままにします。

    11. [作成] をクリックします。 Google Cloud コンソールに、作成したインスタンスの [概要] ページが表示されます。

    スキーマを使用してグラフ データベースを作成する

    このセクションでは、Spanner Graph スキーマを使用して Spanner Graph データベースを作成する方法について説明します。 Google Cloud コンソールまたはクライアント ライブラリを使用できます。

    コンソール

    1. Google Cloud コンソールで、[Spanner インスタンス] ページに移動します。

      [Spanner インスタンス] に移動

    2. 作成したインスタンスをクリックします(例: Test Instance)。

    3. [概要] で、インスタンスの名前の下にある [データベースを作成] をクリックします。

    4. [データベース名] にデータベース名を入力します。例: example-db

    5. [データベース言語の選択] で、Google 標準 SQL を選択します。PostgreSQL 言語では Spanner Graph を使用できません。

    6. 次のスキーマを [DDL テンプレート] エディタタブに入力します。このスキーマには、2 つのノードテーブル定義(PersonAccount)と 2 つのエッジテーブル定義(PersonOwnAccountAccountTransferAccount)が含まれています。Spanner Graph はリレーショナル テーブルを使用してグラフを定義するため、スキーマにはリレーショナル テーブルとグラフ ステートメントの両方が表示されます。Spanner Graph スキーマの詳細については、Spanner Graph スキーマの概要をご覧ください。

      CREATE TABLE Person (
        id               INT64 NOT NULL,
        name             STRING(MAX),
        birthday         TIMESTAMP,
        country          STRING(MAX),
        city             STRING(MAX),
      ) PRIMARY KEY (id);
      
      CREATE TABLE Account (
        id               INT64 NOT NULL,
        create_time      TIMESTAMP,
        is_blocked       BOOL,
        nick_name        STRING(MAX),
      ) PRIMARY KEY (id);
      
      CREATE TABLE PersonOwnAccount (
        id               INT64 NOT NULL,
        account_id       INT64 NOT NULL,
        create_time      TIMESTAMP,
        FOREIGN KEY (account_id) REFERENCES Account (id)
      ) PRIMARY KEY (id, account_id),
        INTERLEAVE IN PARENT Person ON DELETE CASCADE;
      
      CREATE TABLE AccountTransferAccount (
        id               INT64 NOT NULL,
        to_id            INT64 NOT NULL,
        amount           FLOAT64,
        create_time      TIMESTAMP NOT NULL,
        order_number     STRING(MAX),
        FOREIGN KEY (to_id) REFERENCES Account (id)
      ) PRIMARY KEY (id, to_id, create_time),
        INTERLEAVE IN PARENT Account ON DELETE CASCADE;
      
      CREATE OR REPLACE PROPERTY GRAPH FinGraph
        NODE TABLES (Account, Person)
        EDGE TABLES (
          PersonOwnAccount
            SOURCE KEY (id) REFERENCES Person (id)
            DESTINATION KEY (account_id) REFERENCES Account (id)
            LABEL Owns,
          AccountTransferAccount
            SOURCE KEY (id) REFERENCES Account (id)
            DESTINATION KEY (to_id) REFERENCES Account (id)
            LABEL Transfers
        );
      
    7. [暗号化オプションを表示する] のデフォルト設定はそのままにします。

    8. [作成] をクリックします。 Google Cloud コンソールに、作成したデータベースの [概要] ページが表示されます。

    クライアント ライブラリ

    Python

    def create_database_with_property_graph(instance_id, database_id):
        """Creates a database, tables and a property graph for sample data."""
        from google.cloud.spanner_admin_database_v1.types import spanner_database_admin
    
        spanner_client = spanner.Client()
        database_admin_api = spanner_client.database_admin_api
    
        request = spanner_database_admin.CreateDatabaseRequest(
            parent=database_admin_api.instance_path(spanner_client.project, instance_id),
            create_statement=f"CREATE DATABASE `{database_id}`",
            extra_statements=[
                """CREATE TABLE Person (
                id               INT64 NOT NULL,
                name             STRING(MAX),
                birthday         TIMESTAMP,
                country          STRING(MAX),
                city             STRING(MAX),
            ) PRIMARY KEY (id)""",
                """CREATE TABLE Account (
                id               INT64 NOT NULL,
                create_time      TIMESTAMP,
                is_blocked       BOOL,
                nick_name        STRING(MAX),
            ) PRIMARY KEY (id)""",
                """CREATE TABLE PersonOwnAccount (
                id               INT64 NOT NULL,
                account_id       INT64 NOT NULL,
                create_time      TIMESTAMP,
                FOREIGN KEY (account_id)
                    REFERENCES Account (id)
            ) PRIMARY KEY (id, account_id),
            INTERLEAVE IN PARENT Person ON DELETE CASCADE""",
                """CREATE TABLE AccountTransferAccount (
                id               INT64 NOT NULL,
                to_id            INT64 NOT NULL,
                amount           FLOAT64,
                create_time      TIMESTAMP NOT NULL,
                order_number     STRING(MAX),
                FOREIGN KEY (to_id) REFERENCES Account (id)
            ) PRIMARY KEY (id, to_id, create_time),
            INTERLEAVE IN PARENT Account ON DELETE CASCADE""",
                """CREATE OR REPLACE PROPERTY GRAPH FinGraph
                NODE TABLES (Account, Person)
                EDGE TABLES (
                    PersonOwnAccount
                        SOURCE KEY(id) REFERENCES Person(id)
                        DESTINATION KEY(account_id) REFERENCES Account(id)
                        LABEL Owns,
                    AccountTransferAccount
                        SOURCE KEY(id) REFERENCES Account(id)
                        DESTINATION KEY(to_id) REFERENCES Account(id)
                        LABEL Transfers)""",
            ],
        )
    
        operation = database_admin_api.create_database(request=request)
    
        print("Waiting for operation to complete...")
        database = operation.result(OPERATION_TIMEOUT_SECONDS)
    
        print(
            "Created database {} on instance {}".format(
                database.name,
                database_admin_api.instance_path(spanner_client.project, instance_id),
            )
        )
    
    

    Java

    static void createDatabaseWithPropertyGraph(
        DatabaseAdminClient dbAdminClient, InstanceName instanceName, String databaseId) {
      CreateDatabaseRequest createDatabaseRequest =
          CreateDatabaseRequest.newBuilder()
              .setCreateStatement("CREATE DATABASE `" + databaseId + "`")
              .setParent(instanceName.toString())
              .addAllExtraStatements(
                  Arrays.asList(
                      "CREATE TABLE Person ("
                          + "  id               INT64 NOT NULL,"
                          + "  name             STRING(MAX),"
                          + "  birthday         TIMESTAMP,"
                          + "  country          STRING(MAX),"
                          + "  city             STRING(MAX),"
                          + ") PRIMARY KEY (id)",
                      "CREATE TABLE Account ("
                          + "  id               INT64 NOT NULL,"
                          + "  create_time      TIMESTAMP,"
                          + "  is_blocked       BOOL,"
                          + "  nick_name        STRING(MAX),"
                          + ") PRIMARY KEY (id)",
                      "CREATE TABLE PersonOwnAccount ("
                          + "  id               INT64 NOT NULL,"
                          + "  account_id       INT64 NOT NULL,"
                          + "  create_time      TIMESTAMP,"
                          + "  FOREIGN KEY (account_id)"
                          + "  REFERENCES Account (id)"
                          + ") PRIMARY KEY (id, account_id),"
                          + "INTERLEAVE IN PARENT Person ON DELETE CASCADE",
                      "CREATE TABLE AccountTransferAccount ("
                          + "  id               INT64 NOT NULL,"
                          + "  to_id            INT64 NOT NULL,"
                          + "  amount           FLOAT64,"
                          + "  create_time      TIMESTAMP NOT NULL,"
                          + "  order_number     STRING(MAX),"
                          + "  FOREIGN KEY (to_id) REFERENCES Account (id)"
                          + ") PRIMARY KEY (id, to_id, create_time),"
                          + "INTERLEAVE IN PARENT Account ON DELETE CASCADE",
                      "CREATE OR REPLACE PROPERTY GRAPH FinGraph "
                          + "NODE TABLES (Account, Person)"
                          + "EDGE TABLES ("
                          + "  PersonOwnAccount"
                          + "    SOURCE KEY(id) REFERENCES Person(id)"
                          + "    DESTINATION KEY(account_id) REFERENCES Account(id)"
                          + "    LABEL Owns,"
                          + "  AccountTransferAccount"
                          + "    SOURCE KEY(id) REFERENCES Account(id)"
                          + "    DESTINATION KEY(to_id) REFERENCES Account(id)"
                          + "    LABEL Transfers)"))
              .build();
      try {
        // Initiate the request which returns an OperationFuture.
        com.google.spanner.admin.database.v1.Database db =
            dbAdminClient.createDatabaseAsync(createDatabaseRequest).get();
        System.out.println("Created database [" + db.getName() + "]");
      } catch (ExecutionException e) {
        // If the operation failed during execution, expose the cause.
        System.out.println("Encountered exception" + e.getCause());
        throw (SpannerException) e.getCause();
      } catch (InterruptedException e) {
        // Throw when a thread is waiting, sleeping, or otherwise occupied,
        // and the thread is interrupted, either before or during the activity.
        throw SpannerExceptionFactory.propagateInterrupt(e);
      }
    }
    

    Go

    
    import (
    	"context"
    	"fmt"
    	"io"
    	"regexp"
    
    	database "cloud.google.com/go/spanner/admin/database/apiv1"
    	adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
    )
    
    func createDatabaseWithPropertyGraph(ctx context.Context, w io.Writer, dbId string) error {
    	// dbId is of the form:
    	// 	projects/YOUR_PROJECT_ID/instances/YOUR_INSTANCE_ID/databases/YOUR_DATABASE_NAME
    	matches := regexp.MustCompile("^(.*)/databases/(.*)$").FindStringSubmatch(dbId)
    	if matches == nil || len(matches) != 3 {
    		return fmt.Errorf("Invalid database id %s", dbId)
    	}
    
    	adminClient, err := database.NewDatabaseAdminClient(ctx)
    	if err != nil {
    		return err
    	}
    	defer adminClient.Close()
    
    	var instance = matches[1]
    	var dbName = matches[2]
    
    	// The schema defintion for a database with a property graph comprises table
    	// definitions one or more `CREATE PROPERTY GRAPH` statements to define the
    	// property graph(s).
    	//
    	// Here, tables are created for 'Person's and 'Account's. The property graph
    	// definition says that these entities form nodes in the graph. Similarly,
    	// there are 'PersonOwnAccount' and 'AccountTransferAccount' relationship
    	// tables defined. The property graph definition maps these to edges in the graph.
    	var schema_statements = []string{
    		`CREATE TABLE Person (
    			id               INT64 NOT NULL,
    			name             STRING(MAX),
    			birthday         TIMESTAMP,
    			country          STRING(MAX),
    			city             STRING(MAX),
    		) PRIMARY KEY (id)`,
    		`CREATE TABLE Account (
    			id               INT64 NOT NULL,
    			create_time      TIMESTAMP,
    			is_blocked       BOOL,
    			nick_name        STRING(MAX),
    		) PRIMARY KEY (id)`,
    		`CREATE TABLE PersonOwnAccount (
    			id               INT64 NOT NULL,
    			account_id       INT64 NOT NULL,
    			create_time      TIMESTAMP,
    			FOREIGN KEY (account_id)
    				REFERENCES Account (id)
    		) PRIMARY KEY (id, account_id),
    		INTERLEAVE IN PARENT Person ON DELETE CASCADE`,
    		`CREATE TABLE AccountTransferAccount (
    			id               INT64 NOT NULL,
    			to_id            INT64 NOT NULL,
    			amount           FLOAT64,
    			create_time      TIMESTAMP NOT NULL,
    			order_number     STRING(MAX),
    			FOREIGN KEY (to_id) REFERENCES Account (id)
    		) PRIMARY KEY (id, to_id, create_time),
    		INTERLEAVE IN PARENT Account ON DELETE CASCADE`,
    		`CREATE OR REPLACE PROPERTY GRAPH FinGraph
    			NODE TABLES (Account, Person)
    			EDGE TABLES (
    				PersonOwnAccount
    					SOURCE KEY(id) REFERENCES Person(id)
    					DESTINATION KEY(account_id) REFERENCES Account(id)
    					LABEL Owns,
    				AccountTransferAccount
    					SOURCE KEY(id) REFERENCES Account(id)
    					DESTINATION KEY(to_id) REFERENCES Account(id)
    					LABEL Transfers)`,
    	}
    
    	op, err := adminClient.CreateDatabase(ctx, &adminpb.CreateDatabaseRequest{
    		Parent:          instance,
    		CreateStatement: "CREATE DATABASE `" + dbName + "`",
    		ExtraStatements: schema_statements,
    	})
    	if err != nil {
    		return err
    	}
    	if _, err := op.Wait(ctx); err != nil {
    		return err
    	}
    	fmt.Fprintf(w, "Created database [%s]\n", dbId)
    	return nil
    }
    

    C++

    void CreateDatabaseWithPropertyGraph(
        google::cloud::spanner_admin::DatabaseAdminClient client,
        std::string const& project_id, std::string const& instance_id,
        std::string const& database_id) {
      google::cloud::spanner::Database database(project_id, instance_id,
                                                database_id);
      google::spanner::admin::database::v1::CreateDatabaseRequest request;
      request.set_parent(database.instance().FullName());
      request.set_create_statement("CREATE DATABASE `" + database.database_id() +
                                   "`");
      request.add_extra_statements(R"""(
        CREATE TABLE Person (
          id               INT64 NOT NULL,
          name             STRING(MAX),
          birthday         TIMESTAMP,
          country          STRING(MAX),
          city             STRING(MAX),
        ) PRIMARY KEY (id))""");
      request.add_extra_statements(R"""(
        CREATE TABLE Account (
          id               INT64 NOT NULL,
          create_time      TIMESTAMP,
          is_blocked       BOOL,
          nick_name        STRING(MAX),
        ) PRIMARY KEY (id))""");
      request.add_extra_statements(R"""(
        CREATE TABLE PersonOwnAccount (
          id               INT64 NOT NULL,
          account_id       INT64 NOT NULL,
          create_time      TIMESTAMP,
          FOREIGN KEY (account_id)
          REFERENCES Account (id)
        ) PRIMARY KEY (id, account_id),
          INTERLEAVE IN PARENT Person ON DELETE CASCADE)""");
      request.add_extra_statements(R"""(
        CREATE TABLE AccountTransferAccount (
          id               INT64 NOT NULL,
          to_id            INT64 NOT NULL,
          amount           FLOAT64,
          create_time      TIMESTAMP NOT NULL,
          order_number     STRING(MAX),
          FOREIGN KEY (to_id) REFERENCES Account (id)
        ) PRIMARY KEY (id, to_id, create_time),
          INTERLEAVE IN PARENT Account ON DELETE CASCADE)""");
      request.add_extra_statements(R"""(
        CREATE OR REPLACE PROPERTY GRAPH FinGraph
          NODE TABLES (Account, Person)
          EDGE TABLES (
            PersonOwnAccount
              SOURCE KEY(id) REFERENCES Person(id)
              DESTINATION KEY(account_id) REFERENCES Account(id)
              LABEL Owns,
            AccountTransferAccount
              SOURCE KEY(id) REFERENCES Account(id)
              DESTINATION KEY(to_id) REFERENCES Account(id)
              LABEL Transfers))""");
      auto db = client.CreateDatabase(request).get();
      if (!db) throw std::move(db).status();
      std::cout << "Database " << db->name() << " created with property graph.\n";
    }

    グラフデータを挿入する

    データベースを作成したら、データを追加できます。このセクションでは、グラフィカル インターフェース用の Google Cloud コンソールを使用するか、クライアント ライブラリをプログラムで使用して、グラフにデータを挿入する方法について説明します。

    コンソール

    1. [Spanner Studio] ページで、 [新しいタブ] をクリックするか、エディタタブを使用します。

    2. 次のグラフデータ挿入ステートメントをコピーして、ノードとエッジに貼り付けます。

      INSERT INTO Account
        (id, create_time, is_blocked, nick_name)
      VALUES
        (7,"2020-01-10 06:22:20.222",false,"Vacation Fund"),
        (16,"2020-01-27 17:55:09.206",true,"Vacation Fund"),
        (20,"2020-02-18 05:44:20.655",false,"Rainy Day Fund");
      
      INSERT INTO Person
        (id, name, birthday, country, city)
      VALUES
        (1,"Alex","1991-12-21 00:00:00","Australia","Adelaide"),
        (2,"Dana","1980-10-31 00:00:00","Czech_Republic","Moravia"),
        (3,"Lee","1986-12-07 00:00:00","India","Kollam");
      
      INSERT INTO AccountTransferAccount
        (id, to_id, amount, create_time, order_number)
      VALUES
        (7,16,300,"2020-08-29 15:28:58.647","304330008004315"),
        (7,16,100,"2020-10-04 16:55:05.342","304120005529714"),
        (16,20,300,"2020-09-25 02:36:14.926","103650009791820"),
        (20,7,500,"2020-10-04 16:55:05.342","304120005529714"),
        (20,16,200,"2020-10-17 03:59:40.247","302290001255747");
      
      INSERT INTO PersonOwnAccount
        (id, account_id, create_time)
      VALUES
        (1,7,"2020-01-10 06:22:20.222"),
        (2,20,"2020-01-27 17:55:09.206"),
        (3,16,"2020-02-18 05:44:20.655");
      
    3. [実行] をクリックします。実行が完了すると、[結果] タブに 3 行が挿入されたことが示されます。

    クライアント ライブラリ

    Python

    def insert_data(instance_id, database_id):
        """Inserts sample data into the given database.
    
        The database and tables must already exist and can be created using
        `create_database_with_property_graph`.
        """
        spanner_client = spanner.Client()
        instance = spanner_client.instance(instance_id)
        database = instance.database(database_id)
    
        with database.batch() as batch:
            batch.insert(
                table="Account",
                columns=("id", "create_time", "is_blocked", "nick_name"),
                values=[
                    (7, "2020-01-10T06:22:20.12Z", False, "Vacation Fund"),
                    (16, "2020-01-27T17:55:09.12Z", True, "Vacation Fund"),
                    (20, "2020-02-18T05:44:20.12Z", False, "Rainy Day Fund"),
                ],
            )
    
            batch.insert(
                table="Person",
                columns=("id", "name", "birthday", "country", "city"),
                values=[
                    (1, "Alex", "1991-12-21T00:00:00.12Z", "Australia", " Adelaide"),
                    (2, "Dana", "1980-10-31T00:00:00.12Z", "Czech_Republic", "Moravia"),
                    (3, "Lee", "1986-12-07T00:00:00.12Z", "India", "Kollam"),
                ],
            )
    
            batch.insert(
                table="AccountTransferAccount",
                columns=("id", "to_id", "amount", "create_time", "order_number"),
                values=[
                    (7, 16, 300.0, "2020-08-29T15:28:58.12Z", "304330008004315"),
                    (7, 16, 100.0, "2020-10-04T16:55:05.12Z", "304120005529714"),
                    (16, 20, 300.0, "2020-09-25T02:36:14.12Z", "103650009791820"),
                    (20, 7, 500.0, "2020-10-04T16:55:05.12Z", "304120005529714"),
                    (20, 16, 200.0, "2020-10-17T03:59:40.12Z", "302290001255747"),
                ],
            )
    
            batch.insert(
                table="PersonOwnAccount",
                columns=("id", "account_id", "create_time"),
                values=[
                    (1, 7, "2020-01-10T06:22:20.12Z"),
                    (2, 20, "2020-01-27T17:55:09.12Z"),
                    (3, 16, "2020-02-18T05:44:20.12Z"),
                ],
            )
    
        print("Inserted data.")
    
    

    Java

    /** Class to contain sample Person data. */
    static class Person {
    
      final long id;
      final String name;
      final Timestamp birthday;
      final String country;
      final String city;
    
      Person(long id, String name, Timestamp birthday, String country, String city) {
        this.id = id;
        this.name = name;
        this.birthday = birthday;
        this.country = country;
        this.city = city;
      }
    }
    
    /** Class to contain sample Account data. */
    static class Account {
    
      final long id;
      final Timestamp createTime;
      final boolean isBlocked;
      final String nickName;
    
      Account(long id, Timestamp createTime, boolean isBlocked, String nickName) {
        this.id = id;
        this.createTime = createTime;
        this.isBlocked = isBlocked;
        this.nickName = nickName;
      }
    }
    
    /** Class to contain sample Transfer data. */
    static class Transfer {
    
      final long id;
      final long toId;
      final double amount;
      final Timestamp createTime;
      final String orderNumber;
    
      Transfer(long id, long toId, double amount, Timestamp createTime, String orderNumber) {
        this.id = id;
        this.toId = toId;
        this.amount = amount;
        this.createTime = createTime;
        this.orderNumber = orderNumber;
      }
    }
    
    /** Class to contain sample Ownership data. */
    static class Own {
    
      final long id;
      final long accountId;
      final Timestamp createTime;
    
      Own(long id, long accountId, Timestamp createTime) {
        this.id = id;
        this.accountId = accountId;
        this.createTime = createTime;
      }
    }
    
    static final List<Account> ACCOUNTS =
        Arrays.asList(
            new Account(
                7, Timestamp.parseTimestamp("2020-01-10T06:22:20.12Z"), false, "Vacation Fund"),
            new Account(
                16, Timestamp.parseTimestamp("2020-01-27T17:55:09.12Z"), true, "Vacation Fund"),
            new Account(
                20, Timestamp.parseTimestamp("2020-02-18T05:44:20.12Z"), false, "Rainy Day Fund"));
    
    static final List<Person> PERSONS =
        Arrays.asList(
            new Person(
                1,
                "Alex",
                Timestamp.parseTimestamp("1991-12-21T00:00:00.12Z"),
                "Australia",
                " Adelaide"),
            new Person(
                2,
                "Dana",
                Timestamp.parseTimestamp("1980-10-31T00:00:00.12Z"),
                "Czech_Republic",
                "Moravia"),
            new Person(
                3, "Lee", Timestamp.parseTimestamp("1986-12-07T00:00:00.12Z"), "India", "Kollam"));
    
    static final List<Transfer> TRANSFERS =
        Arrays.asList(
            new Transfer(
                7, 16, 300.0, Timestamp.parseTimestamp("2020-08-29T15:28:58.12Z"), "304330008004315"),
            new Transfer(
                7, 16, 100.0, Timestamp.parseTimestamp("2020-10-04T16:55:05.12Z"), "304120005529714"),
            new Transfer(
                16,
                20,
                300.0,
                Timestamp.parseTimestamp("2020-09-25T02:36:14.12Z"),
                "103650009791820"),
            new Transfer(
                20, 7, 500.0, Timestamp.parseTimestamp("2020-10-04T16:55:05.12Z"), "304120005529714"),
            new Transfer(
                20,
                16,
                200.0,
                Timestamp.parseTimestamp("2020-10-17T03:59:40.12Z"),
                "302290001255747"));
    
    static final List<Own> OWNERSHIPS =
        Arrays.asList(
            new Own(1, 7, Timestamp.parseTimestamp("2020-01-10T06:22:20.12Z")),
            new Own(2, 20, Timestamp.parseTimestamp("2020-01-27T17:55:09.12Z")),
            new Own(3, 16, Timestamp.parseTimestamp("2020-02-18T05:44:20.12Z")));
    
    static void insertData(DatabaseClient dbClient) {
      List<Mutation> mutations = new ArrayList<>();
      for (Account account : ACCOUNTS) {
        mutations.add(
            Mutation.newInsertBuilder("Account")
                .set("id")
                .to(account.id)
                .set("create_time")
                .to(account.createTime)
                .set("is_blocked")
                .to(account.isBlocked)
                .set("nick_name")
                .to(account.nickName)
                .build());
      }
      for (Person person : PERSONS) {
        mutations.add(
            Mutation.newInsertBuilder("Person")
                .set("id")
                .to(person.id)
                .set("name")
                .to(person.name)
                .set("birthday")
                .to(person.birthday)
                .set("country")
                .to(person.country)
                .set("city")
                .to(person.city)
                .build());
      }
      for (Transfer transfer : TRANSFERS) {
        mutations.add(
            Mutation.newInsertBuilder("AccountTransferAccount")
                .set("id")
                .to(transfer.id)
                .set("to_id")
                .to(transfer.toId)
                .set("amount")
                .to(transfer.amount)
                .set("create_time")
                .to(transfer.createTime)
                .set("order_number")
                .to(transfer.orderNumber)
                .build());
      }
      for (Own own : OWNERSHIPS) {
        mutations.add(
            Mutation.newInsertBuilder("PersonOwnAccount")
                .set("id")
                .to(own.id)
                .set("account_id")
                .to(own.accountId)
                .set("create_time")
                .to(own.createTime)
                .build());
      }
    
      dbClient.write(mutations);
    }
    

    Go

    
    import (
    	"context"
    	"io"
    	"time"
    
    	"cloud.google.com/go/spanner"
    )
    
    func parseTime(rfc3339Time string) time.Time {
    	t, _ := time.Parse(time.RFC3339, rfc3339Time)
    	return t
    }
    
    func insertGraphData(w io.Writer, db string) error {
    	ctx := context.Background()
    	client, err := spanner.NewClient(ctx, db)
    	if err != nil {
    		return err
    	}
    	defer client.Close()
    
    	// Values are inserted into the node and edge tables corresponding to
    	// using Spanner 'Insert' mutations.
    	// The tables and columns comply with the schema defined for the
    	// property graph 'FinGraph', comprising 'Person' and 'Account' nodes,
    	// and 'PersonOwnAccount' and 'AccountTransferAccount' edges.
    	personColumns := []string{"id", "name", "birthday", "country", "city"}
    	accountColumns := []string{"id", "create_time", "is_blocked", "nick_name"}
    	ownColumns := []string{"id", "account_id", "create_time"}
    	transferColumns := []string{"id", "to_id", "amount", "create_time", "order_number"}
    	m := []*spanner.Mutation{
    		spanner.Insert("Account", accountColumns,
    			[]interface{}{7, parseTime("2020-01-10T06:22:20.12Z"), false, "Vacation Fund"}),
    		spanner.Insert("Account", accountColumns,
    			[]interface{}{16, parseTime("2020-01-27T17:55:09.12Z"), true, "Vacation Fund"}),
    		spanner.Insert("Account", accountColumns,
    			[]interface{}{20, parseTime("2020-02-18T05:44:20.12Z"), false, "Rainy Day Fund"}),
    		spanner.Insert("Person", personColumns,
    			[]interface{}{1, "Alex", parseTime("1991-12-21T00:00:00.12Z"), "Australia", " Adelaide"}),
    		spanner.Insert("Person", personColumns,
    			[]interface{}{2, "Dana", parseTime("1980-10-31T00:00:00.12Z"), "Czech_Republic", "Moravia"}),
    		spanner.Insert("Person", personColumns,
    			[]interface{}{3, "Lee", parseTime("1986-12-07T00:00:00.12Z"), "India", "Kollam"}),
    		spanner.Insert("AccountTransferAccount", transferColumns,
    			[]interface{}{7, 16, 300.0, parseTime("2020-08-29T15:28:58.12Z"), "304330008004315"}),
    		spanner.Insert("AccountTransferAccount", transferColumns,
    			[]interface{}{7, 16, 100.0, parseTime("2020-10-04T16:55:05.12Z"), "304120005529714"}),
    		spanner.Insert("AccountTransferAccount", transferColumns,
    			[]interface{}{16, 20, 300.0, parseTime("2020-09-25T02:36:14.12Z"), "103650009791820"}),
    		spanner.Insert("AccountTransferAccount", transferColumns,
    			[]interface{}{20, 7, 500.0, parseTime("2020-10-04T16:55:05.12Z"), "304120005529714"}),
    		spanner.Insert("AccountTransferAccount", transferColumns,
    			[]interface{}{20, 16, 200.0, parseTime("2020-10-17T03:59:40.12Z"), "302290001255747"}),
    		spanner.Insert("PersonOwnAccount", ownColumns,
    			[]interface{}{1, 7, parseTime("2020-01-10T06:22:20.12Z")}),
    		spanner.Insert("PersonOwnAccount", ownColumns,
    			[]interface{}{2, 20, parseTime("2020-01-27T17:55:09.12Z")}),
    		spanner.Insert("PersonOwnAccount", ownColumns,
    			[]interface{}{3, 16, parseTime("2020-02-18T05:44:20.12Z")}),
    	}
    	_, err = client.Apply(ctx, m)
    	return err
    }
    

    C++

    void InsertData(google::cloud::spanner::Client client) {
      namespace spanner = ::google::cloud::spanner;
    
      auto insert_accounts =
          spanner::InsertMutationBuilder(
              "Account", {"id", "create_time", "is_blocked", "nick_name"})
              .EmplaceRow(7, spanner::Value("2020-01-10T06:22:20.12Z"), false,
                          "Vacation Fund")
              .EmplaceRow(16, spanner::Value("2020-01-27T17:55:09.12Z"), true,
                          "Vacation Fund")
              .EmplaceRow(20, spanner::Value("2020-02-18T05:44:20.12Z"), false,
                          "Rainy Day Fund")
              .Build();
    
      auto insert_persons =
          spanner::InsertMutationBuilder(
              "Person", {"id", "name", "birthday", "country", "city"})
              .EmplaceRow(1, "Alex", spanner::Value("1991-12-21T00:00:00.12Z"),
                          "Australia", "Adelaide")
              .EmplaceRow(2, "Dana", spanner::Value("1980-10-31T00:00:00.12Z"),
                          "Czech_Republic", "Moravia")
              .EmplaceRow(3, "Lee", spanner::Value("1986-12-07T00:00:00.12Z"),
                          "India", "Kollam")
              .Build();
    
      auto insert_transfers =
          spanner::InsertMutationBuilder(
              "AccountTransferAccount",
              {"id", "to_id", "amount", "create_time", "order_number"})
              .EmplaceRow(7, 16, 300.0, spanner::Value("2020-08-29T15:28:58.12Z"),
                          "304330008004315")
              .EmplaceRow(7, 16, 100.0, spanner::Value("2020-10-04T16:55:05.12Z"),
                          "304120005529714")
              .EmplaceRow(16, 20, 300.0, spanner::Value("2020-09-25T02:36:14.12Z"),
                          "103650009791820")
              .EmplaceRow(20, 7, 500.0, spanner::Value("2020-10-04T16:55:05.12Z"),
                          "304120005529714")
              .EmplaceRow(20, 16, 200.0, spanner::Value("2020-10-17T03:59:40.12Z"),
                          "302290001255747")
              .Build();
    
      auto insert_ownerships =
          spanner::InsertMutationBuilder("PersonOwnAccount",
                                         {"id", "account_id", "create_time"})
              .EmplaceRow(1, 7, spanner::Value("2020-01-10T06:22:20.12Z"))
              .EmplaceRow(2, 20, spanner::Value("2020-01-27T17:55:09.12Z"))
              .EmplaceRow(3, 16, spanner::Value("2020-02-18T05:44:20.12Z"))
              .Build();
    
      auto commit_result = client.Commit(spanner::Mutations{
          insert_accounts, insert_persons, insert_transfers, insert_ownerships});
      if (!commit_result) throw std::move(commit_result).status();
      std::cout << "Insert was successful [spanner_insert_graph_data]\n";
    }

    次のグラフは、挿入されたユーザー、アカウント、アカウントの所有権、アカウントの移行を表しています。

    グラフの例。

    グラフクエリを実行する

    グラフにデータを挿入したら、クエリを実行してパターンと関係を見つけることができます。このセクションでは、グラフィカル インターフェース用のGoogle Cloud コンソールを使用するか、クライアント ライブラリを使用してプログラムでクエリを実行する方法について説明します。

    コンソール

    1. データベースの [概要] ページで、ナビゲーション メニューの [Spanner Studio] をクリックします。

    2. [Spanner Studio] ページで、 [新しいタブ] をクリックするか、エディタタブを使用します。

    3. クエリエディタに以下のクエリを入力します。このクエリでは、Dana が送金したすべてのユーザーと、送金の金額を特定できます。

      GRAPH FinGraph
      MATCH
        (from_person:Person {name: "Dana"})-[:Owns]->
        (from_account:Account)-[transfer:Transfers]->
        (to_account:Account)<-[:Owns]-(to_person:Person)
      RETURN
        from_person.name AS from_account_owner,
        from_account.id AS from_account_id,
        to_person.name AS to_account_owner,
        to_account.id AS to_account_id,
        transfer.amount AS amount
      
    4. [実行] をクリックします。

      [結果] タブには、アカウント番号 20 を所有する Dana からの次のパスが表示されます。

      • Alex が所有する Account {id:7} へのパス。
      • Lee が所有する Account {id:16} へのパス。

        グラフクエリの結果の例。

    クライアント ライブラリ

    Python

    def query_data(instance_id, database_id):
        """Queries sample data from the database using GQL."""
        spanner_client = spanner.Client()
        instance = spanner_client.instance(instance_id)
        database = instance.database(database_id)
    
        with database.snapshot() as snapshot:
            results = snapshot.execute_sql(
                """Graph FinGraph
                MATCH (a:Person)-[o:Owns]->()-[t:Transfers]->()<-[p:Owns]-(b:Person)
                RETURN a.name AS sender, b.name AS receiver, t.amount, t.create_time AS transfer_at"""
            )
    
            for row in results:
                print("sender: {}, receiver: {}, amount: {}, transfer_at: {}".format(*row))
    
    

    Java

    static void query(DatabaseClient dbClient) {
      try (ResultSet resultSet =
          dbClient
              .singleUse() // Execute a single query against Cloud Spanner.
              .executeQuery(
                  Statement.of(
                      "Graph FinGraph MATCH"
                          + " (a:Person)-[o:Owns]->()-[t:Transfers]->()<-[p:Owns]-(b:Person)RETURN"
                          + " a.name AS sender, b.name AS receiver, t.amount, t.create_time AS"
                          + " transfer_at"))) {
        while (resultSet.next()) {
          System.out.printf(
              "%s %s %f %s\n",
              resultSet.getString(0),
              resultSet.getString(1),
              resultSet.getDouble(2),
              resultSet.getTimestamp(3));
        }
      }
    }
    

    Go

    
    import (
    	"context"
    	"fmt"
    	"io"
    	"time"
    
    	"cloud.google.com/go/spanner"
    
    	"google.golang.org/api/iterator"
    )
    
    func queryGraphData(w io.Writer, db string) error {
    	ctx := context.Background()
    	client, err := spanner.NewClient(ctx, db)
    	if err != nil {
    		return err
    	}
    	defer client.Close()
    
    	// Execute a Spanner query statement comprising a graph query. Graph queries
    	// are characterized by 'MATCH' statements describing node and edge
    	// patterns.
    	//
    	// This statement finds entities ('Account's) owned by all 'Person's 'b' to
    	// which transfers have been made by entities ('Account's) owned by any
    	// 'Person' 'a' in the graph called 'FinGraph'. It then returns the names of
    	// all such 'Person's 'a' and 'b', and the amount and time of the transfer.
    	stmt := spanner.Statement{SQL: `Graph FinGraph 
    		 MATCH (a:Person)-[o:Owns]->()-[t:Transfers]->()<-[p:Owns]-(b:Person)
    		 RETURN a.name AS sender, b.name AS receiver, t.amount, t.create_time AS transfer_at`}
    	iter := client.Single().Query(ctx, stmt)
    	defer iter.Stop()
    
    	// The results are returned in tabular form. Iterate over the
    	// result rows and print them.
    	for {
    		row, err := iter.Next()
    		if err == iterator.Done {
    			return nil
    		}
    		if err != nil {
    			return err
    		}
    		var sender, receiver string
    		var amount float64
    		var transfer_at time.Time
    		if err := row.Columns(&sender, &receiver, &amount, &transfer_at); err != nil {
    			return err
    		}
    		fmt.Fprintf(w, "%s %s %f %s\n", sender, receiver, amount, transfer_at.Format(time.RFC3339))
    	}
    }
    

    C++

    void QueryData(google::cloud::spanner::Client client) {
      namespace spanner = ::google::cloud::spanner;
    
      spanner::SqlStatement select(R"""(
        Graph FinGraph
        MATCH (a:Person)-[o:Owns]->()-[t:Transfers]->()<-[p:Owns]-(b:Person)
        RETURN a.name AS sender,
               b.name AS receiver,
               t.amount,
              t.create_time AS transfer_at
      )""");
      using RowType =
          std::tuple<std::string, std::string, double, spanner::Timestamp>;
      auto rows = client.ExecuteQuery(std::move(select));
      for (auto& row : spanner::StreamOf<RowType>(rows)) {
        if (!row) throw std::move(row).status();
        std::cout << "sender: " << std::get<0>(*row) << "\t";
        std::cout << "receiver: " << std::get<1>(*row) << "\t";
        std::cout << "amount: " << std::get<2>(*row) << "\t";
        std::cout << "transfer_at: " << std::get<3>(*row) << "\n";
      }
    
      std::cout << "Query completed for [spanner_query_graph_data]\n";
    }

    クリーンアップ

    このセクションでは、作成したリソースをクリーンアップして、Cloud 請求先アカウントに課金されないようにする方法について説明します。次のステップ セクションで他の Spanner Graph の例を確認する場合は、まだこれらの手順は行わないでください。Spanner インスタンスを削除すると、そのインスタンス内のすべてのデータベースも削除されます。

    データベースの削除

    1. Google Cloud コンソールで、[Spanner インスタンス] に移動します。

      Spanner に移動

    2. インスタンス名(テスト インスタンスなど)をクリックします。

    3. データベース名(example-db など)をクリックします。

    4. [データベースの詳細] ページで [データベースを削除] をクリックします。

    5. データベース名を入力して [削除] をクリックし、データベースの削除を確定します。

    インスタンスの削除

    1. Google Cloud コンソールで、[Spanner インスタンス] ページに移動します。

      [Spanner インスタンス] に移動

    2. テスト インスタンス」など、削除するインスタンスの名前をクリックします。

    3. [インスタンスの削除] をクリックします。

    4. インスタンス名を入力し [削除] をクリックして、インスタンスを削除することを確認します。

    次のステップ