pythonからAlgoliaを使う


デジタルキューブグループ Advent Calendar 2023
12/14分の記事です

https://qiita.com/advent-calendar/2023/digitalcube-heptagon

# 概要

apiの1機能として全文検索をしたかったので以下をpythonで実装した
実装内容等を記事に残す

- create時にDB保存と合わせてalgoriaへデータ登録をする
    - たとえば以下みたいなapi
        - ブログ記事投稿api
        - つぶやき投稿api
        - チケット起票api
- list時にAlgoliaの検索結果をもとに、取得されたIDをIN句として組み立てDBに問い合わせ
    - たとえば以下みたいなapi
        - ブログ記事一覧api
        - つぶやき一覧api
        - チケット一覧api

# Algolia とは

全文検索サービスを提供しているSaaS

https://www.algolia.com/

他に全文検索の話で出てくるのはElasticsearchとか

# インストール

```
pip install algoliasearch
```

# 事前準備

## サインアップ/サインイン〜アプリケーションの作成

:::note info
RDBのデータベース作成（CREATE DATABASE）するイメージ
:::

- https://www.algolia.com/ へアクセス
- サインアップ or サインイン

ダッシュボードがこんな感じ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/5638f230-334d-228e-e7af-962b74d9d677.png)


Application > Create Application

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/7f8a6d9a-fda8-8e26-dba4-7a5504a3b39d.png)

とりあえず無料のプランで適当に作る > Next Step: Data Center

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/7af2c28a-a8c9-a6a2-6cf1-2f9ad333ce0f.png)

データセンターを選ぶ > Review Application Details

japanもあるがcontact usとなっているので、テスト段階ではUSとかUKとか適当なところを選んでおく

:::note info
本番利用時にはプランだったりデータセンターのリージョンだったり適切に選択してもらえれば
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/e19a52af-294d-8f07-856d-2abff917bf62.png)

同意のチェックボックスにチェック > Create Application

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/5643bcdd-69a1-0ceb-4574-0fc47ad94ea9.png)

## インデックスの作成

:::note info
RDBのテーブルを作るイメージ
:::

インデックス名を入力 > Create Index

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/2d202727-6a0d-9c76-5719-a70c20bdad23.png)

サンプルデータを突っ込むか聞かれる
SaaSでも選択しておく
自分の手持ちのjsonファイルをアップもできる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/ada4974a-afaa-6b38-84b2-eab5558bb8c1.png)

index（テーブルみたいなもの）にサンプルレコードが入った状態で一覧画面へいく

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/a35de6d8-9995-6877-b6d9-8de8cc657ab4.png)


## アプリID、APIキー取得

作成したアプリケーションのhomeにアクセス > API Keys

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/dda8a68d-eb1d-7e3d-dc3e-a611d0d6483b.png)

`Application ID` と `Admin API Key` を控える

![スクリーンショット 2023-12-07 19.27.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/68ed0abd-4b01-296a-20ba-1f9d7e19063a.png)


# pythonから使う

先ほど控えた `Application ID` と `Admin API Key` と使用するインデックス名を指定

## データ登録

```python
from algoliasearch.search_client import SearchClient

APP_ID = "Application ID"
API_KEY = "Admin API Key"
INDEX_NAME = "test_index"

client = SearchClient.create(APP_ID, API_KEY)
index = client.init_index(INDEX_NAME)
# サンプルデータ
record = {
  "company": "hoge",
  "address": "hoge",
  "city": "hoge",
  "county": "hoge",
  "state": "hoge",
  "zip": "hoge",
  "phone": "hoge",
  "fax": "hoge",
  "email": "hoge",
  "web": "hoge",
  "followers": 11111,
  "name": "hoge",
  "objectID": "saas-sample-data-11111"
}
index.save_object(record).wait()
```

## 検索

```python
from algoliasearch.search_client import SearchClient

APP_ID = "Application ID"
API_KEY = "Admin API Key"
INDEX_NAME = "test_index"

# 検索ワードのサンプル
keyword = "kathleen"

client = SearchClient.create(APP_ID, API_KEY)
index = client.init_index(INDEX_NAME)
response = index.search(keyword)
results = response["hits"] # 検索でヒットしたもの
```

コンソールからも検索できるので見比べてみるのもよし

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/50c7ff0c-2b10-bc6e-b93b-0628eaf2367c.png)

### 参考: IN句の組み立てサンプル

たとえばオブジェクトが以下のような構造の場合

```json
{
    "objectID": "object_1",
    "user_id": "user_1",
    "name": "taro"
}
```

以下の感じでIN句を組み立てるなど

```python
results = response["hits"]
user_ids = []
for result in results:
    user_ids.append(result["user_id"])
in_clause = '"' + '", "'.join(user_ids) + '"'
```

### 参考: 特定のオブジェクトパラメータでフィルタリングしたいなどのケース

たとえばオブジェクトが以下のような構造の場合に、キーワード検索結果をさらに `status` でフィルタリングしたい場合など

```json
{
    "objectID": "object_1",
    "task_id": "task_1",
    "title": "hoge",
    "description": "fuga",
    "status": "in progress"
}

{
    "objectID": "object_2",
    "task_id": "task_2",
    "title": "hoge",
    "description": "fuga",
    "status": "completed"
}

{
    "objectID": "object_3",
    "task_id": "task_3",
    "title": "hoge",
    "description": "fuga",
    "status": "pending"
}
```

ファセットの設定などをする

https://www.algolia.com/ecommerce-merchandising-playbook/configuring-faceting/

https://www.algolia.com/doc/guides/managing-results/refine-results/filtering/how-to/filter-by-attributes/

home > index選択 > Configration > Facets

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213486/b4f420b4-0357-abe3-2e4c-fad7411d9fd2.png)


```python
keyword = "hoge" # 検索ワード
status = "in progress" # フィルタリングしたいステータス

response = index.search(
    keyword,
    {
        "filters": f"status:{status}"
    }
)
```

フィルタリングもいろいろあるようなので調べて自分に合ったものを

https://www.algolia.com/doc/guides/managing-results/refine-results/filtering/

https://www.algolia.com/doc/guides/managing-results/refine-results/filtering/in-depth/filters-and-facetfilters/

### 参考: ページネーションに関する参考サイト

https://shinodogg.com/2021/12/09/pagination/