স্প্যানার দিয়ে একটি অনলাইন ব্যাঙ্কিং অ্যাপ্লিকেশন তৈরি করুন

1. ওভারভিউ

স্প্যানার হল একটি সম্পূর্ণরূপে পরিচালিত, অনুভূমিকভাবে স্কেলযোগ্য, বিশ্বব্যাপী বিতরণ করা ডাটাবেস পরিষেবা যা রিলেশনাল এবং অ-রিলেশনাল অপারেশনাল ওয়ার্কলোড উভয়ের জন্যই দারুণ। এর মূল ক্ষমতার বাইরে, স্প্যানার শক্তিশালী উন্নত বৈশিষ্ট্যগুলি অফার করে যা বুদ্ধিমান এবং ডেটা-চালিত অ্যাপ্লিকেশন তৈরি করতে সক্ষম করে।

এই কোডল্যাবটি স্প্যানারের ভিত্তিগত বোঝার উপর ভিত্তি করে তৈরি করে এবং একটি ভিত্তি হিসাবে একটি অনলাইন ব্যাঙ্কিং অ্যাপ্লিকেশন ব্যবহার করে আপনার ডেটা প্রক্রিয়াকরণ এবং বিশ্লেষণাত্মক ক্ষমতাগুলিকে উন্নত করতে এর উন্নত ইন্টিগ্রেশনগুলিকে কাজে লাগায়।

আমরা তিনটি প্রধান উন্নত বৈশিষ্ট্যের উপর ফোকাস করব:

  • ভার্টেক্স এআই ইন্টিগ্রেশন : গুগল ক্লাউডের এআই প্ল্যাটফর্ম, ভার্টেক্স এআই-এর সাথে স্প্যানারকে কীভাবে নির্বিঘ্নে সংহত করা যায় তা আবিষ্কার করুন। আপনি শিখবেন কিভাবে স্প্যানার এসকিউএল কোয়েরির মধ্যে থেকে সরাসরি ভার্টেক্স এআই মডেলগুলি আনতে হয়, শক্তিশালী ইন-ডাটাবেস রূপান্তর এবং পূর্বাভাস সক্ষম করে, আমাদের ব্যাঙ্কিং অ্যাপ্লিকেশনটিকে বাজেট ট্র্যাকিং এবং অসঙ্গতি সনাক্তকরণের মতো ব্যবহারের ক্ষেত্রে লেনদেনগুলিকে স্বয়ংক্রিয়ভাবে শ্রেণীবদ্ধ করার অনুমতি দেয়।
  • পূর্ণ-পাঠ্য অনুসন্ধান : স্প্যানারের মধ্যে পূর্ণ-পাঠ্য অনুসন্ধান কার্যকারিতা কীভাবে প্রয়োগ করতে হয় তা শিখুন। আপনি আপনার অপারেশনাল ডেটা জুড়ে কীওয়ার্ড-ভিত্তিক অনুসন্ধানগুলি সম্পাদন করতে টেক্সট ডেটা ইন্ডেক্সিং এবং দক্ষ কোয়েরি লিখতে অন্বেষণ করবেন, শক্তিশালী ডেটা আবিষ্কার সক্ষম করে, যেমন আমাদের ব্যাঙ্কিং সিস্টেমের মধ্যে ইমেল ঠিকানার মাধ্যমে গ্রাহকদের দক্ষতার সাথে খুঁজে পাওয়া।
  • BigQuery ফেডারেটেড ক্যোয়ারী : BigQuery-এ থাকা ডেটা সরাসরি কোয়েরির জন্য স্প্যানারের ফেডারেটেড ক্যোয়ারী ক্ষমতাগুলি কীভাবে ব্যবহার করা যায় তা অন্বেষণ করুন। এটি আপনাকে স্প্যানারের রিয়েল-টাইম অপারেশনাল ডেটাকে BigQuery-এর বিশ্লেষণাত্মক ডেটাসেটের সাথে একত্রিত করতে দেয় এবং ডেটা ডুপ্লিকেশন বা জটিল ETL প্রক্রিয়া ছাড়াই রিপোর্ট করার জন্য, আমাদের ব্যাঙ্কিং অ্যাপ্লিকেশানে বিভিন্ন ব্যবহারের ক্ষেত্রে যেমন টার্গেটেড মার্কেটিং ক্যাম্পেইনগুলিকে বিস্তৃত ঐতিহাসিক প্রবণতাগুলির সাথে রিয়েল-টাইম গ্রাহক ডেটা একত্রিত করে।

আপনি কি শিখবেন

  • কিভাবে একটি স্প্যানার উদাহরণ সেটআপ করবেন।
  • কিভাবে একটি ডাটাবেস এবং টেবিল তৈরি করতে হয়।
  • কিভাবে আপনার স্প্যানার ডাটাবেস টেবিলে ডেটা লোড করবেন।
  • স্প্যানার থেকে ভার্টেক্স এআই মডেলগুলিকে কীভাবে কল করবেন।
  • অস্পষ্ট অনুসন্ধান এবং পূর্ণ-পাঠ্য অনুসন্ধান ব্যবহার করে কীভাবে আপনার স্প্যানার ডাটাবেস অনুসন্ধান করবেন।
  • BigQuery থেকে স্প্যানারের বিরুদ্ধে ফেডারেটেড কোয়েরিগুলি কীভাবে সম্পাদন করবেন।
  • কিভাবে আপনার স্প্যানার ইনস্ট্যান্স মুছে ফেলবেন।

আপনি কি প্রয়োজন হবে

  • একটি Google ক্লাউড প্রকল্প যা একটি বিলিং অ্যাকাউন্টের সাথে সংযুক্ত৷
  • একটি ওয়েব ব্রাউজার, যেমন ক্রোম বা ফায়ারফক্স

2. সেটআপ এবং প্রয়োজনীয়তা

একটি প্রকল্প তৈরি করুন

আপনার যদি ইতিমধ্যেই বিলিং সক্ষম সহ একটি Google ক্লাউড প্রকল্প থাকে, তাহলে কনসোলের উপরের বাম দিকে প্রজেক্ট নির্বাচন পুল ডাউন মেনুতে ক্লিক করুন:

বিদ্যমান প্রকল্প

একটি নির্বাচিত প্রকল্পের সাথে, প্রয়োজনীয় API সক্ষম করতে এড়িয়ে যান।

আপনার যদি ইতিমধ্যে একটি Google অ্যাকাউন্ট না থাকে (Gmail বা Google Apps), তাহলে আপনাকে অবশ্যই একটি তৈরি করতে হবে। Google ক্লাউড প্ল্যাটফর্ম কনসোলে সাইন-ইন করুন ( console.cloud.google.com ) এবং একটি নতুন প্রকল্প তৈরি করুন৷

একটি নতুন প্রকল্প তৈরি করতে ফলস্বরূপ ডায়ালগে "নতুন প্রকল্প" বোতামে ক্লিক করুন:

নতুন প্রকল্প

আপনার যদি ইতিমধ্যে একটি প্রকল্প না থাকে, তাহলে আপনার প্রথমটি তৈরি করতে আপনাকে এই মত একটি ডায়ালগ দেখতে হবে:

প্রকল্পের ডায়ালগ

পরবর্তী প্রকল্প তৈরির ডায়ালগ আপনাকে আপনার নতুন প্রকল্পের বিশদ বিবরণ লিখতে দেয়।

প্রোজেক্ট আইডি মনে রাখবেন, যা সমস্ত Google ক্লাউড প্রোজেক্ট জুড়ে একটি অনন্য নাম। এটি পরে এই কোডল্যাবে PROJECT_ID হিসাবে উল্লেখ করা হবে।

প্রকল্পের বিবরণ

এর পরে, আপনি যদি ইতিমধ্যে এটি না করে থাকেন, তাহলে Google ক্লাউড সংস্থানগুলি ব্যবহার করতে এবং Spanner API , Vertex AI API , BigQuery API , এবং BigQuery সংযোগ API সক্ষম করার জন্য আপনাকে বিকাশকারী কনসোলে বিলিং সক্ষম করতে হবে৷

প্রকল্প বিলিং

স্প্যানার মূল্য এখানে নথিভুক্ত করা হয়েছে। অন্যান্য সম্পদের সাথে সম্পর্কিত অন্যান্য খরচ তাদের নির্দিষ্ট মূল্য পৃষ্ঠাগুলিতে নথিভুক্ত করা হবে।

Google ক্লাউড প্ল্যাটফর্মের নতুন ব্যবহারকারীরা $300 বিনামূল্যের ট্রায়ালের জন্য যোগ্য৷

গুগল ক্লাউড শেল সেটআপ

এই কোডল্যাবে আমরা Google ক্লাউড শেল ব্যবহার করব, ক্লাউডে চলমান একটি কমান্ড লাইন পরিবেশ।

এই ডেবিয়ান-ভিত্তিক ভার্চুয়াল মেশিনটি আপনার প্রয়োজনীয় সমস্ত বিকাশের সরঞ্জামগুলির সাথে লোড করা হয়েছে। এটি একটি ক্রমাগত 5 GB হোম ডিরেক্টরি অফার করে এবং Google ক্লাউডে চলে, যা নেটওয়ার্ক কর্মক্ষমতা এবং প্রমাণীকরণকে ব্যাপকভাবে উন্নত করে। এর মানে হল যে এই কোডল্যাবের জন্য আপনার যা দরকার তা হল একটি ব্রাউজার।

ক্লাউড কনসোল থেকে ক্লাউড শেল সক্রিয় করতে, কেবল ক্লাউড শেল সক্রিয় করুন ক্লিক করুন ক্লাউড শেল আইকন (পরিবেশের সাথে সংযোগ স্থাপন এবং সংযোগের জন্য এটি শুধুমাত্র কয়েক মুহূর্ত নিতে হবে)।

মেঘের শেল

একবার ক্লাউড শেলের সাথে সংযুক্ত হয়ে গেলে, আপনি দেখতে পাবেন যে আপনি ইতিমধ্যেই প্রমাণীকৃত এবং প্রকল্পটি ইতিমধ্যেই আপনার 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 খুঁজছেন? সেটআপ ধাপে আপনি কোন আইডি ব্যবহার করেছেন তা দেখুন বা ক্লাউড কনসোল ড্যাশবোর্ডে দেখুন:

প্রকল্প আইডি

ক্লাউড শেল ডিফল্টরূপে কিছু এনভায়রনমেন্ট ভেরিয়েবলও সেট করে, যা আপনার ভবিষ্যত কমান্ড চালানোর সময় কার্যকর হতে পারে।

echo $GOOGLE_CLOUD_PROJECT

প্রত্যাশিত আউটপুট:

<PROJECT_ID>

প্রয়োজনীয় API সক্রিয় করুন

আপনার প্রকল্পের জন্য Spanner, Vertex AI, এবং BigQuery APIs সক্ষম করুন:

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

সারাংশ

এই ধাপে, আপনি আপনার প্রজেক্ট সেট আপ করেছেন যদি আপনার কাছে ইতিমধ্যে একটি না থাকে, ক্লাউড শেল সক্রিয় করা থাকে এবং প্রয়োজনীয় API গুলি সক্ষম করে থাকে।

পরবর্তী আপ

এর পরে, আপনি স্প্যানার ইনস্ট্যান্স সেট আপ করবেন।

3. একটি স্প্যানার উদাহরণ সেটআপ করুন

স্প্যানার উদাহরণ তৈরি করুন

এই ধাপে, আপনি কোডল্যাবের জন্য একটি স্প্যানার ইনস্ট্যান্স সেট আপ করবেন। এটি করতে, ক্লাউড শেল খুলুন এবং এই কমান্ডটি চালান:

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. একটি ডাটাবেস এবং স্কিমা তৈরি করুন

প্রাথমিক আবেদন প্রস্তুত করুন

এই ধাপে, আপনি কোডের মাধ্যমে ডাটাবেস এবং স্কিমা তৈরি করবেন।

প্রথমে, Maven ব্যবহার করে onlinebanking নামে একটি জাভা অ্যাপ্লিকেশন তৈরি করুন:

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 ফাইলটি খুলুন। Google ক্লাউড লাইব্রেরির সংস্করণ পরিচালনা করতে Maven BOM ব্যবহার করতে নির্ভরতা ব্যবস্থাপনা বিভাগ যোগ করুন:

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

ক্লাউড শেল এডিটরের "ফাইল" মেনুতে "সংরক্ষণ করুন" নির্বাচন করে অথবা Ctrl+S টিপে আপনি pom.xml ফাইলে করা পরিবর্তনগুলি সংরক্ষণ করুন৷

এখন যেহেতু নির্ভরতাগুলি প্রস্তুত, আপনি একটি স্কিমা তৈরি করতে অ্যাপে কোড যোগ করবেন, কিছু সূচী (অনুসন্ধান সহ) এবং একটি দূরবর্তী প্রান্তের সাথে সংযুক্ত একটি এআই মডেল। আপনি এই শিল্পকর্মগুলি তৈরি করবেন এবং এই কোডল্যাব বরাবর এই ক্লাসে আরও পদ্ধতি যুক্ত করবেন।

onlinebanking/src/main/java/com/google/codelabs এর অধীনে App.java খুলুন এবং নিম্নলিখিত কোড দিয়ে বিষয়বস্তু প্রতিস্থাপন করুন:

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

স্প্যানারে স্কিমা পরীক্ষা করুন

স্প্যানার কনসোলে , আপনার ইন্সট্যান্স এবং ডাটাবেসে নেভিগেট করুন যা এইমাত্র তৈরি করা হয়েছে।

আপনি 3টি টেবিল দেখতে পাবেন - Accounts , Customers এবং TransactionLedger

স্কিমা দেখুন

এই ক্রিয়াটি Accounts , Customers , এবং TransactionLedger টেবিল সহ ডাটাবেস স্কিমা তৈরি করে, সাথে অপ্টিমাইজ করা ডেটা পুনরুদ্ধারের জন্য সেকেন্ডারি ইনডেক্স এবং একটি Vertex AI মডেল রেফারেন্স।

এন্টিটি রিলেশনশিপ ডায়াগ্রাম

TransactionLedger টেবিলটি অ্যাকাউন্টের মধ্যে ইন্টারলিভ করা হয়েছে যাতে উন্নত ডেটা লোকেলিটির মাধ্যমে অ্যাকাউন্ট-নির্দিষ্ট লেনদেনের জন্য ক্যোয়ারী কর্মক্ষমতা বাড়ানো যায়।

সেকেন্ডারি ইনডেক্স ( CustomersByEmail , CustomersFuzzyEmail , AccountsByCustomer , TransactionLedgerByAccountType , TransactionLedgerByCategory , TransactionLedgerTextSearch ) এই সাধারণ ডেটা অ্যাক্সেস প্যাটার্নগুলিকে অপ্টিমাইজ করার জন্য বাস্তবায়িত করা হয়েছে, যেমন এই ইমেল কোড-অ্যাক্ট-ল্যাবে ব্যবহার করা সাধারণ ডেটা এক্সেস প্যাটার্ন এবং গ্রাহকের কোড-অ্যাক্ট-ল্যাবে ব্যবহার করা হয়েছে। গ্রাহক দ্বারা অ্যাকাউন্ট, এবং দক্ষতার সাথে অনুসন্ধান এবং লেনদেনের ডেটা অনুসন্ধান।

TransactionCategoryModel একটি LLM-এ সরাসরি এসকিউএল কল সক্ষম করতে Vertex AI ব্যবহার করে, যা এই কোডল্যাবে গতিশীল লেনদেন শ্রেণীকরণের জন্য ব্যবহৃত হয়।

সারাংশ

এই ধাপে, আপনি স্প্যানার ডাটাবেস এবং স্কিমা তৈরি করেছেন।

পরবর্তী আপ

এর পরে, আপনি নমুনা অ্যাপ্লিকেশন ডেটা লোড করবেন।

5. ডেটা লোড করুন

এখন, আপনি ডাটাবেসে CSV ফাইল থেকে নমুনা ডেটা লোড করার জন্য কার্যকারিতা যোগ করবেন।

App.java খুলুন এবং আমদানি প্রতিস্থাপন করে শুরু করুন:

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;

তারপর ক্লাস 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;
        });
  }

switch (command) এর মধ্যে সন্নিবেশ করার জন্য main পদ্ধতিতে আরেকটি কেস স্টেটমেন্ট যোগ করুন:

        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;

অবশেষে, 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

স্প্যানার কনসোলে , আপনার উদাহরণ এবং ডাটাবেসের জন্য স্প্যানার স্টুডিওতে ফিরে যান। তারপর TransactionLedger টেবিলটি নির্বাচন করুন, এবং ডেটা লোড হয়েছে তা যাচাই করতে সাইডবারে "ডেটা" ক্লিক করুন৷ টেবিলে 200টি সারি থাকতে হবে।

ডেটা দেখুন

সারাংশ

এই ধাপে, আপনি ডাটাবেসে নমুনা তথ্য সন্নিবেশ করান।

পরবর্তী আপ

এর পরে, আপনি স্প্যানার এসকিউএল-এর মধ্যে সরাসরি ব্যাঙ্কিং লেনদেনগুলিকে স্বয়ংক্রিয়ভাবে শ্রেণীবদ্ধ করতে Vertex AI ইন্টিগ্রেশনের সুবিধা নেবেন।

6. ভার্টেক্স এআই দিয়ে ডেটা শ্রেণীবদ্ধ করুন

এই ধাপে, আপনি স্প্যানার এসকিউএল-এর মধ্যে সরাসরি আপনার আর্থিক লেনদেনগুলিকে স্বয়ংক্রিয়ভাবে শ্রেণীবদ্ধ করতে Vertex AI-এর শক্তি ব্যবহার করবেন। Vertex AI এর সাহায্যে আপনি একটি বিদ্যমান প্রাক-প্রশিক্ষিত মডেল বা ট্রেন বেছে নিতে পারেন এবং আপনার নিজস্ব স্থাপন করতে পারেন। ভার্টেক্স এআই মডেল গার্ডেনে উপলব্ধ মডেলগুলি দেখুন।

এই কোডল্যাবের জন্য আমরা জেমিনি মডেলগুলির একটি, Gemini Flash Lite ব্যবহার করব। মিথুনের এই সংস্করণটি ব্যয়-কার্যকর তবে এখনও বেশিরভাগ দৈনন্দিন কাজের চাপ সামলাতে পারে।

বর্তমানে, আমাদের কাছে বেশ কিছু আর্থিক লেনদেন আছে যেগুলিকে আমরা বর্ণনার উপর নির্ভর করে ( groceries , transportation , ইত্যাদি) শ্রেণিবদ্ধ করতে চাই। আমরা স্প্যানারে একটি মডেল নিবন্ধন করে এবং AI মডেলকে কল করার জন্য ML.PREDICT ব্যবহার করে এটি করতে পারি।

আমাদের ব্যাঙ্কিং অ্যাপ্লিকেশনে আমরা গ্রাহকের আচরণের গভীর অন্তর্দৃষ্টি পেতে লেনদেনগুলিকে শ্রেণীবদ্ধ করতে চাই যাতে আমরা পরিষেবাগুলিকে ব্যক্তিগতকৃত করতে পারি, আরও কার্যকরভাবে অসঙ্গতিগুলি সনাক্ত করতে পারি, অথবা গ্রাহককে তাদের বাজেট মাসে মাসে ট্র্যাক করার ক্ষমতা প্রদান করতে পারি।

প্রথম ধাপটি ইতিমধ্যে সম্পন্ন হয়েছিল যখন আমরা ডাটাবেস এবং স্কিমা তৈরি করেছি, যা এই মত একটি মডেল তৈরি করেছে:

মডেল বিবৃতি তৈরি করুন

এর পরে, আমরা 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;
    }
  }

শ্রেণীবদ্ধ করার জন্য 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

স্প্যানার স্টুডিওতে, TransactionLedger টেবিলের জন্য প্রিভিউ ডেটা স্টেটমেন্ট চালান। Category কলাম এখন সমস্ত সারির জন্য পপুলেট করা উচিত।

শ্রেণীবদ্ধ ডেটা দেখুন

এখন যেহেতু আমরা লেনদেনগুলিকে শ্রেণীবদ্ধ করেছি আমরা এই তথ্যটি অভ্যন্তরীণ বা গ্রাহক-মুখী প্রশ্নের জন্য ব্যবহার করতে পারি, নিম্নলিখিত ধাপে আমরা দেখব কিভাবে একটি প্রদত্ত গ্রাহক মাসে মাসে একটি বিভাগে কত খরচ করছে।

সারাংশ

এই ধাপে, আপনি আপনার ডেটার AI-চালিত শ্রেণীকরণ সম্পাদন করতে একটি প্রাক-প্রশিক্ষিত মডেল ব্যবহার করেছেন।

পরবর্তী আপ

এর পরে, আপনি অস্পষ্ট এবং পূর্ণ-পাঠ্য অনুসন্ধানগুলি সম্পাদন করতে টোকেনাইজেশন ব্যবহার করবেন।

7. পূর্ণ-পাঠ্য অনুসন্ধান ব্যবহার করে ক্যোয়ারী

ক্যোয়ারী কোড যোগ করুন

স্প্যানার অনেক পূর্ণ-পাঠ্য অনুসন্ধান প্রশ্ন প্রদান করে। এই ধাপে আপনি একটি সঠিক-মিল অনুসন্ধান সঞ্চালন করবেন, তারপর একটি অস্পষ্ট অনুসন্ধান এবং একটি পূর্ণ-পাঠ্য অনুসন্ধান সঞ্চালন করবেন।

App.java খুলুন এবং আমদানি প্রতিস্থাপন করে শুরু করুন:

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

প্রশ্নের জন্য main পদ্ধতিতে অন্য case বিবৃতি যোগ করুন:

        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;

অবশেষে, 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 পদ্ধতিটি প্রদত্ত কাস্টমারআইডির সাথে মেলে এমন গ্রাহকদের খুঁজে পেতে এবং সেই গ্রাহকের অ্যাকাউন্টে যোগদানের জন্য এই সূচকটি অন্তর্নিহিতভাবে ব্যবহার করে।

স্প্যানার স্টুডিওতে সরাসরি সম্পাদিত হলে ক্যোয়ারীটি কেমন দেখায়: ম্যানুয়ালি কোয়েরি ব্যালেন্স

কমান্ড চালানোর মাধ্যমে গ্রাহক 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

গ্রাহক ইমেলের বিরুদ্ধে একটি অস্পষ্ট অনুসন্ধান সঞ্চালন

অস্পষ্ট অনুসন্ধানগুলি বানান বৈচিত্র এবং টাইপো সহ অনুসন্ধান পদগুলির জন্য আনুমানিক মিলগুলি খুঁজে পেতে অনুমতি দেয়৷

আপনি যখন ডাটাবেস এবং স্কিমা তৈরি করেছিলেন তখন একটি এন-গ্রাম সূচক ইতিমধ্যেই যোগ করা হয়েছিল:

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 ব্যবহার করে ইমেলের মাধ্যমে গ্রাহকদের খুঁজে পেতে এই সূচকের বিরুদ্ধে অনুসন্ধান করতে। কারণ ইমেল কলামটি এন-গ্রাম টোকেনাইজ করা হয়েছে এই ক্যোয়ারীতে বানান ভুল থাকতে পারে এবং তারপরও একটি সঠিক উত্তর দিতে পারে। ফলাফল সেরা ম্যাচের উপর ভিত্তি করে আদেশ করা হয়.

কমান্ড চালানোর মাধ্যমে 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 বা অনুরূপ স্ট্রিং অন্তর্ভুক্ত সবচেয়ে কাছের ম্যাচগুলি দেখায়।

স্প্যানার স্টুডিওতে সরাসরি সম্পাদিত হলে ক্যোয়ারীটি কেমন দেখায়: ম্যানুয়ালি মাডি খুঁজছি

অস্পষ্ট অনুসন্ধান বানান ভুলের ক্ষেত্রেও সাহায্য করতে পারে যেমন 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

প্রতিটি ক্ষেত্রে প্রত্যাশিত গ্রাহক ইমেল শীর্ষ হিট হিসাবে ফিরে আসে।

স্প্যানারের ফুল-টেক্সট অনুসন্ধান বৈশিষ্ট্যটি কীওয়ার্ড বা বাক্যাংশের উপর ভিত্তি করে রেকর্ড পুনরুদ্ধার করতে ব্যবহৃত হয়। এটিতে বানান ভুল বা প্রতিশব্দ অনুসন্ধানের জন্য সংশোধন করার ক্ষমতা রয়েছে।

আপনি ডাটাবেস এবং স্কিমা তৈরি করার সময় একটি পূর্ণ-পাঠ্য অনুসন্ধান সূচক ইতিমধ্যেই যোগ করা হয়েছে:

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 দিনের সমস্ত ব্যয় (ডেবিট) সন্ধান করে৷

কমান্ডটি চালানোর মাধ্যমে groceries বিভাগে গ্রাহক 1 জন্য গত মাসে মোট খরচ পান:

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

সারাংশ

এই ধাপে, আপনি সঠিক-ম্যাচ কোয়েরির পাশাপাশি অস্পষ্ট এবং পূর্ণ-পাঠ্য অনুসন্ধানগুলি সম্পাদন করেছেন।

পরবর্তী আপ

এর পরে, আপনি ফেডারেটেড কোয়েরি সম্পাদন করতে Google BigQuery-এর সাথে স্প্যানারকে একীভূত করবেন, যা আপনাকে BigQuery ডেটার সাথে আপনার রিয়েল-টাইম স্প্যানার ডেটা একত্রিত করতে দেয়।

8. BigQuery-এর সাথে ফেডারেটেড কোয়েরি চালান

BigQuery ডেটাসেট তৈরি করুন

এই ধাপে, আপনি ফেডারেটেড কোয়েরি ব্যবহারের মাধ্যমে BigQuery এবং Spanner ডেটা একত্রিত করবেন।

এটি করার জন্য, ক্লাউড শেল কমান্ড লাইনে, প্রথমে একটি 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 থেকে স্প্যানারে একটি সংযোগ তৈরি করুন:

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 টেবিলে কিছু গ্রাহক যোগ করুন যেগুলি আমাদের স্প্যানার ডেটার সাথে যুক্ত হতে পারে:

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 খুলুন এবং আমদানি প্রতিস্থাপন করে শুরু করুন:

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

প্রচারণার জন্য 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

campaign কমান্ড চালানোর মাধ্যমে গত 3 মাসে কমপক্ষে $5000 খরচ করে থাকলে বিপণন প্রচারাভিযানে ( 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 থেকে ফেডারেটেড কোয়েরি সফলভাবে সম্পাদন করেছেন যা রিয়েল-টাইম স্প্যানার ডেটা এনেছে।

পরবর্তী আপ

এর পরে, আপনি চার্জ এড়াতে এই কোডল্যাবের জন্য তৈরি সংস্থানগুলি পরিষ্কার করতে পারেন।

9. পরিষ্কার করা (ঐচ্ছিক)

এই ধাপটি ঐচ্ছিক। আপনি যদি আপনার স্প্যানার দৃষ্টান্তের সাথে পরীক্ষা চালিয়ে যেতে চান তবে আপনাকে এই সময়ে এটি পরিষ্কার করার দরকার নেই৷ যাইহোক, আপনি যে প্রকল্পটি ব্যবহার করছেন সেটির জন্য চার্জ করা অব্যাহত থাকবে। আপনার যদি এই উদাহরণের জন্য আর কোন প্রয়োজন না থাকে, তাহলে এই চার্জগুলি এড়াতে আপনার এই সময়ে এটি মুছে ফেলা উচিত। স্প্যানার ইন্সট্যান্স ছাড়াও, এই কোডল্যাবটি একটি BigQuery ডেটাসেট এবং কানেকশনও তৈরি করেছে যা আর প্রয়োজন না হলে পরিষ্কার করা উচিত।

স্প্যানার উদাহরণ মুছুন:

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. অভিনন্দন

🚀 আপনি একটি নতুন ক্লাউড স্প্যানার দৃষ্টান্ত তৈরি করেছেন, একটি খালি ডাটাবেস তৈরি করেছেন, নমুনা ডেটা লোড করেছেন, উন্নত ক্রিয়াকলাপ এবং প্রশ্নগুলি সম্পাদন করেছেন এবং (ঐচ্ছিকভাবে) ক্লাউড স্প্যানার উদাহরণটি মুছে ফেলেছেন৷

আমরা কভার করেছি কি

  • কিভাবে একটি স্প্যানার উদাহরণ সেটআপ করবেন।
  • কিভাবে একটি ডাটাবেস এবং টেবিল তৈরি করতে হয়।
  • কিভাবে আপনার স্প্যানার ডাটাবেস টেবিলে ডেটা লোড করবেন।
  • স্প্যানার থেকে ভার্টেক্স এআই মডেলগুলিকে কীভাবে কল করবেন।
  • অস্পষ্ট অনুসন্ধান এবং পূর্ণ-পাঠ্য অনুসন্ধান ব্যবহার করে কীভাবে আপনার স্প্যানার ডাটাবেস অনুসন্ধান করবেন।
  • BigQuery থেকে Spanner-এর বিরুদ্ধে ফেডারেটেড কোয়েরিগুলি কীভাবে সম্পাদন করবেন।
  • কিভাবে আপনার স্প্যানার ইনস্ট্যান্স মুছে ফেলবেন।

এরপর কি?