使用 MongoDB C#/.NET 驱动程序为不区分变音符号的搜索设置排序规则
Setting up collation for diacritic-insensitive search with MongoDB C#/.NET Driver
我正在尝试使用 MongoDB C# 驱动程序实现变音符号不敏感的搜索,例如当我搜索“Joao”时,它应该 return 包含“João”和“Joao”(如果有)的所有结果。
以下命令适用于 MongoDBCompass,即如果我 运行 它针对我的 MongoDB 集合(当前仅包含带有“João”的文档,none 加上 "Joao"),它将 return 正确的文档:
db.profiles.find({FirstName:"Joao"}).collation({locale:"pt", strength: 1})
但是,当我尝试将其转置为 C# 时,它不起作用(例如,如果我搜索“Joao”,则 return 没有任何结果,只有当我搜索“João”时才会出现) :
private IFindFluent<ProfessionalProfile, ProfessionalProfile> BuildProfessionalLocationFilter(BaseQuery criteria)
{
FilterDefinition<ProfessionalProfile> filter = FilterDefinition<ProfessionalProfile>.Empty;
if (!string.IsNullOrEmpty(criteria.SearchWords))
{
var searchWords = criteria.SearchWords.ToLower().Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList();
FilterDefinition<ProfessionalProfile> searchWordFilter = FilterDefinition<ProfessionalProfile>.Empty;
foreach (string searchWord in searchWords)
{
var newFilter = Builders<ProfessionalProfile>.Filter.And(Builders<ProfessionalProfile>.Filter.Where(profile =>
profile.FirstName.ToLower().Contains(searchWord) ||
profile.LastName.ToLower().Contains(searchWord) ||
profile.Description.ToLower().Contains(searchWord) ||
profile.Email.ToLower().Contains(searchWord) ||
profile.Facebook.ToLower().Contains(searchWord) ||
profile.Instagram.ToLower().Contains(searchWord) ||
profile.LinkedIn.ToLower().Contains(searchWord) ||
profile.Locations.Any(location =>
location.Name.ToLower().Contains(searchWord) ||
location.District.Name.ToLower().Contains(searchWord) ||
location.Council.Name.ToLower().Contains(searchWord)) &&
!profile.IsDeleted &&
profile.WizardStep == 6));
if (searchWordFilter == FilterDefinition<ProfessionalProfile>.Empty)
searchWordFilter = newFilter;
else
searchWordFilter = searchWordFilter | newFilter;
}
filter = filter & searchWordFilter;
}
IFindFluent<ProfessionalProfile, ProfessionalProfile> findFluent = _professionalCollection.Find(filter);
findFluent.Options.Collation = new Collation("pt", strength: CollationStrength.Primary);
//IFindFluent<ProfessionalProfile, ProfessionalProfile> findFluent = _professionalCollection.Find(filter, new FindOptions() { Collation = new Collation("pt", strength: CollationStrength.Primary) } );
return findFluent;
}
请注意,我也尝试了上面的注释行,结果相同(可预测)。
可能缺少什么?谢谢。
编辑:
正如@Đĵ ΝιΓΞΗΛψκ 在下面的评论中所问,我正在添加一些关于配置文件集合整理的信息。
Mongo shell 命令:
db.getCollectionInfos({ name: 'profiles' })
输出:
[
{
name: 'profiles',
type: 'collection',
options: {},
info: {
readOnly: false,
uuid: UUID("4606f027-03b1-45e8-bf7f-6c99461db042")
},
idIndex: { v: 2, key: [Object], name: '_id_', ns: 'dev.profiles' }
}
]
由于有关该问题的文档很少,因此进行了一些调查,但我想我知道发生了什么。
profile.FirstName.ToLower().Contains(searchWord)
由驱动程序转换为 $regex
查询。
据我所知,mongodb 中的正则表达式搜索不支持排序规则。所以您不能使用正则表达式功能进行变音符号不敏感搜索。
但是,满足您要求的解决方案是创建一个 Text Index
包含您要搜索的所有字段,并利用该索引对您的搜索词进行变音符号和不区分大小写的搜索。这也将是实现您要求的最有效方式。
使用文本索引的一个限制是它不会让您搜索 Jo
等词的部分匹配项。 mongo不幸的是,db 全文搜索只适用于完整的单词。
这是一个测试程序(为简洁起见,使用 mongodb.entities 库):
using MongoDB.Driver;
using MongoDB.Entities;
using System.Threading.Tasks;
namespace TestApplication
{
public class ProfessionalProfile : Entity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsDeleted { get; set; }
public int WizardStep { get; set; }
}
public static class Program
{
private static async Task Main()
{
await DB.InitAsync("test", "localhost");
await DB.Index<ProfessionalProfile>()
.Key(p => p.FirstName, KeyType.Text)
.Key(p => p.LastName, KeyType.Text)
.CreateAsync();
await new[] {
new ProfessionalProfile{ FirstName = "João", LastName = "Balboa", IsDeleted = false, WizardStep = 6},
new ProfessionalProfile{ FirstName = "Joao", LastName = "Balboa", IsDeleted = false, WizardStep = 6},
}.SaveAsync();
IAggregateFluent<ProfessionalProfile> aggregateFluent =
DB.FluentTextSearch<ProfessionalProfile>(
searchType: Search.Full,
searchTerm: "Joao Mary John",
caseSensitive: false,
diacriticSensitive: false)
.Match(p => !p.IsDeleted && p.WizardStep == 6);
var result = await aggregateFluent.ToListAsync();
}
}
}
我敢肯定,将其转换为 mongo 驱动程序代码不会有太大问题。
我正在尝试使用 MongoDB C# 驱动程序实现变音符号不敏感的搜索,例如当我搜索“Joao”时,它应该 return 包含“João”和“Joao”(如果有)的所有结果。
以下命令适用于 MongoDBCompass,即如果我 运行 它针对我的 MongoDB 集合(当前仅包含带有“João”的文档,none 加上 "Joao"),它将 return 正确的文档:
db.profiles.find({FirstName:"Joao"}).collation({locale:"pt", strength: 1})
但是,当我尝试将其转置为 C# 时,它不起作用(例如,如果我搜索“Joao”,则 return 没有任何结果,只有当我搜索“João”时才会出现) :
private IFindFluent<ProfessionalProfile, ProfessionalProfile> BuildProfessionalLocationFilter(BaseQuery criteria)
{
FilterDefinition<ProfessionalProfile> filter = FilterDefinition<ProfessionalProfile>.Empty;
if (!string.IsNullOrEmpty(criteria.SearchWords))
{
var searchWords = criteria.SearchWords.ToLower().Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList();
FilterDefinition<ProfessionalProfile> searchWordFilter = FilterDefinition<ProfessionalProfile>.Empty;
foreach (string searchWord in searchWords)
{
var newFilter = Builders<ProfessionalProfile>.Filter.And(Builders<ProfessionalProfile>.Filter.Where(profile =>
profile.FirstName.ToLower().Contains(searchWord) ||
profile.LastName.ToLower().Contains(searchWord) ||
profile.Description.ToLower().Contains(searchWord) ||
profile.Email.ToLower().Contains(searchWord) ||
profile.Facebook.ToLower().Contains(searchWord) ||
profile.Instagram.ToLower().Contains(searchWord) ||
profile.LinkedIn.ToLower().Contains(searchWord) ||
profile.Locations.Any(location =>
location.Name.ToLower().Contains(searchWord) ||
location.District.Name.ToLower().Contains(searchWord) ||
location.Council.Name.ToLower().Contains(searchWord)) &&
!profile.IsDeleted &&
profile.WizardStep == 6));
if (searchWordFilter == FilterDefinition<ProfessionalProfile>.Empty)
searchWordFilter = newFilter;
else
searchWordFilter = searchWordFilter | newFilter;
}
filter = filter & searchWordFilter;
}
IFindFluent<ProfessionalProfile, ProfessionalProfile> findFluent = _professionalCollection.Find(filter);
findFluent.Options.Collation = new Collation("pt", strength: CollationStrength.Primary);
//IFindFluent<ProfessionalProfile, ProfessionalProfile> findFluent = _professionalCollection.Find(filter, new FindOptions() { Collation = new Collation("pt", strength: CollationStrength.Primary) } );
return findFluent;
}
请注意,我也尝试了上面的注释行,结果相同(可预测)。
可能缺少什么?谢谢。
编辑:
正如@Đĵ ΝιΓΞΗΛψκ 在下面的评论中所问,我正在添加一些关于配置文件集合整理的信息。
Mongo shell 命令:
db.getCollectionInfos({ name: 'profiles' })
输出:
[
{
name: 'profiles',
type: 'collection',
options: {},
info: {
readOnly: false,
uuid: UUID("4606f027-03b1-45e8-bf7f-6c99461db042")
},
idIndex: { v: 2, key: [Object], name: '_id_', ns: 'dev.profiles' }
}
]
由于有关该问题的文档很少,因此进行了一些调查,但我想我知道发生了什么。
profile.FirstName.ToLower().Contains(searchWord)
由驱动程序转换为 $regex
查询。
据我所知,mongodb 中的正则表达式搜索不支持排序规则。所以您不能使用正则表达式功能进行变音符号不敏感搜索。
但是,满足您要求的解决方案是创建一个 Text Index
包含您要搜索的所有字段,并利用该索引对您的搜索词进行变音符号和不区分大小写的搜索。这也将是实现您要求的最有效方式。
使用文本索引的一个限制是它不会让您搜索 Jo
等词的部分匹配项。 mongo不幸的是,db 全文搜索只适用于完整的单词。
这是一个测试程序(为简洁起见,使用 mongodb.entities 库):
using MongoDB.Driver;
using MongoDB.Entities;
using System.Threading.Tasks;
namespace TestApplication
{
public class ProfessionalProfile : Entity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsDeleted { get; set; }
public int WizardStep { get; set; }
}
public static class Program
{
private static async Task Main()
{
await DB.InitAsync("test", "localhost");
await DB.Index<ProfessionalProfile>()
.Key(p => p.FirstName, KeyType.Text)
.Key(p => p.LastName, KeyType.Text)
.CreateAsync();
await new[] {
new ProfessionalProfile{ FirstName = "João", LastName = "Balboa", IsDeleted = false, WizardStep = 6},
new ProfessionalProfile{ FirstName = "Joao", LastName = "Balboa", IsDeleted = false, WizardStep = 6},
}.SaveAsync();
IAggregateFluent<ProfessionalProfile> aggregateFluent =
DB.FluentTextSearch<ProfessionalProfile>(
searchType: Search.Full,
searchTerm: "Joao Mary John",
caseSensitive: false,
diacriticSensitive: false)
.Match(p => !p.IsDeleted && p.WizardStep == 6);
var result = await aggregateFluent.ToListAsync();
}
}
}
我敢肯定,将其转换为 mongo 驱动程序代码不会有太大问题。