複数の部分からなる名前を検索する場合は、全文検索が最も簡単で適切な方法のようです (間違っていたら訂正してください)。他の選択肢は ですがLIKE '%query%'
、欠点が多すぎます。
- インデックススキャンを行うため、ひどいパフォーマンス
- 用語の順序が重要です。たとえば、「John Smith」と「Smith John」を検索すると、異なる結果が返されます。
- 単語の境界は無視されます。たとえば、"Ann" を検索すると "Joanna" と "Danny" も検索されますが、これらは有効な一致ではありません。
そこで、全文検索を実装しました。私のクエリは次のようになります。
SELECT * FROM Users WHERE CONTAINS(Name, '"John*"')
唯一の難点は、ユーザー クエリ (John) を CONTAINS に適したクエリ ("John*") に変換しなければならなかったことです。そのために、UserRepository に次のメソッドを実装しました。
/// <summary>
/// Converts user-entered search query into a query that can be consumed by CONTAINS keyword of SQL Server.
/// </summary>
/// <example>If query is "John S Ju", the result will be "\"John*\" AND \"S*\" AND \"Ju*\"".</example>
/// <param name="query">Query entered by user.</param>
/// <returns>String instance.</returns>
public static string GetContainsQuery(string query)
{
string containsQuery = string.Empty;
var terms = query.Split(new[] { ' ' }, StringSplitOptions.None);
if (terms.Length > 1)
{
for (int i = 0; i < terms.Length; i++)
{
string term = terms[i].Trim();
// Add wildcard term, e.g. - "term*". The reason to add wildcard is because we want
// to allow search by partially entered name parts (partially entered first name and/or
// partially entered last name, etc).
containsQuery += "\"" + term + "*\"";
// If it's not the last term.
if (i < terms.Length - 1)
{
// We want all terms inside user query to match.
containsQuery += " AND ";
}
}
containsQuery = containsQuery.Trim();
}
else
{
containsQuery = "\"" + query + "*\"";
}
return containsQuery;
}
これが同じ問題に遭遇した人に役立つことを願っています.
PS - これを文書化したブログ投稿を書きました。