Java SDKを使ってImage Searchの類似検索を試す【検索編】

# 今回の目的（Java SDKを使って検索してみる）
前回、Image Searchの設定と画像取り込みまで終わりました。今回は、前回作ったImage Searchにプログラムでアクセスしてみようと思います。

## 実装環境の構築
今回はJavaでプロジェクト作成します。Javaの開発環境が整っていない方は、JavaとGradleのインストールを行っておいてください。（サンプルはGradleで作成していますが、Mavenでも構いません）

参考：Windows+Scoopの場合

``` Javaインストール
PS C:\Users\user> scoop install oraclejdk14
```

``` Mavenインストール
PS C:\Users\user> scoop install gradle
```
## Javaプロジェクトの作成
それでは、Javaのプロジェクトを作ってみましょう

プロジェクトディレクトリの作成

```
PS C:\Users\user\Documents\temp> mkdir imagesearch-sample


    Directory: C:\Users\user\Documents\temp

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----          2020/05/19    15:59                imagesearch-sample

PS C:\Users\user\Documents\temp> cd .\imagesearch-sample\
PS C:\Users\user\Documents\temp\imagesearch-sample>
```

Javaプロジェクトの作成

```
PS C:\Users\user\Documents\temp\imagesearch-sample> gradle init

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Swift
Enter selection (default: Java) [1..5] 3

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit 4) [1..4] 1

Project name (default: imagesearch-sample):
Source package (default: imagesearch.sample):

> Task :init
Get more help with your project: https://docs.gradle.org/6.4.1/userguide/tutorial_java_projects.html

BUILD SUCCESSFUL in 29s
2 actionable tasks: 2 executed
PS C:\Users\user\Documents\temp\imagesearch-sample>
```

``` プロジェクト階層
PS C:\Users\user\Documents\temp\imagesearch-sample> tree /F
フォルダー パスの一覧:  ボリューム Windows
ボリューム シリアル番号は 9411-0B65 です
C:.
│  .gitattributes
│  .gitignore
│  build.gradle
│  gradlew
│  gradlew.bat
│  settings.gradle
│
├─.gradle
│  ├─6.4.1
│  │  │  gc.properties
│  │  │
│  │  ├─executionHistory
│  │  │      executionHistory.bin
│  │  │      executionHistory.lock
│  │  │
│  │  ├─fileChanges
│  │  │      last-build.bin
│  │  │
│  │  ├─fileHashes
│  │  │      fileHashes.bin
│  │  │      fileHashes.lock
│  │  │
│  │  └─vcsMetadata-1
│  ├─buildOutputCleanup
│  │      buildOutputCleanup.lock
│  │      cache.properties
│  │      outputFiles.bin
│  │
│  ├─checksums
│  │      checksums.lock
│  │
│  └─vcs-1
│          gc.properties
│
├─gradle
│  └─wrapper
│          gradle-wrapper.jar
│          gradle-wrapper.properties
│
└─src
    ├─main
    │  ├─java
    │  │  └─imagesearch
    │  │      └─sample
    │  │              App.java
    │  │
    │  └─resources
    └─test
        ├─java
        │  └─imagesearch
        │      └─sample
        │              AppTest.java
        │
        └─resources
PS C:\Users\user\Documents\temp\imagesearch-sample>
```

### 依存関係の追加
Alibaba CloudのSDKを追加します。

``` build.gradle
/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Java project to get you started.
 * For more details take a look at the Java Quickstart chapter in the Gradle
 * User Manual available at https://docs.gradle.org/6.4.1/userguide/tutorial_java_projects.html
 */

plugins {
    // Apply the java plugin to add support for Java
    id 'java'

    // Apply the application plugin to add support for building a CLI application.
    id 'application'
}

repositories {
    // Use jcenter for resolving dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}

dependencies {
    // This dependency is used by the application.
    implementation 'com.google.guava:guava:28.2-jre'
    
    // Alibaba Cloud
    compile 'com.aliyun:aliyun-java-sdk-imagesearch:2.0.0'
    compile 'com.aliyun:aliyun-java-sdk-core:[4.3.2,5.0.0)'

    // Use JUnit test framework
    testImplementation 'junit:junit:4.12'
}

application {
    // Define the main class for the application.
    mainClassName = 'imagesearch.sample.App'
}
```

### 前回作成したImage Searchにアクセスする

アクセスキーとシークレットキーは前回作成したものを使用します。
また、リージョンとエンドポイントについては、以下の表から設定します。今回は日本（東京）を設定します。

| リージョン | エンドポイント |
|:-:|:-:|
| シンガポール  | imagesearch.ap-southeast-1.aliyuncs.com  |
| 中国 (香港)  | imagesearch.cn-hongkong.aliyuncs.com  |
| 日本 (東京)  | imagesearch.ap-northeast-1.aliyuncs.com  |
| オーストラリア (シドニー)  | imagesearch.ap-southeast-2.aliyuncs.com  |

検索対象の画像は/src/resources の中に置きました。今回は "search01.jpg"という名前で作成しています。
![2020-05-19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18582/2e5c686b-6dd9-0f6c-0c09-cfba12b9f106.png)

``` App.java
/*
 * This Java source file was generated by the Gradle 'init' task.
 */
package imagesearch.sample;

import java.io.InputStream;
import java.util.Base64;
import java.util.List;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.imagesearch.model.v20190325.SearchImageRequest;
import com.aliyuncs.imagesearch.model.v20190325.SearchImageResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;

public class App {
    /** AccessKey */
    private static final String ACCESS_KEY = "XXXXXXXXXXXXXXXXXXX";
    /** SeacretKey */
    private static final String KEY_SEACRET = "YYYYYYYYYYYYYYYYYY";

    public static void main(String[] args) throws Exception {
                //初期化(Tokyoリージョン)
                DefaultProfile.addEndpoint("ap-northeast-1", "ImageSearch", "imagesearch.ap-northeast-1.aliyuncs.com");
                IClientProfile profile = DefaultProfile.getProfile("ap-northeast-1", ACCESS_KEY, KEY_SEACRET);
                IAcsClient client = new DefaultAcsClient(profile);
        
                //検索用のリクエストを作成
                SearchImageRequest request = new SearchImageRequest();
                // 入力必須。Image Search インスタンス名。
                request.setInstanceName("itemsearch");
                // 画像をBase64に変換
                ClassLoader loader = Thread.currentThread().getContextClassLoader();
                InputStream is = loader.getResourceAsStream("search01.jpg");
                String image = Base64.getEncoder().encodeToString(is.readAllBytes());
                //画像で検索
                request.setPicContent(image);
                SearchImageResponse response = client.getAcsResponse(request);
        
                // 検索結果
                Boolean checkShowJsonItemName = response.checkShowJsonItemName();
                Integer code = response.getCode();
                SearchImageResponse.Head head = response.getHead();
                Integer docsFound = head.getDocsFound();
                Integer docsReturn = head.getDocsReturn();
                Integer searchTime = head.getSearchTime();
                String msg = response.getMsg();
                String requestId = response.getRequestId();
                Boolean success = response.getSuccess();
                SearchImageResponse.PicInfo picInfo = response.getPicInfo();
                List<SearchImageResponse.PicInfo.Category> categories = picInfo.getAllCategories();
                Integer categoryId = picInfo.getCategoryId();
                String region = picInfo.getRegion();
        
                System.out.printf("checkShowJsonItemName: %s%ncode: %s%ndocsFound: %s%nsearchTime:%s %nmsg: %s%nrequestId: %s%nsucess: %s%ncategoryId: %s%nregion: %s%n", checkShowJsonItemName, code, docsFound, docsReturn, searchTime, msg, requestId, success, categoryId, region);
                categories.forEach(s -> {
                    Integer id = s.getId();
                    String name = s.getName();
                    System.out.printf("Categories %n\tid: %s, name: %s%n", id, name);
                });
                List<SearchImageResponse.Auction> list = response.getAuctions();
                list.forEach(s -> {
                    System.out.println("-----------");
                    String productid = s.getProductId();
                    Integer categoryid = s.getCategoryId();
                    String customCountent = s.getCustomContent();
                    Integer intAttr = s.getIntAttr();
                    String picName = s.getPicName();
                    String sortExpValues = s.getSortExprValues();
                    String strAttr = s.getStrAttr();
                    System.out.printf("productId: %s %nCategoryId: %s %ncustomContent: %s %nintAttr: %s %npicName: %s %nsortExpValues: %s %nStrAttr: %s%n", productid, categoryid, customCountent, intAttr, picName, sortExpValues, strAttr);
                });
    }
}

```

``` 検索結果
checkShowJsonItemName: false
code: 0
docsFound: 11
searchTime:10
msg: 99
requestId: success
sucess: 85B66F6E-9932-4BF6-A05F-4AA0AF4D7CD5
categoryId: true
region: 9
Categories
        id: 0, name: Tops
Categories
        id: 1, name: Dress
Categories 
        id: 2, name: Bottoms
Categories
        id: 3, name: Bag
Categories
        id: 4, name: Shoes
Categories 
        id: 5, name: Accessories
Categories
        id: 6, name: Snack
Categories
        id: 7, name: Makeup
Categories
        id: 8, name: Bottle
Categories
        id: 9, name: Furniture
Categories
        id: 20, name: Toy
Categories
        id: 21, name: Underwear
Categories
        id: 22, name: Digital device
Categories
        id: 88888888, name: Other
-----------
productId: 1011
CategoryId: 9
customContent: k1:v12,k2:v211,k3:v311
intAttr: null 
picName: 12.jpg
sortExpValues: 3.07306838035583;217
StrAttr: null
-----------
productId: 1010
CategoryId: 9
customContent: k1:v11,k2:v210,k3:v310
intAttr: null
picName: 11.jpg
sortExpValues: 2.97270393371582;222
StrAttr: null
-----------
productId: 1008
CategoryId: 9
customContent: k1:v09,k2:v208,k3:v308
intAttr: null
picName: 09.jpg
sortExpValues: 2.87724995613098;238
StrAttr: null
-----------
productId: 1009
CategoryId: 9
customContent: k1:v10,k2:v209,k3:v309
intAttr: null
picName: 10.jpg 
sortExpValues: 2.79507827758789;235
StrAttr: null
-----------
productId: 1001
CategoryId: 9
customContent: k1:v02,k2:v201,k3:v301
intAttr: null
picName: 02.jpg
sortExpValues: 2.67687916755676;251
StrAttr: null
-----------
productId: 1004
CategoryId: 9
customContent: k1:v05,k2:v204,k3:v304 
intAttr: null
picName: 05.jpg
sortExpValues: 2.67470407485962;249
StrAttr: null
-----------
productId: 1005
CategoryId: 9
customContent: k1:v06,k2:v205,k3:v305
intAttr: null
picName: 06.jpg
sortExpValues: 2.66586232185364;254
StrAttr: null
-----------
productId: 1003
CategoryId: 9
customContent: k1:v04,k2:v203,k3:v303
intAttr: null
picName: 04.jpg
sortExpValues: 2.63756942749023;255
StrAttr: null
-----------
productId: 1000
CategoryId: 9
customContent: k1:v01,k2:v200,k3:v300 
intAttr: null 
picName: 01.jpg
sortExpValues: 2.57631182670593;270
StrAttr: null
-----------
productId: 1006
CategoryId: 9
customContent: k1:v07,k2:v206,k3:v306
intAttr: null
picName: 07.jpg
sortExpValues: 2.52564144134521;253
StrAttr: null
```

前回、管理画面から投げた結果と同じ結果が出力されました。


## ソースコード解説

``` App.java
//初期化(Tokyoリージョンのケース
DefaultProfile.addEndpoint({regionId}, {product}, {endpoint});
IClientProfile profile = DefaultProfile.getProfile({regionId}, {accessKeyId}, {secret})
IAcsClient client = new DefaultAcsClient(profile);
```

regionIdには、東京リージョンの場合「ap-northeast-1」を指定します。productには「ImageSearch」、endpointには、上記リストのエンドポイントURLを設定します。
productは固定の値ですね。regionIdは他のリージョンの場合の値を調べるのが面倒ですね。。。

``` App.java
//検索用のリクエストを作成
SearchImageRequest request = new SearchImageRequest();
// 入力必須。Image Search インスタンス名。
request.setInstanceName("itemsearch");
```

検索には検索用のリクエスト、追加、削除にはそれぞれ追加用、削除用のリクエストオブジェクトが用意されています。
インスタンス名は注意が必要です。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18582/9abf9ce6-0611-ff35-dd3a-2073924267a5.png)

「ID」ではなく、「名前」の方を指定する必要があります。

``` App.java
//画像で検索
request.setPicContent({BASE64エンコードした画像});
SearchImageResponse response = client.getAcsResponse(request);
```

今回は画像で検索しました。他にも登録した画像で検索する方法や、パラメータによる絞り込みが可能です。これらについては、次回検証します。

今回はここまで。SDKを使うと簡単に検索できますね。