tmytのらくがき

個人の日記レベルです

Windows+Qでのサジェストを実装する

Windows+Qを押すと、システムグローバルな検索や、利用出来る場合はアプリ内の検索ができます。これをWindows Storeを見る”ストア”アプリで実行し、検索ボックスになにか文字列を入力するとこのように表示されます。

検索機能を使ってるアプリならせっかくなので実装してみたい!ということでMSDNを眺めてみると、どうやらSearchPaneクラスを使えば良さそうです。
ためしに以下のコードを動かすと、このようになりました。

using ...;

namespace App3
{
    public class App : Application
    {
        /** 省略 **/
        protected override void OnWindowCreated(WindowCreatedEventArgs args)
        {
            base.OnWindowCreated(args);

            var pane = SearchPane.GetForCurrentView();
            pane.SuggestionsRequested += pane_SuggestionsRequested;
        }

        void pane_SuggestionsRequested(SearchPane sender, SearchPaneSuggestionsRequestedEventArgs args)
        {
            args.Request.SearchSuggestionCollection.AppendSearchSeparator("もしかして?");
            // 検索文字列のサジェストを追加
            args.Request.SearchSuggestionCollection.AppendQuerySuggestion("hoge");
            args.Request.SearchSuggestionCollection.AppendSearchSeparator("おすすめ!");
            var file = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx://Assets/StoreLogo.png"));
            // 検索結果のサジェストを追加
            args.Request.SearchSuggestionCollection.AppendResultSuggestion("fuga", "lipsum", "xxx", file, "0");
        }
    }
}


なかなかよさそうです。アプリの検索ページとかではなくグローバルでサジェストに対応するには、AppクラスのOnWindowCreatedでSuggestionsRequestedイベントをハンドリングすればいいそうです。*1ちなみに、アプリケーションマニフェストで、"検索"を宣言しておかないとGetForCurrentViewでUnAuthorizedAccessExceptionになります。

なんとなく動いてるのでせっかくなのでGoogleのサジェストでも使って遊んでみます。APIとして使っていいのか分からないですけどまぁ次の様な感じです。

EndPoint: https://www.google.co.jp/s?hl=ja&q={クエリ文字列}&xhr=t&oe=utf8
Response: ["google サジェ",[["google サジェスト","",],["google サジェスト 無効","",],["google サジェスト 削除","",],["google サジェスト api","",],["google サジェスト 仕組み","",],["google サジェスト機能","",],["google サジェスト seo","",],["google サジェスト 邪魔","",],["google サジェスト 一覧","",],["google サジェスト 広告","",]],{"k":1,"q":"iP9g-2VSGLeSuL42o3sNh21jVds"}]

そんなに難しい構造でもないので、C#でぱぱっとパースしちゃいます。クラスにしてみましたが特に深い意味はありません。

using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Windows.Data.Json;

namespace App3
{
    public class GoogleSuggest
    {
        public async Task<IEnumerable<string>> GetSuggestsAsync(string query)
        {
            // 結果をダウンロード
            var client = new HttpClient();
            var json = await client.GetStringAsync(
                string.Format("https://www.google.co.jp/s?hl=ja&q={0}&xhr=t&oe=utf8",
                WebUtility.UrlEncode(query)));
            // てきとうにパース
            var root = JsonArray.Parse(json);
            return root.Skip(1).Take(1).SelectMany(_ => _.GetArray())
                .Select(_ => _.GetArray().First().GetString());
        }
    }
}

これを使って、さっきのコードをちょこっと書き換えてこんな感じにしてみました。これを実行してなにか入力してみると次のようになります。

async void pane_SuggestionsRequested(SearchPane sender, SearchPaneSuggestionsRequestedEventArgs args)
{
    var deferral = args.Request.GetDeferral();
    var suggest = new GoogleSuggest();
    args.Request.SearchSuggestionCollection.AppendSearchSeparator("もしかして?");
    args.Request.SearchSuggestionCollection.AppendQuerySuggestions(await suggest.GetSuggestsAsync(args.QueryText));
    deferral.Complete();

}


思ったより簡単でした。

*1:ってMSDNに書いてた!