Configure e consulte o Spanner Graph

Este documento mostra como configurar e consultar uma base de dados de grafos do Spanner para modelar, armazenar e analisar relações de dados complexas através da Google Cloud consola e das bibliotecas cliente.

Os seguintes tópicos ajudam a saber como:

Para saber mais sobre os detalhes dos preços do Spanner, consulte o artigo Preços do Spanner.

Para experimentar um codelab, consulte o artigo Introdução ao Spanner Graph.

Antes de começar

  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. Ative a API Spanner:
    Aceda a Ativar API Spanner
    1. Para receber as autorizações de que precisa para criar instâncias e bases de dados, peça ao seu administrador para lhe conceder a função de IAM de administrador do Cloud Spanner (roles/spanner.admin) no seu projeto.

    1. Para receber as autorizações de que precisa para consultar gráficos do Spanner se não lhe for atribuída a função de administrador do Cloud Spanner, peça ao seu administrador para lhe atribuir a função do IAM Leitor da base de dados do Cloud Spanner (roles/spanner.databaseReader) no seu projeto.

    Crie uma instância do Spanner

    Quando usa o Spanner pela primeira vez, tem de criar uma instância, que atribui recursos para bases de dados do Spanner. Esta secção mostra como criar uma instância através da Google Cloud consola.

    1. Na Google Cloud consola, aceda à página Spanner.

      Aceda ao Spanner

    2. Selecione ou crie um Google Cloud projeto, se ainda não o tiver feito.

    3. Efetue um dos seguintes passos:

      1. Se nunca criou uma instância do Spanner, na página Bem-vindo ao Spanner, clique em Criar uma instância aprovisionada.

      2. Se criou uma instância do Spanner, na página Instances, clique em Create instance.

    4. Na página Selecionar uma edição, selecione Enterprise Plus ou Enterprise.

      O Spanner Graph só está disponível na edição Enterprise ou Enterprise Plus. Para comparar as diferentes edições, clique em Comparar edições. Para mais informações, consulte a Vista geral das edições do Spanner.

    5. Clique em Continuar.

    6. Em Nome da instância, introduza um nome da instância, por exemplo, test-instance.

    7. Em ID da instância, mantenha ou altere o ID da instância. O ID da instância é, por predefinição, o nome da instância, mas pode alterá-lo. O nome da instância e o ID da instância podem ser iguais ou diferentes.

    8. Clique em Continuar.

    9. Em Escolha uma configuração, faça o seguinte:

      1. Mantenha a opção Regional selecionada.

      2. Em Selecionar uma configuração, selecione uma região. O Spanner armazena e replica as suas instâncias na região que selecionar.

      3. Clique em Continuar.

    10. Em Configurar capacidade de computação, faça o seguinte:

      1. Em Selecionar unidade, selecione Unidades de processamento (PUs).

      2. Em Escolha um modo de dimensionamento, mantenha a opção Atribuição manual selecionada e, em Quantidade, mantenha 1000 unidades de processamento.

    11. Clique em Criar. A Google Cloud consola mostra a página Vista geral da instância que criou.

    Crie uma base de dados de grafos com um esquema

    Esta secção mostra como criar uma base de dados do Spanner Graph usando um esquema do Spanner Graph. Pode usar a Google Cloud consola ou as bibliotecas de cliente.

    Consola

    1. Na Google Cloud consola, aceda à página Spanner Instances.

      Aceda às instâncias do Spanner

    2. Clique na instância que criou, por exemplo, Test Instance.

    3. Em Vista geral, abaixo do nome da instância, clique em Criar base de dados.

    4. Em Nome da base de dados, introduza um nome da base de dados. Por exemplo, example-db.

    5. Em Selecionar dialeto da base de dados, escolha SQL padrão da Google. O gráfico do Spanner não está disponível no dialeto PostgreSQL.

    6. Introduza o seguinte esquema no separador do editor Modelos de LDD. O esquema contém duas definições de tabelas de nós, Person e Account, e duas definições de tabelas de arestas, PersonOwnAccount e AccountTransferAccount. O Spanner Graph usa tabelas relacionais para definir gráficos, pelo que vê tabelas relacionais e declarações de gráficos no esquema. Para saber mais acerca do esquema do gráfico do Spanner, consulte a vista geral do esquema do gráfico do Spanner:

      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. Mantenha as predefinições em Mostrar opções de encriptação.

    8. Clique em Criar. Google Cloud A consola apresenta a página Vista geral da base de dados que criou.

    Bibliotecas cliente

    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";
    }

    Inserir dados do gráfico

    Depois de criar a base de dados, pode adicionar-lhe dados. Esta secção mostra-lhe como inserir dados no seu gráfico através da consola Google Cloud para uma interface gráfica ou através das bibliotecas cliente por programação.

    Consola

    1. Na página Spanner Studio, clique em Novo separador ou use o separador do editor.

    2. Copie e cole as seguintes declarações de inserção de dados do gráfico nos nós e nas arestas.

      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. Clique em Executar. Quando a execução estiver concluída, o separador Resultados mostra que foram inseridas 3 linhas.

    Bibliotecas cliente

    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";
    }

    O gráfico seguinte mostra as pessoas, as contas, a propriedade das contas e as transferências de contas das inserções:

    Gráfico de exemplo.

    Execute uma consulta de gráfico

    Depois de inserir dados no gráfico, pode consultá-los para encontrar padrões e relações. Esta secção mostra-lhe como executar consultas através da consola para uma interface gráfica ou através de programação com bibliotecas cliente.Google Cloud

    Consola

    1. Na página Vista geral da base de dados, clique em Spanner Studio no menu de navegação.

    2. Na página Spanner Studio, clique em Novo separador ou use o separador do editor.

    3. Introduza a seguinte consulta no editor de consultas. A consulta encontra todas as pessoas a quem a Dana transferiu dinheiro e o valor dessas transferências.

      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. Clique em Executar.

      O separador Resultados apresenta os seguintes caminhos da Daniela, proprietária da conta número 20:

      • Para Account {id:7} pertencente a Alex.
      • Para Account {id:16} propriedade de Lee.

        Exemplo de resultados de consultas de gráficos.

    Bibliotecas cliente

    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";
    }

    Limpar

    Esta secção mostra-lhe como limpar os recursos que criou para evitar incorrer em custos na sua conta do Cloud Billing. Se planeia explorar outros exemplos do Spanner Graph na secção O que fazer a seguir, não execute estes passos ainda. A eliminação de uma instância do Spanner também elimina todas as bases de dados nessa instância.

    Elimine a base de dados

    1. Na Google Cloud consola, aceda a Instâncias do Spanner.

      Aceda ao Spanner

    2. Clique no nome da instância, por exemplo, Instância de teste.

    3. Clique no nome da base de dados, por exemplo, example-db.

    4. Na página Detalhes da base de dados, clique em Eliminar base de dados.

    5. Confirme que quer eliminar a base de dados introduzindo o nome da base de dados e clicando em Eliminar.

    Elimine a instância

    1. Na Google Cloud consola, aceda à página Instâncias do Spanner.

      Aceda às instâncias do Spanner

    2. Clique no nome da instância que quer eliminar, por exemplo, Instância de teste.

    3. Clique em Eliminar instância.

    4. Confirme que quer eliminar a instância introduzindo o nome da instância e clicando em Eliminar.

    O que se segue?