Closed7

ベクトルデータベース「Weaviate」を試す 9:フィルタとマルチテナント

kun432kun432

前回の続き。

https://zenn.dev/kun432/scraps/1c457c09774cd9

ベクトル検索やキーワード検索ではなく、データ管理も踏まえると、何らかの条件検索を行いケースが出てくる。Weavaiteではこれに関連した機能として、

  • 事前条件フィルタ
  • マルチテナント

が提供されているので、これを見ていく。

kun432kun432

フィルタ

https://weaviate.io/developers/weaviate/concepts/prefiltering

https://weaviate.io/developers/weaviate/search/filters

https://weaviate.io/developers/weaviate/api/graphql/filters

今回はEmbeddedで。なお、フィルタを使うためには以下が必要な模様。

  • トークナイザーを適切に設定してキーワード検索を有効にしておく
    • プロパティにindex_searchable=Trueを設定すると、キーワード検索&フィルタが使える(デフォルト:True
      • ただしRoaling Bitmapsを使ったフィルタに比べると遅いらしい。
    • プロパティにindex_filterable=Trueを設定すると、より高速なRoaling Bitmapsを使った高速なフィルタリングが使える
      • ただしフィルタリングのみらしい。
      • どのように適用されているかの詳細はちょっとわかっていない。
    • トークナイザーは、日本語の場合はgseかトリグラム
      • WCS/Embeddedでは2024/3/24時点でトリグラムのみの模様。gseを使いたい場合はdockerで。
      • 今回はトリグラムを使う。

ということでやっていく。

パッケージインストール

!pip install weaviate-client

ベクトル検索モジュールを使うために、OpenAIキーをセットしてクライアント初期化。

import weaviate
import weaviate.classes as wvc
import os
from google.colab import userdata


client = weaviate.connect_to_embedded(
    version="1.24.5",
    headers={
        "X-OpenAI-Api-Key": userdata.get('OPENAI_API_KEY'),
    }
)

ではコレクションの作成。ここは過去のやり方を流用するので説明は割愛。

コレクション作成
!pip install llama-index llama-index-readers-file

データ
https://ja.wikipedia.org/wiki/オグリキャップ

from pathlib import Path
import requests
import re

def replace_heading(match):
    level = len(match.group(1))
    return '#' * level + ' ' + match.group(2).strip()

# Wikipediaからのデータ読み込み
wiki_titles = ["オグリキャップ"]
for title in wiki_titles:
    response = requests.get(
        "https://ja.wikipedia.org/w/api.php",
        params={
            "action": "query",
            "format": "json",
            "titles": title,
            "prop": "extracts",
            # 'exintro': True,
            "explaintext": True,
        },
    ).json()
    page = next(iter(response["query"]["pages"].values()))
    wiki_text = f"# {title}\n\n## 概要\n\n"
    wiki_text += page["extract"]

    wiki_text = re.sub(r"(=+)([^=]+)\1", replace_heading, wiki_text)
    wiki_text = re.sub(r"\t+", "", wiki_text)
    wiki_text = re.sub(r"\n{3,}", "\n\n", wiki_text)
    data_path = Path("data")
    if not data_path.exists():
        Path.mkdir(data_path)

    with open(data_path / f"{title}.md", "w") as fp:
        fp.write(wiki_text)
from pathlib import Path
import glob
import os

from llama_index.core.node_parser import MarkdownNodeParser
from llama_index.readers.file import FlatReader
from llama_index.core.schema import MetadataMode

files = glob.glob('data/*.md')

docs = []
for f in files:
    doc = FlatReader().load_data(Path(f))
    docs.extend(doc)

parser = MarkdownNodeParser()
nodes = parser.get_nodes_from_documents(docs)

nodes_for_delete = []
sections_for_delete = ["競走成績", "外部リンク", "参考文献", "関連作品"]

for idx, n in enumerate(nodes):
    # メタデータからセクション情報を取り出す。
    metadatas = []
    header_keys = []
    for m in n.metadata:
        if m.startswith("Header"):
            metadatas.append(n.metadata[m])
            header_keys.append(m)
    if len(metadatas) > 0:
        # セクション情報を新たなメタデータに設定
        n.metadata["section"] = metadata_str = " > ".join(metadatas)
        # 古いセクション情報を削除
        for k in header_keys:
            if k.startswith("Header"):
               del n.metadata[k]

    # コンテンツ整形
    contents = n.get_content().split("\n")
    if len(contents) == 1:
        # コンテンツが1つだけ≒セクションタイトルのみの場合は削除対象
        nodes_for_delete.append(idx)
    elif contents[0] in sections_for_delete:
        # 任意のセクションを削除対象
        nodes_for_delete.append(idx)
    else:
        # コンテンツの冒頭にあるセクションタイトル部分、及びそれに続く改行を削除
        content_for_delete = []
        for c_idx, c in enumerate(contents):
            if c in (metadatas):
                content_for_delete.append(c_idx)
            elif c in ["", "\n", None]:
                content_for_delete.append(c_idx)
            else:
                break
        contents = [item for i, item in enumerate(contents) if i not in content_for_delete]

    # 整形したコンテンツでノードを書き換え
    n.set_content("\n".join(contents))

base_nodes = [item for i, item in enumerate(nodes) if i not in nodes_for_delete]
import re
import weaviate.classes as wvc

def text_split(text, max_length=400):
    chunks = re.split(r'([。!?])', text)
    temp_chunk = ""
    final_chunks = []

    for chunk in chunks:
        if len(temp_chunk + chunk) <= max_length:
            temp_chunk += chunk
        else:
            final_chunks.append(temp_chunk)
            temp_chunk = chunk

    if temp_chunk:
        final_chunks.append(temp_chunk)

    return final_chunks

wvc_objs = []
for n in base_nodes:
    content = n.get_content().replace("\n", " ")
    chunks = text_split(content, 400)
    if len(chunks) == 1:
        idx = len(wvc_objs) + 1
        wvc_objs.append(wvc.data.DataObject(
            properties={
                "chunk_id": id,
                "chapter_title": n.metadata["section"],
                "chunk": chunks[0],
            }
        ))
    else:
        for chunk_idx, chunk in enumerate(chunks, start=1):
            id = len(wvc_objs) + 1
            wvc_objs.append(wvc.data.DataObject(
                properties={
                    "chunk_id": id,
                    "chapter_title": n.metadata["section"] + f"({chunk_idx})",
                    "chunk": chunk,
                }
            ))

こういうデータが作成される。

print(len(wvc_objs))
print(wvc_objs[0])
107
DataObject(properties={'chunk_id': 1, 'chapter_title': 'オグリキャップ > 概要(1)', 'chunk': 'オグリキャップ(欧字名:Oguri Cap、1985年3月27日 - 2010年7月3日)は、日本の競走馬、種牡馬。 1987年5月に岐阜県の地方競馬・笠松競馬場でデビュー。8連勝、重賞5勝を含む12戦10勝を記録した後、1988年1月に中央競馬へ移籍し、重賞12勝(うちGI4勝)を記録した。1988年度のJRA賞最優秀4歳牡馬、1989年度のJRA賞特別賞、1990年度のJRA賞最優秀5歳以上牡馬および年度代表馬。1991年、JRA顕彰馬に選出。愛称は「オグリ」「芦毛の怪物」など多数。 中央競馬時代はスーパークリーク、イナリワンの二頭とともに「平成三強」と総称され、自身と騎手である武豊の活躍を中心として起こった第二次競馬ブーム期において、第一次競馬ブームの立役者とされるハイセイコーに比肩するとも評される高い人気を得た。'}, uuid=None, vector=None, references=None)

コレクションのスキーマを定義。chapter_titleとchunkでキーワード検索・ベクトル検索を有効にして、トークナイザーにトリグラムを指定。

oguricap = client.collections.create(
    name="OguriCap",
    vectorizer_config=wvc.config.Configure.Vectorizer.text2vec_openai(vectorize_collection_name=False),
    properties=[
        wvc.config.Property(
            name="chunk_id",
            data_type=wvc.config.DataType.INT,
        ),
        wvc.config.Property(
            name="chapter_title",
            data_type=wvc.config.DataType.TEXT,
            skip_vectorization=False,
            vectorize_property_name=False,
            index_searchable=True,
            index_filterable=True,
            tokenization=wvc.config.Tokenization.TRIGRAM
        ),
        wvc.config.Property(
            name="chunk",
            data_type=wvc.config.DataType.TEXT,
            skip_vectorization=False,
            vectorize_property_name=False,
            index_searchable=True,
            index_filterable=True,
            tokenization=wvc.config.Tokenization.TRIGRAM
        ),
    ]
)

データを登録

oguricap.data.insert_many(wvc_objs)

では軽く検索できることを確かめておく。

ベクトル検索

response = oguricap.query.near_text(
    limit=10,
    query="タマモクロス",
    return_metadata=wvc.query.MetadataQuery(certainty=True, distance=True, score=True, explain_score=True)
)

for p in response.objects:
    print(f"===== {p.properties['chunk_id']} =====")
    print(f"certainty: {p.metadata.certainty}")
    print(f"chapter_title: {p.properties['chapter_title']}")
    print(f"chunk: {p.properties['chunk'][:100]}...")
    print()
===== 20 =====
certainty: 0.9132622480392456
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(7)
chunk: 第4コーナーから進路を確保しつつ前方への進出を開始したがペイザバトラーとタマモクロスを抜けず3着に敗れた。レース後、次走の有馬記念で挽回を果たしたいと考えた佐橋は、瀬戸口を通じてこの時点で有馬記念での...

===== 19 =====
certainty: 0.9129544496536255
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(6)
chunk: レースでは馬群のやや後方につけて追い込みを図り、出走馬中最も速い上がりを記録したものの、2番手を先行し直線で先頭になったタマモクロスを抜くことができず、2着に敗れた(レースに関する詳細については第98...

===== 21 =====
certainty: 0.9071015119552612
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(8)
chunk: 瀬戸口はレース前に岡部に「4コーナーあたりで、前の馬との差を1、2馬身に持っていって、勝負に出てほしいんや」と指示し、レースでは終始5、6番手の位置を進み、第4コーナーで前方への進出を開始すると直線で...

===== 4 =====
certainty: 0.9034284949302673
chapter_title: オグリキャップ > デビューまで > 誕生・生い立ち > 美山育成牧場時代
chunk: 1986年の10月、ハツラツは岐阜県山県郡美山町(現:山県市)にあった美山育成牧場に移り、3か月間馴致を施された。当時の美山育成牧場では、従業員の吉田謙治が1人で30頭あまりの馬の管理をしていたため全...

===== 94 =====
certainty: 0.9027305841445923
chapter_title: オグリキャップ > 人気 > 関連グッズ(2)
chunk: ライターの山本徹美は、ぬいぐるみのブームが従来馬券愛好家が構成していた競馬ファンに騎手や競走馬を応援するために競馬場を訪れる層や女性ファンを取り込んだとしている。ぬいぐるみの他にも様々なグッズが発売さ...

===== 88 =====
certainty: 0.9023364782333374
chapter_title: オグリキャップ > 人気 > 概要(2)
chunk: 斎藤修は、日本人が好む「田舎から裸一貫で出てきて都会で名をあげる」という立身出世物語に当てはまったことに加え、クラシックに出走することができないという挫折や、タマモクロス、イナリワン、スーパークリーク...

===== 24 =====
certainty: 0.8999159336090088
chapter_title: オグリキャップ > 競走馬時代 > クラシック登録(2)
chunk: 一方で毎日杯の結果を根拠にヤエノムテキをはじめとする同世代のクラシック優勝馬の実力が低く評価されることもあった。なお、前述のように1992年から、中央競馬はクラシックの追加登録制度を導入した。...

===== 82 =====
certainty: 0.8998212814331055
chapter_title: オグリキャップ > 特徴・評価 > 総合的な評価(5)
chunk:  大川慶次郎は、フジテレビが放送した第35回有馬記念の最後の直線でメジロライアンの競走馬名を連呼したことから競馬ファンからオグリキャップが嫌いだったのかと思われることもあったというが、本人はこれを否定...

===== 4 =====
certainty: 0.8992668390274048
chapter_title: オグリキャップ > デビューまで > 誕生・生い立ち > 稲葉牧場時代
chunk: オグリキャップは1985年3月27日の深夜に誕生した。誕生時には右前脚が大きく外向しており、出生直後はなかなか自力で立ち上がることができず、牧場関係者が抱きかかえて初乳を飲ませた。これは競走馬としては...

===== 2 =====
certainty: 0.8991654515266418
chapter_title: オグリキャップ > 概要(2)
chunk:  競走馬引退後は北海道新冠町の優駿スタリオンステーションで種牡馬となったが、産駒から中央競馬の重賞優勝馬を出すことができず、2007年に種牡馬を引退。種牡馬引退後は同施設で功労馬として繋養されていたが...

キーワード検索

response = oguricap.query.bm25(
    limit=10,
    query="タマモクロス",
    return_metadata=wvc.query.MetadataQuery(certainty=True, distance=True, score=True, explain_score=True)
)

for p in response.objects:
    print(f"===== {p.properties['chunk_id']} =====")
    print(f"score: {p.metadata.score}")
    print(f"explain_score: {p.metadata.explain_score}")
    print(f"chapter_title: {p.properties['chapter_title']}")
    print(f"chunk: {p.properties['chunk'][:100]}...")
    print()
===== 20 =====
score: 6.653660774230957
explain_score: , BM25F_タマモ_propLength:269, BM25F_マモク_frequency:3, BM25F_マモク_propLength:269, BM25F_モクロ_frequency:3, BM25F_モクロ_propLength:269, BM25F_クロス_frequency:3, BM25F_クロス_propLength:269, BM25F_タマモ_frequency:3
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(7)
chunk: 第4コーナーから進路を確保しつつ前方への進出を開始したがペイザバトラーとタマモクロスを抜けず3着に敗れた。レース後、次走の有馬記念で挽回を果たしたいと考えた佐橋は、瀬戸口を通じてこの時点で有馬記念での...

===== 19 =====
score: 6.136303424835205
explain_score: , BM25F_クロス_frequency:3, BM25F_クロス_propLength:334, BM25F_タマモ_frequency:3, BM25F_タマモ_propLength:334, BM25F_マモク_frequency:3, BM25F_マモク_propLength:334, BM25F_モクロ_frequency:3, BM25F_モクロ_propLength:334
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(6)
chunk: レースでは馬群のやや後方につけて追い込みを図り、出走馬中最も速い上がりを記録したものの、2番手を先行し直線で先頭になったタマモクロスを抜くことができず、2着に敗れた(レースに関する詳細については第98...

===== 21 =====
score: 5.358907699584961
explain_score: , BM25F_クロス_propLength:324, BM25F_タマモ_frequency:3, BM25F_タマモ_propLength:324, BM25F_マモク_frequency:2, BM25F_マモク_propLength:324, BM25F_モクロ_frequency:2, BM25F_モクロ_propLength:324, BM25F_クロス_frequency:2
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(8)
chunk: 瀬戸口はレース前に岡部に「4コーナーあたりで、前の馬との差を1、2馬身に持っていって、勝負に出てほしいんや」と指示し、レースでは終始5、6番手の位置を進み、第4コーナーで前方への進出を開始すると直線で...

===== 75 =====
score: 5.12060546875
explain_score: , BM25F_タマモ_propLength:318, BM25F_マモク_frequency:2, BM25F_マモク_propLength:318, BM25F_モクロ_frequency:2, BM25F_モクロ_propLength:318, BM25F_クロス_frequency:2, BM25F_クロス_propLength:318, BM25F_タマモ_frequency:2
chapter_title: オグリキャップ > 特徴・評価 > 走行・レースぶりに関する特徴・評価(4)
chunk:  河内の次に主戦騎手を務めた南井克巳は、オグリキャップを「力そのもの、パワーそのものを感じさせる馬」「どんなレースでもできる馬」「レースを知っている」と評し、1989年の毎日王冠のレース後には「この馬...

===== 18 =====
score: 3.5866355895996094
explain_score: , BM25F_クロス_propLength:279, BM25F_タマモ_frequency:1, BM25F_タマモ_propLength:279, BM25F_マモク_frequency:1, BM25F_マモク_propLength:279, BM25F_モクロ_frequency:1, BM25F_モクロ_propLength:279, BM25F_クロス_frequency:1
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(5)
chunk:  毎日王冠では終始後方からレースを進め、第3コーナーからまくりをかけて優勝した。この勝利により、当時のJRA重賞連勝記録である6連勝を達成した(メジロラモーヌと並ぶタイ記録)。当時競馬評論家として活動...

===== 88 =====
score: 3.1857001781463623
explain_score: , BM25F_モクロ_propLength:339, BM25F_クロス_frequency:1, BM25F_クロス_propLength:339, BM25F_タマモ_frequency:1, BM25F_タマモ_propLength:339, BM25F_マモク_frequency:1, BM25F_マモク_propLength:339, BM25F_モクロ_frequency:1
chapter_title: オグリキャップ > 人気 > 概要(2)
chunk: 斎藤修は、日本人が好む「田舎から裸一貫で出てきて都会で名をあげる」という立身出世物語に当てはまったことに加え、クラシックに出走することができないという挫折や、タマモクロス、イナリワン、スーパークリーク...

とりあえず動作確認ができたので、ここからフィルタを試していく。使える条件は以下を参照。

https://weaviate.io/developers/weaviate/api/graphql/filters

ベクトル検索で、特定の文字列を含む(.contains_any)カテゴリーでフィルタ

response = oguricap.query.near_text(
    limit=10,
    query="タマモクロス",
    filters=wvc.query.Filter.by_property("chapter_title").contains_any(["中央競馬時代"]),
    return_metadata=wvc.query.MetadataQuery(certainty=True, distance=True, score=True, explain_score=True)
)

for p in response.objects:
    print(f"===== {p.properties['chunk_id']} =====")
    print(f"certainty: {p.metadata.certainty}")
    print(f"chapter_title: {p.properties['chapter_title']}")
    print(f"chunk: {p.properties['chunk'][:100]}...")
    print()
===== 20 =====
certainty: 0.9132622480392456
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(7)
chunk: 第4コーナーから進路を確保しつつ前方への進出を開始したがペイザバトラーとタマモクロスを抜けず3着に敗れた。レース後、次走の有馬記念で挽回を果たしたいと考えた佐橋は、瀬戸口を通じてこの時点で有馬記念での...

===== 19 =====
certainty: 0.9129544496536255
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(6)
chunk: レースでは馬群のやや後方につけて追い込みを図り、出走馬中最も速い上がりを記録したものの、2番手を先行し直線で先頭になったタマモクロスを抜くことができず、2着に敗れた(レースに関する詳細については第98...

===== 21 =====
certainty: 0.9070790410041809
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(8)
chunk: 瀬戸口はレース前に岡部に「4コーナーあたりで、前の馬との差を1、2馬身に持っていって、勝負に出てほしいんや」と指示し、レースでは終始5、6番手の位置を進み、第4コーナーで前方への進出を開始すると直線で...

===== 18 =====
certainty: 0.8963621854782104
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(5)
chunk:  毎日王冠では終始後方からレースを進め、第3コーナーからまくりをかけて優勝した。この勝利により、当時のJRA重賞連勝記録である6連勝を達成した(メジロラモーヌと並ぶタイ記録)。当時競馬評論家として活動...

===== 22 =====
certainty: 0.8942211270332336
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(9)
chunk: 当時オグリキャップに与えられていた飼い葉の中に、レースに使っている馬には必要がない体を太らせるための成分が含まれており、指摘を受けた辻本はすぐにその配合を取り止めた。有馬記念終了後に、井高は「俺は結果...

===== 12 =====
certainty: 0.8928471207618713
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年)
chunk: 中央競馬移籍後のオグリキャップは栗東トレーニングセンターの調教師瀬戸口勉の厩舎で管理されることが決まり、1月28日に鷲見厩舎から瀬戸口厩舎へ移送された。...

===== 14 =====
certainty: 0.8927158117294312
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(1)
chunk: オグリキャップの中央移籍後の初戦にはペガサスステークスが選ばれ、鞍上は佐橋の希望により河内洋に決まった。地方での快進撃は知られていたものの、当日の単勝オッズは2番人気であった。レースでは序盤は後方に控...

===== 16 =====
certainty: 0.8917427062988281
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(3)
chunk:  クラシック登録をしていないオグリキャップは東京優駿(日本ダービー)にも出走することができず、代わりに「断念ダービー」と言われていたニュージーランドトロフィー4歳ステークスに出走した。鞍上に河内が復帰...

===== 15 =====
certainty: 0.8862055540084839
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(2)
chunk: このレースでは馬場状態が追い込み馬に不利とされる重馬場と発表され、オグリキャップが馬場状態に対応できるかどうかに注目が集まった。オグリキャップは第3コーナーで最後方の位置から馬群の外を通って前方へ進出...

===== 17 =====
certainty: 0.8860073089599609
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(4)
chunk:  続く高松宮杯では、中央競馬移籍後初の古馬との対戦、特に重賞優勝馬でありこの年の宝塚記念で4着となったランドヒリュウとの対戦にファンの注目が集まった。レースではランドヒリュウが先頭に立って逃げたのに対...

キーワード検索で、特定の文字列と合致(.equal)するカテゴリーでフィルタ

response = oguricap.query.bm25(
    limit=10,
    query="タマモクロス",
    filters=wvc.query.Filter.by_property("chapter_title").equal("オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(8)"),
    return_metadata=wvc.query.MetadataQuery(certainty=True, distance=True, score=True, explain_score=True)
)

for p in response.objects:
    print(f"===== {p.properties['chunk_id']} =====")
    print(f"score: {p.metadata.score}")
    print(f"explain_score: {p.metadata.explain_score}")
    print(f"chapter_title: {p.properties['chapter_title']}")
    print(f"chunk: {p.properties['chunk'][:100]}...")
    print()
===== 21 =====
score: 5.358907699584961
explain_score: , BM25F_クロス_frequency:2, BM25F_クロス_propLength:324, BM25F_タマモ_frequency:3, BM25F_タマモ_propLength:324, BM25F_マモク_frequency:2, BM25F_マモク_propLength:324, BM25F_モクロ_frequency:2, BM25F_モクロ_propLength:324
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(8)
chunk: 瀬戸口はレース前に岡部に「4コーナーあたりで、前の馬との差を1、2馬身に持っていって、勝負に出てほしいんや」と指示し、レースでは終始5、6番手の位置を進み、第4コーナーで前方への進出を開始すると直線で...

ベクトル検索で、特定の文字列を含む(.contains_any)カテゴリー、かつ、チャンクIDが20未満、という複数条件

response = oguricap.query.near_text(
    limit=10,
    query="タマモクロス",
    filters=wvc.query.Filter.by_property("chapter_title").contains_any(["中央競馬時代"]) &
           wvc.query.Filter.by_property("chunk_id").less_than(20),
    return_metadata=wvc.query.MetadataQuery(certainty=True, distance=True, score=True, explain_score=True)
)

for p in response.objects:
    print(f"===== {p.properties['chunk_id']} =====")
    print(f"certainty: {p.metadata.certainty}")
    print(f"chapter_title: {p.properties['chapter_title']}")
    print(f"chunk: {p.properties['chunk'][:100]}...")
    #print(f"   distance      : {p.metadata.distance}")
    #print(f"   score         : {p.metadata.score}")
    #print(f"   explain_score : {p.metadata.explain_score}")
    print()
===== 19 =====
certainty: 0.9129544496536255
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(6)
chunk: レースでは馬群のやや後方につけて追い込みを図り、出走馬中最も速い上がりを記録したものの、2番手を先行し直線で先頭になったタマモクロスを抜くことができず、2着に敗れた(レースに関する詳細については第98...

===== 18 =====
certainty: 0.8963621854782104
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(5)
chunk:  毎日王冠では終始後方からレースを進め、第3コーナーからまくりをかけて優勝した。この勝利により、当時のJRA重賞連勝記録である6連勝を達成した(メジロラモーヌと並ぶタイ記録)。当時競馬評論家として活動...

===== 12 =====
certainty: 0.8928471207618713
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年)
chunk: 中央競馬移籍後のオグリキャップは栗東トレーニングセンターの調教師瀬戸口勉の厩舎で管理されることが決まり、1月28日に鷲見厩舎から瀬戸口厩舎へ移送された。...

===== 14 =====
certainty: 0.8927158117294312
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(1)
chunk: オグリキャップの中央移籍後の初戦にはペガサスステークスが選ばれ、鞍上は佐橋の希望により河内洋に決まった。地方での快進撃は知られていたものの、当日の単勝オッズは2番人気であった。レースでは序盤は後方に控...

===== 16 =====
certainty: 0.8917427062988281
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(3)
chunk:  クラシック登録をしていないオグリキャップは東京優駿(日本ダービー)にも出走することができず、代わりに「断念ダービー」と言われていたニュージーランドトロフィー4歳ステークスに出走した。鞍上に河内が復帰...

===== 15 =====
certainty: 0.8862055540084839
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(2)
chunk: このレースでは馬場状態が追い込み馬に不利とされる重馬場と発表され、オグリキャップが馬場状態に対応できるかどうかに注目が集まった。オグリキャップは第3コーナーで最後方の位置から馬群の外を通って前方へ進出...

===== 17 =====
certainty: 0.8860073089599609
chapter_title: オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(4)
chunk:  続く高松宮杯では、中央競馬移籍後初の古馬との対戦、特に重賞優勝馬でありこの年の宝塚記念で4着となったランドヒリュウとの対戦にファンの注目が集まった。レースではランドヒリュウが先頭に立って逃げたのに対...
kun432kun432

ただし、likeだけはダメだった。内部的に正規表現が使われているっぽいのだけど、文字コードの処理に問題があるみたい。

response = oguricap.query.near_text(
    limit=10,
    query="タマモクロス",
    filters=wvc.query.Filter.by_property("chapter_title").contains_any(["中央競馬時代"]) &
           wvc.query.Filter.by_property("chunk").like("*瀬戸口*"),
    return_metadata=wvc.query.MetadataQuery(certainty=True, distance=True, score=True, explain_score=True)
)

for p in response.objects:
    print(f"===== {p.properties['chunk_id']} =====")
    print(f"certainty: {p.metadata.certainty}")
    print(f"chapter_title: {p.properties['chapter_title']}")
    print(f"chunk: {p.properties['chunk'][:100]}...")
    print()
WeaviateQueryError: Query call with protocol GRPC search failed with message explorer: get class: vector search: object vector search at index oguricap: shard oguricap_uuLFzmowT01T: build inverted filter allow list: fetch doc ids for prop/value pair: nested query: nested child 1: nested query: nested child 3: read row: parse like value: compile regex from 'like' string: error parsing regexp: invalid UTF-8: `���$`.

まあcontains_anyで代替はできそうなので問題ないといえばない。現時点では、日本語テキストのプロパティでフィルタを使う場合は少し注意が必要かも。

gseの場合でも同じかどうかは確認していない。

kun432kun432

検索ではなくて、データの管理、いわゆるCRUD操作を行うようなメソッドを使う場合でもフィルタは使える。

response = oguricap.query.fetch_objects(
    filters=wvc.query.Filter.by_property("chapter_title").contains_any(["中央競馬時代"])
)

for p in response.objects:
    print(p.uuid)
    print(p.properties)
    print()
00b85651-2f73-46fe-a6c1-39e9e9353836
{'chapter_title': 'オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年)', 'chunk_id': 12, 'chunk': '中央競馬移籍後のオグリキャップは栗東トレーニングセンターの調教師瀬戸口勉の厩舎で管理されることが決まり、1月28日に鷲見厩舎から瀬戸口厩舎へ移送された。'}

0017c850-090f-4377-a9dc-a79b7d5fa69e
{'chapter_title': 'オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(1)', 'chunk_id': 14, 'chunk': 'オグリキャップの中央移籍後の初戦にはペガサスステークスが選ばれ、鞍上は佐橋の希望により河内洋に決まった。地方での快進撃は知られていたものの、当日の単勝オッズは2番人気であった。レースでは序盤は後方に控え、第3コーナーから馬群の外を通って前方へ進出を開始。第4コーナーを過ぎてからスパートをかけて他馬を追い抜き、優勝した。出走前の時点では陣営の期待は必ずしも高いものではなく、優勝は予想を上回る結果だった。レースで実況を担当した杉本清は最後の直線で「これは噂にたがわない強さだ」と実況した。なお、この日中京競馬場で行われた中日新聞杯では佐橋が所有するトキノオリエントが優勝し、佐橋は中央競馬史上初となる同一馬主による地方出身馬の同日開催重賞制覇を達成している。 移籍2戦目には毎日杯が選ばれた。'}

5c4756ed-953e-401c-8585-10bea80046e4
{'chapter_title': 'オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(2)', 'chunk_id': 15, 'chunk': 'このレースでは馬場状態が追い込み馬に不利とされる重馬場と発表され、オグリキャップが馬場状態に対応できるかどうかに注目が集まった。オグリキャップは第3コーナーで最後方の位置から馬群の外を通って前方へ進出を開始し、ゴール直前で先頭に立って優勝した。 オグリキャップは初代馬主の小栗孝一が中央で走らせるつもりがなかったことでクラシック登録をしていなかったため、前哨戦である毎日杯を優勝して本賞金額では優位に立ったものの皐月賞に登録できず、代わりに京都4歳特別に出走した。このレースでは翌1989年の全戦に騎乗することとなる南井克巳が鞍上を務め、オグリキャップ一頭だけが58キロの斤量を背負ったが第3コーナーで後方からまくりをかけ、優勝した。'}

2c23f9a9-bcdb-48cd-82e1-2916471fa6a7
{'chapter_title': 'オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(3)', 'chunk_id': 16, 'chunk': ' クラシック登録をしていないオグリキャップは東京優駿(日本ダービー)にも出走することができず、代わりに「断念ダービー」と言われていたニュージーランドトロフィー4歳ステークスに出走した。鞍上に河内が復帰したが、この時オグリキャップには疲労が蓄積し、治療のために注射が打たれるなど体調面に不安を抱えていたが、レースでは序盤は最後方に位置したが向こう正面で前方へ進出を開始すると第4コーナーを通過した直後に先頭に立ち、そのまま優勝した。このレースでのオグリキャップの走破タイムは同レースのレースレコードを記録し、このタイムは前月に同じ東京芝1600mで行われた古馬GIの安田記念で優勝馬のニッポーテイオーが記録したタイムよりも0秒2速かったにもかかわらず、河内はレース中に一度も本格的なゴーサインを出すことがなかった(レースに関する詳細については第6回ニュージーランドトロフィー4歳ステークスを参照)。'}

2ff4ff5e-fd3f-4d4f-b960-7768885da0a5
{'chapter_title': 'オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(4)', 'chunk_id': 17, 'chunk': ' 続く高松宮杯では、中央競馬移籍後初の古馬との対戦、特に重賞優勝馬でありこの年の宝塚記念で4着となったランドヒリュウとの対戦にファンの注目が集まった。レースではランドヒリュウが先頭に立って逃げたのに対してオグリキャップは序盤は4番手に位置して第3コーナーから前方への進出を開始する。第4コーナーで2番手に立つと直線でランドヒリュウをかわし、中京競馬場芝2000mのコースレコードを記録して優勝した。この勝利により、地方競馬からの移籍馬による重賞連勝記録である5連勝を達成した。 高松宮杯のレース後、陣営は秋シーズンのオグリキャップのローテーションを検討し、毎日王冠を経て天皇賞(秋)でGIに初出走することを決定した。毎日王冠までは避暑 を行わず、栗東トレーニングセンターで調整を行い、8月下旬から本格的な調教を開始。9月末に東京競馬場に移送された。'}

2929ea1d-39f6-4d7b-8e2e-00de63302bd1
{'chapter_title': 'オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(5)', 'chunk_id': 18, 'chunk': ' 毎日王冠では終始後方からレースを進め、第3コーナーからまくりをかけて優勝した。この勝利により、当時のJRA重賞連勝記録である6連勝を達成した(メジロラモーヌと並ぶタイ記録)。当時競馬評論家として活動していた大橋巨泉は、オグリキャップのレース内容について「毎日王冠で古馬の一線級を相手に、スローペースを後方から大外廻って、一気に差し切るなどという芸当は、今まで見たことがない」「どうやらオグリキャップは本当のホンモノの怪物らしい」と評した。毎日王冠の後、オグリキャップはそのまま東京競馬場に留まって調整を続けた(レースに関する詳細については第39回毎日王冠を参照)。 続く天皇賞(秋)では、前年秋から7連勝中であった古馬のタマモクロスを凌いで1番人気に支持された。'}

442caf64-6333-43a5-b7b3-c2f73b407d30
{'chapter_title': 'オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(6)', 'chunk_id': 19, 'chunk': 'レースでは馬群のやや後方につけて追い込みを図り、出走馬中最も速い上がりを記録したものの、2番手を先行し直線で先頭になったタマモクロスを抜くことができず、2着に敗れた(レースに関する詳細については第98回天皇賞を参照)。中央移籍後初黒星を喫したが、オグリキャップは天皇賞で初めて連対した4歳馬となった。 天皇賞(秋)の結果を受け、馬主の佐橋がタマモクロスにリベンジを果たしたいという思いを強く抱いたことからオグリキャップの次走にはタマモクロスが出走を決めていたジャパンカップが選ばれ、オグリキャップは引き続き東京競馬場で調整された。レースでは天皇賞(秋)の騎乗について佐橋が「もう少し積極的に行ってほしかった」と不満を表したことを受けて河内は瀬戸口と相談の上で先行策をとり、序盤は3、4番手に位置した。しかし向こう正面で折り合いを欠いて後方へ下がり、第3コーナーで馬群の中に閉じ込められる形となった。'}

5cbc4d17-fb12-409a-9895-580106053f86
{'chapter_title': 'オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(7)', 'chunk_id': 20, 'chunk': '第4コーナーから進路を確保しつつ前方への進出を開始したがペイザバトラーとタマモクロスを抜けず3着に敗れた。レース後、次走の有馬記念で挽回を果たしたいと考えた佐橋は、瀬戸口を通じてこの時点で有馬記念での騎乗馬が決まっていなかった岡部幸雄を鞍上に希望し、瀬戸口を通じて騎乗依頼が出されたものの、岡部は「西(栗東)の馬はよくわからないから」と婉曲に断った。しかし瀬戸口が「一回だけ」という条件付きで依頼し、これを岡部が承知したことで有馬記念での騎乗が決まった。  有馬記念までの間は美浦トレーニングセンターで調整を行うこととなった。オグリキャップはタマモクロスに次ぐファン投票2位で出走が決まり、当日の単勝オッズもタマモクロスに次ぐ2番人気に支持された。'}

3e1936c0-61e9-4b10-a5d0-a770dc392239
{'chapter_title': 'オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(8)', 'chunk_id': 21, 'chunk': '瀬戸口はレース前に岡部に「4コーナーあたりで、前の馬との差を1、2馬身に持っていって、勝負に出てほしいんや」と指示し、レースでは終始5、6番手の位置を進み、第4コーナーで前方への進出を開始すると直線で先頭に立ち、優勝。GI競走初制覇を達成し、芦毛馬初の有馬記念優勝馬となった。作家の山口瞳は有馬記念の結果を受けて、「タマモクロスは日本一の馬、オグリキャップは史上最強の馬だ」といい、オグリキャップを称えた(レースに関する詳細については第33回有馬記念を参照)。翌1989年1月10日には、1988年度のJRA賞最優秀4歳牡馬に選出された。 タマモクロスを担当した調教助手の井高淳一と、オグリキャップの調教助手であった辻本は仲が良く、麻雀仲間でもあったが、天皇賞後に井高はオグリキャップの飼い葉桶を覗き「こんなもんを食わせていたんじゃ、オグリはずっとタマモに勝てへんで。」と声を掛けた。'}

87a55825-f8fd-4927-9483-139731e80c45
{'chapter_title': 'オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年) > 競走内容(9)', 'chunk_id': 22, 'chunk': '当時オグリキャップに与えられていた飼い葉の中に、レースに使っている馬には必要がない体を太らせるための成分が含まれており、指摘を受けた辻本はすぐにその配合を取り止めた。有馬記念終了後に、井高は「俺は結果的に、敵に塩を送る事になったんだな。」と苦笑した。厩務員の池江敏郎は、オグリキャップが寝藁を食べようとするほど食欲旺盛で太め残りとなるため、有馬記念前は汗取りをつけて調教していたと述べている。'}
response = oguricap.query.fetch_objects(
    filters=wvc.query.Filter.by_id().equal("00b85651-2f73-46fe-a6c1-39e9e9353836")
)

for p in response.objects:
    print(p.uuid)
    print(p.properties)
    print()
00b85651-2f73-46fe-a6c1-39e9e9353836
{'chapter_title': 'オグリキャップ > 競走馬時代 > 中央競馬時代 > 4歳(1988年)', 'chunk_id': 12, 'chunk': '中央競馬移籍後のオグリキャップは栗東トレーニングセンターの調教師瀬戸口勉の厩舎で管理されることが決まり、1月28日に鷲見厩舎から瀬戸口厩舎へ移送された。'}
kun432kun432

マルチテナント

https://weaviate.io/blog/multi-tenancy-vector-search

https://weaviate.io/developers/weaviate/manage-data/multi-tenancy

マルチテナントは、例えば、別のクラスを作る、とか、あとはフィルタを使って例えばお客様IDのようなものでフィルタする、など、既存の機能でも実現できるのだけれど、Weaviateではマルチテナントは独立した専用の機能として提供されている。

今回はWCSを使ってやってみる。

クライアントをインストール

!pip install weaviate-client

データを準備する。今回は以下の2つのFAQデータを使って、それぞれを別のテナントとして扱う。

ということでデータを作成。

!mkdir -p output/{faq_kosodate,faq_amagasaki}

# 子育てFAQ
!wget https://d.line-scdn.net/stf/linecorp/ja/csr/dataset_.zip
!!unzip dataset_.zip

# 尼崎市FAQ
!wget https://tulip.kuee.kyoto-u.ac.jp/localgovfaq/localgovfaq.zip
!unzip localgovfaq.zip
import pandas as pd

# 子育てFAQ

df_kosodate = pd.read_excel("dataset_.xlsx")
df_kosodate.drop(columns=["ID", "カテゴリ1", "カテゴリ2", "出典", "<参考>UMカテゴリタグ", "<参考>UMサービスメニュー\n(標準的な行政サービス名称)"], inplace=True)
df_kosodate.rename(columns={
    'サンプルID': 'faq_id',
    'サンプル 問い合わせ文': 'question',
    'サンプル 応答文': 'answer',
}, inplace=True)
df_kosodate["source"] = "子育てFAQ"

display(df_kosodate)

# 尼崎市FAQ

def file2list(filename):
    lines = []
    ids = []
    with open(filename, 'r') as file:
        for line in file:
            line = line.strip().replace(" ","")
            id, line = line.split('\t')
            lines.append(line)
            ids.append(int(id) + 1)
        return lines, ids

questions, ids = file2list("localgovfaq/qas/questions_in_Amagasaki.txt")
answers, _ = file2list("localgovfaq/qas/answers_in_Amagasaki.txt")

df_amagasaki = pd.DataFrame({'faq_id': ids, 'question': questions, 'answer': answers})
df_amagasaki["source"] = "尼崎市FAQ"
display(df_amagasaki)

それぞれこういうpandasのデータフレームができる。

Weaviateに投入するための辞書に変換。

faq_objs_kosodate = df_kosodate.to_dict(orient='records')
print(len(faq_objs_kosodate))
print(faq_objs_kosodate[0])

faq_objs_amagasaki = df_amagasaki.to_dict(orient='records')
print(len(faq_objs_amagasaki))
print(faq_objs_amagasaki[0])

作成されたデータはこんな感じになる。sourceは、後で検索した際にどちらのデータを見ているかをわかりやすくするために入れているだけで、マルチテナントのためにで必要になるわけではない。

662
{'faq_id': 1, 'question': '母子手帳を受け取りたいのですが、手続きを教えてください。', 'answer': '窓口で妊娠届をご記入いただき、母子手帳をお渡しします。\n住民票の世帯が別の方が代理で窓口に来られる場合は、委任状が必要になります。\n\n▼詳しくはこちら\n(自治体HP内関連ページのURL)', 'source': '子育てFAQ'}
1786
{'faq_id': 1, 'question': '乳幼児とその親が集う場、地域の母親同士の情報交換や交流の相談先を知りたい。', 'answer': '■市には乳幼児(乳児)とその親が集う場として、次のような取組をしています。以下のいずれについて知りたいですか。1.つどいの広場事業、すこやかプラザ運営事業子育て支援ゾーンPAL。2.保育所子育て支援事業。3.幼稚園すこやか子育て事業。4.子育てサークル活動支援事業。5.子育て学習世代間交流事業。6.双子のための育児教室学級。7.健康教室。', 'source': '尼崎市FAQ'}

ではコレクションを作成していく。

クライアント初期化。ベクトル化モジュールを使うためにOpenAI APIキーを渡している。

import weaviate
import weaviate.classes as wvc
import os
from google.colab import userdata

client = weaviate.connect_to_wcs(
    cluster_url=userdata.get('WEAVIATE_CLUSTER_URL'),
    auth_credentials=weaviate.auth.AuthApiKey(userdata.get('WEAVIATE_API_KEY')),
    headers={
        "X-OpenAI-Api-Key": userdata.get('OPENAI_API_KEY')
    }
)

コレクションのスキーマを作成。

multi_faq = client.collections.create(
    name="Multi_FAQ",
    multi_tenancy_config=wvc.config.Configure.multi_tenancy(True),
    vectorizer_config=wvc.config.Configure.Vectorizer.text2vec_openai(vectorize_collection_name=False),
    properties=[
        wvc.config.Property(
            name="faq_id",
            data_type=wvc.config.DataType.INT,
        ),
        wvc.config.Property(
            name="question",
            data_type=wvc.config.DataType.TEXT,
            skip_vectorization=False,
            vectorize_property_name=False,
            index_searchable=True,
            index_filterable=True,
            tokenization=wvc.config.Tokenization.TRIGRAM
        ),
        wvc.config.Property(
            name="answer",
            data_type=wvc.config.DataType.TEXT,
            skip_vectorization=False,
            vectorize_property_name=False,
            index_searchable=True,
            index_filterable=True,
            tokenization=wvc.config.Tokenization.TRIGRAM
        ),
        wvc.config.Property(
            name="source",
            data_type=wvc.config.DataType.TEXT,
            skip_vectorization=True,
            index_searchable=False,
            index_filterable=False,
        ),
    ]
)

マルチテナントのポイントは以下の1行。マルチテナントはデフォルトで無効になっているので、有効化する必要がある。

    multi_tenancy_config=wvc.config.Configure.multi_tenancy(True),

それ以外については別にデフォルトで作らせてもよいのだけども、一応こんな感じで。

  • 質問と回答だけを連結してベクトル化。
    • コレクション名はベクトル化に含めない。
    • プロパティ名はベクトル化に含めない。
  • キーワード検索は一応有効化
    • トークナイザーはトリグラムを指定
      • WCSで使える日本語のトークナイザーは現時点ではトリグラムだけ。
    • 今回のスコープからは外れるので使わない。

次にテナントを作成する。

multi_faq.tenants.create(
    tenants=[
        wvc.tenants.Tenant(name="kosodate"),
        wvc.tenants.Tenant(name="amagasaki"),
    ]
)

作成されているテナントの確認。

multi_faq.tenants.get()

テナントが2つ作成されている。TenantActivityStatusはテナントの有効状態を示している。HOTであれば有効。COLDにして無効化することもできる。

{'amagasaki': Tenant(name='amagasaki', activityStatus=<TenantActivityStatus.HOT: 'HOT'>),
 'kosodate': Tenant(name='kosodate', activityStatus=<TenantActivityStatus.HOT: 'HOT'>)}

ではデータを投入する。マルチテナントの場合はコレクションにwith_tenant("テナント名")を指定したコレクションのインスタンスを作成して、それに対してデータを投入する。

tenant_kosodate = multi_faq.with_tenant("kosodate")
tenant_kosodate.data.insert_many(faq_objs_kosodate)

tenant_amagasaki = multi_faq.with_tenant("amagasaki")
tenant_amagasaki.data.insert_many(faq_objs_amagasaki)

登録数を確認

display(tenant_kosodate.aggregate.over_all(total_count=True))
display(tenant_amagasaki.aggregate.over_all(total_count=True))
AggregateReturn(properties={}, total_count=662)
AggregateReturn(properties={}, total_count=1786)

ではそれぞれのコレクションに対してベクトル検索してみる。

query="母子手帳の手続きについて教えて"

response = tenant_kosodate.query.near_text(
    query=query,
    limit=5,
    return_metadata=wvc.query.MetadataQuery(certainty=True)
)

print("==== tenant_kosodate ====")
for p in response.objects:
    print(f"certainty: {p.metadata.certainty}")
    print(f"question: {p.properties['question']}")
    print("answer: {}...".format(p.properties['answer'].replace('\n','')[:100]))
    print(f"source: {p.properties['source']}")
    print()

response = tenant_amagasaki.query.near_text(
    query=query,
    limit=5,
    return_metadata=wvc.query.MetadataQuery(certainty=True)
)

print("==== tenant_amagasaki ====")
for p in response.objects:
    print(f"certainty: {p.metadata.certainty}")
    print(f"question: {p.properties['question']}")
    print("answer: {}...".format(p.properties['answer'].replace('\n','')[:100]))
    print(f"source: {p.properties['source']}")
    print()

同じクエリに対して、異なる検索結果担っているのがわかる。

==== tenant_kosodate ====
certainty: 0.9501070380210876
question: 母子手帳はすぐに発行してもらえますか?
answer: 母子手帳は、妊娠届の内容を確認させていただき、その場でお渡しします。▼詳しくはこちら(自治体HP内関連ページのURL)...
source: 子育てFAQ

certainty: 0.943825900554657
question: 母子手帳の他に産前に市役所でやるべき手続きはありますか?
answer: 産前は母子手帳以外の手続きは特にありません。産後に、出生の届出や出生通知書の提出、(自治体が行う出産助成等)の申請をお願いします。...
source: 子育てFAQ

certainty: 0.9422510266304016
question: 母子手帳を受け取りたいのですが、手続きを教えてください。
answer: 窓口で妊娠届をご記入いただき、母子手帳をお渡しします。住民票の世帯が別の方が代理で窓口に来られる場合は、委任状が必要になります。▼詳しくはこちら(自治体HP内関連ページのURL)...
source: 子育てFAQ

certainty: 0.9418485164642334
question: 母子手帳の申請には医師の診断書が必要ですか?
answer: 母子手帳の申請には診断書はいりませんが、妊娠届に診断を受けた病院名・医師名を記入していただきます。...
source: 子育てFAQ

certainty: 0.9417502880096436
question: 母子手帳をなくした場合は再発行できますか?
answer: 母子手帳をなくしたときは、再交付を受けてください。お子さんが出生前の母子手帳については、(再交付を受けられる場所)で再交付を受けられます。お子さんが出生後の母子手帳については、(再交付を受けられる場所...
source: 子育てFAQ

==== tenant_amagasaki ====
certainty: 0.9341765642166138
question: 【母子健康手帳】母子健康手帳が欲しい。
answer: ■母子健康手帳(母子手帳)の交付手続きは、次のとおりです。健やかな妊娠と出産のために、できるだけ早い時期に妊娠の届出をして、母子健康手帳(母子手帳)の交付を受けてください。お母さんと子どもさんの健康診...
source: 尼崎市FAQ

certainty: 0.9254933595657349
question: 【母子健康手帳】母子健康手帳は、どこでもらえますか?
answer: ■母子健康手帳(母子手帳)は南部・北部保健福祉センター地域保健課または健康増進課の窓口にてお渡ししています。直接窓口にて「妊娠届出書」をご記入の上、申し込んでください。要件を確認の上、即日お渡ししてい...
source: 尼崎市FAQ

certainty: 0.9228112697601318
question: 【児童手当】父母が海外に住んでおり、国内で祖父母が子どもを養育している場合、児童手当は支給されますか。
answer: ■児童の生計を維持している父母が海外に居住している場合は、父又は母が祖父又は祖母を「父母指定者」に指定することで、祖父又は祖母が児童手当を支給されることとなります。ただし、通常とは異なる書類などが必要...
source: 尼崎市FAQ

certainty: 0.9220273494720459
question: 【母子健康手帳】市内から市外に引っ越しをします。母子健康手帳(母子手帳)はどうしたらいいですか。
answer: ■母子健康手帳は母子保健法に基づいた内容より、全国共通ですので、他市へお引っ越しされてもそのまま尼崎市発行の母子健康手帳を引き続き使用できます。<改>。【お問い合わせ】。●健康福祉局健康増進課。母子保...
source: 尼崎市FAQ

certainty: 0.9215610027313232
question: 【母子健康手帳】母子健康手帳(母子手帳)を再交付してほしい。
answer: ■やむを得ない事由により著しく手帳を損傷し、または紛失した場合は、その理由を母子健康手帳交付申請書に記載し提出ください。確認後、再交付しています。<改>。■窓口。再交付の手続きは南部・北部保健福祉セン...
source: 尼崎市FAQ
kun432kun432

上にも書いた通り、クラスやフィルタを使えばマルチテナントっぽいことは実現できる。

マルチテナントは、例えば、別のクラスを作る、とか、あとはフィルタを使って例えばお客様IDのようなものでフィルタする、など、既存の機能でも実現できるのだけれど、Weaviateではマルチテナントは独立した専用の機能として提供されている。

それでもマルチテナント機能を使うことにどのようなメリットがあるか?は以下に記載されている。

https://weaviate.io/blog/multi-tenancy-vector-search

  • マルチテナントの場合、テナントごとにクラスタ内の別のシャードにデータは分離して登録される。これにより・・・
    • コンプライアンス的にリソース分離が必要なケースに対応できる
    • 検索したいテナントのシャードだけを検索すればいいので、高速
    • テナントごとに、無効化したり、削除したり、と管理が楽になる
  • クラスを使う場合
    • データのリソース分離はできる
    • が、クラスは大量のテナントを保持できない(らしい)
  • フィルタを使う場合
    • 検索したい対象が限られているのにすべてを検索しないといけなくなる、検索効率が悪い
    • 特定のフィルタ条件でデータを削除するとインデックスの再構築が必要になる
    • リソースを分離できない(論理的な分離にとどまる)、フィルタ設定によってはミスる可能性はある

クラスタとノードがどのように管理されるのか?などについてはまだ確認できていないが、複数のお客様向けにサービス提供するようなケースでは有用だと思う。

このスクラップは1ヶ月前にクローズされました