Amazon国別サイト横断検索のために - Apache Solr (その2)
前回で Solr が使用可能になったので、今回は検索で取得したデータを登録してみる。
対象データ
以下の条件で取得したデータ(2013-08-15時点で25件)を利用する。日本語も入れとかないとなあ、ということで Artist=カステラ としてみた。
http://ecs.amazonaws.jp/onca/xml ?Service=AWSECommerceService &Version=2011-08-01 &Operation=ItemSearch &AssociateTag=kahnn-22 &SearchIndex=Music &Artist=カステラ &Sort=titlerank &Availability=Available &ResponseGroup=ItemAttributes,Tracks &ItemPage=1
スキーマ設計
Solrの方は、example ディレクトリを利用し、example/solr/collection1/conf/schema.xml を変更したもので試す。
まずは商品データにあったスキーマ設計が必要となる。最初から全部の要素を扱うのは大変なので、幾つか特徴的な要素を選んでみたのが以下の表になる。
項目 | フィールド名 | 型 | indexed | stored | その他 |
---|---|---|---|---|---|
ID(post時にASINの値をそのまま利用) | id | long | true | true | required="true", uniqueKey |
VERSION | _version_ | long | true | true | |
Timestamp | timestamp | date | true | true | default="NOW" |
ASIN | asin | string | true | true | required="true" |
EAN | ean | string | true | true | |
DetailPageURL(商品詳細ページのURL) | detail_url | string | false | true | |
Title | title | text_ja | true | true | |
Artist | artist | text_ja | true | true | multiValued="true" |
Label | label | text_ja | true | true | |
ReleaseDate | release_date | date | true | true | |
ListPrice/FormattedPrice(国別に整形された値段表示) | formatted_price | string | false | true | |
ListPrice/Amount(値段の数字(単位無視)) | price | long | true | true |
この設計に従って schema.xml を以下のように記述した。fieldType についてはサンプルの通りで、比較用に text_cjk type の CopyField である text を用意している以外は、ほとんど上記設計のままとなっている。
<?xml version="1.0" encoding="UTF-8" ?> <schema name="amazon_product_sample" version="1.5"> <types> <fieldType name="string" class="solr.StrField" sortMissingLast="true" /> <fieldType name="long" class="solr.TrieLongField" precisionStep="0" positionIncrementGap="0"/> <fieldType name="date" class="solr.TrieDateField" precisionStep="0" positionIncrementGap="0"/> <fieldType name="text_ja" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="false"> <analyzer> <tokenizer class="solr.JapaneseTokenizerFactory" mode="search"/> <filter class="solr.JapaneseBaseFormFilterFactory"/> <filter class="solr.JapanesePartOfSpeechStopFilterFactory" tags="lang/stoptags_ja.txt" /> <filter class="solr.CJKWidthFilterFactory"/> <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_ja.txt" /> <filter class="solr.JapaneseKatakanaStemFilterFactory" minimumLength="4"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType> <fieldType name="text_cjk" class="solr.TextField" positionIncrementGap="100"> <analyzer> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.CJKWidthFilterFactory"/> <filter class="solr.LowerCaseFilterFactory"/> <filter class="solr.CJKBigramFilterFactory"/> </analyzer> </fieldType> </types> <fields> <field name="id" type="string" indexed="true" stored="true" required="true" /> <field name="_version_" type="long" indexed="true" stored="true" /> <field name="timestamp" type="date" indexed="true" stored="true" default="NOW" /> <field name="asin" type="string" indexed="true" stored="true" required="true" /> <field name="ean" type="string" indexed="true" stored="true" /> <field name="detail_url" type="string" indexed="false" stored="true" /> <field name="title" type="text_ja" indexed="true" stored="true" /> <field name="artist" type="text_ja" indexed="true" stored="true" /> <field name="label" type="text_ja" indexed="true" stored="true" /> <field name="release_date" type="date" indexed="true" stored="true" /> <field name="formatted_price" type="string" indexed="false" stored="true" /> <field name="price" type="long" indexed="true" stored="true" /> <field name="text" type="text_cjk" indexed="true" stored="false" multiValued="true" /> </fields> <uniqueKey>id</uniqueKey> <copyField source="title" dest="text" /> <copyField source="artist" dest="text" /> <copyField source="label" dest="text" /> </schema>
- 余談1
- Solr4.4の schema.xml を見てると多言語対応周りが強化されていて 3.6より前の情報と随分異なる。これからさわる人にとっては随分と楽になっているという訳で、先人の貢献に感謝。
- 余談2
- 複数の CopyField としてフィールド名 "text" を使用しているが、この "text" フィールドが無いと、起動時にエラーが出る。example/solr/collection1/conf/solrconfig.xml では、"text" フィールドが有ることを前提にして各種設定が書かれており、これらを修正するくらいならば、schema で用意した方が楽。(まあ、本格的に使用する場合は、ちゃんと吟味すべきでしょうけど)
データ加工と登録
次に商品データを登録する必要があるが、post.jar を 使って登録する場合、Solr で追加する際の型式に合わせてやる必要がある。今回は xslt で所定の xml 型式に変換する事にした。
完成イメージは以下のようになる。文法については UpdateXmlMessages を参照。
<add> <doc> <field name="id">XXXXXXXXX</field> <field name="asin">XXXXXXXXX</field> <field name="detail_url">http://......</field> <field name="ean">XXXXXXXXXX</field> <field name="title">世界の娯楽</field> <field name="artist">カステラ</field> <field name="label">SME</field> <field name="release_date">1991-11-12</field> <field name="formatted_price">¥1,000</field> <field name="price">1000</field> </doc> </add>
そのための xslt スクリプト(amazon-to-solr.xsl)は以下の通り。
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:aws="http://webservices.amazon.com/AWSECommerceService/2011-08-01" version="1.0"> <xsl:output method="xml" encoding="UTF-8"/> <!-- Root Node --> <xsl:template match="/"> <add> <xsl:apply-templates select="aws:ItemSearchResponse/aws:Items/aws:Item"/> </add> </xsl:template> <xsl:template match="aws:ItemSearchResponse/aws:Items/aws:Item"> <doc> <field name="id"><xsl:value-of select="aws:ASIN" /></field> <field name="asin"><xsl:value-of select="aws:ASIN" /></field> <field name="detail_url"><xsl:value-of select="aws:DetailPageURL" /></field> <field name="ean"><xsl:value-of select="aws:ItemAttributes/aws:EAN" /></field> <field name="title"><xsl:value-of select="aws:ItemAttributes/aws:Title" /></field> <field name="artist"><xsl:value-of select="aws:ItemAttributes/aws:Artist" /></field> <field name="label"><xsl:value-of select="aws:ItemAttributes/aws:Label" /></field> <field name="release_date"><xsl:value-of select="aws:ItemAttributes/aws:ReleaseDate" />T00:00:00Z</field> <xsl:apply-templates select="aws:ItemAttributes/aws:ListPrice" /> </doc> </xsl:template> <xsl:template match="aws:ItemAttributes/aws:ListPrice"> <xsl:element name="field"> <xsl:attribute name="name">formatted_price</xsl:attribute> <xsl:value-of select="aws:FormattedPrice" /> </xsl:element> <xsl:element name="field"> <xsl:attribute name="name">price</xsl:attribute> <xsl:value-of select="aws:Amount" /> </xsl:element> </xsl:template> </xsl:stylesheet>
Macには xsltproc が入っているので、以下のようにして変換可能。なお、amazon-data-1.xmlはamazonから取得した商品xmlデータ。
$ xsltproc amazon-to-solr.xsl amazon-data-1.xml > solr-data-1.xml
後は post すればよい。
$ cd example/exampledocs/ $ java -jar post.jar ..../solr-data-*.xml
collection1 の Overviewで見ると、ドキュメントが25件登録された。これで準備OK。
フィールドの設定と解析状況については、Analysisから色々とキーワードを入力して試すことが出来る。text_cjk, text_ja の違いなどもわかりやすい。
実際の検索結果については、Queryから検索キーを入力して試すことが出来る。
検索による商品の確認
例えば、'artist:カステラ AND title:新世界' で検索したとする。なお、scoreとリリース日付でソートして、json型式で取得する。
$ curl 'http://localhost:8983/solr/collection1/select/' -d 'indent=on' \ -d 'fl=score,asin,title,artist,price,label,release_date' -d 'wt=json' \ -d 'sort=score+desc,release_date+desc' --data-urlencode 'q=artist:カステラ AND title:新世界' { "responseHeader":{ "status":0, "QTime":29, "params":{ "sort":"score desc,release_date desc", "fl":"score,asin,title,artist,price,label,release_date", "indent":"on", "q":"artist:カステラ AND title:新世界", "wt":"json"}}, "response":{"numFound":4,"start":0,"maxScore":2.792567,"docs":[ { "asin":"B00005G4UJ", "title":"新世界", "artist":"カステラ", "label":"エピックレコードジャパン", "release_date":"1998-03-21T00:00:00Z", "price":2039, "score":2.792567}, { "asin":"B000064QIH", "title":"新世界", "artist":"カステラ", "label":"ソニーレコード", "release_date":"1993-11-21T00:00:00Z", "price":2854, "score":2.792567}, { "asin":"B00005G4UF", "title":"世界の娯楽", "artist":"カステラ", "label":"エピックレコードジャパン", "release_date":"1998-03-21T00:00:00Z", "price":2039, "score":1.0660849}, { "asin":"B000UUPTKG", "title":"世界の娯楽", "artist":"カステラ", "label":"株式会社ソニー・ミュージックレコーズ", "release_date":"1989-09-01T00:00:00Z", "score":1.0660849}] }}
これで4つヒットしたわけだけど、タイトルとしては2つ。ここまで取得できれば、title と artist でマージするのは簡単だろう。その上で、「新世界」については asin=B00005G4UJ の 2039円 の方をお薦めするとかができそうである。同様に、「世界の娯楽」については price が設定されている*1 asin= B00005G4UF を薦めれば良い。
他にも色々と試してみたが、Amazonからの検索データをそのまま使うのではなく、キャッシュも兼ねた Solr に一度格納してから最適化するというのは結構使えそうに感じた。
参考
Solrはドキュメントも結構そろっているのだけど、やりたいことに辿り着くのが意外と難しくて、最初は慣れないこともあって苦労した。この書籍は対象が1.4と古いものの、概要や基本的な考え方を抑えるのに便利だと思う。正直、最初にこれを読んでから取り組めば良かった。(買って読んだのは設定をいじりだしてから)
4.4 では色々と変更も多いので、新版がでると良いんですけどね。
*1:設定があるかどうかだけなら、検索条件に "AND +price:*" を追加しても良い