はじめての DynamicMTML #08 - MTSearchEntries (4) MTSplitVars を組み合わせて複数ワードのAND検索

前回は MTSearchEntries タグと MTSplitVars タグを使って複数のキーワードを OR 検索する方法を紹介しました。

今回は、AND 検索をする方法を紹介します。なお、前回の OR 検索の記事をお読みいただいている前提で進めます。

今回のテンプレートの仕様

AND 検索以外は、前回の記事と同様です。

  • 検索ワードはキーワード欄にカンマ区切りで指定する
  • 複数のキーワードは AND 検索
  • 現在のブログ内のブログ記事から検索する
  • 最新5件を表示する
  • 現在のブログ記事を除く

結果をイメージするために

今回のサンプルに使ったブログには、次のような10本のブログ記事が公開されているとします。

この中で「つめ」「dynamicmtml」という2つのキーワードを両方含むブログ記事を、最大5本リストアップするようにします。「つめ」というキーワードは、普通ではあり得ませんが、結果を意図的に操作するためですので気にしないでください。

ブログ記事#10 : これはブログ「はじめての DynamicMTML ブログ」の10番目の記事です。
ブログ記事#09 : これはブログ「はじめての DynamicMTML ブログ」の9番目の記事です。
ブログ記事#08 : これはブログ「はじめての DynamicMTML ブログ」の8番目の記事です。
ブログ記事#07 : これはブログ「はじめての DynamicMTML ブログ」の7番目の記事です。
ブログ記事#06 : これはブログ「はじめての DynamicMTML ブログ」の6番目の記事です。
ブログ記事#05
: これはブログ「はじめての DynamicMTML ブログ」の5つめの記事です。
ブログ記事#04
: これはブログ「はじめての DynamicMTML ブログ」の4つめの記事です。
ブログ記事#03 : 3つ目の記事です。
ブログ記事#02
: これはブログ「はじめての DynamicMTML ブログ」の2つめの記事です。
ブログ記事#01
: これはブログ「はじめての DynamicMTML ブログ」の1つめの記事です。

赤字になっているブログ記事の「01, 02, 04, 05」が検索結果になります。

完成したコード

今回の記事の完成版のコードは次のようになります。強調されている部分が、前回の OR 検索との変更点になります。

※2012-01-23 MTLoop のところを少し修正しました。

<$mtml tag='mt:SetVars'$>
blog_id=<$mt:BlogID$>
entry_id=<$mt:EntryID$>
keywords=<$mt:EntryKeywords$>
keywords_length=<$mt:EntryKeywords regex_replace="/[^,]|\s/g","" cat="," count_characters="1"$>
limit=5
<$mtml tag='/mt:SetVars'$>

<mt:DynamicMTML>
<mt:SplitVars name="keyword" text="$keywords">
  <$mt:Var name="__counter__" setvar="split_counter"$>
  <$mt:Var name="keyword" trim="1" regex_replace="/\s| /g","%" setvar="keyword"$>
  <mt:SearchEntries query="$keyword" blog_id="$blog_id" not_entry_id="$entry_id">
    <$mt:EntryDate format="%Y%m%d%H%M%S" setvar="date"$>
    <mt:If name="split_counter" eq="1">
      <mt:SetVarBlock name="entries" key="$date">
        <li><a href="<$mt:EntryPermalink$>"><$mt:EntryTitle$></a></li>
      </mt:SetVarBlock>
      <$mt:SetVar name="match_count" key="$date" value="1"$>
    <mt:Else>
      <mt:If name="entries" key="$date">
        <$mt:SetVar name="match_count" key="$date" op="++"$>
      </mt:If>
    </mt:If>
  </mt:SearchEntries>
  <mt:If name="split_counter" eq="$keywords_length">
<mt:Loop name="entries">
<mt:If name="match_count" key="$__key__" eq="$keywords_length">
<$mt:SetVar name="result" key="$__key__" value="$__value__"$>
</mt:If>
</mt:Loop>
    <mt:Loop name="result" sort_by="key reverse">
      <mt:If name="__counter__" eq="1">
        <ul>
      </mt:If>
      <mt:If name="__counter__" le="$limit">
        <mt:Var name="__value__">
      </mt:If>
      <mt:If name="__counter__" eq="$limit">
        </ul>
      </mt:If>
    </mt:Loop>
  </mt:If>
</mt:SplitVars>
</mt:DynamicMTML>

以下、変更されている部分を見ていきます。

MTSplitVars のループの1回目とそれ以外で分岐する

<mt:If name="split_counter" eq="1">
  <mt:SetVarBlock name="entries" key="$date">
    <li><a href="<$mt:EntryPermalink$>"><$mt:EntryTitle$></a></li>
  </mt:SetVarBlock>
  <$mt:SetVar name="match_count" key="$date" value="1"$>
<mt:Else>
  <mt:If name="entries" key="$date">
    <$mt:SetVar name="match_count" key="$date" op="++"$>
  </mt:If>
</mt:If>

MTSearchEntries タグの中で、MTSplitVars のループの1回目とそれ以外とで動作を分岐します。変数 split_counter には、MTSplitVars タグのループの回数がセットされています(1から始まる)。この split_counter は、MTSplitVars タグの先頭で初期化してある必要があります(前回記事参照)。

最初のループ(1つめのキーワード)

MTSplitVars タグの最初のループ、つまり1つめのキーワードについては、そのまま MTSearchEntries を回しています。

ここでは、投稿日時(年月日時分秒)がキーであるハッシュ変数 entries に値を入れていきます。今回は AND 検索なので、最終的な検索結果は、この変数 entries から絞り込まれることになります

さらに、同様に投稿日時がキーであるハッシュ変数 match_entries の各値に 1 をセットします。この entries と match_entries のキーを同じにしておくことがポイントとなります(後述)。

※これ以降の解説を読むと、ここでセットするキーは、投稿日時よりもエントリーID の方がしっくりくるのですが、最終的に出力するときに、投稿日時でソートするためにそのようにしています。

2回目以降のループ(2つめのキーワード以降)

MTSplitVars タグの2回目のループ、つまり2つめのキーワード以降で、1つめのキーワードの検索結果の中にそのブログ記事があるかどうかを調べます。

  <mt:If name="entries" key="$date">
    <$mt:SetVar name="match_count" key="$date" op="++"$>
  </mt:If>

もし該当するブログ記事が既に変数 entries にセットされているのであれば、変数 match_entries の値をインクリメント(1増やす)します。

つまり、この変数 match_entries の各キーに対応する値が、キーワードにマッチした回数ということになります。

キーワードにマッチした回数がキーワード数と同じものを出力する

最後に、MTLoop を2回まわしています。

1つめの MTLoop で変数 entries について、ループ中のブログ記事のキーワードにマッチした回数がキーワード数と同じもの、つまりすべてのキーワードにマッチしたものだけを、ハッシュ変数 result にセットします。このとき、ハッシュ変数 result の key と value は、元のハッシュ変数 entries の $__key__ と $__value__ を引き継ぎます。

<mt:Loop name="entries">
<mt:If name="match_count" key="$__key__" eq="$keywords_length">
<$mt:SetVar name="result" key="$__key__" value="$__value__"$>
</mt:If>
</mt:Loop>

ここで見て分かるように、entries と match_entries のキーを同じにしておけば、entries のループの時に、キー( __key__ )をそのまま match_entries のキーとして渡すことで、そのブログ記事のマッチ回数を調べることができます。

このマッチ回数がキーワード数(keyword_length : 先頭の MTSetVars でセット。こちらを参照)と同じであれば AND 検索の結果に該当(すべてのキーワードがマッチ)することになります。

そして、2つめの MTLoop で、先頭の MTSetVars でセットした limit の数だけ出力します。

以上です。複雑ですね。

  • このエントリーをはてなブックマークに追加
Just a second...