Trước tiên, tôi cần chỉ mục RavenDB bao gồm tất cả các trường tôi sẽ tìm kiếm. Điều này thật dễ dàng.
Index
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
});
}
}
Tiếp theo, tôi cần một phương pháp dịch vụ để thực hiện các truy vấn. Đây là nơi tất cả các phép thuật xảy ra.
Dịch vụ tìm kiếm
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);
}
Lưu ý trong đoạn mã dưới đây, tôi có không viết máy phát điện chuỗi Lucene Query của riêng tôi. Tôi đã làm, trên thực tế, viết này, và nó là một thứ của cái đẹp, nhưng sau đó tôi phát hiện ra rằng RavenDB có nhiều giao diện thông thạo hơn để xây dựng các truy vấn động.Vì vậy, hãy tiết kiệm nước mắt của bạn và sử dụng giao diện truy vấn gốc từ đầu.
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);
Tiếp theo, bạn có thể thấy rằng tôi đang kiểm tra hay không tìm kiếm đã trôi qua trong bất kỳ giá trị cho các yếu tố điều kiện của truy vấn, ví dụ:
if (expertiseAreas.HasValue())
query = query
.AndAlso()
.WhereIn("ExpertiseAreas", expertiseAreas.SafeSplit());
này sử dụng một vài phương pháp mở rộng mà Tôi thấy hữu ích nói chung:
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(',');
}
Sau đó, chúng tôi có bit hoạt động trong số Relevance
của mỗi kết quả. Hãy nhớ rằng tôi muốn hiển thị kết quả từ 1 đến 5 sao vì vậy tôi muốn giá trị Mức độ liên quan của tôi được chuẩn hóa trong phạm vi này. Để làm điều này tôi phải tìm ra mức độ liên quan tối đa, trong trường hợp này là giá trị của Người dùng đầu tiên trong danh sách. Điều này là do Raven tự động sắp xếp các kết quả theo mức độ liên quan nếu bạn không chỉ định thứ tự sắp xếp - rất tiện dụng.
if (mentors.Count > 0)
{
var max = db.GetRelevance(mentors[0]);
mentors.ForEach(mentor =>
mentor.Relevance = Math.Floor((db.GetRelevance(mentor)/max)*5));
}
Extracting sự phù hợp dựa vào thêm một phương pháp khuyến nông mà kéo số điểm Lucene từ siêu dữ liệu tài liệu ravendb của, như thế này:
public static double GetRelevance<T>(this IDocumentSession db, T candidate)
{
return db
.Advanced
.GetMetadataFor(candidate)
.Value<double>("Temp-Index-Score");
}
Cuối cùng chúng tôi trở lại danh sách kết quả cùng với số liệu thống kê truy vấn sử dụng tiện ích Tuple
mới. Nếu bạn, như tôi, đã không sử dụng Tuple
trước, nó quay ra được một cách dễ dàng để gửi nhiều hơn một giá trị trở lại từ một phương pháp mà không sử dụng out
params. Đó là nó. Vì vậy, hãy xác định loại trả về của phương thức và sau đó sử dụng 'Tuple.Create()', như sau:
public static Tuple<List<User>, RavenQueryStatistics> FindMentors(...)
{
...
return Tuple.Create(mentors, stats);
}
Và đó là truy vấn.
Nhưng những gì về điều đó mát mẻ sao giá tôi đã đề cập? Cũng kể từ khi tôi là loại coder ai muốn mặt trăng-on-a-stick, tôi sử dụng một plugin jQuery đẹp gọi raty mà chỉ làm việc tốt cho tôi. Dưới đây là một số HTML5 + dao cạo + jQuery để cung cấp cho bạn ý tưởng:
<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>
Và đó thực sự là nó. Rất nhiều để nhai, rất nhiều để cải thiện. Đừng giữ lại nếu bạn nghĩ rằng có một cách tốt hơn/hiệu quả hơn.
Đây là một ảnh chụp màn hình của một số dữ liệu thử nghiệm:

Cảm ơn bạn đã đặt việc nghiên cứu này và gửi nó cho chồng. Tôi thấy nó khá hữu ích. – jamesamuir