@johtaniの日記 2nd

@johtani ‘s blog 2nd edition

スキーマレスモード?(SOLR-4897)を調べて見ました。

Solr 4.4に取り込まれる予定のチケットで、気になるものを見つけたのでいつものごとく調べてみました。

元となるチケットはこちら。SOLR-4897

スキーマレス?

Solrはschema.xmlにデータの定義(フィールドタイプやフィールドなど)を記述して、データを登録する全文検索システムです。 これまでのSolrではこの設定ファイルを元にデータを登録するフィールド名を決定しており、 変更を行う場合はSolrのコアを再起動するなどの手順が必要でした。(※ダイナミックフィールドはすこし特殊)

それだと、Solrを管理するのがめんどくさいですね?という感じで現れたのがSchemaREST APIです。(たぶん。)

Schema REST API

Solr 4.2から導入されたSolrのスキーマに関する情報を提供するためのREST APIです。 4.2で導入されたのはあくまでもschema.xmlの情報を取得するためのAPIでした。 たとえば、Fieldの一覧を取得するとか。

4.4から、フィールドの追加(変更、削除はできない)ができるようになりました。あくまでも、フィールドの追加で、フィールドタイプなどの追加はまだできません。(できるようになるのかもわからないですが。) フィールドの追加方法などはWikiに記載がありました。

ということで、簡単に試してみることに。

起動方法

exampleディレクトリの下にexample-schemalessというディレクトリが新設されています。 ここに、スキーマレスモード用の設定がされているファイルが入っているので、こちらを利用します。

1
2
cd $SOLR/example
java -Dsolr.solr.home=example-schemaless/solr -jar start.jar

ログにいくつかWARNが出ますが、影響の内パス設定ミスなので無視してOKです。

最初に定義されているフィールドは「id」と「_version_」のみになります。(Schema Browserなどで確認できます。あ、REST APIでもいいですね。http://localhost:8983/solr/schema/fields

スキーマの更新

さて、フィールドを追加してみます。 PUTを利用すると1フィールドの追加が可能です。 「fugatext」というフィールド名でフィールドを追加しています。今のところJSONのみ対応みたいです。

1
2
3
4
5
$ curl -X PUT http://localhost:8983/solr/schema/fields/fugatext -H 'Content-Type: application/json' -d '{"type":"text_ja","stored":false,"multiValued":true}'
{
  "responseHeader":{
    "status":0,
    "QTime":18}}

追加できたかどうかもREST APIで取得してみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ curl http://localhost:8983/solr/schema/fields
{
  "responseHeader":{
    "status":0,
    "QTime":0},
  "fields":[{
      "name":"_version_",
      "type":"long",
      "indexed":true,
      "stored":true},
    {
      "name":"fugatext",
      "type":"text_ja",
      "multiValued":true,
      "stored":false},
    {
      "name":"id",
      "type":"string",
      "multiValued":false,
      "indexed":true,
      "required":true,
      "stored":true,
      "uniqueKey":true}]}

追加できました。 ちなみに、同じフィールド名を追加しようとするとエラーが帰ってきます。

1
2
3
4
5
6
7
8
$ curl -X PUT http://localhost:8983/solr/schema/fields/fugatext -H 'Content-Type: application/json' -d '{"type":"text_ja","stored":false,"multiValued":true}'
{
  "responseHeader":{
    "status":400,
    "QTime":1},
  "error":{
    "msg":"Field 'fugatext' already exists.",
    "code":400}}

設定の違い

example-schemalessのsolrconfig.xmlは以下の設定が通常のexampleとは異なるようです。

schemaFactoryの設定

schemaをAPIから変更可能にする設定です。これまでの変更しない設定の場合はClassicIndexSchemaFactoryを指定します。

1
2
3
4
5
6
...
  <schemaFactory class="ManagedIndexSchemaFactory">
    <bool name="mutable">true</bool>
    <str name="managedSchemaResourceName">managed-schema</str>
  </schemaFactory>
...

update.chainの設定

更新処理(update関連のリクエストハンドラ「/update」とか)には次のような設定が追加されていました。(1006行目あたり)

1
2
3
4
5
6
7
8
9
  <requestHandler name="/update" class="solr.UpdateRequestHandler">
    <!-- See below for information on defining 
         updateRequestProcessorChains that can be used by name 
         on each Update Request
      -->
    <lst name="defaults">
      <str name="update.chain">add-unknown-fields-to-the-schema</str>
    </lst>
  </requestHandler>

「add-unknown-fields-to-the-schema」というupdate.chainが指定されています。このchainの定義自体は1669行目くらいに存在します。 (長い。。。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  <!-- Add unknown fields to the schema 
  
       An example field type guessing update processor that will
       attempt to parse string-typed field values as Booleans, Longs,
       Doubles, or Dates, and then add schema fields with the guessed
       field types.  
       
       This requires that the schema is both managed and mutable, by
       declaring schemaFactory as ManagedIndexSchemaFactory, with
       mutable specified as true. 
       
       See http://wiki.apache.org/solr/GuessingFieldTypes
    -->
  <updateRequestProcessorChain name="add-unknown-fields-to-the-schema">
    <processor class="solr.RemoveBlankFieldUpdateProcessorFactory"/>
    <processor class="solr.ParseBooleanFieldUpdateProcessorFactory"/>
    <processor class="solr.ParseLongFieldUpdateProcessorFactory"/>
    <processor class="solr.ParseDoubleFieldUpdateProcessorFactory"/>
    <processor class="solr.ParseDateFieldUpdateProcessorFactory">
      <arr name="format">
        <str>yyyy-MM-dd'T'HH:mm:ss.SSSZ</str>
        <str>yyyy-MM-dd'T'HH:mm:ss,SSSZ</str>
        <str>yyyy-MM-dd'T'HH:mm:ss.SSS</str>
        <str>yyyy-MM-dd'T'HH:mm:ss,SSS</str>
        <str>yyyy-MM-dd'T'HH:mm:ssZ</str>
        <str>yyyy-MM-dd'T'HH:mm:ss</str>
        <str>yyyy-MM-dd'T'HH:mmZ</str>
        <str>yyyy-MM-dd'T'HH:mm</str>
        <str>yyyy-MM-dd HH:mm:ss.SSSZ</str>
        <str>yyyy-MM-dd HH:mm:ss,SSSZ</str>
        <str>yyyy-MM-dd HH:mm:ss.SSS</str>
        <str>yyyy-MM-dd HH:mm:ss,SSS</str>
        <str>yyyy-MM-dd HH:mm:ssZ</str>
        <str>yyyy-MM-dd HH:mm:ss</str>
        <str>yyyy-MM-dd HH:mmZ</str>
        <str>yyyy-MM-dd HH:mm</str>
        <str>yyyy-MM-dd</str>
      </arr>
    </processor>
    <processor class="solr.AddSchemaFieldsUpdateProcessorFactory">
      <str name="defaultFieldType">text_general</str>
      <lst name="typeMapping">
        <str name="valueClass">java.lang.Boolean</str>
        <str name="fieldType">booleans</str>
      </lst>
      <lst name="typeMapping">
        <str name="valueClass">java.util.Date</str>
        <str name="fieldType">tdates</str>
      </lst>
      <lst name="typeMapping">
        <str name="valueClass">java.lang.Long</str>
        <str name="valueClass">java.lang.Integer</str>
        <str name="fieldType">tlongs</str>
      </lst>
      <lst name="typeMapping">
        <str name="valueClass">java.lang.Number</str>
        <str name="fieldType">tdoubles</str>
      </lst>
    </processor>
    <processor class="solr.LogUpdateProcessorFactory"/>
    <processor class="solr.RunUpdateProcessorFactory"/>
  </updateRequestProcessorChain>

使ってるUpdateProcessorはこんな感じみたいです。最後の2つはこれ用じゃないので省略。

プロセッサ名説明
RemoveBlankFieldUpdateProcessorFactory値がないフィールドは除去
ParseBooleanFieldUpdateProcessorFactoryスキーマに定義されていないフィールドで、値がBooleanとしてパースできたら、Boolean型とする。
ParseLongFieldUpdateProcessorFactoryスキーマに定義されていないフィールドで、値がLongとしてパースできたら、Long型とする。
ParseDoubleFieldUpdateProcessorFactoryスキーマに定義されていないフィールドで、値がDoubleとしてパースできたら、Double型とする。
ParseDateFieldUpdateProcessorFactoryスキーマに定義されていないフィールドで、値がDateとしてパースできたら、Date型とする。(パースの形式がformatで列挙されてる)
AddSchemaFieldsUpdateProcessorFactory入力されたドキュメントの中でスキーマに定義されていないフィールド(静的、動的両方)を見つけた時に、フィールドの値の型を元にフィールド型をマッピングする。

とここまで見てきたところで、スキーマレスという名前の意図がちょっとわかったかも。

定義されてないフィールドを持ったデータを登録

起動時には定義されてないフィールドをもったデータを登録してみます。 boolean型で試してみることに。以下のデータを管理画面のデータ登録画面から登録します。(http://localhost:8983/solr/#/collection1/documents%EF%BC%89 (タイトルでbooleanってわかりにくいですが)

1
{"id":"change.me","title":true, "price":1.25, "fuga":"100,200"}

エラーは出ません。で、またフィールド一覧を取得すると。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ curl http://localhost:8983/solr/schema/fields
{
  "responseHeader":{
    "status":0,
    "QTime":1},
  "fields":[{
      "name":"_version_",
      "type":"long",
      "indexed":true,
      "stored":true},
    {
      "name":"fuga",
      "type":"tlongs"},
    {
      "name":"fugatext",
      "type":"text_ja",
      "multiValued":true,
      "stored":false},
    {
      "name":"id",
      "type":"string",
      "multiValued":false,
      "indexed":true,
      "required":true,
      "stored":true,
      "uniqueKey":true},
    {
      "name":"price",
      "type":"tdoubles"},
    {
      "name":"title",
      "type":"booleans"}]}

おー、最後にtitleが追加されてます。他にもfugaやpriceも。(日付は手を抜きました。。。)

感想

詳細までは追いかけてないですが、こんなかんじです。 フィールド追加が可能になるのはいいんじゃないでしょうか。SolrCloudの機能との関連もあるのかもしれません。ZooKeeperへの出力も実装されてそうなので。

ただ、機械的に出力されたschema.xml(exampleだとmanaged-schemaというファイル)には「DO NOT EDIT」との記述があるので、修正するとなにかおきてしまうかもしれないですねぇ。 現時点では、フィールドタイプの変更やフィールドの更新、削除に関してはSolrCoreの再起動などの手順が必要です。 あと、変なデータ(タイプミスとか)が登録されたりしないかってのは気になりますね。

※ちなみに、別の人が気づいたんですが、ちょっとバグが有ったみたいで、代わりにチケットつくったらキリ番(SOLR-5000) ゲットしましたw

Comments