メモ

プログラミングなどの備忘録を書きます

elasticsearchでfilterとpost_filterを使ったらうまくいかなかった

termによる検索結果をさらに絞り込むためにpost_filterを使ったがうまくいかなかった。

post_filter

検索結果からさらに絞り込みを行うためのクエリで主にaggregationと組み合わせる。

このようなクエリを投げたが、 filterの結果から絞り込むのではなく、post_filterのみを適用した結果が返ってきてしまう。(2.2で確認。5以降では動くかも)

{
  "filter":{
      {"term":{"field1":"hoge"}},
  },
  "from":0,
  "size":100,
  "sort":{"createdAt":{"order":"desc"}},
  "post_filter":{"not":{"term":{"field2":"huga"}}}
}

queryと一緒に使うようにしたら動いた。

{
  "query":{
      {"term":{"field1":"hoge"}},
  },
  "from":0,
  "size":100,
  "sort":{"createdAt":{"order":"desc"}},
  "post_filter":{"not":{"term":{"field2":"huga"}}}
}

filterとqueryの違いについては公式のドキュメントで説明されている。 https://www.elastic.co/guide/en/elasticsearch/guide/current/_queries_and_filters.html

これによるとqueryはマッチした結果のスコアも一緒に知りたい時に使用するもので、filterは単にマッチするかどうか知りたい時に使うものらしい。 post_filterが使われる場合は単なる絞り込みではないと言うことなのだろうか。

python環境構築

今までデフォルトで入ってたpythonを使ってたけど、ちょっとpythonで作ってみたいものが出てきたので環境構築する。 といってもpyenv、virtualenvを入れただけ。 https://github.com/yyuu/pyenv#basic-github-checkout

pyenv

brewが使えるようなのでbrewで入れる。

brew upgrade
brew install pyenv

任意のバージョンをインストー

pyenv install 3.5.0

コマンドラインツールがなくてエラーが出ることがあるらしい。3.5.0をインストールしようとしたら下記のエラーが出た

make: *** [Python/importlib_external.h] Trace/BPT trap: 5
make: *** Waiting for unfinished jobs....
make: *** [Python/importlib.h] Trace/BPT trap: 5

その場合以下を実行

xcode-select --install。

リハッシュしてグローバルに設定。

pyenv rehash
pyenv global 3.5.0

pathの設定をする。下記はbashの例。

echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(pyenv init -)"' >> ~/.bash_profile

下記のコマンドでpyenv下のpythonが帰ってきたら完了。

which python

virtualenv

virtualenvを使うとプロジェクト毎に異なるバージョンのモジュールを使用できるようになる。
pipでインストールできる。

pip install --upgrade pip
pip install virtualenv

下記のようにプロジェクト用の仮想環境を作り、activateする

virtualenv [仮想環境の名前]
source [仮想環境の名前]/bin/activate

これでグローバルではなく、プロジェクト毎の環境にパッケージがインストールされるようになる。

iosアプリの画面遷移をプログラムから呼び出す

プログラムから任意のタイミングでsegueを呼び出したかったが、やり方をググっても何故か中々ヒットしなかったのでメモしておく。
ついでにViewControllerを指定して画面遷移する方法もメモしておく。

Segueを使用して遷移する場合

事前準備

StoryBoard上で、ViewController間をつなぐsegueを作っておく。 作ったsegueのidentifierはちゃんと埋めておく。

コード

以下のコードでsegueを呼び出せる。

performSegue(withIdentifier: "segueのidentifier", sender: self)

またsegueが呼ばれた際の遷移前処理はVieController内で以下のように書ける。

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  // やりたい処理
}

遷移先の画面に何か渡したい時には下記のように利用するらしい。

//segueを呼び出す処理
func callSegue( ) {
    let message = "hogehoge"
    performSegue(withIdentifier: "targetSegue", sender: message)
}
//次のviewのcontrollerに値を渡す
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if (segue.identifier == "targetSegue") {
        let nextViewController : NextViewController = segue.destination as! NextViewController
        nextViewController.item = sender as! String
    }
}

ViewControllerを指定して遷移する場合

プログラムから画面遷移する場合、こっちの方がよく使われるかも

事前準備

storyboard上で遷移先のViewControllerのIdentifierを設定しておく

コード

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let nextView = storyboard.instantiateViewController(withIdentifier: "nextViewController")
let navi = UINavigationController(rootViewController: nextView)
navi.modalTransitionStyle = .coverVertical
self.present(navi, animated: true, completion: nil)

iosアプリの起動時の画面をコントロールする

ログイン状態など条件に応じてrootViewControllerを切り替えたかった。 ちょっと調べたところUIWindowのrootViewControllerを設定すれば良いらしい。

var window: UIWindow?

let storyboard = UIStoryboard(name: "Main", bundle: nil)
if (flag) {
  let mainViewController = storyboard.instantiateViewControllerWithIdentifier("MainViewController")
  let navigationController = UINavigationController(rootViewController: mainViewController)
  self.window!.rootViewController = navigationController
} else {
  let loginViewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController")
  let navigationController = UINavigationController(rootViewController: loginViewController)
  self.window!.rootViewController = navigationController
}

しかし、このようにすると最初のviewが表示される前に一瞬真っ暗な画面が表示されてしまった。
原因はデフォルトのrootViewControllerが設定されておらず、上記のコードの実行前に未設定のrootViewControllerを表示しようとしているためだった。 ひとまずlaunch中に表示するviewを作りデフォルトのrootViewControllerとして設定することで見かけ上は解決した。

launch前にrootViewControllerを設定する方法があればそれを使いたいが、ググっても見つからなかった。 より良い方法が見つかったら追記したい。

NSUserDefaultsによる永続化

iosアプリを停止させても消したくない情報を永続化する方法を調べた。 永続化の方法としてはNSUserDefaults、NSKeyedArchiver、プロパティリスト、CoreDataなどの方法があるらしい。

CoreDataはちょっとしたデータの保存には大変すぎる。 NSKeyedArchiverはシリアライズしたデータをファイルとして保存する方法で、保存する場所を指定する必要がある。 プロパティリストはkey、valueを指定して.pistファイルに情報を保存する方法で、アプリの設定などを扱っていることが多い。

NSUserDefaultsは保存する場所の指定などが不要で、最も手軽そうだったので今回はこれを使う。

使い方

let userDefaults = NSUserDefaults.standardUserDefaults()


//データを保存する
self.userDefaults.setObject("hoge", forKey: "huga")

//データを取り出す
let authData = self.userDefaults.objectForKey("huga") as? String

かなり手軽にデータの保存ができるが上記のコードをそのまま使うと、キーや型をベタ書きすることになり管理がし難い。 ググってみたところ、wrapperを用意して使うのが良さそう。 データがある程度複雑ならプロパティリストやCoreDataを使用するべきだと思う。

また、NSUserDefaultsは消える恐れがあるらしいく、消えてもいいデータに限ってしか使えないようだ。 今回のケースでは問題なかったけど、アプリ中断時に開いていたページの情報とか、トークンのような揮発しても問題ないなものにしか使うべきじゃないのかもしれない。

UIImageをリサイズする

IOSアプリで画像を表示する時、画像サイズに依存せずにViewいっぱいに表示したい。
その際、縦長の画像が縦に押しつぶされた状態になるなど、画像が変にリサイズされて歪んでしまうのは避けたいというコード。

一旦リサイズしさらにトリミングするという処理をしているため、無駄が多いように思える。
もっといい方法がありそうだけ、swift初心者なので思いつかなかった。。。

func fixImageSize(image: UIImage, width: CGFloat, height: CGFloat) -> UIImage{
        let widthRatio = width / image.size.width
        let heightRatio = height / image.size.height
        
        let (resizeWidth, resizeHeight) = heightRatio > widthRatio ? (image.size.width * heightRatio, height):(width, image.size.height * widthRatio)
        
        let (x, y) = ((resizeWidth - width) / 2, (resizeHeight - height) / 2)
        
        let size: CGSize = CGSize(width: resizeWidth, height: resizeHeight)
        UIGraphicsBeginImageContext(size)
        image.drawInRect(CGRectMake(0, 0, resizeWidth, resizeHeight))
        let resizeImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        let trimRect  = CGRectMake(x, y, width, height)
        let trimRef   = CGImageCreateWithImageInRect(resizeImage.CGImage, trimRect)
        let trimImage = UIImage(CGImage: trimRef!)
        
        return trimImage
}