Sitecore 7 以降で標準で組み込まれた サイトコアサーチ API を使用することでさまざまな検索条件でアイテムを柔軟に検索することができます。Sitecore Search API はLINQが提供されており、LINQ式を使用して動的に検索条件を組み立てることができます。

Where 拡張関数を繰り返し使用することで、検索条件をコンパイル時ではなく実行時に 動的に組み立てることが簡単に行えます。

例えば次のような検索条件をコンパイル時に作成できます。

var query = context.GetQueryable<SearchResultItem>().Where(i => i.Name.Contains("home") && i.Name.Contains("test") && i.Name.Contains("sample"));

上記式を実行時に作成する場合は、以下のように記述できます。

// 実行時に決まるフィルタ条件
string[] dynamicCondition = new string[] { "home", "test", "sample" };
var query = context.GetQueryable<SearchResultItem>();

foreach (var term in dynamicCondition)
{
    query = query.Where(i => i.Name.Contains(term));
}

Where 拡張関数を繰り返して適用して検索条件を作成した場合 各条件は AND 条件により結合されます。

そこで、今回は PredicateBuilder というヘルパークラスを使用して AND や OR を含む複雑な検索条件を動的に作成する方法のサンプルを記載します。

検証環境

  • Sitecore 7.2 Update 2
  • Visual Studio 2013

PredicateBuildeerに関する詳細に関しては、以下のブログの記事も参考になります。

Sitecore 7 Predicate Builder
http://www.sitecore.net/Learn/Blogs/Technical-Blogs/Sitecore-7-Development-Team/Posts/2013/05/Sitecore-7-Predicate-Builder.aspx

1.いろいろな検索条件を PredicateBuilder で作成してみる

まず、以下のように動的に検索条件を組み立てるサンプルソースを作成しました。

    using System.Linq;
    using System;
    using Sitecore.ContentSearch;
    using Sitecore.ContentSearch.Linq;
    using Sitecore.ContentSearch.SearchTypes;
    using Sitecore.ContentSearch.Linq.Utilities;
    using System.Collections.Generic;

    public partial class SearchSample : System.Web.UI.UserControl
    {
        /// <summary>
        /// PredicateBuilder クラスは下記名前空間で定義されています。
        /// Sitecore.ContentSearch.Linq.Utilitiesで定義意されています。
        /// </summary>
        private void SearchWithPredicateBuilder()
        {
            // 仮想的な動的検索を想定した検索条件準備
            Dictionary<string, string> conditions = new Dictionary<string, string>{
                {Sitecore.ContentSearch.BuiltinFields.Name, "home"},
                {Sitecore.ContentSearch.BuiltinFields.TemplateName, "Sample Item"},
                {"title", "value"}
            };
            
            // 検索処理
            var item = new SitecoreIndexableItem(Sitecore.Context.Item);
            using (var context = Sitecore.ContentSearch.ContentSearchManager.GetIndex(item).CreateSearchContext())
            {
                var query = context.GetQueryable<SearchResultItem>();
                foreach (var cond in conditions)
                {
                    // テキストフィールドの部分一致検索
                    query = query.Where(i => ((string)i[cond.Key]).Contains(cond.Value)); 
                    // 検索語で検索ほかの例
                    //query = query.Where(i => i[cond.Key] == cond.Value);
                }
                var result = query.GetResults();

            }
        }
    }

サンプルソースはまだ PredicateBuilder を使用せずに検索条件を作成した例になります。上記サンプルの場合 query.GetResults() を呼び出したタイミングで以下のような検索条件でインデックス検索が行われます。

q=((_name:(*home*) AND _templatename:(*Sample Item*)) AND title_t:(*value*))

それでは PredicateBuilder を使用して同じ検索条件を作成するプログラムを作成します。ちなみにサンプルソース上にも載せていますが、PredicateBuilderクラスは Sitecore.ContentSearch.Linq.Utilities 名前空間に定義されています。

                // PredictBuilderを使用して同様のクエリーを生成する
                // AND条件で作成するのでまず、必ずTrueを返すExpression作成する
                var predict1 = PredicateBuilder.True<SearchResultItem>();
                foreach (var cond in conditions)
                {
                    // AND条件からなるExpressionを作成
                    predict1 = predict1.And(i => ((string)i[cond.Key]).Contains(cond.Value));
                }
                var result1 = context.GetQueryable<SearchResultItem>().Where(predict1).GetResults();

ANDで検索条件を組み立てるため、まずTrue拡張関数を使用して必ずTrueを返すExpressionを作成します。あとはAND条件で各条件を結合していきます。サンプルソースを実行すると次のようなインデックス検索クエリが作成されます。

q=((_name:(*home*) AND _templatename:(*Sample Item*)) AND title_t:(*value*))

PredicateBuilderを使わない方法と同じインデックス検索条件が作成されていることがわかります。

今度はそれぞれの検索条件を OR で結合するサンプルを以下に掲載します。

                // ANDではなくOR条件で検索条件が結合して作成されるようにしてみる
                // 複数の検索条件がORで結合されるため、まず必ずFalseを返すExpressionを作成する
                var predict2 = PredicateBuilder.False<SearchResultItem>();
                foreach (var cond in conditions)
                {
                    // OR条件からなるExpressionを作成
                    predict2 = predict2.Or(i => ((string)i[cond.Key]).Contains(cond.Value));
                }
                // 生成されるクエリー:q=(_name:(*home*) OR _templatename:(*Sample Item*) OR title_t:(*value*))
                var result2 = context.GetQueryable<SearchResultItem>().Where(predict2).GetResults();

各検索条件をORで結合するため、最初に False<T>拡張関数をしようして必ず False を返す Expression を生成します。こうしないと実際の検索条件がすべてFALSEにもかかわらず検索条件にマッチすることになっていしまいます。サンプルソースが実行されると次のような検索条件でインデックス検索が行われます。

q=(_name:(*home*) OR _templatename:(*Sample Item*) OR title_t:(*value*))

このように AND, OR など PredicateBuilder で用意されている拡張関数を使用することで簡単に動的な検索クエリーを作成することができます。

最後に AND と OR が含まれる ( 条件A || 条件B) && 条件C のような検索条件のクエリを生成するサンプルを掲載します。

                // AND OR 混合の 複雑な条件を作成しみてる
                // たとえば ( 条件A || 条件B) && 条件C のような式を生成する
                // 考え方は式のまとまりごとにPredicateBuilderを生成する
                // 条件A || 条件B に該当する部分を作成する
                var predict3_1 = PredicateBuilder.False<SearchResultItem>();
                predict3_1 = predict3_1.Or(i => ((string)i["_name"]).Contains(conditions["_name"]));
                predict3_1 = predict3_1.Or(i => ((string)i["_templatename"]).Contains(conditions["_templatename"]));
                // predict3_1 && 条件Cの部分を作成
                var predict3_2 = PredicateBuilder.And<SearchResultItem>(predict3_1, i => ((string)i["title"]).Contains(conditions["title"]));
                var result3 = context.GetQueryable<SearchResultItem>().Where(predict3_2).GetResults();

                // こういう風に記述することもできます。
                //var predict3_3 = PredicateBuilder.True<SearchResultItem>();
                //predict3_3 = predict3_3.And(predict3_1);
                //predict3_3 = predict3_3.And(i => ((string)i["title"]).Contains(conditions["title"]));
                //var result31 = context.GetQueryable<SearchResultItem>().Where(predict3_3).GetResults();

プログラムを実行すると次のようなインデックス検索クエリが作成されます。

q=((_name:(*home*) OR _templatename:(*Sample Item*)) AND title_t:(*value*))

簡単ですが、検索条件を作成するいくつかのサンプルをご紹介しました。

2.まとめ

説明は以上です。PredicateBuilderには True, False, Or, And 以外のNOTなど拡張関数が用意されています。LINQ Expression の仕組みを理解していると紹介したサンプルよりも複雑なクエリーを PredicateBuilderを使用して動的に簡単に組み立てることができるようになります。