Spanner ile internet bankacılığı uygulaması oluşturma

1. Genel Bakış

Spanner, hem ilişkisel hem de ilişkisel olmayan operasyonel iş yükleri için mükemmel olan, tümüyle yönetilen, yatay olarak ölçeklenebilir, küresel olarak dağıtılmış bir veritabanı hizmetidir. Spanner, temel özelliklerinin ötesinde akıllı ve veriye dayalı uygulamalar oluşturmayı sağlayan güçlü gelişmiş özellikler sunar.

Bu codelab, Spanner'in temel bilgilerini temel alır ve temel olarak bir internet bankacılığı uygulaması kullanarak veri işleme ve analitik özelliklerinizi geliştirmek için gelişmiş entegrasyonlarından yararlanmayı ayrıntılı olarak açıklar.

Üç önemli gelişmiş özelliğe odaklanacağız:

  • Vertex AI entegrasyonu: Spanner'ı Google Cloud'un yapay zeka platformu Vertex AI ile nasıl sorunsuz bir şekilde entegre edeceğinizi öğrenin. Vertex AI modellerini doğrudan Spanner SQL sorgularından nasıl çağıracağınızı öğreneceksiniz. Bu sayede, güçlü veritabanı içi dönüşümler ve tahminler etkinleştirilerek bankacılık uygulamamızın işlemleri bütçe takibi ve anormallik algılama gibi kullanım alanları için otomatik olarak kategorize etmesine olanak tanınacaktır.
  • Tam metin arama: Spanner'da tam metin arama işlevini nasıl uygulayacağınızı öğrenin. İşlem verileriniz genelinde anahtar kelimeye dayalı aramalar yapmak için metin verilerini dizine ekleme ve verimli sorgular yazma konularını keşfederek bankacılık sistemimizde e-posta adresine göre müşterileri verimli bir şekilde bulma gibi güçlü veri keşifleri yapabilirsiniz.
  • BigQuery birleşik sorguları: BigQuery'de bulunan verileri doğrudan sorgulamak için Spanner'ın birleşik sorgu özelliklerinden nasıl yararlanacağınızı keşfedin. Bu sayede, Spanner'ın gerçek zamanlı operasyonel verilerini BigQuery'nin analitik veri kümeleriyle birleştirerek veri kopyalama veya karmaşık ETL süreçleri olmadan kapsamlı analizler ve raporlar oluşturabilirsiniz. Ayrıca, gerçek zamanlı müşteri verilerini BigQuery'deki daha geniş geçmiş trendlerle birleştirerek bankacılık uygulamamızda hedeflenen pazarlama kampanyaları gibi çeşitli kullanım alanlarını destekleyebilirsiniz.

Neler öğreneceksiniz?

  • Spanner örneği oluşturma.
  • Veritabanı ve tablo oluşturma.
  • Spanner veritabanı tablolarınıza veri yükleme.
  • Spanner'dan Vertex AI modellerini çağırma.
  • Bulanık arama ve tam metin aramayı kullanarak Spanner veritabanınızı sorgulama.
  • BigQuery'den Spanner'a karşı birleşik sorgular gerçekleştirme.
  • Spanner örneğinizi silme.

İhtiyacınız olanlar

  • Bir faturalandırma hesabına bağlı Google Cloud projesi.
  • Chrome veya Firefox gibi bir web tarayıcısı.

2. Kurulum ve şartlar

Proje oluşturma

Faturalandırma özelliğinin etkin olduğu bir Google Cloud projeniz varsa konsolun sol üst kısmındaki proje seçim açılır menüsünü tıklayın:

Mevcut proje

Seçili bir projeyle Gerekli API'leri etkinleştirme bölümüne atlayın.

Google Hesabınız (Gmail veya Google Apps) yoksa hesap oluşturmanız gerekir. Google Cloud Platform Console'da (console.cloud.google.com) oturum açın ve yeni bir proje oluşturun.

Yeni bir proje oluşturmak için açılan iletişim kutusunda "YENİ PROJE" düğmesini tıklayın:

Yeni proje

Henüz projeniz yoksa ilk projenizi oluşturmak için aşağıdaki gibi bir iletişim kutusu görürsünüz:

Proje iletişim kutusu

Ardından açılan proje oluşturma iletişim kutusunda yeni projenizin ayrıntılarını girebilirsiniz.

Tüm Google Cloud projeleri genelinde benzersiz bir ad olan proje kimliğini not edin. Bu kod laboratuvarının ilerleyen bölümlerinde PROJECT_ID olarak atıfta bulunulacak.

Proje ayrıntıları

Ardından, Google Cloud kaynaklarını kullanmak ve Spanner API, Vertex AI API, BigQuery API ve BigQuery Connection API'yi etkinleştirmek için Developers Console'da faturalandırmayı etkinleştirmeniz gerekir.

Proje faturalandırması

Spanner fiyatlandırması burada açıklanmıştır. Diğer kaynaklarla ilişkili diğer maliyetler, ilgili fiyatlandırma sayfalarında belirtilir.

Google Cloud Platform'un yeni kullanıcıları 300 ABD doları değerindeki ücretsiz deneme sürümünden yararlanabilir.

Google Cloud Shell Kurulumu

Bu kod laboratuvarında, Cloud'da çalışan bir komut satırı ortamı olan Google Cloud Shell'i kullanacağız.

Debian tabanlı bu sanal makinede ihtiyacınız olan tüm geliştirme araçları yüklüdür. 5 GB boyutunda kalıcı bir ana dizin sunar ve Google Cloud'da çalışır. Bu sayede ağ performansını ve kimlik doğrulamayı büyük ölçüde iyileştirir. Bu nedenle, bu codelab için tek ihtiyacınız bir tarayıcıdır.

Cloud Shell'i Cloud Console'dan etkinleştirmek için Cloud Shell'i etkinleştir'i Cloud Shell simgesi tıklamanız yeterlidir (ortam sağlanıp bağlantı kurulabilmesi için birkaç dakika beklemeniz gerekir).

Cloud Shell

Cloud Shell'e bağlandıktan sonra kimliğinizin doğrulandığını ve projenin PROJECT_ID olarak ayarlandığını görürsünüz.

gcloud auth list

Beklenen çıkış:

Credentialed Accounts

ACTIVE: *
ACCOUNT: <myaccount>@<mydomain>.com
gcloud config list project

Beklenen çıkış:

[core]
project = <PROJECT_ID>

Proje herhangi bir nedenle ayarlanmamışsa aşağıdaki komutu verin:

gcloud config set project <PROJECT_ID>

PROJECT_ID cihazınızı mı arıyorsunuz? Kurulum adımlarında hangi kimliği kullandığınızı kontrol edin veya Cloud Console kontrol panelinde arayın:

Proje kimliği

Cloud Shell, bazı ortam değişkenlerini varsayılan olarak da ayarlar. Bu değişkenler, gelecekte komut çalıştırırken faydalı olabilir.

echo $GOOGLE_CLOUD_PROJECT

Beklenen çıkış:

<PROJECT_ID>

Gerekli API'leri etkinleştirme

Projeniz için Spanner, Vertex AI ve BigQuery API'lerini etkinleştirin:

gcloud services enable spanner.googleapis.com
gcloud services enable aiplatform.googleapis.com
gcloud services enable bigquery.googleapis.com
gcloud services enable bigqueryconnection.googleapis.com

Özet

Bu adımda, projeniz yoksa projenizi oluşturdunuz, Cloud Shell'i etkinleştirdiniz ve gerekli API'leri etkinleştirdiniz.

Sıradaki

Ardından Spanner örneğini ayarlayacaksınız.

3. Spanner örneği oluşturma

Spanner örneğini oluşturma

Bu adımda, codelab için bir Spanner örneği oluşturacaksınız. Bunun için Cloud Shell'i açıp şu komutu çalıştırın:

export SPANNER_INSTANCE=cloudspanner-onlinebanking
gcloud spanner instances create $SPANNER_INSTANCE \
  --config=regional-us-central1 \
  --description="Spanner Online Banking" \
  --nodes=1 \
  --edition=ENTERPRISE \
  --default-backup-schedule-type=NONE

Beklenen çıkış:

Creating instance...done.

Özet

Bu adımda Spanner örneğini oluşturdunuz.

Sıradaki

Ardından, ilk uygulamayı hazırlayıp veritabanını ve şemayı oluşturursunuz.

4. Veritabanı ve şema oluşturma

İlk başvuruyu hazırlama

Bu adımda, kod aracılığıyla veritabanını ve şemayı oluşturacaksınız.

Öncelikle Maven kullanarak onlinebanking adlı bir Java uygulaması oluşturun:

mvn -B archetype:generate \
  -DarchetypeGroupId=org.apache.maven.archetypes \
  -DgroupId=com.google.codelabs \
  -DartifactId=onlinebanking \
  -DjavaCompilerVersion=1.8 \
  -DjunitVersion=4.13.2 \
  -DarchetypeVersion=1.5

Veritabanına ekleyeceğimiz veri dosyalarını kontrol edip kopyalayın (kod deposu için buraya bakın):

git clone https://github.com/GoogleCloudPlatform/cloud-spanner-samples.git
cp -r ./cloud-spanner-samples/banking/data ./onlinebanking

Uygulama klasörüne gidin:

cd onlinebanking

Maven pom.xml dosyasını açın. Google Cloud kitaplıklarının sürümünü yönetmek için Maven BOM'u kullanmak üzere bağımlılık yönetimi bölümünü ekleyin:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>libraries-bom</artifactId>
      <version>26.56.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Düzenleyici ve dosya şu şekilde görünür: Cloud Shell

dependencies bölümünde, uygulamanın kullanacağı kitaplıkların yer aldığından emin olun:

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-nop</artifactId>
    <version>2.0.9</version>
  </dependency>
  <dependency>
    <groupId>com.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>5.10</version>
  </dependency>
  <dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-spanner</artifactId>
  </dependency>
  <dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-bigquery</artifactId>
  </dependency>
  <dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-bigqueryconnection</artifactId>
  </dependency>
</dependencies>

Son olarak, derleme eklentilerini, uygulamanın çalıştırılabilir bir JAR dosyasına paketlenmesi için değiştirin:

<build>
  <plugins>
    <plugin>
      <artifactId>maven-resources-plugin</artifactId>
      <version>3.3.1</version>
      <executions>
        <execution>
          <id>copy-resources</id>
          <phase>process-resources</phase>
          <goals>
            <goal>copy-resources</goal>
          </goals>
          <configuration>
            <outputDirectory>${project.build.directory}/${project.artifactId}-resources</outputDirectory>
            <resources>
              <resource>
                <directory>resources</directory>
                <filtering>true</filtering>
              </resource>
            </resources>
          </configuration>
        </execution>
      </executions>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-dependency-plugin</artifactId>
      <version>3.8.1</version>
      <executions>
        <execution>
          <id>copy-dependencies</id>
          <phase>prepare-package</phase>
          <goals>
            <goal>copy-dependencies</goal>
          </goals>
          <configuration>
            <outputDirectory>${project.build.directory}/${project.artifactId}-resources/lib</outputDirectory>
            <overWriteReleases>false</overWriteReleases>
            <overWriteSnapshots>false</overWriteSnapshots>
            <overWriteIfNewer>true</overWriteIfNewer>
          </configuration>
        </execution>
      </executions>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <version>3.4.2</version>
      <configuration>
        <finalName>${project.artifactId}</finalName>
        <outputDirectory>${project.build.directory}</outputDirectory>
        <archive>
          <index>false</index>
          <manifest>
            <mainClass>com.google.codelabs.App</mainClass>
            <addClasspath>true</addClasspath>
            <classpathPrefix>${project.artifactId}-resources/lib/</classpathPrefix>
          </manifest>
        </archive>
      </configuration>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-failsafe-plugin</artifactId>
      <version>3.2.5</version>
      <executions>
        <execution>
          <goals>
            <goal>integration-test</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>3.2.5</version>
      <configuration>
        <useSystemClassLoader>false</useSystemClassLoader>
      </configuration>
    </plugin>
  </plugins>
</build>

Cloud Shell Düzenleyici'nin"Dosya" menüsünden "Kaydet"i seçerek veya Ctrl+S tuşuna basarak pom.xml dosyasında yaptığınız değişiklikleri kaydedin.

Bağımlılıklar hazır olduğunda uygulamaya bir şema, bazı dizinler (arama dahil) ve uzak bir uç noktaya bağlı bir yapay zeka modeli oluşturacak kod ekleyeceksiniz. Bu yapıları temel alacak ve bu codelab'de bu sınıfa daha fazla yöntem ekleyeceksiniz.

onlinebanking/src/main/java/com/google/codelabs altındaki App.java dosyasını açın ve içeriği aşağıdaki kodla değiştirin:

package com.google.codelabs;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;

import com.google.api.gax.longrunning.OperationFuture;
import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.spanner.Database;
import com.google.cloud.spanner.DatabaseAdminClient;
import com.google.cloud.spanner.DatabaseClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;

public class App {

  // Create the Spanner database and schema
  public static void create(DatabaseAdminClient dbAdminClient, DatabaseId db,
      String location, String model) {
    System.out.println("Creating Spanner database...");
    List<String> statements = Arrays.asList(
      "CREATE TABLE Customers (\n"
          + "  CustomerId INT64 NOT NULL,\n"
          + "  FirstName STRING(256) NOT NULL,\n"
          + "  LastName STRING(256) NOT NULL,\n"
          + "  FullName STRING(512) AS (FirstName || ' ' || LastName) STORED,\n"
          + "  Email STRING(512) NOT NULL,\n"
          + "  EmailTokens TOKENLIST AS\n"
          + "    (TOKENIZE_SUBSTRING(Email, ngram_size_min=>2, ngram_size_max=>3,\n"
          + "      relative_search_types=>[\"all\"])) HIDDEN,\n"
          + "  Address STRING(MAX)\n"
          + ") PRIMARY KEY (CustomerId)",

      "CREATE INDEX CustomersByEmail\n"
          + "ON Customers(Email)",

      "CREATE SEARCH INDEX CustomersFuzzyEmail\n"
          + "ON Customers(EmailTokens)",

      "CREATE TABLE Accounts (\n"
          + "  AccountId INT64 NOT NULL,\n"
          + "  CustomerId INT64 NOT NULL,\n"
          + "  AccountType STRING(256) NOT NULL,\n"
          + "  Balance NUMERIC NOT NULL,\n"
          + "  OpenDate TIMESTAMP NOT NULL\n"
          + ") PRIMARY KEY (AccountId)",

      "CREATE INDEX AccountsByCustomer\n"
          + "ON Accounts (CustomerId)",

      "CREATE TABLE TransactionLedger (\n"
          + "  TransactionId INT64 NOT NULL,\n"
          + "  AccountId INT64 NOT NULL,\n"
          + "  TransactionType STRING(256) NOT NULL,\n"
          + "  Amount NUMERIC NOT NULL,\n"
          + "  Timestamp TIMESTAMP NOT NULL"
          + "  OPTIONS(allow_commit_timestamp=true),\n"
          + "  Category STRING(256),\n"
          + "  Description STRING(MAX),\n"
          + "  CategoryTokens TOKENLIST AS (TOKENIZE_FULLTEXT(Category)) HIDDEN,\n"
          + "  DescriptionTokens TOKENLIST AS (TOKENIZE_FULLTEXT(Description)) HIDDEN\n"
          + ") PRIMARY KEY (AccountId, TransactionId),\n"
          + "INTERLEAVE IN PARENT Accounts ON DELETE CASCADE",

      "CREATE INDEX TransactionLedgerByAccountType\n"
          + "ON TransactionLedger(AccountId, TransactionType)",

      "CREATE INDEX TransactionLedgerByCategory\n"
          + "ON TransactionLedger(AccountId, Category)",

      "CREATE SEARCH INDEX TransactionLedgerTextSearch\n"
          + "ON TransactionLedger(CategoryTokens, DescriptionTokens)",

      "CREATE MODEL TransactionCategoryModel\n"
          + "INPUT (prompt STRING(MAX))\n"
          + "OUTPUT (content STRING(MAX))\n"
          + "REMOTE OPTIONS (\n"
          + "  endpoint = '//aiplatform.googleapis.com/projects/" + db.getInstanceId().getProject()
              + "/locations/" + location + "/publishers/google/models/" + model + "',\n"
          + "  default_batch_size = 1\n"
          + ")");
    OperationFuture<Database, CreateDatabaseMetadata> op = dbAdminClient.createDatabase(
        db.getInstanceId().getInstance(),
        db.getDatabase(),
        statements);
    try {
      Database dbOperation = op.get();
      System.out.println("Created Spanner database [" + dbOperation.getId() + "]");
    } catch (ExecutionException e) {
      throw (SpannerException) e.getCause();
    } catch (InterruptedException e) {
      throw SpannerExceptionFactory.propagateInterrupt(e);
    }
  }

  static void printUsageAndExit() {
    System.out.println("Online Online Banking Application 1.0.0");
    System.out.println("Usage:");
    System.out.println("  java -jar target/onlinebanking.jar <command> [command_option(s)]");
    System.out.println("");
    System.out.println("Examples:");
    System.out.println("  java -jar target/onlinebanking.jar create");
    System.out.println("      - Create a sample Spanner database and schema in your "
        + "project.\n");
    System.exit(1);
  }

  public static void main(String[] args) {
    if (args.length < 1) {
      printUsageAndExit();
    }

    String instanceId = System.getProperty("SPANNER_INSTANCE", System.getenv("SPANNER_INSTANCE"));
    String databaseId = System.getProperty("SPANNER_DATABASE", System.getenv("SPANNER_DATABASE"));
    String location = System.getenv().getOrDefault("SPANNER_LOCATION", "us-central1");
    String model = System.getenv().getOrDefault("SPANNER_MODEL", "gemini-2.0-flash-lite");
    if (instanceId == null || databaseId == null) {
      System.err.println("Missing one or more required environment variables: SPANNER_INSTANCE or "
          + "SPANNER_DATABASE");
      System.exit(1);
    }

    BigQueryOptions bigqueryOptions = BigQueryOptions.newBuilder().build();
    BigQuery bigquery = bigqueryOptions.getService();

    SpannerOptions spannerOptions = SpannerOptions.newBuilder().build();
    try (Spanner spanner = spannerOptions.getService()) {
      String command = args[0];
      DatabaseId db = DatabaseId.of(spannerOptions.getProjectId(), instanceId, databaseId);
      DatabaseClient dbClient = spanner.getDatabaseClient(db);
      DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient();

      switch (command) {
        case "create":
          create(dbAdminClient, db, location, model);
          break;
        default:
          printUsageAndExit();
      }
    }
  }
}

Değişiklikleri App.java dosyasına kaydedin.

Kodunuzun oluşturduğu farklı öğelere göz atın ve uygulama JAR'ını derleyin:

mvn package

Beklenen çıkış:

[INFO] Building jar: /home/your_user/onlinebanking/target/onlinebanking.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

Kullanım bilgilerini görmek için uygulamayı çalıştırın:

java -jar target/onlinebanking.jar

Beklenen çıkış:

Online Banking Application 1.0.0
Usage:
  java -jar target/onlinebanking.jar <command> [command_option(s)]

Examples:
  java -jar target/onlinebanking.jar create
      - Create a sample Spanner database and schema in your project.

Veritabanı ve şemayı oluşturma

Gerekli uygulama ortamı değişkenlerini ayarlayın:

export SPANNER_INSTANCE=cloudspanner-onlinebanking
export SPANNER_DATABASE=onlinebanking

create komutunu çalıştırarak veritabanını ve şemayı oluşturun:

java -jar target/onlinebanking.jar create

Beklenen çıkış:

Creating Spanner database...
Created Spanner database [<DATABASE_RESOURCE_NAME>]

Spanner'da şemayı kontrol etme

Spanner konsolunda, yeni oluşturulan örneğinize ve veritabanınıza gidin.

Accounts, Customers ve TransactionLedger olmak üzere 3 tabloyu görürsünüz.

Şemayı görüntüleme

Bu işlem, Accounts, Customers ve TransactionLedger tabloları da dahil olmak üzere veritabanı şemasını, optimize edilmiş veri alma için ikincil dizinleri ve bir Vertex AI model referansını oluşturur.

Varlık İlişki Şeması

TransactionLedger tablosu, iyileştirilmiş veri yerelliği aracılığıyla hesaba özgü işlemler için sorgu performansını artırmak amacıyla Hesaplar tablosuna yerleştirilir.

Bu kod laboratuvarının kullanıldığı yaygın veri erişimi kalıplarını (ör. tam ve bulanık e-posta adresine göre müşteri aramaları, müşteriye göre hesapları alma ve işlem verilerini verimli bir şekilde sorgulama ve arama) optimize etmek için ikincil dizinler (CustomersByEmail, CustomersFuzzyEmail, AccountsByCustomer, TransactionLedgerByAccountType, TransactionLedgerByCategory, TransactionLedgerTextSearch) uygulandı.

TransactionCategoryModel, bu kod laboratuvarında dinamik işlem sınıflandırması için kullanılan bir LLM'ye doğrudan SQL çağrıları yapmak üzere Vertex AI'dan yararlanır.

Özet

Bu adımda Spanner veritabanını ve şemasını oluşturdunuz.

Sıradaki

Ardından, örnek uygulama verilerini yüklersiniz.

5. Verileri yükle

Artık CSV dosyalarındaki örnek verileri veritabanına yükleyen işlevleri ekleyeceksiniz.

App.java'ü açın ve içe aktarma işlemlerini değiştirerek başlayın:

package com.google.codelabs;

import java.io.FileReader;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutionException;

import com.google.api.gax.longrunning.OperationFuture;
import com.google.cloud.Timestamp;
import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.spanner.Database;
import com.google.cloud.spanner.DatabaseAdminClient;
import com.google.cloud.spanner.DatabaseClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Key;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.Statement;
import com.google.cloud.spanner.Struct;
import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;
import com.opencsv.CSVReader;

Ardından, ekleme yöntemlerini App sınıfına ekleyin:

  // Insert customers from CSV
  public static void insertCustomers(DatabaseClient dbClient) {
    System.out.println("Inserting customers...");
    dbClient
        .readWriteTransaction()
        .run(transaction -> {
          int count = 0;
          List<Statement> statements = new ArrayList<>();
          try (CSVReader reader = new CSVReader(new FileReader("data/customers.csv"))) {
            reader.skip(1);
            String[] line;
            while ((line = reader.readNext()) != null) {
              Statement statement = Statement.newBuilder(
                  "INSERT INTO Customers (CustomerId, FirstName, LastName, Email, Address) "
                      + "VALUES (@customerId, @firstName, @lastName, @email, @address)")
                  .bind("customerId").to(Long.parseLong(line[0]))
                  .bind("firstName").to(line[1])
                  .bind("lastName").to(line[2])
                  .bind("email").to(line[3])
                  .bind("address").to(line[4])
                  .build();
              statements.add(statement);
              count++;
            }
            transaction.batchUpdate(statements);
            System.out.println("Inserted " + count + " customers");
            return null;
          }
        });
  }

  // Insert accounts from CSV
  public static void insertAccounts(DatabaseClient dbClient) {
    System.out.println("Inserting accounts...");
    dbClient
        .readWriteTransaction()
        .run(transaction -> {
          int count = 0;
          List<Statement> statements = new ArrayList<>();
          try (CSVReader reader = new CSVReader(new FileReader("data/accounts.csv"))) {
            reader.skip(1);
            String[] line;
            while ((line = reader.readNext()) != null) {
              Statement statement = Statement.newBuilder(
                "INSERT INTO Accounts (AccountId, CustomerId, AccountType, Balance, OpenDate) "
                    + "VALUES (@accountId, @customerId, @accountType, @balance, @openDate)")
                .bind("accountId").to(Long.parseLong(line[0]))
                .bind("customerId").to(Long.parseLong(line[1]))
                .bind("accountType").to(line[2])
                .bind("balance").to(new BigDecimal(line[3]))
                .bind("openDate").to(line[4])
                .build();
              statements.add(statement);
              count++;
            }
            transaction.batchUpdate(statements);
            System.out.println("Inserted " + count + " accounts");
            return null;
          }
        });
  }

  // Insert transactions from CSV
  public static void insertTransactions(DatabaseClient dbClient) {
    System.out.println("Inserting transactions...");
    dbClient
        .readWriteTransaction()
        .run(transaction -> {
          int count = 0;
          List<Statement> statements = new ArrayList<>();
          try (CSVReader reader = new CSVReader(new FileReader("data/transactions.csv"))) {
            reader.skip(1);
            String[] line;

            // Specify timestamps that are within last 30 days
            Random random = new Random();
            Instant startTime = Instant.now().minus(15, ChronoUnit.DAYS);
            Instant currentTimestamp = startTime;

            Map<Long, BigDecimal> balanceChanges = new HashMap<>();
            while ((line = reader.readNext()) != null) {
              long accountId = Long.parseLong(line[1]);
              String transactionType = line[2];
              BigDecimal amount = new BigDecimal(line[3]);
              int randomMinutes = random.nextInt(60) + 1;
              currentTimestamp = currentTimestamp.plus(Duration.ofMinutes(randomMinutes));
              Timestamp timestamp = Timestamp.ofTimeSecondsAndNanos(
                  currentTimestamp.getEpochSecond(), currentTimestamp.getNano());
              Statement statement = Statement.newBuilder(
                "INSERT INTO TransactionLedger (TransactionId, AccountId, TransactionType, Amount,"
                    + "Timestamp, Category, Description) "
                    + "VALUES (@transactionId, @accountId, @transactionType, @amount, @timestamp,"
                    + "@category, @description)")
                .bind("transactionId").to(Long.parseLong(line[0]))
                .bind("accountId").to(accountId)
                .bind("transactionType").to(transactionType)
                .bind("amount").to(amount)
                .bind("timestamp").to(timestamp)
                .bind("category").to(line[5])
                .bind("description").to(line[6])
                .build();
              statements.add(statement);

              // Track balance changes per account
              BigDecimal balanceChange = balanceChanges.getOrDefault(accountId,
                  BigDecimal.ZERO);
              if ("Credit".equalsIgnoreCase(transactionType)) {
                balanceChanges.put(accountId, balanceChange.add(amount));
              } else if ("Debit".equalsIgnoreCase(transactionType)) {
                balanceChanges.put(accountId, balanceChange.subtract(amount));
              } else {
                System.err.println("Unsupported transaction type: " + transactionType);
                continue;
              }

              count++;
            }

            // Apply final balance updates
            for (Map.Entry<Long, BigDecimal> entry : balanceChanges.entrySet()) {
              long accountId = entry.getKey();
              BigDecimal balanceChange = entry.getValue();

              Struct row = transaction.readRow(
                  "Accounts",
                  Key.of(accountId),
                  List.of("Balance"));
              if (row != null) {
                BigDecimal currentBalance = row.getBigDecimal("Balance");
                BigDecimal updatedBalance = currentBalance.add(balanceChange);
                Statement statement = Statement.newBuilder(
                  "UPDATE Accounts SET Balance = @balance WHERE AccountId = @accountId")
                  .bind("accountId").to(accountId)
                  .bind("balance").to(updatedBalance)
                  .build();
                statements.add(statement);
              }
            }

            transaction.batchUpdate(statements);
            System.out.println("Inserted " + count + " transactions");
          }
          return null;
        });
  }

switch (command) içine ekleme için main yöntemine başka bir case ifadesi ekleyin:

        case "insert":
          String insertType = (args.length >= 2) ? args[1] : "";
          if (insertType.equals("customers")) {
            insertCustomers(dbClient);
          } else if (insertType.equals("accounts")) {
            insertAccounts(dbClient);
          } else if (insertType.equals("transactions")) {
            insertTransactions(dbClient);
          } else {
            insertCustomers(dbClient);
            insertAccounts(dbClient);
            insertTransactions(dbClient);
          }
          break;

Son olarak, printUsageAndExit yöntemine ekleme işlevinin nasıl kullanılacağını ekleyin:

    System.out.println("  java -jar target/onlinebanking.jar insert");
    System.out.println("      - Insert sample Customers, Accounts, and Transactions into the "
        + "database.\n");

App.java dosyasına yaptığınız değişiklikleri kaydedin.

Uygulamayı yeniden oluşturun:

mvn package

insert komutunu çalıştırarak örnek verileri ekleyin:

java -jar target/onlinebanking.jar insert

Beklenen çıkış:

Inserting customers...
Inserted 100 customers
Inserting accounts...
Inserted 125 accounts
Inserting transactions...
Inserted 200 transactions

Spanner Console'da, örneğiniz ve veritabanınız için Spanner Studio'ya geri dönün. Ardından TransactionLedger tablosunu seçin ve verilerin yüklendiğini doğrulamak için kenar çubuğundaki "Veriler"i tıklayın. Tabloda 200 satır olmalıdır.

Verileri göster

Özet

Bu adımda, örnek verileri veritabanına eklediniz.

Sıradaki

Ardından, bankacılık işlemlerini doğrudan Spanner SQL'de otomatik olarak sınıflandırmak için Vertex AI entegrasyonundan yararlanacaksınız.

6. Vertex AI ile verileri kategorilere ayırma

Bu adımda, mali işlemlerinizi doğrudan Spanner SQL'de otomatik olarak kategorize etmek için Vertex AI'ın gücünden yararlanacaksınız. Vertex AI ile önceden eğitilmiş bir model seçebilir veya kendi modelinizi eğitip dağıtabilirsiniz. Kullanılabilir modelleri Vertex AI Model Garden'da görebilirsiniz.

Bu codelab'de Gemini modellerinden birini, Gemini Flash Lite modelini kullanacağız. Gemini'ın bu sürümü uygun maliyetli olmasına rağmen günlük iş yüklerinin çoğunu yönetebilir.

Şu anda açıklamaya bağlı olarak kategorize etmek istediğimiz (groceries, transportation vb.) bir dizi finansal işlemimiz var. Bunu, Spanner'a bir model kaydedip yapay zeka modelini çağırmak için ML.PREDICT'ü kullanarak yapabiliriz.

Bankacılık uygulamamızda, hizmetleri kişiselleştirmek, anormallikleri daha etkili bir şekilde tespit etmek veya müşteriye bütçesini aylık olarak takip etme olanağı sunmak için müşteri davranışları hakkında daha ayrıntılı analizler elde etmek amacıyla işlemleri kategorilere ayırmak isteyebiliriz.

İlk adım, veritabanını ve şemayı oluştururken atılmıştı. Bu işlem, aşağıdaki gibi bir model oluşturdu:

create model ifadesi

Ardından, uygulamaya ML.PREDICT işlevini çağıracak bir yöntem ekleyeceğiz.

App.java'ü açın ve categorize yöntemini ekleyin:

  // Use Vertex AI to set the category of transactions
  public static void categorize(DatabaseClient dbClient) {
    System.out.println("Categorizing transactions...");
    try {
      // Create a prompt to instruct the LLM how to categorize the transactions
      String categories = String.join(", ", Arrays.asList("Entertainment", "Gifts", "Groceries",
          "Investment", "Medical", "Movies", "Online Shopping", "Other", "Purchases", "Refund",
          "Restaurants", "Salary", "Transfer", "Transportation", "Utilities"));
      String prompt = "Categorize the following financial activity into one of these "
          + "categories: " +  categories + ". Return Other if the description cannot be mapped to "
          + "one of these categories.  Only return the exact category string, no other text or "
          + "punctuation or reasoning. Description: ";
      String sql = "UPDATE TransactionLedger SET Category = (\n"
          + "  SELECT content FROM ML.PREDICT(MODEL `TransactionCategoryModel`, (\n"
          + "    SELECT CONCAT('" + prompt + "', CASE WHEN TRIM(Description) = ''\n"
          + "    THEN 'Other' ELSE Description END) AS prompt\n"
          + "  ))\n"
          + ") WHERE TRUE";

      // Use partitioned update to batch update a large number of rows
      dbClient.executePartitionedUpdate(Statement.of(sql));
      System.out.println("Completed categorizing transactions");
    } catch (SpannerException e) {
      throw e;
    }
  }

Kategorize etmek için main yöntemine başka bir case ifadesi ekleyin:

        case "categorize":
          categorize(dbClient);
          break;

Son olarak, kategorize et yönteminin nasıl kullanılacağını printUsageAndExit yöntemine ekleyin:

    System.out.println("  java -jar target/onlinebanking.jar categorize");
    System.out.println("      - Use AI to categorize transactions in the database.\n");

App.java dosyasına yaptığınız değişiklikleri kaydedin.

Uygulamayı yeniden oluşturun:

mvn package

categorize komutunu çalıştırarak veritabanındaki işlemleri kategorilere ayırın:

java -jar target/onlinebanking.jar categorize

Beklenen çıkış:

Categorizing transactions...
Completed categorizing transactions

Spanner Studio'da TransactionLedger tablosu için Verileri Önizle ifadesini çalıştırın. Category sütunu artık tüm satırlar için doldurulmuştur.

Kategorize edilmiş verileri görüntüleme

İşlemleri kategorilere ayırdığımıza göre bu bilgileri şirket içi veya müşteriye yönelik sorgular için kullanabiliriz. Bir sonraki adımda, belirli bir müşterinin bir kategoride ayda ne kadar harcama yaptığını nasıl bulacağınıza bakacağız.

Özet

Bu adımda, verilerinizi yapay zeka destekli olarak kategorize etmek için önceden eğitilmiş bir model kullandınız.

Sıradaki

Ardından, bulanık ve tam metin aramaları yapmak için jeton oluşturma işlemini kullanacaksınız.

7. Tam metin aramayı kullanarak sorgu oluşturma

Sorgu kodunu ekleme

Spanner birçok tam metin arama sorgusu sağlar. Bu adımda, tam eşleşme araması, ardından bulanık arama ve tam metin araması yaparsınız.

App.java'ü açın ve içe aktarma işlemlerini değiştirerek başlayın:

package com.google.codelabs;

import java.io.FileReader;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import com.google.api.gax.longrunning.OperationFuture;
import com.google.cloud.Timestamp;
import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.spanner.Database;
import com.google.cloud.spanner.DatabaseAdminClient;
import com.google.cloud.spanner.DatabaseClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Key;
import com.google.cloud.spanner.ReadOnlyTransaction;
import com.google.cloud.spanner.ResultSet;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.Statement;
import com.google.cloud.spanner.Struct;
import com.google.cloud.spanner.TimestampBound;
import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;
import com.opencsv.CSVReader;

Ardından sorgu yöntemlerini ekleyin:

  // Get current account balance(s) by customer
  public static void getBalance(DatabaseClient dbClient, long customerId) {
    String query = "SELECT AccountId, Balance\n"
        + "FROM Accounts\n"
        + "WHERE CustomerId = @customerId";
    Statement statement = Statement.newBuilder(query)
        .bind("customerId").to(customerId)
        .build();

    // Ignore ongoing transactions, use stale reads as seconds-old data is sufficient
    TimestampBound stalenessBound = TimestampBound.ofMaxStaleness(5, TimeUnit.SECONDS);
    try (ReadOnlyTransaction transaction = dbClient.singleUseReadOnlyTransaction(stalenessBound);
        ResultSet resultSet = transaction.executeQuery(statement);) {
      System.out.println("Account balances for customer " + customerId + ":");
      while (resultSet.next()) {
        System.out.println("  Account " + resultSet.getLong("AccountId") + ": "
            + resultSet.getBigDecimal("Balance"));
      }
    }
  }

  // Find customers by email
  public static void findCustomers(DatabaseClient dbClient, String email) {
    // Query using fuzzy search (ngrams) to allow for spelling mistakes
    String query = "SELECT CustomerId, Email\n"
        + "FROM Customers\n"
        + "WHERE SEARCH_NGRAMS(EmailTokens, @email)\n"
        + "ORDER BY SCORE_NGRAMS(EmailTokens, @email) DESC\n"
        + "LIMIT 10";
    Statement statement = Statement.newBuilder(query)
        .bind("email").to(email)
        .build();

    try (ReadOnlyTransaction transaction = dbClient.singleUseReadOnlyTransaction();
        ResultSet resultSet = transaction.executeQuery(statement)) {
      System.out.println("Customer emails matching " + email + " (top 10 matches):");
      while (resultSet.next()) {
        System.out.println("  Customer " + resultSet.getLong("CustomerId") + ": "
            + resultSet.getString("Email"));
      }
    }
  }

  // Get total monthly spending for a customer by category
  public static void getSpending(DatabaseClient dbClient, long customerId, String category) {
    // Query category using full-text search
    String query = "SELECT SUM(Amount) as TotalSpending\n"
        + "FROM TransactionLedger t\n"
        + "JOIN Accounts a\n"
        + "  ON t.AccountId = a.AccountId\n"
        + "WHERE t.TransactionType = 'Debit'\n"
        + "  AND a.CustomerId = @customerId\n"
        + "  AND t.Timestamp >= TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL -30 DAY)\n"
        + "  AND (SEARCH(t.CategoryTokens, @category) OR SEARCH(t.DescriptionTokens, @category))";
    Statement statement = Statement.newBuilder(query)
        .bind("customerId").to(customerId)
        .bind("category").to(category)
        .build();

    try (ReadOnlyTransaction transaction = dbClient.singleUseReadOnlyTransaction();
        ResultSet resultSet = transaction.executeQuery(statement);) {
      System.out.println("Total spending for customer " + customerId + " under category "
          + category + ":");
      while (resultSet.next()) {
        BigDecimal totalSpending = BigDecimal.ZERO;
        if (!resultSet.isNull("TotalSpending")) {
          totalSpending = resultSet.getBigDecimal("TotalSpending");
        }
        System.out.println("  " + totalSpending);
      }
    }
  }

Sorgu için main yöntemine başka bir case ifadesi ekleyin:

        case "query":
          String queryType = (args.length >= 2) ? args[1] : "";
          if (queryType.equals("balance")) {
            long customerId = (args.length >= 3) ? Long.parseLong(args[2]) : 1L;
            getBalance(dbClient, customerId);
          } else if (queryType.equals("email")) {
            String email = (args.length >= 3) ? args[2] : "";
            findCustomers(dbClient, email);
          } else if (queryType.equals("spending")) {
            long customerId = (args.length >= 3) ? Long.parseLong(args[2]) : 1L;
            String category = (args.length >= 4) ? args[3] : "";
            getSpending(dbClient, customerId, category);
          } else {
            printUsageAndExit();
          }
          break;

Son olarak, sorgu komutlarının printUsageAndExit yönteminde nasıl kullanılacağını ekleyin:

    System.out.println("  java -jar target/onlinebanking.jar query balance 1");
    System.out.println("      - Query customer account balance(s) by customer id.\n");
    System.out.println("  java -jar target/onlinebanking.jar query email madi");
    System.out.println("      - Find customers by email using fuzzy search.\n");
    System.out.println("  java -jar target/onlinebanking.jar query spending 1 groceries");
    System.out.println("      - Query customer spending by customer id and category using "
        + "full-text search.\n");

App.java dosyasına yaptığınız değişiklikleri kaydedin.

Uygulamayı yeniden oluşturun:

mvn package

Müşteri hesabı bakiyeleri için tam eşleme araması yapma

Tam eşleme sorgusu, bir terimle tam olarak eşleşen eşleşen satırları arar.

Veritabanı ve şemayı oluşturduğunuzda performansı artırmak için bir dizin eklenmiştir:

  "CREATE INDEX AccountsByCustomer\n"
          + "ON Accounts (CustomerId)",

getBalance yöntemi, sağlanan customerId ile eşleşen müşterileri bulmak için bu dizini dolaylı olarak kullanır ve ayrıca söz konusu müşteriye ait hesaplarla da birleştirme yapar.

Sorgu, doğrudan Spanner Studio'da yürütüldüğünde şu şekilde görünür: bakiyeyi manuel olarak sorgulamak

Aşağıdaki komutu çalıştırarak 1 müşterisinin hesap bakiyelerini listeleyin:

java -jar target/onlinebanking.jar query balance 1

Beklenen çıkış:

Account balances for customer 1:
  Account 1: 9875.25
  Account 7: 9900
  Account 110: 38200

100 müşteri olduğu için farklı bir müşteri kimliği belirterek diğer müşteri hesap bakiyelerinden herhangi birini de sorgulayabilirsiniz:

java -jar target/onlinebanking.jar query balance 5
java -jar target/onlinebanking.jar query balance 10
java -jar target/onlinebanking.jar query balance 99

Müşteri e-postalarına karşı bulanık arama yapma

Bulanık aramalar, yazım varyasyonları ve yazım hataları dahil olmak üzere arama terimleriyle yaklaşık eşleşmeler bulmanızı sağlar.

Veritabanı ve şemayı oluşturduğunuzda bir n-gram dizini eklenmişse:

CREATE TABLE Customers (
  ...
  EmailTokens TOKENLIST AS (TOKENIZE_SUBSTRING(Email,
    ngram_size_min=>2,
    ngram_size_max=>3,
    relative_search_types=>["all"])) HIDDEN,
) PRIMARY KEY(CustomerId);

CREATE SEARCH INDEX CustomersFuzzyEmail ON Customers(EmailTokens);

findCustomers yöntemi, müşterilere e-posta üzerinden ulaşmak için bu dizinde sorgu yapmak üzere SEARCH_NGRAMS ve SCORE_NGRAMS öğelerini kullanır. E-posta sütunu n-gram jetonuna ayrıldığı için bu sorgu yazım hataları içerse bile doğru yanıtı döndürebilir. Sonuçlar en iyi eşleşmeye göre sıralanır.

Aşağıdaki komutu çalıştırarak madi içeren eşleşen müşteri e-posta adreslerini bulun:

java -jar target/onlinebanking.jar query email madi

Beklenen çıkış:

Customer emails matching madi (top 10 matches):
  Customer 39: madison.perez@example.com
  Customer 64: mason.gray@example.com
  Customer 91: mabel.alexander@example.com

Bu yanıtta, madi veya benzer bir dize içeren en yakın eşleşmeler sıralı olarak gösterilir.

Sorgu, doğrudan Spanner Studio'da çalıştırıldığında şu şekilde görünür: madi için manuel olarak arama yapma

Bulanık arama, emily gibi kelimelerin yanlış yazılmasında da yardımcı olabilir:

java -jar target/onlinebanking.jar query email emily
java -jar target/onlinebanking.jar query email emliy
java -jar target/onlinebanking.jar query email emilee

Beklenen çıkış:

Customer emails matching emliy (top 10 matches):
  Customer 31: emily.lopez@example.com

Her durumda, beklenen müşteri e-postası en iyi eşleşme olarak döndürülür.

Spanner'ın tam metin arama özelliği, anahtar kelimelere veya kelime öbeklerine göre kayıtları almak için kullanılır. Yazım hatalarını düzeltebilir veya eş anlamlı kelimeleri arayabilir.

Veritabanı ve şemayı oluşturduğunuzda tam metin arama dizini zaten eklenmiştir:

CREATE TABLE TransactionLedger (
  ...
  CategoryTokens TOKENLIST AS (TOKENIZE_FULLTEXT(Category)) HIDDEN,
  DescriptionTokens TOKENLIST AS (TOKENIZE_FULLTEXT(Description)) HIDDEN,
) PRIMARY KEY(AccountId, TransactionId),
  INTERLEAVE IN PARENT Accounts ON DELETE CASCADE;

CREATE SEARCH INDEX TransactionLedgerTextSearch ON TransactionLedger(CategoryTokens, DescriptionTokens);

getSpending yöntemi, bu dizinle eşleştirme yapmak için SEARCH tam metin arama işlevini kullanır. Belirtilen müşteri kimliği için son 30 gün içindeki tüm harcamaları (alacak kayıtlarını) arar.

Aşağıdaki komutu çalıştırarak groceries kategorisindeki 1 müşterisi için son bir aydaki toplam harcamayı alın:

java -jar target/onlinebanking.jar query spending 1 groceries

Beklenen çıkış:

Total spending for customer 1 under category groceries:
  50

Harcamalarınızı diğer kategorilerde (önceki bir adımda kategorize ettiğimiz) de bulabilir veya farklı bir müşteri kimliği kullanabilirsiniz:

java -jar target/onlinebanking.jar query spending 1 transportation
java -jar target/onlinebanking.jar query spending 1 restaurants
java -jar target/onlinebanking.jar query spending 12 entertainment

Özet

Bu adımda, tam eşleme sorgularının yanı sıra tam ve tam metin aramaları da gerçekleştirdiniz.

Sıradaki

Ardından, birleşik sorgular yürütmek için Spanner'ı Google BigQuery ile entegre edersiniz. Bu sayede gerçek zamanlı Spanner verilerinizi BigQuery verileriyle birleştirebilirsiniz.

8. BigQuery ile birleşik sorgular çalıştırma

BigQuery veri kümesini oluşturma

Bu adımda, birleşik sorgular kullanarak BigQuery ve Spanner verilerini bir araya getireceksiniz.

Bunu yapmak için Cloud Shell komut satırında önce bir MarketingCampaigns veri kümesi oluşturun:

bq mk --location=us-central1 MarketingCampaigns

Beklenen çıkış:

Dataset '<PROJECT_ID>:MarketingCampaigns' successfully created.

Veri kümesindeki bir CustomerSegments tablosu:

bq mk --table MarketingCampaigns.CustomerSegments CampaignId:STRING,CampaignName:STRING,CustomerId:INT64

Beklenen çıkış:

Table '<PROJECT_ID>:MarketingCampaigns.CustomerSegments' successfully created.

Ardından, BigQuery'den Spanner'a bağlantı oluşturun:

bq mk --connection \
  --connection_type=CLOUD_SPANNER \
  --properties="{\"database\": \"projects/$GOOGLE_CLOUD_PROJECT/instances/cloudspanner-onlinebanking/databases/onlinebanking\", \"useParallelism\": true, \"useDataBoost\": true}" \
  --location=us-central1 \
  spanner-connection

Beklenen çıkış:

Connection <PROJECT_NUMBER>.us-central1.spanner-connection successfully created

Son olarak, BigQuery tablosuna Spanner verilerimizle birleştirilebilecek bazı müşteriler ekleyin:

bq query --use_legacy_sql=false '
INSERT INTO MarketingCampaigns.CustomerSegments (CampaignId, CampaignName, CustomerId)
VALUES
  ("campaign1", "Spring Promotion", 1),
  ("campaign1", "Spring Promotion", 3),
  ("campaign1", "Spring Promotion", 5),
  ("campaign1", "Spring Promotion", 7),
  ("campaign1", "Spring Promotion", 9),
  ("campaign1", "Spring Promotion", 11)'

Beklenen çıkış:

Waiting on bqjob_r76a7ce76c5ec948f_0000019644bda052_1 ... (0s) Current status: DONE
Number of affected rows: 6

BigQuery'yi sorgularak verilerin kullanılabilir olduğunu doğrulayabilirsiniz:

bq query --use_legacy_sql=false "SELECT * FROM MarketingCampaigns.CustomerSegments"

Beklenen çıkış:

+------------+------------------+------------+
| CampaignId |   CampaignName   | CustomerId |
+------------+------------------+------------+
| campaign1  | Spring Promotion |          1 |
| campaign1  | Spring Promotion |          5 |
| campaign1  | Spring Promotion |          7 |
| campaign1  | Spring Promotion |          9 |
| campaign1  | Spring Promotion |         11 |
| campaign1  | Spring Promotion |          3 |
+------------+------------------+------------+

BigQuery'deki bu veriler, çeşitli banka iş akışları aracılığıyla eklenen verileri temsil eder. Örneğin, bu liste kısa süre önce hesap açan veya bir pazarlama promosyonuna kaydolan müşterilerin listesi olabilir. Pazarlama kampanyamızda hedeflemek istediğimiz müşterilerin listesini belirlemek için hem BigQuery'deki bu verileri hem de Spanner'daki gerçek zamanlı verileri sorgulamamız gerekir. Birleştirilmiş sorgu, bunu tek bir sorgu ile yapmamıza olanak tanır.

BigQuery ile birleşik sorgu çalıştırma

Ardından, birleşik sorguyu gerçekleştirmek için EXTERNAL_QUERY işlevini çağıracak bir yöntemi uygulamaya ekleyeceğiz. Bu sayede, BigQuery ve Spanner'daki müşteri verilerini birleştirip analiz edebilirsiniz. Örneğin, son harcamalarına göre hangi müşterilerin pazarlama kampanyamızın ölçütlerini karşıladığını belirleyebilirsiniz.

App.java'ü açın ve içe aktarma işlemlerini değiştirerek başlayın:

package com.google.codelabs;

import java.io.FileReader;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import com.google.api.gax.longrunning.OperationFuture;
import com.google.cloud.Timestamp;
import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.connection.v1.ConnectionName;
import com.google.cloud.bigquery.JobException;
import com.google.cloud.bigquery.QueryJobConfiguration;
import com.google.cloud.bigquery.TableResult;
import com.google.cloud.spanner.Database;
import com.google.cloud.spanner.DatabaseAdminClient;
import com.google.cloud.spanner.DatabaseClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Key;
import com.google.cloud.spanner.ReadOnlyTransaction;
import com.google.cloud.spanner.ResultSet;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.Statement;
import com.google.cloud.spanner.Struct;
import com.google.cloud.spanner.TimestampBound;
import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;
import com.opencsv.CSVReader;

Ardından campaign yöntemini ekleyin:

  // Get customers for quarterly marketing campaign in BigQuery using Spanner data
  public static void campaign(BigQuery bq, DatabaseId db, String location, String campaignId,
      int threshold) {
    // The BigQuery dataset, table, and Spanner connection must already exist for this to succeed
    ConnectionName connection = ConnectionName.of(db.getInstanceId().getProject(), location,
        "spanner-connection");

    // Use a federated query to bring Spanner data into BigQuery
    String bqQuery = "SELECT cs.CampaignName, c.CustomerId, c.FullName, t.TotalSpending\n"
        + "FROM MarketingCampaigns.CustomerSegments cs\n"
        + "JOIN EXTERNAL_QUERY('" + connection.toString() + "',\n"
        + "  \"SELECT t.AccountId, SUM(t.Amount) AS TotalSpending"
        + "   FROM TransactionLedger t"
        + "   WHERE t.Timestamp >= TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL -90 DAY)"
        + "   GROUP BY t.AccountId"
        + "   HAVING SUM(t.Amount) > " + threshold + "\"\n"
        + ") t ON cs.CustomerId = t.AccountId\n"
        + "JOIN EXTERNAL_QUERY('" + connection.toString() + "',\n"
        + "  \"SELECT CustomerId, FullName"
        + "   FROM Customers\"\n"
        + ") c ON c.CustomerId = cs.CustomerId\n"
        + "WHERE cs.CampaignId = '" + campaignId + "'";
    try {
      QueryJobConfiguration queryConfig = QueryJobConfiguration.newBuilder(bqQuery).build();
      TableResult results = bq.query(queryConfig);

      System.out.println("Customers for campaign (" + campaignId + "):");
      results.iterateAll().forEach(row -> {
        System.out.println("  " + row.get("FullName").getStringValue()
            + " (" + row.get("CustomerId").getStringValue() + ")");
      });
    } catch (JobException e) {
      throw (BigQueryException) e.getCause();
    } catch (InterruptedException e) {
      throw SpannerExceptionFactory.propagateInterrupt(e);
    }
  }

Kampanya için main yöntemine başka bir case ifadesi ekleyin:

        case "campaign":
          String campaignId = (args.length >= 2) ? args[1] : "";
          int threshold = (args.length >= 3) ? Integer.parseInt(args[2]) : 5000;
          campaign(bigquery, db, location, campaignId, threshold);
          break;

Son olarak, kampanyanın nasıl kullanılacağını printUsageAndExit yöntemine ekleyin:

    System.out.println("  java -jar target/onlinebanking.jar campaign campaign1 5000");
    System.out.println("      - Use Federated Queries (BigQuery) to find customers that match a "
        + "marketing campaign by name based on a recent spending threshold.\n");

App.java dosyasına yaptığınız değişiklikleri kaydedin.

Uygulamayı yeniden oluşturun:

mvn package

campaign komutunu çalıştırarak son 3 ay içinde en az $5000 harcama yapmış olan ve pazarlama kampanyasına (campaign1) dahil edilmesi gereken müşterileri belirlemek için birleşik sorgu çalıştırın:

java -jar target/onlinebanking.jar campaign campaign1 5000

Beklenen çıkış:

Customers for campaign (campaign1):
  Alice Smith (1)
  Eve Davis (5)
  Kelly Thomas (11)

Artık bu müşterileri özel teklifler veya ödüllerle hedefleyebiliriz.

Alternatif olarak, son 3 ay içinde daha düşük bir harcama eşiğine ulaşan daha geniş bir müşteri grubuna bakabiliriz:

java -jar target/onlinebanking.jar campaign campaign1 2500

Beklenen çıkış:

Customers for campaign (campaign1):
  Alice Smith (1)
  Charlie Williams (3)
  Eve Davis (5)
  Ivy Taylor (9)
  Kelly Thomas (11)

Özet

Bu adımda, BigQuery'den gerçek zamanlı Spanner verileri getiren birleşik sorgular başarıyla çalıştırdınız.

Sıradaki

Ardından, ücret ödememek için bu codelab için oluşturulan kaynakları temizleyebilirsiniz.

9. Temizlik (isteğe bağlı)

Bu adım isteğe bağlıdır. Spanner örneğinizle denemeye devam etmek istiyorsanız bu aşamada temizlemeniz gerekmez. Ancak kullandığınız proje, örnek için ödeme almaya devam eder. Bu örneği artık kullanmayacaksanız bu ücretleri ödememek için hemen silmeniz gerekir. Bu kod laboratuvarının oluşturduğu Spanner örneğine ek olarak, artık gerekli olmayan bir BigQuery veri kümesi ve bağlantısı da oluşturulur.

Spanner örneğini silin:

gcloud spanner instances delete cloudspanner-onlinebanking

Devam etmek istediğinizi onaylayın (Y yazın):

Delete instance [cloudspanner-onlinebanking]. Are you sure?

Do you want to continue (Y/n)?

BigQuery bağlantısını ve veri kümesini silin:

bq rm --connection --location=us-central1 spanner-connection
bq rm -r MarketingCampaigns

BigQuery veri kümesinin silinmesini onaylayın (Y yazın):

rm: remove dataset '<PROJECT_ID>:MarketingCampaigns'? (y/N)

10. Tebrikler

🚀 Yeni bir Cloud Spanner örneği oluşturdunuz, boş bir veritabanı oluşturdunuz, örnek veriler yüklediniz, gelişmiş işlemler ve sorgular gerçekleştirdiniz ve (isteğe bağlı olarak) Cloud Spanner örneğini sildiniz.

İşlediğimiz konular

  • Spanner örneği oluşturma.
  • Veritabanı ve tablo oluşturma.
  • Spanner veritabanı tablolarınıza veri yükleme.
  • Spanner'dan Vertex AI modellerini çağırma.
  • Bulanık arama ve tam metin aramayı kullanarak Spanner veritabanınızı sorgulama.
  • BigQuery'den Spanner'a karşı birleşik sorgular gerçekleştirme.
  • Spanner örneğinizi silme.

Sırada ne var?