MongoDB 带投影的文本搜索

MongoDB Text Search with projection

将 MongoDB 与 C# 和驱动程序 2.0 一起使用,我正在尝试执行以下操作:

这是 classes 的(简化版):

class BigClass
{
    [BsonIgnoreIfDefault]
    public ObjectId             _id                 { get; set; }
    public string               Guid                { get; set; }
    public string               Title               { get; set; }
    public DateTime             CreationTime        { get; set; }
    // lots of other stuff

    [BsonIgnoreIfNull]
    public double?              TextMatchScore      { get; set; }       // Temporary place for the text match score, for sorting
}

class SmallClass
{
    [BsonIgnoreIfDefault]
    public ObjectId             _id                 { get; set; }
    public string               Title               { get; set; }

    [BsonIgnoreIfNull]
    public double?              TextMatchScore      { get; set; }       // Temporary place for the text match score, for sorting
}

如果我进行文本搜索,则非常简单:

var F = Builders<BigClass>.Filter.Text("text I am looking for");
var Result = MongoDriver.Find(F).ToListAsync().Result;


如果我想按文本搜索的分数排序,那就有点乱了(而且记录很差):

var F = Builders<BigClass>.Filter.Text("text I am looking for");
var P = Builders<BigClass>.Projection.MetaTextScore("TextMatchScore");
var S = Builders<BigClass>.Sort.MetaTextScore("TextMatchScore");
var Result = MongoDriver.Find(F).Project<BigClass>.Sort(S).ToListAsync().Result;

基本上它需要我在 class (TextMatchScore) 中添加一个字段来保存结果。


如果我想获取数据,不排序,投影到SmallClass,直接:

var F = Builders<BigClass>.Filter.Text("text I am looking for");
var P = Builders<BigClass>.Projection.Include(_ => _.id).Include(_ => _.Title);
var Result = MongoDriver.Find(F).Project<SmallClass>(P).ToListAsync().Result;


现在如果 "I want it all",那就是问题出现的地方:

var F = Builders<BigClass>.Filter.Text("text I am looking for");
var P = Builders<BigClass>.Projection.MetaTextScore("TextMatchScore").Include(_ => _.id).Include(_ => _.Title).Include(_ => _.TextMatchScore);
var S = Builders<BigClass>.Sort.MetaTextScore("TextMatchScore");
var Result = MongoDriver.Find(F).Project<SmallClass>.Sort(S).ToListAsync().Result;

我遇到异常:

Message = "QueryFailure flag was true (response was { \"$err\" : \"Can't canonicalize query: BadValue must have $meta projection for all $meta sort keys\", \"code\" : 17287 })."

正如预期的那样,错误没有在任何地方记录,因为 Mongo 伙计们希望用户自行记录所有内容。

如果我投影到 'BigClass',没有问题,代码运行并填写正确的字段。

如果您 google 使用 C# 发送文本,那么当我试图弄清楚文本搜索时,您找到的帖子是我的,这也没有很好的记录。

所以当我们结合投影、文本搜索和排序时,似乎没有任何例子,我就是无法让它工作。

有人知道这个问题的原因吗?

这对我有用:

var client = new MongoClient();
var db = client.GetDatabase("test");
var col = db.GetCollection<BigClass>("big");
await db.DropCollectionAsync(col.CollectionNamespace.CollectionName);

await col.Indexes.CreateOneAsync(Builders<BigClass>.IndexKeys.Text(x => x.Title));

await col.InsertManyAsync(new[]
{
    new BigClass { Title = "One Jumped Over The Moon" },
    new BigClass { Title = "Two went Jumping Over The Sun" }
});


var filter = Builders<BigClass>.Filter.Text("Jump Over");
// don't need to Include(x => x.TextMatchScore) because it's already been included with MetaTextScore.
var projection = Builders<BigClass>.Projection.MetaTextScore("TextMatchScore").Include(x => x._id).Include(x => x.Title);
var sort = Builders<BigClass>.Sort.MetaTextScore("TextMatchScore");

var result = await col.Find(filter).Project<SmallClass>(projection).Sort(sort).ToListAsync();

我删除了 TextMatchScore 的包含。它仍然回来了,因为它被包含在 MetaTextScore("TextMatchScore").

文档正在制作中。我们首先处理主要用例,因为这些用例影响了大多数人。这个用例并不常见,也没有记录在案。我们当然接受代码和文档的拉取请求。另外,请随时在 CSHARP 项目下的 jira.mongodb.org 提交文档票证。

在MongoDB.Driver2.x中有效的解决方案如下。重要的是不要做 Include in Projection,因为它会删除默认的,(或者记得添加适当的投影)

查询:

 {
   "find":"SoceCollection",
   "filter":{
      "$text":{
         "$search":"some text to search"
      }
   },
   "sort":{
      "TextScore":{
         "$meta":"textScore"
      }
   },
   "projection":{
      "TextScore":{
         "$meta":"textScore"
      },
      "_id":0,
      "CreatedDate":0
   },
   "limit":20,
   "collation":{
      "locale":"en",
      "strength":1
   } ...

代码

var sort = Builders<BigModel>.Sort.MetaTextScore(nameof(LightModel.TextScore));
            var projection = Builders<BigModel>.Projection
            .MetaTextScore(nameof(LightModel.TextScore))
                    .Exclude(x => x.Id)
                    .Exclude(x => x.CreatedDate); 

 

            return await Collection()
               .Find(filter, new FindOptions { Collation = new Collation("en", strength: CollationStrength.Primary) })
                .Project<LightModel>(projection)
                .Sort(sort)
                .Limit(20)
                .ToListAsync();