0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SaveData開発記録#21 igdb APIで『ダークソウル』と検索してもゲームが出ない不具合

0
Last updated at Posted at 2026-04-05

前回の続き
ゲーム登録画面の入力を簡単にするため、
IGDB APIを導入し タイトル 画像 ゲームジャンルなどを簡単に
呼び出せるようにした

game_howto.gif

IGDBとはなんぞやという方はこちらの記事をご参考にしてください。

さて
前回、とりあえずは検索、ヒット、取得ができたので
よしとしていましたが、

今回は三本の柱でより簡単に登録
できるようにしていきます。

IGDB三本の柱大作戦

1:日本語対応
→ 現在日本語に対応していない 
API利用で切り抜ける

2:検索三種盛り作戦
→曖昧検索だけだったものを 
部分一致 分割キーワード検索 スペース除去
この三つを盛り込んでよりヒットするように

3:重複を無くしていっぱい表示作戦
→ 50件の制限があるためそこをなるべく重複を避けた上で
表示させる

結論こうなりました

IGDBロジック.png

search("ダークソウル")

contains_japanese? → true なので日本語ルートへ

① search_by_alternative_names("ダークソウル") # 日本語のまま検索
② translate_to_english("ダークソウル") → "Dark Souls"
③ search_games_directly("Dark Souls") # 英語に変換して検索

① + ③ を uniq でマージして返す

内容解説

作戦1 ゲーム検索機能

ゲームを検索する(日本語・英語を自動判定して振り分け)
日本語の場合は alternative_names 検索 + 英語翻訳して英語検索も実行する

  def self.search(query)
    token = get_token
    if contains_japanese?(query)
      results = search_by_alternative_names(query, token)

日本語→英語に変換して英語検索も実行
(IGDBに日本語alternative_nameが登録されていないゲームを拾うための補完)


      english_query = translate_to_english(query)
      results += search_games_directly(english_query, token) if english_query

      results.uniq { |g| g["id"] }
    else
      search_games_directly(query, token)
    end
  end

日本語対応に関しては今回、
DeepLのAPIを利用します。

選定理由は2点

1:無料で1か月に500,000文字まで翻訳
という脅威の性能をしていた点
2:emailの登録だけで簡単に利用可能

翻訳系のロジックはここら辺に

DeepL APIで日本語クエリを英語に変換する
  → 変換失敗時は nil を返し、呼び出し元でスキップさせる
  → .env に DEEPL_API_KEY の設定が必要

  
  private_class_method def self.translate_to_english(query)
    uri  = URI("https://api-free.deepl.com/v2/translate")
    http = Net::HTTP.new(uri.host, 443)
    http.use_ssl = true

    req = Net::HTTP::Post.new(uri).tap do |r|
      r["Authorization"] = "DeepL-Auth-Key #{ENV['DEEPL_API_KEY']}"
      r["Content-Type"]  = "application/json"
      r.body = { text: [ query ], source_lang: "JA", target_lang: "EN" }.to_json
    end

    res = http.request(req)
    unless res.code == "200"
      return nil
    end

    JSON.parse(res.body).dig("translations", 0, "text")
  rescue => e
    nil
  end

作戦2 検索三種盛り

1:部分一致

部分一致 (name ~ *"query"*)
  → 入力がタイトルの一部でもヒット。
  例: "DARK SOULS" → "DARK SOULS III", "DARK SOULS: REMASTERED" 等にヒット。


部分一致検索(name ~ *"query"*)
  private_class_method def self.partial_match_search(query, token)
    body = <<~BODY
      fields name, platforms.name, genres.name, cover.image_id;
      where name ~ *"#{query}"*;
      limit 50;
    BODY
    igdb_request("/games", body, token) || []
  end

2:単語分割 AND条件に対応

単語分割 AND 条件検索("DARK" & "SOULS" 両方を含むゲームを取得)
  → "DARK SOULS", "DARK SOULS III" などがヒットする
  
  
  private_class_method def self.word_and_search(words, token)
    conditions = words.map { |w| "name ~ *\"#{w}\"*" }.join(" & ")
    body = <<~BODY
      fields name, platforms.name, genres.name, cover.image_id;
      where #{conditions};
      limit 50;
    BODY
    igdb_request("/games", body, token) || []
  end

3:空白の削除

スペース除去版で部分一致検索(逆パターン補完)

    no_space = sanitized.delete(" ")
    results  += partial_match_search(no_space, token) if no_space != sanitized

作戦3 重複除去

検索結果の重複除去 & カバーURL付加をまとめて行う

  private_class_method def self.build_results(results)
    results
      .uniq { |g| g["id"] }
      .map  { |g| g.merge("cover_url" => build_cover_url(g.dig("cover", "image_id"))) }
  end

中身についてはだいぶ端折ったのですが
大まかに動かしているロジックは上記の通りです。

実際にやってみた

ついに「ダークソウル」 で出ましたー!!!!!!
スクリーンショット 2026-04-05 14.24.17.png

これにより、今までよりも快適に検索、登録ができるようになりました。

最後に

このダークソウルというタイトルを選んだことで

「Dark Souls」
間に空白が入っている
DarkSoulsで検索しても出てこない
ダークソウルと日本語で入力しても出てこない
Darkのみでも検索件数が多すぎて出てこない

などいろんな「引っかからない問題」を発見できました。
ロジックを考えるのは大変でしたが、
しっかりと機能してくれてよかったです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?