使用 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 驱动程序代码不会有太大问题。