Monacaのサンプルアプリである「フォトシェアアプリデザインテンプレート」にバックエンド機能を実装する連載の第4回目。前回のプロフィール編集に続いて、今回は写真検索機能を作ります。

その1:認証編
その2:プロフィール編集
その3:写真アップロード

検索方法について

mBaaSにはインデックス型検索機能は今のところありません。そのため、検索用のクラスをあらかじめ用意しておくか、クラスデータに対する正規表現検索になるかと思います。多くの検索においてスペースを使って検索文字列の連結を行いますが、今回はデモということもありますのでOR検索になるように実装してみます。

検索実行

検索ボタンを押した際の実装になります。Photoクラスの regularExpressionTo() を使って検索条件を正規表現で指定できるようにします。その上で、検索条件が複数あった場合(aryOr.lengthが2以上だった場合)、Photoクラスのorメソッドに対して検索条件を指定します。検索条件が一つだった場合は or を使う意味がありませんので、aryOrの最初の条件を使います。

const searchPhoto = (e) => {
  const dom = $('#search-page');
  const photoView = dom.find('#photos');
  const Photo = ncmb.DataStore('Photo');
  if (e.keyCode === 13 && 
     (e.shiftKey === false || e.ctrlKey === false || e.altKey === false)
   ) {
  } else {
    // 通常入力
    return true;
  }
  // 検索実行
  if (e.target.value.trim() === '') {
    return false;
  }
  // スペースで分割
  const keywords = e.target.value.split(' ');
  const aryOr = [];
  for (let i = 0; i < keywords.length; i += 1) {
    const subPhoto = ncmb.DataStore('Photo');
    const keyword = keywords[i];
    // 配列の中に検索条件を追加していきます
    aryOr.push(
      Photo
        .regularExpressionTo('message', `.*${keyword}.*`)
    );
  }
  // 複数条件指定された場合は or 検索とします
  const promise = aryOr.length === 1 ? 
    showPhotos(photoView, aryOr[0]) :
    showPhotos(photoView, Photo.or(aryOr));
}

そして条件が設定できたら、検索を実行します。結果の取得上限数はデフォルトで20件までとし、検索結果をテンプレートに適用します。

const showPhotos = (dom, Photo) => {
  return new Promise((res, rej) => {
    $(dom).hide();
    const thumbnailTemplate = $('#thumbnailTemplate').html();
    Photo
      .limit(20)
      .include('user')
      .order('-createDate')
      .fetchAll()
      .then((photos) => {
        const thumbnail = Mustache.render(thumbnailTemplate, {
          photos: photos
        });
        $(dom).html(ons.createElement(thumbnail));
        $(dom).show();
        $('.loading').hide();
        res(photos);
      });
  });
}

検索結果の表示

指定するテンプレートは以下のようになります。写真をタップするとtapPhoto()が呼ばれます。

<template id="thumbnailTemplate">
  <div>
    {{#photos}}
      <ons-col>
        <img class="search_thumbnail" onclick="tapPhoto('{{objectId}}')" src="{{fileUrl}}">
      </ons-col>
    {{/photos}}
  </div>
</template>

tapPhoto()は次のようになります。テンプレートでタップされた写真のobjectIdを使ってPhotoクラスをあらためて検索し、その写真を1枚表示します。

const tapPhoto = (objectId) => {
  const Photo = ncmb.DataStore('Photo');
  Photo
    .equalTo('objectId', objectId)
    .fetch()
    .then((photo) => {
      $('#nav')[0].pushPage('single.html', {animation: 'slide', data: {photo: photo}});
    });
}

写真の一枚表示

写真を1枚表示する処理はタイムラインの写真表示処理と変わりません。

document.addEventListener('show', function(event) {
  var page = event.target;
  if (page.id == 'single-page') {
    showSinglePage($(page), page.data.photo);
  }
  // 省略

})

写真を表示する際にはLikeクラスも検索し、いいねやコメントのデータも取得します。(この処理については、次回詳しく紹介します)

const showSinglePage = (dom, photo) => { 
  const Like = ncmb.DataStore('Like');
  Like
    .equalTo('photoObjectId', photo.objectId)
    .fetch()
    .then((like) => {
      appendPhoto(dom.find('.post'), photo, like);
    });
  dom.find('.photo-title').text(photo.message);
}

ここまでの処理で写真の検索とその結果表示、そして写真をタップした際に写真を表示する処理が完了しました。処理をなるべく使い回していますので、実装するコードは徐々に少なくなっているはずです。mBaaSには高機能な文字列検索検索は今のところありませんが、正規表現検索を使うことで一般的な検索条件は実装できるでしょう。位置情報検索というスマートフォン向きの検索機能もありますので、ぜひ試してみてください

ここまでのソースコードはNCMBMania/photoshare at v0.6にアップロードしてあります。実装時の参考にしてください。