DEV Community

Takumi Yamada
Takumi Yamada

Posted on • Updated on

ContentSearch APIで常に真になる条件の生成方法とその使い道

以下のコードで常に真になる条件の作成することができます。

var truePred = PredicateBuilder.Create<SearchResultItem>(item => item.Name.MatchWildcard("*"));
Enter fullscreen mode Exit fullscreen mode

_nameフィールドは全てのドキュメントが持っているので、この条件は常に真(全てのドキュメントにヒットする)になります。あまりリーダブルではないので、コメントで補足するかラップしたメソッドを用意することをおすすめします。

一見以下のコードでもうまく動きそうですが、単体で使用すると例外が発生する上に、ORで連結すると条件が無視されてしまうため期待した動作にはなりません。

// うまく動かない例
var truePred1 = PredicateBuilder.True<SearchResultItem>();
var truePred2 = PredicateBuilder.Create<SearchResultItem>(item => true);
Enter fullscreen mode Exit fullscreen mode

使い道

検索結果に重み付けを行う

{常に真になる条件} OR {重み付けのための条件}」でフィルターをかけることで、検索結果に重みをつけることができます。

以下の例では、Contentフィールドに"Foo"を含むものに絞り込み、さらにTitleフィールドと完全一致したものに重みを付けています。

var index = ContentSearchManager.GetIndex("sitecore_master_index");
using (var context = index.CreateSearchContext())
{
    var query = context.GetQueryable<SearchResultItem>();

    // Contentフィールドが"Foo"を含むものに絞り込み
    query = query.Filter(item => item["Content"].Contains("Foo"));

    // Titleフィールドが"Foo"と一致する場合に重みをつける
    // ORで連結しているので、条件全体として常に真となる。
    var boostPred = PredicateBuilder
        .Create<SearchResultItem>(item => item.Name.MatchWildcard("*")).Boost(0) // 常に真
        .Or(item => item["Title"].Equals("Foo").Boost(10));

    // 条件をクエリに適用(Filterだと重み付けを無視するためWhereを使用)
    query = query.Where(boostPred);

    // 重みの降順で並び替えて取得
    var result = query.OrderByDescending(item => item["score"]).GetResults();
}
Enter fullscreen mode Exit fullscreen mode

boostPredで真となる条件をORで結合しています。初期値にPredicateBuilder.True<SearchResultItem>()を使用すると、クエリ生成時に条件が無視されてしまい、期待した動作になりません。

またフィルター処理と重み付けを同時に行うこともできますが、多くの場合条件を分けることで柔軟で効率の良いクエリを作成することができます。

OR/AND条件を動的生成する

リストの値それぞれに対して条件をかけたい場合、AND/OR条件を動的生成する必要があります。

以下の例では、values変数の全ての値がアイテムのTitleフィールドに含まれている条件を生成しています。

var values = new [] { "Foo", "Bar", "Baz" };

var index = ContentSearchManager.GetIndex("sitecore_master_index");
using (var context = index.CreateSearchContext())
{
    var query = context.GetQueryable<SearchResultItem>();

    // true && item["Title"].Contains(value1) && item["Title"].Contains(value2) && ... 
    // という条件を動的生成
    var pred = PredicateBuilder.Create<SearchResultItem>(item => item.Name.MatchWildcard("*"));
    foreach (var value in values)
    {
        pred = pred.And(item => item["Title"].Contains(value));
    }

    // 条件をクエリに適用
    query = query.Filter(pred);

    var result = query.GetResults();
}
Enter fullscreen mode Exit fullscreen mode

predの初期値として、常に真になる条件を与えています。代わりにPredicateBuilder.True<SearchResultItem>()を使用しても動作しますが、values変数が空の場合に例外が発生してしまいます。

Discussion (0)