MongoDB 带投影的文本搜索
MongoDB Text Search with projection
将 MongoDB 与 C# 和驱动程序 2.0 一起使用,我正在尝试执行以下操作:
- 文本搜索
- 按文本搜索分数对命中进行排序
- 将 BigClass 投射到 SmallClass
这是 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();
将 MongoDB 与 C# 和驱动程序 2.0 一起使用,我正在尝试执行以下操作:
- 文本搜索
- 按文本搜索分数对命中进行排序
- 将 BigClass 投射到 SmallClass
这是 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();