gpt4 book ai didi

jquery - 如何执行相当复杂的 RavenDB 查询并将 Lucene 分数包含在结果中?

转载 作者:行者123 更新时间:2023-12-03 22:30:30 25 4
gpt4 key购买 nike

假设我有以下用户

public class User
{
// ... lots of other stuff
public string Id{ get; set; }
public double Relevance { get; set; }
public bool IsMentor { get; set; }
public string JobRole { get; set; }
public bool IsUnavailable { get; set; }
public List<string> ExpertiseAreas { get; set; }
public List<string> OrganisationalAreas { get; set; }
}

现在我想执行一次搜索,查找完全符合以下条件的所有用户:

  • IsMentor 等于 true
  • IsUnavailable 等于 false
  • Id 不等于单个排除用户(执行该操作的人)搜索)

我还希望结果完全或部分匹配以下条件,但前提是提供了搜索词,否则我希望忽略约束。

  • JobRole = []
  • ExpertiseAreas 包含来自 [value-1value-2value-n]<
  • OrganizationalAreas 包含来自 [value-1value-2值-n]

从此查询返回的用户列表可能并不全部都与条件匹配。有些会比其他的更匹配。所以我想根据结果的匹配程度对结果进行排序。

当我显示结果时,我希望为每个结果指定一个星级 (1-5),以表明用户与搜索的匹配程度。

我花了几天时间研究如何做到这一点。所以我现在将回答我自己的问题,希望能为您节省一些精力。当然,答案不会是完美的,所以如果你能改进它,请这样做。

最佳答案

首先,我需要一个 RavenDB 索引,其中包含我将搜索的所有字段。这很容易。

索引

public class User_FindMentor : AbstractIndexCreationTask<User>
{
public User_FindMentor()
{
Map = users => users.Select(user => new
{
user.Id,
user.IsUnavailable,
user.IsMentor,
user.OrganisationalAreas,
user.ExpertiseAreas,
user.JobRole
});
}
}

接下来我需要一个服务方法来执行查询。这就是所有魔法发生的地方。

搜索服务

public static Tuple<List<User>, RavenQueryStatistics> FindMentors(
IDocumentSession db,
string excludedUserId = null,
string expertiseAreas = null,
string jobRoles = null,
string organisationalAreas = null,
int take = 50)
{
RavenQueryStatistics stats;
var query = db
.Advanced
.LuceneQuery<User, RavenIndexes.User_FindMentor>()
.Statistics(out stats)
.Take(take)
.WhereEquals("IsMentor", true).AndAlso()
.WhereEquals("IsUnavailable", false).AndAlso()
.Not.WhereEquals("Id", excludedUserId);

if (expertiseAreas.HasValue())
query = query
.AndAlso()
.WhereIn("ExpertiseAreas", expertiseAreas.SafeSplit());

if (jobRoles.HasValue())
query = query
.AndAlso()
.WhereIn("JobRole", jobRoles.SafeSplit());

if (organisationalAreas.HasValue())
query = query
.AndAlso()
.WhereIn("OrganisationalAreas", organisationalAreas.SafeSplit());

var mentors = query.ToList();

if (mentors.Count > 0)
{
var max = db.GetRelevance(mentors[0]);
mentors.ForEach(mentor =>
mentor.Relevance = Math.Floor((db.GetRelevance(mentor)/max)*5));
}

return Tuple.Create(mentors, stats);
}

请注意下面的代码片段,我没有编写自己的 Lucene 查询字符串生成器。事实上,我确实写了这篇文章,这是一件很美妙的事情,但后来我发现 RavenDB 有一个更好的流畅界面来构建动态查询。因此,省住眼泪,从一开始就使用 native 查询界面。

RavenQueryStatistics stats;
var query = db
.Advanced
.LuceneQuery<User, RavenIndexes.User_FindMentor>()
.Statistics(out stats)
.Take(take)
.WhereEquals("IsMentor", true).AndAlso()
.WhereEquals("IsUnavailable", false).AndAlso()
.Not.WhereEquals("Id", excludedUserId);

接下来您可以看到我正在检查搜索是否已传入查询条件元素的任何值,例如:

if (expertiseAreas.HasValue())
query = query
.AndAlso()
.WhereIn("ExpertiseAreas", expertiseAreas.SafeSplit());

这使用了一些我发现通常有用的扩展方法:

public static bool HasValue(this string candidate)
{
return !string.IsNullOrEmpty(candidate);
}

public static bool IsEmpty(this string candidate)
{
return string.IsNullOrEmpty(candidate);
}

public static string[] SafeSplit(this string commaDelimited)
{
return commaDelimited.IsEmpty() ? new string[] { } : commaDelimited.Split(',');
}

然后我们就可以计算出每个结果的相关性。请记住,我希望结果显示 1 到 5 颗星,因此我希望我的相关性值在此范围内标准化。为此,我必须找出最大相关性,在本例中是列表中第一个用户的值。这是因为如果您没有另外指定排序顺序,Raven 会神奇地自动按相关性对结果进行排序 - 非常方便。

if (mentors.Count > 0)
{
var max = db.GetRelevance(mentors[0]);
mentors.ForEach(mentor =>
mentor.Relevance = Math.Floor((db.GetRelevance(mentor)/max)*5));
}

提取相关性依赖于另一种扩展方法,该方法从 ravendb 文档的元数据中提取 lucene 分数,如下所示:

public static double GetRelevance<T>(this IDocumentSession db, T candidate)
{
return db
.Advanced
.GetMetadataFor(candidate)
.Value<double>("Temp-Index-Score");
}

最后,我们使用新的 Tuple 小部件返回结果列表以及查询统计信息。如果您像我一样以前没有使用过 Tuple,那么这是一种无需使用 out 参数即可从方法发回多个值的简单方法。就是这样。因此,定义您的方法返回类型,然后使用“Tuple.Create()”,如下所示:

public static Tuple<List<User>, RavenQueryStatistics> FindMentors(...)
{
...
return Tuple.Create(mentors, stats);
}

这就是查询的内容。

但是我提到的那个很酷的星级评定呢?好吧,因为我是那种想要“棒上月亮”的程序员,所以我使用了一个不错的 jQuery 插件,名为 raty这对我来说效果很好。这里有一些 HTML5 + razor + jQuery 可以给你一些想法:

<div id="find-mentor-results"> 
@foreach (User user in Model.Results)
{
...stuff
<div class="row">
<img id="headshot" src="@user.Headshot" alt="headshot"/>
<h5>@user.DisplayName</h5>
<div class="star-rating" data-relevance="@user.Relevance"></div>
</div>
...stuff
}
</div>

<script>
$(function () {
$('.star-rating').raty({
readOnly: true,
score: function () {
return $(this).attr('data-relevance');
}
});
});
</script>

确实就是这样。有很多值得咀嚼的地方,还有很多需要改进的地方。如果您认为有更好/更有效的方法,请不要犹豫。

以下是一些测试数据的屏幕截图:

enter image description here

关于jquery - 如何执行相当复杂的 RavenDB 查询并将 Lucene 分数包含在结果中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13270998/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com