Asopitech With · 音楽配信
Spotify with Alopex DB
Spotifyは、音楽やポッドキャストを見つけて聴ける音楽配信のサービスです。Alopex DBで同じものを作るとしたら…?
更新: 2026/10/12
どんなサービスか
Spotifyのような音声配信のサービスでは、利用者が音楽、ポッドキャスト、オーディオブックを見つけ、管理し、楽しみます。扱うデータは、曲やアーティストのカタログ、プレイリスト、そして利用者ごとの再生の履歴です。おすすめは、このカタログと履歴から作ります。
データの置き場所
- SQLテーブル:曲、アーティスト、アルバム、プレイリストと、その中の曲の並びをテーブルに持ちます。
- ベクトル:曲ごとの埋め込みをベクトル列に持ちます。「この曲に似た曲」は、HNSW索引での近傍検索です。ジャンルや公開年の条件を、同じSQLで重ねます。
- カラムナーテーブル:再生の履歴をカラムナーテーブルに、HTTPの取り込みでまとめて入れます。利用者ごとのよく聴くアーティストや、時間帯ごとの傾向を、SQLで集計します。
曲の埋め込みはアプリが作ります。おすすめは、集計で出した好みのアーティストを条件にして、近い曲をベクトルで探す、ひとつのSQLになります。
配置の形
Alopex DBのサーバーを立て、アプリのバックエンドからHTTPかgRPCでつなぎます。カタログ、履歴、ベクトルが同じデータベースにあるので、おすすめのためにデータを別のシステムへ写す手間がありません。
Alopex DBが合う理由
おすすめは、似ているもの(ベクトル)と、よく選ばれているもの(集計)を組み合わせて作ります。Alopex DBは、ベクトル検索とカラムナーの集計を、同じサーバーのSQLで扱います。
コードで見る
再生の履歴からよく聴くアーティストを集計し、その傾向に近い曲をベクトルでおすすめします。おすすめの中心だけを取り出したコードです。
import alopex
import numpy as np
db = alopex.Database.open("spotify.alopex")
db.execute_sql("""
CREATE TABLE IF NOT EXISTS tracks (
id INTEGER PRIMARY KEY,
title TEXT,
artist TEXT,
embedding VECTOR(4)
)
""")
db.execute_sql("CREATE TABLE IF NOT EXISTS plays (track_id INTEGER)")
tracks = [
(1, "Song A", "Aqua", [0.9, 0.1, 0.0, 0.0]),
(2, "Song B", "Aqua", [0.85, 0.15, 0.0, 0.0]),
(3, "Song C", "Lofi", [0.0, 0.0, 0.9, 0.1]),
]
for tid, title, artist, emb in tracks:
db.execute_sql("INSERT INTO tracks (id, title, artist, embedding) VALUES (?, ?, ?, ?)", [tid, title, artist, np.array(emb, dtype=np.float32)])
for track_id in [1, 1, 2, 3]:
db.execute_sql("INSERT INTO plays (track_id) VALUES (?)", [track_id])
# よく聴くアーティストを、再生の履歴から集計する
top = db.execute_sql("""
SELECT t.artist, COUNT(*) AS cnt
FROM plays AS p
JOIN tracks AS t ON t.id = p.track_id
GROUP BY t.artist
ORDER BY cnt DESC
LIMIT 1
""")[0]
print("よく聴く:", top["artist"], top["cnt"])
# その傾向(Song A)に近い曲を、ベクトルでおすすめする
query = np.array([0.9, 0.1, 0.0, 0.0], dtype=np.float32)
rows = db.execute_sql("""
SELECT title, artist, vector_similarity(embedding, ?, 'cosine') AS score
FROM tracks
WHERE id <> ?
ORDER BY score DESC
LIMIT 1
""", [query, 1])
for row in rows:
print("おすすめ:", round(row["score"], 3), row["title"], row["artist"])よく選ばれているもの(集計)と、似ているもの(ベクトル)を、同じサーバーのSQLで組み合わせられます。
似た曲のベクトルと再生の傾向のカラムナーテーブルを同じサーバーで扱えるAlopex DBで、おすすめの仕組みを試してみませんか。出発点はクイックスタートです。