Parse Server Java SDKの使い方（Kotlinでのデータストアクエリー操作）

[ニフクラ mobile backendは3月末で終了します](https://mbaas.nifcloud.com/info/2023/12/post-171.html)。

ニフクラ mobile backendからの移行先として、お勧めしているのが[Parse Server](https://parseplatform.org/)です。設計思想が近く、変更するコード量が少なく済むのではないかと思います。

Parse ServerではAndroid向けにSDKを提供していますが、言語はJavaのみとなっています。そのため、Kotlin向けには、Java SDKの内容を翻訳しながら実装する必要があります。そこで、本記事にて、各機能の使い方を解説します。今回はデータストアのデータ取得（クエリー）について紹介します。

## Android SDKのインストール

Android SDKのインストールは幾つかの設定が必要です。

### settings.gradleの修正

プロジェクトルートにある `settings.gradle` を修正します。

```groovy
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
				// 以下を追加
        maven {
            url "https://jitpack.io"
        }
    }
}
```

### app/build.gradleの修正

app以下にある `build.gradle` を修正します。

```groovy
dependencies {
	// 以下を追加
	implementation "com.github.parse-community.Parse-SDK-Android:parse:4.2.1"
}
```

バージョンはインストールするタイミングによって異なりますが、現在は以下です。

![](https://jitpack.io/v/parse-community/Parse-SDK-Android.svg)

### AndroidManifest.xmlの修正

開発中で、ローカルのParse Serverを使う場合には、以下の設定が必要です。

```xml
<application
	android:usesCleartextTraffic="true" <!-- 追加 -->
>
```

## 初期化

SDKの初期化は `MainActivity` にて行います。Androidはエミュレーターなので、 `localhost:1337` ではなく、 `192.168.0.2:1337` などがParse ServerのURLになるでしょう。

オフライン対応する際には `.enableLocalDataStore()` を追加します。これがないと、オフライン時にデータ操作できないので注意してください。

```kotlin
import com.parse.Parse

class MainActivity : AppCompatActivity() {

    private lateinit var appBarConfiguration: AppBarConfiguration
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Parse.initialize(
            Parse.Configuration.Builder(this)
                .applicationId("appId")
                .clientKey("clientKey")
								.enableLocalDataStore()
                .server("http://your_parse_server/parse/")
                .build()
        )
				// 省略
		}
}
```

## データの取得

データの取得には `ParseQuery` を利用します。基本形は以下の通りです。

```kotlin
val query = ParseQuery.getQuery<ParseObject>("GameScore")
query.whereEqualTo("playerName", "Sean Plott")
query.findInBackground { scoreList, e ->
		if (e == null) {
				Log.d("DEBUG", "Retrieved " + scoreList.size + " scores")
		} else {
				Log.d("DEBUG", "Error: " + e.message)
		}
}
```

同期処理の場合は `find` を使います。

```kotlin
val items = query.find()
Log.d("DEBUG", "Retrieved " + items.size + " scores")
```

## 絞り込み条件

絞り込み条件は `whereEqualTo` などを使います。他にも `whereMatches` や `whereContainedIn` などがあります。以下は一部です。

- whereEqualTo
- whereNotEqualTo
- whereExists
- whereDoesNotExist
- whereMatches
- whereMatchesQuery
- whereDoesNotMatchQuery
- whereMatchesKeyInQuery
- whereDoesNotMatchKeyInQuery
- whereStartsWith
- whereEndsWith
- whereContains
- whereContainedIn
- whereContainsAll
- whereContainsAllStartsWith
- whereFullText
- whereLessThan
- whereLessThanOrEqualTo
- whereGreaterThan
- whereGreaterThanOrEqualTo
- whereNear
- wherePolygonContains
- whereWithinGeoBox
- whereWithinKilometers

## 最初の結果だけが必要な場合

最初の結果だけが必要な場合は `getFirstInBackground` を使います。この場合は、配列ではなくParseObjectが返ってきます。

```kotlin
query.getFirstInBackground { obj, e ->
		if (obj == null) {
				Log.d("score", "The getFirst request failed.")
		} else {
				Log.d("score", "Retrieved the object.")
		}
}
```

同期での取得は `getFirst` を使います。

```kotlin
val obj = query.getFirst()
```

## 取得件数の絞り込み

取得件数を制限したり、ページネーションを実装する際には、 `setLimit` と `setSkip` を使います。

```kotlin
query.setLimit(10)
query.setSkip(10)
```

## ソート

ソートは `orderByAscending` と `orderByDescending` を使います。

```kotlin
query.orderByAscending("score")
query.orderByDescending("score")
```

ソートキーを追加する際には、 `addAscendingOrder` と `addDescendingOrder` を使います。

```kotlin
query.addAscendingOrder("score")
query.addDescendingOrder("score")
```

## 件数の取得

件数を取得する際には、 `countInBackground` または `count` を使います。

```kotlin
query.countInBackground { count, e ->
		if (e == null) {
				Log.d("score", "There are " + count + " scores")
		} else {
				Log.d("score", "Error: " + e.message)
		}
}
```

同期での取得は `count` を使います。

```kotlin
val count = query.count()
Log.d("score", "There are " + count + " scores")
```

## or条件

or条件を使う際には、 `ParseQuery.or` を使います。

```kotlin
val lotsOfWins = ParseQuery.getQuery<ParseObject>("Player")
lotsOfWins.whereGreaterThan("score", 150)

val fewWins = ParseQuery.getQuery<ParseObject>("Player")
fewWins.whereLessThan("score", 5)

val queries: MutableList<ParseQuery<ParseObject>> = ArrayList()
queries.add(lotsOfWins)
queries.add(fewWins)

val mainQuery = ParseQuery.or(queries)
mainQuery.findInBackground { results, e ->
		if (e == null) {
				Log.d("DEBUG", "Retrieved " + results.size + " players")
		} else {
				Log.d("DEBUG", e.toString())
		}
}
```

## まとめ

Parse ServerとNCMBのデータストア操作は、Android SDK同士では大きくは異なりません。載せ替える際には、大きなコードにはならなそうです。ParseのAndroid SDKの方が、クエリー操作の際には、より柔軟に使えるメソッドが多いです。フルテキスト検索は便利そうです。

データの管理方法などはParse ServerとNCMBで似ています。他のmBaaSと比べると、全体の修正量はそこまで多くないと思われます。載せ替え先として検討に挙げてください。

[Android Developers Guide \| Parse](https://docs.parseplatform.org/android/guide/)