技術メモ

Scala,React Nativaなどの技術メモ

Parse終了のお知らせ。でもやってみる。

Parseサービス終了するらしい

blog.parse.com

UI好きだったので残念だ。いまReact Nativeで作っているアプリのために使う予定だったのだけど。 他のサービスや自分でサーバサイドを一から書くことを検討もしたけど、やっぱりこのままParse使ってみようと思う。

理由

  • 終了まであと1年くらいある
  • Parse Server(上記記事参照)などもでたので、こっちも使ってみたい
  • まだParseというサービスの良し悪しを体感できるほど使えていない
  • 移行作業もそれはそれでいい機会

Parseの特徴

  • UserやRoleごとに細かいアクセス制御ができる
  • 多対多の関連を表現するための関連テーブル(Parseでいうclass)はいらない

ParseReactについて

github.com

Seamlessly bringing Parse data into your React applications.

とあるので、ReactからParseを使うための実装かと。

Parse Javascript SDK との違い

READMEによると

Parse + React is an interface layer on top of the Parse JS SDK that provides simple access to the Parse API from React.

とあるので、Parse Javascript SDKをReactでラップしてくれているぽい。 ブラウザなどでのJSからの用途ではParse JS SDKをReact NativeからはParseReactを使う感じなんだと思う。 ReactNativeは、JSの実行環境が特殊だったりするので、そのための対応などだろうか。

今後の調べるべきこと
  • ブラウザ(React)とReactNativeの実行環境の違い

実装されたAPI

github.com

データの取得やバインドのような機能を提供するAPIも独自に追加されている。 このobserveは確か多対多関連の参照でうまく動かなかった記憶があるので、後ほど検証する。

ParseNativeでParseを使う

一対多関連の表現

AuthorBookを例にあげる。

一対多関連の選択肢
  • Pointers
  • Arrays

今回はPointersを使ってみる。

下記リンクによるとParse Relationsによる関連付けはwebからはできないらしいので、ParseダッシュボードのAPIコンソールから、実際の関連付けを行う。

www.parse.com

エンドポイントは、

PUT classes/Book/AAA

で、これに対して、

{
   "author":{
      "__type":"Pointer",
      "className":"Author",
      "objectId":"BBB"
   }
}

のようにauthorカラムにidがBBBAuthorPointerとして紐付けている。カラムは自動で生成される。 これをReact Nativeから取得するには、

observe: function (props, state) {
   return {
      user: ParseReact.currentUser,
      books: new Parse.Query('Book').include('author')
   };
},

とすると、

{
   id: { className: 'Book', objectId: 'AAA' },
   className: 'Book',
   objectId: 'AAA',
   createdAt: ***,
   updatedAt: ,***
   ACL: ***,
   author: {
      id: { className: 'Author', objectId: 'BBB' },
      className: 'Author',
      objectId: 'BBB',
      createdAt: ***,
      updatedAt: ***,
      name: 'name'
     }
}

のようにあるデータが取得でき、books.author.nameにアクセスできる。 includeについてはParseのREST API Guide

Use on Pointer columns to return the full object

とあったので、Pointer型専用ぽい。JS SDKのDocsには特にそんな記述ない感じだけど、APIレベルでPointer型専用ならそうなんでしょう。

多対多関連の表現

BookAuthorの多対多の関連を例にあげる。

parse.com

上記ドキュメントによれば、次の3つの選択肢があるようだ。

多対多関連の選択肢
  • Arrays
  • Parse Relations
  • Join Table

今回は、多対多関連の片方のオブジェクト数(Book)が100以上になりそうなこと、 BookAuthorに限れば、関連の作成日時などのメタデータは必要ないことからParse Relationsを選択する。

Arraysの選択時は、2016/02/04現在Pointer型の配列展開に関するバグが報告されているようなので注意が必要だ。

github.com

Parse Relationによる多対多の関連付け

エンドポイントは、

PUT classes/Book/AAA

で、これに対してQuery Parameter

{
   "authors":{
      "__op":"AddRelation",
      "objects":[{
         "__type":"Pointer",
         "className":"Author",
         "objectId":"BBB"
      }]
   }
}

でリクエストする。 これはidがAAAのBookクラスのauthorsカラムに、idがBBBのAuthorクラスをRelation型として関連付けることを表す。 カラムがなければ自動で生成される。

関連を含むデータを取得

ParseReactのobserveを使う
observe: function (props, state) {
   return {
      user: ParseReact.currentUser,
      books: new Parse.Query('Book')
   };
}

observeを使って、Bookデータを取得する。

{ 
   id: { className: 'Book', objectId: 'AAA' },
   className: 'Book',
   objectId: 'AAA',
   createdAt: ***,
   updatedAt: ***,
   ACL: ***,
   authors: {
      parent: { _objCount: 9, className: 'Book', id: 'AAA' },
      key: 'author',
      targetClassName: 'Author'
   },
}

取得できたBookのあるデータを示す。 関連は持ってるぽいが、Authorのnameプロパティは直接取れていない。

new Parse.Query('Book').include('authors')

とかしたら、データが取得できなくなることからも、includeはやはりPointer型専用か。

authors: {
      parent: { _objCount: 9, className: 'Book', id: 'AAA' },
      key: 'author',
      targetClassName: 'Author'
   }

これだけの情報じゃ、Authorテーブルからも引けないけどどうするんだ。

matchesQueryを使う
var query = new Parse.Query(Parse.Object.extend("Book"));
var innerQuery = new Parse.Query(Parse.Object.extend("Author"));
query.matchesQuery("authors", innerQuery);
query.get("AAA", function(book) {
      console.log(subjectObj);
});
{ className: 'Book', _objCount: 12, id: 'AAA' }

得られたのは、BookクラスのParse.Objectっぽいが、

get('authors')
{
   parent: { _objCount: 12, className: 'Book', id: 'AAA' },
   key: 'authors',
   targetClassName: 'Author'
}

authorsプロパティは取得できない。

innerQuery.exists('not_exists_property');
null

となることからも、matchesQueryBookに対してもAuthorに対してもクエリをかけてくれるようなものではなく、 ある条件を満たすクエリをBookにかけるような挙動であると予測できる。

Parse.Relationを使う
new Parse.Query('Book').get('AAA').then(function (book) {
   book.relation("authors").query().find().then(function (authors) {
      console.log(authors);
   });
});
[ { _objCount: 11, className: 'Author', id: 'BBB' }, { _objCount: 12, className: 'Author', id: 'CCC' } ]

このような出力が得られるが、一見するとnameプロパティは含まれていないよう。 しかし、Parse.Objectからは.getメソッドでプロパティを取得できる。

 authors.map(function (x) {
    return x.get('name');
 })

まとめ

多対多の関連の参照

Parse.Relationを使った多対多関連を一つのクエリで参照することはできないらしい。 しかしParse.Relation使えば、複合的な記述にはなるが十分に使える。

observeについても、うまく複合クエリをセットできればちゃんと使えるのかも。

今後に生かすこと

JSのobjectと、Parse.Objectの区別がつかず、getメソッドによるプロパティ取得に気付けなかったことが時間がかかった原因になった。typeofconsole.logからはその違いが見つけづらかった。

それと英語をもっと速く読めるようになりたい。急がば回れだ。