はじめに
この記事は Elasticsearch
とSwift
のAdvent Calendarを同時に攻略しようとする一石二鳥な実験的記事です。
Elasticsearch Advent Calendar 2014 4日目
とSwift Advent Calendar 2014 4日目
を兼ねています。
概要
解析用サーバとしての利用が多いElasticsearchの「データストア」としての利用を促すために、最近流行りのmBaaSサーバとして簡単に構築してみました。
全てのロジックはSwift側で実装しており、Elasticsearchへは直接アクセスしています。
なので、本番環境としての利用はセキュリティ的におすすめしません。
実際に使うためには、SwiftとElasticsearchの間にWebアプリケーションを挟んで認証をショートカットされないようにする必要があります。
(ElasticsearchのPlugin作成が可能ならWebアプリケーションを利用せずに完結することも可能です。)
Elasticsearchの認証部分にelasticsearch-authを利用しています。
また、Swift側では、Alamofire とSwiftyJSONを利用しています。
この実験に使ったコードはtadyjp/ElasticSwiftにあります。
``
$ version
Elasticsearch 1.4.0
Swift version 1.1 (swift-600.0.56.1)
![Screen Shot 2014-12-04 at 5.04.04.png](https://qiita-image-store.s3.amazonaws.com/0/10272/7c671e60-e04e-c734-dc63-fc45ef806ca2.png)
![Screen Shot 2014-12-04 at 5.03.56.png](https://qiita-image-store.s3.amazonaws.com/0/10272/b836446a-c91a-0c58-9a30-e9ddb1e1552b.png)
# Elasticsearchのコード
ありません!
indexの作成や検索は全てSwiftで実装しています。
# Swiftのコード
SwiftでのElasticsearchへの検索は次のコードがメインコードになります。
```swift
let res = Alamofire.request(.POST, "http://localhost:9200/twitter/tweet/_search?q=user:\(self.username)", parameters: nil, encoding: .JSON)
.responseJSON { (_, _, json, _) -> Void in
let sjson = JSON(json!)
if let hits = sjson["hits"]["hits"].array {
var tweets = [] as [String]
for subJson: JSON in hits {
println(subJson["_source"]["message"].string!)
tweets.append(subJson["_source"]["message"].string!)
}
block(tweets)
} else {
println("getTweets error")
}
}
単純にJSONリクエストを行っているだけなので難しいところはありません。
user:<ユーザー名>
という検索クエリを発行することで、自分の投稿のみを抽出しています。
工夫が必要なのはログイン処理の部分です。
class func currentUser() -> ElasticUser? {
let ud = NSUserDefaults.standardUserDefaults()
if let username = ud.objectForKey("username") as? String {
let token = ud.objectForKey("token") as String
let user = ElasticUser(username: username, token: token)
println("currentUser = \(username)")
return user
} else {
return nil
}
}
class func signUp(username: String, password: String, block: (user: ElasticUser?) -> ()) {
let params = [
"authenticator": "index",
"username": username,
"password": password,
"roles": "[\"user\"]"
]
println("signUp...")
let res = Alamofire.request(.PUT, "http://localhost:9200/_auth/account", parameters: params, encoding: .JSON)
.responseJSON { (_, _, json, _) -> Void in
let sjson = JSON(json!)
println(sjson["status"])
if sjson["status"] == 200 {
self.logIn(username, password: password, { (user2: ElasticUser?) in
block(user: user2)
})
}
}
}
class func logIn(username: String, password: String, block: (user: ElasticUser?) -> ()) {
let params = [
"username": username,
"password": password
]
println("logIn...")
let res = Alamofire.request(.POST, "http://localhost:9200/login", parameters: params, encoding: .JSON)
.responseJSON { (_, _, json, _) -> Void in
let sjson = JSON(json!)
println(sjson["status"])
if sjson["status"] == 200 {
let user: ElasticUser? = ElasticUser(username: username, token: sjson["token"].string!)
block(user: user)
} else {
block(user: nil)
}
}
}
初回はログイン情報がないのでelasticsearch-authにユーザー作成を依頼します。
すると次回からはこのユーザー名とパスワードでログイン可能になり、一意のtoken
が発行されるので、NSUserDefaults
などに保存して次回以降のアクセス時に利用します。
まとめ
ちょっとしたクライアントサイドのコードだけでElasticsearchのスキーマレス性を活用したなんちゃってmBaaSが動いてしまいました。
これだけでは趣味の範囲は超えませんが、これをたたき台として、なにか新しいアプリケーションの発想が生まれると幸いです。