Rhythm & Biology

Engineering, Science, et al.

Solrに入門する

文書検索のシステムを導入しようとしており、詳しい人に相談したら、まずはSolr試そうという話でまとまって帰って来た。

今回の要件にSolrはマッチしないだろうな、というなんとなくの両者コンセンサスはあるが、仮にマッチしたら楽で済むし、マッチしないにしても比較対象としてSolrを持っておくのは良い。

とにかく、Solrを一切触ったことがない状態であるのがいまの自分の問題なので、まずは使い始めて試行錯誤できる状態までもっていく。

インストール・起動

まずmacにSolrをインストール。バージョンは7.3.1。

$ brew info solr
solr: stable 7.3.1
...(略)...

$ brew install solr

$ solr version
7.3.1

Solrのstandalone serverを起動。Javaのバージョンは10を利用。

$ java -version
java version "10.0.1" 2018-04-17
Java(TM) SE Runtime Environment 18.3 (build 10.0.1+10)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.1+10, mixed mode)

$ solr start

Solrが起動できたらブラウザでアクセスする。デフォルトでは8983番ポートで起動している。
http://localhost:8983/

※ standalone以外にいくつか構成がとれるらしい。今回はその詳しいところは踏み込まないようにして、一番シンプルなstandaloneにしている。
検索システムだって高可用性にしたい!SolrCloudを用いた高可用性構成の紹介 - Start Today Technologies TECH BLOG

文書の登録

コア?コレクション?

文書を登録するには、まずコアまたはコレクションと呼ばれるものを作らないといけないらしい。

Solrにはコアとコレクションという概念があり、ドキュメント上はこう説明されている。

Collection

In Solr, one or more Documents grouped together in a single logical index using a single configuration and Schema.

In SolrCloud a collection may be divided up into multiple logical shards, which may in turn be distributed across many nodes, or in a Single node Solr installation, a collection may be a single Core.

Core

An individual Solr instance (represents a logical index). Multiple cores can run on a single node. See also SolrCloud.

Solr Glossary | Apache Solr Reference Guide 7.3

なんとなくは分かるが・・・。

stackoverflowに書かれている内容が、なんとなくの今の理解とあってるので、いったん「standalone構成ではcoreとcollectionは同一」と捉えておく。SolrのWeb UI上でも「Add Core」しかなくて「Add Collection」はない。今は深入りしない。
lucene - Solr Collection vs Cores - Stack Overflow

コアの作成

コアを「testcore」という名前で作る。warningが出るが無視。

$ solr create_core -c testcore
WARNING: Using _default configset with data driven schema functionality. NOT RECOMMENDED for production use.
         To turn off: bin/solr config -c testcore -p 8983 -property update.autoCreateFields -value false

Created new core 'testcore'

文書の登録

Solrインストール時にサンプルの文書がついてくるので、それを利用する。

$ post -c testcore /usr/local/Cellar/solr/7.3.1/example/exampledocs/*.xml
...(中略)...
COMMITting Solr index changes to http://localhost:8983/solr/testcore/update...
Time spent: 0:00:00.884

検索

Solr TutorialのBasic Searchingを試す。全ドキュメント(のうちの最初の10件)を取り出している。
http://lucene.apache.org/solr/guide/7_3/solr-tutorial.html#tutorial-searching

$ curl 'http://localhost:8983/solr/testcore/select?q=*:*'
{
  "responseHeader":{
    "status":0,
    "QTime":0,
    "params":{
      "q":"*:*"}},
  "response":{"numFound":32,"start":0,"docs":[
      {
...(中略)...
  }}

次いで、Search for a Single Termを試す。クエリの単語が含まれる文書すべてが返却されてくるはず、、、が、レスポンス0件。
http://lucene.apache.org/solr/guide/7_3/solr-tutorial.html#search-for-a-single-term

$ curl 'http://localhost:8983/solr/testcore/select?q=foundation'
{
  "responseHeader":{
    "status":0,
    "QTime":0,
    "params":{
      "q":"foundation"}},
  "response":{"numFound":0,"start":0,"docs":[]
  }}

原因不明のまま、いったんここで打ち止め。

その他

solrコマンドのヘルプを見る。

$ solr

Usage: solr COMMAND OPTIONS
       where COMMAND is one of: start, stop, restart, status, healthcheck, create, create_core, create_collection, delete, version, zk, auth, assert, config

  Standalone server example (start Solr running in the background on port 8984):

    ./solr start -p 8984

  SolrCloud example (start Solr running in SolrCloud mode using localhost:2181 to connect to Zookeeper, with 1g max heap size and remote Java debug options enabled):

    ./solr start -c -m 1g -z localhost:2181 -a "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044"

Pass -help after any COMMAND to see command-specific usage information,
  such as:    ./solr start -help or ./solr stop -help

solrの状態を確認する。

$ solr status

Found 1 Solr nodes:

Solr process 22634 running on port 8983
{
  "solr_home":"/usr/local/Cellar/solr/7.3.1/server/solr",
  "version":"7.3.1 ae0705edb59eaa567fe13ed3a222fdadc7153680 - caomanhdat - 2018-05-09 09:30:57",
  "startTime":"2018-06-02T06:17:57.876Z",
  "uptime":"0 days, 0 hours, 7 minutes, 42 seconds",
  "memory":"59.4 MB (%12.1) of 490.7 MB"}

今後

Search for a Single Termのレスポンス0件問題の解決を試みる。

SolrのチュートリアルはSolrCloud前提で書かれているので、standaloneではなくSolrCloud構成で試してみるか。

あと、登録する文書のフォーマットについても調査する。

Swift: typealiasのスコープ

Type Aliasのスコープってどうなってるんだろうという単純な疑問。

ドキュメントを見ても書いてない(見てるところが違う?)。
The Swift Programming Language (Swift 4.0.3): Declarations

通常の変数と同じスコープ管理されてるだろうという仮説のもと、playgroundで試してみる。

do {
    typealias ColorCode = UInt8
    let r = ColorCode.max
    print(r)
}

let g = ColorCode.min
print(g)

エラーが出る。

Playground execution failed:

error: TypeAlias.playground:5:9: error: use of unresolved identifier 'ColorCode'
let g = ColorCode.min
        ^~~~~~~~~

予想通りの結果となった。
Type Aliasに限らず、Declarationsは同じスコープ管理がされているようだ。

The Swift Programming Language (Swift 4.0.3)をざっと読む限りscopeの定義が見当たらないのだけど、暗黙の共通認識という扱いなのだろうか。

2018年 Rustチャレンジ

2018年に新しく学ぶ言語としてRustにチャレンジしてみる。

Rustのドキュメント(邦訳)を見ながら進める。
http://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/

ドキュメントの本家はこっち。
https://doc.rust-lang.org/book/

環境構築

macOS Sierra上に環境構築する。

Rustの開発ツールはHomebrewで簡単にインストールできる。
rustccargoといったコマンドがインストールされる。

$ brew install rust

新規プロジェクト作成

Cargoを利用してプロジェクトを作成する。
バイナリを作成するため--binオプションをつける。

$ cargo new rustlang-intro --bin
$ cd rustlang-intro/
$ cargo build
$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/rustlang-intro`
Hello, world!

リリースビルドをするには--releaseオプションをつける。
すると、target/releaseの下に成果物が作成される。

$ cargo build --release

標準入力

標準入力で受けた文字列を標準エラー出力に表示する。

use std::io;

fn print_stdin() {
    let mut line = String::new();
    let _ = io::stdin().read_line(&mut line);
    eprintln!("{}", line.trim());
}

乱数生成

randクレートを利用して乱数を生成・表示する。

まず、Cargo.tomlに依存関係を記述する。

[dependencies]
rand = "0.4.1"

10回乱数を生成して表示する。

extern crate rand;

fn print_random_numbers() {
    for _ in 0..10 {
        let n = rand::random::<i32>();
        println!("{}", n)
    }
}

こう書くこともできる。

extern crate rand;

use rand::Rng;

fn print_random_numbers() {
    let mut rng = rand::thread_rng();
    for _ in 0..10 {
        let n = rng.gen::<i32>();
        println!("{}", n)
    }
}

構造体

構造体を作る。メソッド実装にはimplブロックを利用する。
newメソッドで構造体初期化を行なっているが、メソッド名は慣習でしかなく、名前はなんでも良い。
また、このようにselfを引数に持たないメソッドは関連関数(associated function)と呼ばれるが、他の言語で言う静的メソッド(static method)に該当するものと思っておけば良い。

struct Person {
    name: String,
}

impl Person {
    fn new(name: &str) -> Person {
        Person {
            name: name.to_string(),
        }
    }

    fn get_name(&self) -> &String {
        &self.name
    }
}

fn main() {
    let p = Person::new("person's name");
    println!("{}", p.get_name());
}

ちなみに、以下のようなコードを書いてしまうとエラーになる。
借り物(self)から勝手に所有権をmoveできないということ。
上のコードのように参照で返すようにしなければコンパイルが通らない。

    fn get_name(&self) -> String {
        self.name
    }
error[E0507]: cannot move out of borrowed content
  --> src/main.rs:33:9
   |
33 |         self.name
   |         ^^^^ cannot move out of borrowed content

トレイト

trait Hello {
    fn say(&self);
}

impl Hello for Person {
    fn say(&self) {
        println!("Hello {}", self.name);
    }
}

fn main() {
    let h = Person::new("trait");
    h.say();
}

マクロ

引数なし版と引数あり版の簡単なものを作ってみる。

macro_rules! say_hello {
    () => {
        println!("Hello");
    };
    ($x:expr) => {
        println!("Hello {}", $x);
    };
}

fn main() {
    say_hello!();
    say_hello!("name");
}

テスト

テストを書いてみる。
慣習的にテストコードを入れる専用のモジュールを作っておくらしい。cargo testのときだけコンパイルされる条件付きコンパイルのフラグをつけておく。

fn add(x: i64, y: i64) -> i64 {
    return x + y;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        assert_eq!(10, add(4, 6));
    }
}
$ cargo test
...(中略)...
running 1 test
test tests::test_add ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

まとめ

環境構築から基本的な記述まで試すことができた。
借用の概念がまだ自分の中で綺麗に整理できていないのでもう少し資料を漁りながら試してみることにする。
今度はマルチスレッドでHTTPサーバでも実装してみる。