با Spanner یک برنامه بانکداری آنلاین ایجاد کنید

1. بررسی اجمالی

Spanner یک سرویس پایگاه داده کاملاً مدیریت شده، مقیاس پذیر افقی و توزیع شده در سطح جهانی است که برای بارهای کاری عملیاتی رابطه ای و غیر رابطه ای عالی است. فراتر از قابلیت‌های اصلی، Spanner ویژگی‌های پیشرفته قدرتمندی را ارائه می‌کند که امکان ساخت برنامه‌های هوشمند و مبتنی بر داده را فراهم می‌کند.

این نرم افزار کد مبتنی بر درک اساسی از Spanner است و با استفاده از یک برنامه بانکداری آنلاین به عنوان پایه، از ادغام های پیشرفته آن برای تقویت پردازش داده ها و قابلیت های تحلیلی شما استفاده می کند.

ما بر روی سه ویژگی پیشرفته کلیدی تمرکز خواهیم کرد:

  • ادغام Vertex AI : نحوه ادغام یکپارچه Spanner با پلتفرم هوش مصنوعی Google Cloud، Vertex AI را کشف کنید. شما یاد خواهید گرفت که چگونه مدل‌های Vertex AI را مستقیماً از درون پرس‌وجوهای Spanner SQL فراخوانی کنید، تبدیل‌ها و پیش‌بینی‌های قدرتمند درون پایگاه داده را ممکن می‌کند، به برنامه بانکی ما اجازه می‌دهد تا به طور خودکار تراکنش‌ها را برای موارد استفاده مانند ردیابی بودجه و تشخیص ناهنجاری دسته‌بندی کند.
  • جستجوی متن کامل : نحوه پیاده‌سازی عملکرد جستجوی متن کامل را در Spanner بیاموزید. شما فهرست‌بندی داده‌های متنی و نوشتن پرس و جوهای کارآمد را برای انجام جستجوهای مبتنی بر کلیدواژه در داده‌های عملیاتی خود کاوش خواهید کرد، که امکان کشف داده‌های قدرتمند را فراهم می‌کند، مانند یافتن کارآمد مشتریان از طریق آدرس ایمیل در سیستم بانکی ما.
  • پرس و جوهای فدرال شده BigQuery : نحوه استفاده از قابلیت های جستجوی فدرال Spanner برای پرس و جو مستقیم داده های موجود در BigQuery را بررسی کنید. این به شما امکان می‌دهد داده‌های عملیاتی بی‌درنگ Spanner را با مجموعه داده‌های تحلیلی BigQuery برای بینش‌های جامع و گزارش‌دهی بدون تکرار داده‌ها یا فرآیندهای پیچیده ETL ترکیب کنید، و موارد استفاده مختلف را در برنامه‌های بانکی ما مانند کمپین‌های بازاریابی هدفمند با ترکیب داده‌های بی‌درنگ مشتری با روندهای تاریخی گسترده‌تر از BigQuery تقویت کنید.

چیزی که یاد خواهید گرفت

  • نحوه راه اندازی یک نمونه Spanner.
  • نحوه ایجاد پایگاه داده و جداول
  • چگونه داده ها را در جداول پایگاه داده Spanner بارگیری کنیم.
  • نحوه فراخوانی مدل های Vertex AI از Spanner.
  • چگونه با استفاده از جستجوی فازی و جستجوی متن کامل، پایگاه داده Spanner خود را پرس و جو کنید.
  • نحوه انجام کوئری های فدرال در برابر Spanner از BigQuery.
  • چگونه نمونه Spanner خود را حذف کنیم.

آنچه شما نیاز دارید

  • یک پروژه Google Cloud که به یک حساب صورت‌حساب متصل است.
  • یک مرورگر وب، مانند کروم یا فایرفاکس .

2. راه اندازی و الزامات

یک پروژه ایجاد کنید

اگر قبلاً یک پروژه Google Cloud با فعال کردن صورت‌حساب دارید، روی منوی کشویی انتخاب پروژه در سمت چپ بالای کنسول کلیک کنید:

پروژه موجود

با یک پروژه انتخابی، به فعال کردن APIهای مورد نیاز بروید.

اگر قبلاً یک حساب Google (Gmail یا Google Apps) ندارید، باید یک حساب ایجاد کنید . به کنسول Google Cloud Platform ( consol.cloud.google.com ) وارد شوید و یک پروژه جدید ایجاد کنید.

برای ایجاد یک پروژه جدید، روی دکمه "پروژه جدید" در گفتگوی حاصل کلیک کنید:

پروژه جدید

اگر قبلاً پروژه ای ندارید، باید یک دیالوگ مانند این را ببینید تا اولین پروژه خود را ایجاد کنید:

گفتگوی پروژه

گفتگوی بعدی ایجاد پروژه به شما امکان می دهد جزئیات پروژه جدید خود را وارد کنید.

شناسه پروژه را به خاطر بسپارید، که یک نام منحصر به فرد در تمام پروژه های Google Cloud است. بعداً در این آزمایشگاه کد به عنوان PROJECT_ID نامیده خواهد شد.

جزئیات پروژه

سپس، اگر قبلاً این کار را انجام نداده‌اید، باید صورت‌حساب را در Developers Console فعال کنید تا از منابع Google Cloud استفاده کنید و Spanner API ، Vertex AI API ، BigQuery API و BigQuery Connection API را فعال کنید.

صورتحساب پروژه

قیمت آچار در اینجا مستند شده است. سایر هزینه های مرتبط با منابع دیگر در صفحات قیمت گذاری خاص آنها ثبت می شود.

کاربران جدید Google Cloud Platform واجد شرایط استفاده آزمایشی رایگان 300 دلاری هستند.

Google Cloud Shell Setup

در این لبه کد ما از Google Cloud Shell استفاده خواهیم کرد، یک محیط خط فرمان که در Cloud اجرا می شود.

این ماشین مجازی مبتنی بر دبیان با تمام ابزارهای توسعه که شما نیاز دارید بارگذاری شده است. این یک فهرست اصلی 5 گیگابایتی دائمی ارائه می‌کند و در Google Cloud اجرا می‌شود، که عملکرد و احراز هویت شبکه را بسیار افزایش می‌دهد. این بدان معناست که تنها چیزی که برای این کد لبه نیاز دارید یک مرورگر است.

برای فعال کردن Cloud Shell از Cloud Console، کافی است روی Activate Cloud Shell کلیک کنید. نماد پوسته ابری (تهیه و اتصال به محیط فقط چند لحظه طول می کشد).

پوسته ابری

پس از اتصال به Cloud Shell، باید ببینید که قبلاً احراز هویت شده اید و پروژه قبلاً روی PROJECT_ID شما تنظیم شده است.

gcloud auth list

خروجی مورد انتظار:

Credentialed Accounts

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

خروجی مورد انتظار:

[core]
project = <PROJECT_ID>

اگر به دلایلی پروژه تنظیم نشد، دستور زیر را صادر کنید:

gcloud config set project <PROJECT_ID>

به دنبال PROJECT_ID خود هستید؟ بررسی کنید از چه شناسه ای در مراحل راه اندازی استفاده کرده اید یا آن را در داشبورد Cloud Console جستجو کنید:

شناسه پروژه

Cloud Shell همچنین برخی از متغیرهای محیطی را به صورت پیش‌فرض تنظیم می‌کند که ممکن است هنگام اجرای دستورات آینده مفید باشند.

echo $GOOGLE_CLOUD_PROJECT

خروجی مورد انتظار:

<PROJECT_ID>

API های مورد نیاز را فعال کنید

API های Spanner، Vertex AI و BigQuery را برای پروژه خود فعال کنید:

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

خلاصه

در این مرحله، اگر پروژه خود را نداشتید، پروژه خود را راه اندازی کرده اید، Cloud Shell را فعال کرده اید و API های مورد نیاز را فعال کرده اید.

بعدی

در مرحله بعد، نمونه Spanner را تنظیم خواهید کرد.

3. یک نمونه Spanner را راه اندازی کنید

نمونه Spanner را ایجاد کنید

در این مرحله، یک نمونه Spanner برای Codelab راه اندازی می کنید. برای انجام این کار، Cloud Shell را باز کنید و این دستور را اجرا کنید:

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

خروجی مورد انتظار:

Creating instance...done.

خلاصه

در این مرحله شما نمونه Spanner را ایجاد کرده اید.

بعدی

در مرحله بعد، برنامه اولیه را آماده کرده و پایگاه داده و طرحواره را ایجاد می کنید.

4. یک پایگاه داده و طرحواره ایجاد کنید

درخواست اولیه را آماده کنید

در این مرحله از طریق کد دیتابیس و schema را ایجاد می کنید.

ابتدا یک برنامه جاوا به نام onlinebanking با استفاده از Maven ایجاد کنید:

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

فایل های داده ای را که به پایگاه داده اضافه می کنیم بررسی و کپی کنید (برای مخزن کد اینجا را ببینید):

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

به پوشه برنامه بروید:

cd onlinebanking

فایل Maven pom.xml را باز کنید. برای استفاده از Maven BOM برای مدیریت نسخه کتابخانه‌های Google Cloud، بخش مدیریت وابستگی را اضافه کنید:

<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>

ویرایشگر و فایل به این صورت است: پوسته ابر

مطمئن شوید که بخش dependencies شامل کتابخانه هایی است که برنامه استفاده می کند:

<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>

در نهایت، افزونه های ساخت را جایگزین کنید تا برنامه در یک JAR قابل اجرا بسته بندی شود:

<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 یا با فشار دادن Ctrl+S تغییراتی را که در فایل pom.xml ایجاد کرده‌اید، ذخیره کنید.

اکنون که وابستگی‌ها آماده هستند، کدی را به برنامه اضافه می‌کنید تا یک طرحواره، برخی فهرست‌ها (از جمله جستجو) و یک مدل هوش مصنوعی متصل به یک نقطه پایانی راه دور ایجاد کنید. شما بر روی این مصنوعات ساخته خواهید شد و متدهای بیشتری را به این کلاس در امتداد این کد لبه اضافه خواهید کرد.

App.java در آدرس onlinebanking/src/main/java/com/google/codelabs باز کنید و کد زیر را جایگزین محتوا کنید:

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();
      }
    }
  }
}

تغییرات را در App.java ذخیره کنید.

به موجودیت های مختلفی که کد شما ایجاد می کند نگاه کنید و برنامه JAR را بسازید:

mvn package

خروجی مورد انتظار:

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

برای مشاهده اطلاعات استفاده، برنامه را اجرا کنید:

java -jar target/onlinebanking.jar

خروجی مورد انتظار:

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.

پایگاه داده و طرحواره ایجاد کنید

متغیرهای محیط برنامه مورد نیاز را تنظیم کنید:

export SPANNER_INSTANCE=cloudspanner-onlinebanking
export SPANNER_DATABASE=onlinebanking

با اجرای دستور create دیتابیس و طرحواره را ایجاد کنید:

java -jar target/onlinebanking.jar create

خروجی مورد انتظار:

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

طرحواره را در Spanner بررسی کنید

در کنسول Spanner ، به نمونه و پایگاه داده خود که به تازگی ایجاد شده است بروید.

شما باید هر 3 جدول - Accounts ، Customers و TransactionLedger را ببینید.

مشاهده طرحواره

این اقدام طرح پایگاه داده شامل جداول Accounts ، Customers و TransactionLedger را به همراه فهرست‌های ثانویه برای بازیابی بهینه داده‌ها و مرجع مدل Vertex AI ایجاد می‌کند.

نمودار رابطه موجودیت

جدول TransactionLedger در حساب‌ها قرار می‌گیرد تا عملکرد پرس و جو را برای تراکنش‌های خاص حساب از طریق بهبود موقعیت داده‌ها افزایش دهد.

نمایه‌های ثانویه ( CustomersByEmail ، CustomersFuzzyEmail ، AccountsByCustomer ، TransactionLedgerByAccountType ، TransactionLedgerByCategory ، TransactionLedgerTextSearch ) برای بهینه‌سازی الگوهای دسترسی به داده‌های رایج مورد استفاده در این نرم‌افزارهای ایمیل کدنویسی و جستجوی مجدد مشتری، از قبیل جستجوی مجدد اکانت مشتری، الگوهای دسترسی به داده‌های رایج استفاده شده است. و پرس و جو و جستجوی موثر داده های تراکنش.

TransactionCategoryModel از Vertex AI برای فعال کردن تماس‌های مستقیم SQL به یک LLM استفاده می‌کند که برای دسته‌بندی تراکنش‌های پویا در این Codelab استفاده می‌شود.

خلاصه

در این مرحله شما پایگاه داده و طرحواره Spanner را ایجاد کرده اید.

بعدی

در مرحله بعد، داده های نمونه برنامه را بارگیری می کنید.

5. داده ها را بارگذاری کنید

اکنون، عملکردی را برای بارگیری داده های نمونه از فایل های CSV در پایگاه داده اضافه خواهید کرد.

App.java را باز کنید و با جایگزین کردن importها شروع کنید:

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;

سپس متدهای insert را به کلاس App اضافه کنید:

  // 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;
        });
  }

عبارت case دیگری را در متد main برای درج در switch (command) اضافه کنید:

        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;

در نهایت نحوه استفاده از insert را به متد printUsageAndExit اضافه کنید:

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

تغییراتی را که در App.java ایجاد کردید ذخیره کنید.

برنامه را بازسازی کنید:

mvn package

داده های نمونه را با اجرای دستور insert وارد کنید:

java -jar target/onlinebanking.jar insert

خروجی مورد انتظار:

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

در کنسول Spanner ، برای نمونه و پایگاه داده خود به Spanner Studio برگردید. سپس جدول TransactionLedger را انتخاب کنید و روی "داده" در نوار کناری کلیک کنید تا تأیید شود که داده ها بارگذاری شده اند. در جدول باید 200 ردیف وجود داشته باشد.

مشاهده داده ها

خلاصه

در این مرحله داده های نمونه را در پایگاه داده وارد کردید.

بعدی

در مرحله بعد، از ادغام Vertex AI برای دسته‌بندی خودکار تراکنش‌های بانکی مستقیماً در Spanner SQL استفاده می‌کنید.

6. داده ها را با Vertex AI دسته بندی کنید

در این مرحله، از قدرت Vertex AI برای دسته بندی خودکار تراکنش های مالی خود به طور مستقیم در Spanner SQL استفاده می کنید. با Vertex AI می توانید یک مدل از پیش آموزش دیده یا آموزش را انتخاب کنید و مدل خود را مستقر کنید. مدل های موجود را در Vertex AI Model Garden ببینید.

برای این نرم افزار از یکی از مدل های Gemini، Gemini Flash Lite استفاده می کنیم. این نسخه از Gemini مقرون به صرفه است، اما هنوز هم می تواند بسیاری از کارهای روزانه را تحمل کند.

در حال حاضر، تعدادی تراکنش مالی داریم که می‌خواهیم بسته به توضیحات، آنها را دسته‌بندی کنیم ( groceries ، transportation و غیره). ما می توانیم این کار را با ثبت یک مدل در Spanner و سپس استفاده از ML.PREDICT برای فراخوانی مدل AI انجام دهیم.

در برنامه بانکی خود ممکن است بخواهیم تراکنش‌ها را دسته‌بندی کنیم تا بینش عمیق‌تری درباره رفتار مشتری به دست آوریم تا بتوانیم خدمات را شخصی‌سازی کنیم، ناهنجاری‌ها را به‌طور مؤثرتری شناسایی کنیم، یا به مشتری توانایی ردیابی ماه به ماه بودجه خود را ارائه دهیم.

اولین مرحله از قبل انجام شده بود که ما پایگاه داده و طرحواره را ایجاد کردیم، که مدلی مانند این ایجاد کرد:

ایجاد بیانیه مدل

در مرحله بعد، روشی را برای فراخوانی ML.PREDICT به برنامه اضافه می کنیم.

App.java را باز کنید و متد categorize اضافه کنید:

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

برای دسته بندی عبارت case دیگری در متد main اضافه کنید:

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

در نهایت نحوه استفاده از دسته بندی را به متد printUsageAndExit اضافه کنید:

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

تغییراتی را که در App.java ایجاد کردید ذخیره کنید.

برنامه را بازسازی کنید:

mvn package

با اجرای دستور categorize تراکنش های موجود در پایگاه داده را دسته بندی کنید:

java -jar target/onlinebanking.jar categorize

خروجی مورد انتظار:

Categorizing transactions...
Completed categorizing transactions

در Spanner Studio، عبارت Preview Data را برای جدول TransactionLedger اجرا کنید. اکنون باید ستون Category برای تمام سطرها پر شود.

مشاهده داده های طبقه بندی شده

اکنون که تراکنش‌هایی را دسته‌بندی کرده‌ایم که می‌توانیم از این اطلاعات برای پرس و جوهای داخلی یا مشتری استفاده کنیم، در مرحله بعدی به نحوه یافتن میزان هزینه یک مشتری در یک دسته در طول ماه نگاه خواهیم کرد.

خلاصه

در این مرحله، شما از یک مدل از پیش آموزش‌دیده برای دسته‌بندی داده‌های خود با هوش مصنوعی استفاده کردید.

بعدی

در مرحله بعد، از توکن سازی برای انجام جستجوهای فازی و متن کامل استفاده خواهید کرد.

7. پرس و جو با استفاده از جستجوی متن کامل

کد پرس و جو را اضافه کنید

Spanner بسیاری از عبارت های جستجوی متن کامل را ارائه می دهد. در این مرحله شما یک جستجوی تطابق دقیق انجام می‌دهید، سپس یک جستجوی فازی و یک جستجوی تمام متن را انجام می‌دهید.

App.java را باز کنید و با جایگزین کردن importها شروع کنید:

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;

سپس متدهای پرس و جو را اضافه کنید:

  // 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);
      }
    }
  }

عبارت case دیگری را در متد main برای query اضافه کنید:

        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;

در نهایت نحوه استفاده از دستورات query را به متد printUsageAndExit اضافه کنید:

    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 ایجاد کردید ذخیره کنید.

برنامه را بازسازی کنید:

mvn package

برای موجودی حساب مشتری جستجوی دقیق مطابقت انجام دهید

یک پرس و جو با تطابق دقیق به دنبال ردیف های منطبق است که دقیقاً با یک عبارت مطابقت دارند.

برای بهبود عملکرد، زمانی که پایگاه داده و طرحواره را ایجاد کردید، یک نمایه اضافه شده است:

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

روش getBalance به طور ضمنی از این شاخص برای یافتن مشتریانی استفاده می کند که با شناسه مشتری ارائه شده مطابقت دارند و همچنین به حساب های متعلق به آن مشتری می پیوندد.

این همان چیزی است که پرس و جو هنگام اجرای مستقیم در Spanner Studio به نظر می رسد: پرس و جو دستی تعادل

موجودی حساب مشتری 1 را با اجرای دستور فهرست کنید:

java -jar target/onlinebanking.jar query balance 1

خروجی مورد انتظار:

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

100 مشتری وجود دارد، بنابراین می‌توانید با مشخص کردن یک شناسه مشتری دیگر، موجودی حساب مشتری دیگر را نیز پرس و جو کنید:

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

یک جستجوی فازی در برابر ایمیل های مشتری انجام دهید

جستجوهای فازی امکان یافتن تطابق تقریبی برای عبارات جستجو، از جمله تغییرات املایی و اشتباهات املایی را فراهم می کند.

هنگامی که پایگاه داده و طرحواره را ایجاد کردید، یک شاخص n-gram قبلاً اضافه شده است:

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 از SEARCH_NGRAMS و SCORE_NGRAMS برای پرس و جو در برابر این فهرست برای یافتن مشتریان از طریق ایمیل استفاده می کند. از آنجایی که ستون ایمیل n-gram نشانه گذاری شده است، این پرس و جو می تواند حاوی اشتباهات املایی باشد و همچنان پاسخ صحیح را بازگرداند. نتایج بر اساس بهترین تطابق مرتب می شوند.

با اجرای دستور، آدرس‌های ایمیل مشتری منطبق را که حاوی madi هستند پیدا کنید:

java -jar target/onlinebanking.jar query email madi

خروجی مورد انتظار:

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

این پاسخ نزدیک‌ترین موارد منطبق را که شامل madi یا رشته‌ای مشابه هستند را به ترتیب رتبه‌بندی نشان می‌دهد.

در صورت اجرای مستقیم در Spanner Studio، پرس و جو به این صورت است: جستجوی دستی برای madi

جستجوی فازی همچنین می تواند به اشتباهات املایی مانند غلط املایی emily کمک کند:

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

خروجی مورد انتظار:

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

در هر مورد، ایمیل مورد انتظار مشتری به عنوان بهترین ضربه برگردانده می شود.

ویژگی جستجوی متن کامل Spanner برای بازیابی رکوردها بر اساس کلمات کلیدی یا عبارات استفاده می شود. قابلیت تصحیح غلط های املایی یا جستجوی مترادف ها را دارد.

هنگامی که پایگاه داده و طرحواره را ایجاد کردید، یک فهرست جستجوی کامل متن قبلاً اضافه شده است:

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 از تابع جستجوی متن کامل SEARCH برای مطابقت با آن شاخص استفاده می کند. تمام هزینه ها (بدهی ها) در 30 روز گذشته برای شناسه مشتری داده شده را جستجو می کند.

با اجرای دستور زیر، کل هزینه‌های یک ماه گذشته برای مشتری 1 در دسته groceries را دریافت کنید:

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

خروجی مورد انتظار:

Total spending for customer 1 under category groceries:
  50

همچنین می‌توانید هزینه‌ها را در دسته‌های دیگر (که در مرحله قبل دسته‌بندی کردیم) پیدا کنید یا از یک شناسه مشتری متفاوت استفاده کنید:

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

خلاصه

در این مرحله، پرس و جوهای تطبیق دقیق و همچنین جستجوهای فازی و متن کامل را انجام دادید.

بعدی

سپس، Spanner را با Google BigQuery ادغام می‌کنید تا درخواست‌های فدرال را انجام دهید، و به شما این امکان را می‌دهد که داده‌های Spanner بلادرنگ خود را با داده‌های BigQuery ترکیب کنید.

8. کوئری های فدرال را با BigQuery اجرا کنید

مجموعه داده BigQuery را ایجاد کنید

در این مرحله، داده های BigQuery و Spanner را با استفاده از پرس و جوهای فدرال گرد هم می آورید.

برای انجام این کار، در خط فرمان Cloud Shell، ابتدا یک مجموعه داده MarketingCampaigns ایجاد کنید:

bq mk --location=us-central1 MarketingCampaigns

خروجی مورد انتظار:

Dataset '<PROJECT_ID>:MarketingCampaigns' successfully created.

و یک جدول CustomerSegments در مجموعه داده:

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

خروجی مورد انتظار:

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

سپس، از BigQuery به Spanner یک اتصال برقرار کنید:

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

خروجی مورد انتظار:

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

در نهایت، تعدادی مشتری را به جدول BigQuery اضافه کنید که می‌توان آنها را با داده‌های Spanner ما ملحق کرد:

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)'

خروجی مورد انتظار:

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

می‌توانید با جستجو در BigQuery تأیید کنید که داده‌ها در دسترس هستند:

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

خروجی مورد انتظار:

+------------+------------------+------------+
| 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 داده‌هایی را نشان می‌دهند که از طریق گردش‌های کاری مختلف بانک اضافه شده‌اند. به عنوان مثال، این ممکن است لیست مشتریانی باشد که اخیراً حساب باز کرده اند یا برای تبلیغات بازاریابی ثبت نام کرده اند. برای تعیین لیست مشتریانی که می‌خواهیم در کمپین بازاریابی خود هدف قرار دهیم، باید هم این داده‌ها را در BigQuery و هم داده‌های بلادرنگ را در Spanner پرس و جو کنیم، و یک پرس و جو فدرال به ما این امکان را می‌دهد که این کار را در یک جستجو انجام دهیم.

یک پرس و جو فدرال را با BigQuery اجرا کنید

در مرحله بعد، روشی را به برنامه اضافه می کنیم تا EXTERNAL_QUERY را برای انجام پرس و جو فدرال فراخوانی کند. این امکان پیوستن و تجزیه و تحلیل داده‌های مشتری را در BigQuery و Spanner فراهم می‌کند، مانند شناسایی مشتریانی که معیارهای کمپین بازاریابی ما را بر اساس هزینه‌های اخیرشان برآورده می‌کنند.

App.java را باز کنید و با جایگزین کردن importها شروع کنید:

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;

سپس روش campaign را اضافه کنید:

  // 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);
    }
  }

عبارت case دیگری را در متد main برای کمپین اضافه کنید:

        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;

در نهایت نحوه استفاده از کمپین را به متد printUsageAndExit اضافه کنید:

    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 ایجاد کردید ذخیره کنید.

برنامه را بازسازی کنید:

mvn package

برای تعیین اینکه مشتریانی که در طول 3 ماه گذشته حداقل $5000 هزینه کرده اند، با اجرای دستور campaign ، campaign1 پرس و جو فدرال را برای تعیین مشتریانی که باید در کمپین بازاریابی گنجانده شوند، اجرا کنید:

java -jar target/onlinebanking.jar campaign campaign1 5000

خروجی مورد انتظار:

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

اکنون می توانیم این مشتریان را با پیشنهادات یا جوایز انحصاری هدف قرار دهیم.

یا می‌توانیم به دنبال تعداد بیشتری از مشتریان باشیم که در 3 ماه گذشته به آستانه خرج کمتری دست یافته‌اند:

java -jar target/onlinebanking.jar campaign campaign1 2500

خروجی مورد انتظار:

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

خلاصه

در این مرحله، پرس‌و‌جوهای فدرال‌شده را از BigQuery که داده‌های Spanner بلادرنگ را وارد می‌کردند، با موفقیت اجرا کردید.

بعدی

در مرحله بعد، می‌توانید منابع ایجاد شده برای این کد لبه را پاکسازی کنید تا از پرداخت هزینه جلوگیری کنید.

9. پاکسازی (اختیاری)

این مرحله اختیاری است. اگر می‌خواهید به آزمایش نمونه Spanner خود ادامه دهید، در حال حاضر نیازی به پاکسازی آن ندارید. با این حال، پروژه ای که از آن استفاده می کنید همچنان برای نمونه هزینه دریافت می کند. اگر دیگر نیازی به این نمونه ندارید، باید در این زمان آن را حذف کنید تا از این هزینه ها جلوگیری کنید. علاوه بر نمونه Spanner، این Codelab همچنین یک مجموعه داده و اتصال BigQuery ایجاد کرد که باید در مواقعی که دیگر مورد نیاز نیستند پاک شوند.

نمونه Spanner را حذف کنید:

gcloud spanner instances delete cloudspanner-onlinebanking

تأیید کنید که می‌خواهید ادامه دهید ( Y تایپ کنید):

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

Do you want to continue (Y/n)?

اتصال و مجموعه داده BigQuery را حذف کنید:

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

تأیید حذف مجموعه داده BigQuery (نوع Y ):

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

10. تبریک می گویم

🚀 شما یک نمونه Cloud Spanner جدید ایجاد کرده‌اید، یک پایگاه داده خالی ایجاد کرده‌اید، داده‌های نمونه را بارگیری کرده‌اید، عملیات‌ها و جستجوهای پیشرفته را انجام داده‌اید، و (به صورت اختیاری) نمونه Cloud Spanner را حذف کرده‌اید.

آنچه را پوشش داده ایم

  • نحوه راه اندازی یک نمونه Spanner.
  • نحوه ایجاد پایگاه داده و جداول
  • چگونه داده ها را در جداول پایگاه داده Spanner بارگیری کنیم.
  • نحوه فراخوانی مدل های Vertex AI از Spanner.
  • چگونه با استفاده از جستجوی فازی و جستجوی متن کامل، پایگاه داده Spanner خود را پرس و جو کنید.
  • نحوه انجام کوئری های فدرال در برابر Spanner از BigQuery.
  • چگونه نمونه Spanner خود را حذف کنیم.

بعدش چی؟