487 lines
22 KiB
C#
487 lines
22 KiB
C#
using JSMR.Application.Common.Search;
|
|
using JSMR.Application.VoiceWorks.Queries.Search;
|
|
using JSMR.Domain.Entities;
|
|
using JSMR.Domain.Enums;
|
|
using JSMR.Infrastructure.Common.Queries;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Linq.Expressions;
|
|
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
|
|
using CircleStatus = JSMR.Application.VoiceWorks.Queries.Search.CircleStatus;
|
|
|
|
namespace JSMR.Infrastructure.Data.Repositories.VoiceWorks;
|
|
|
|
public class VoiceWorkQuery
|
|
{
|
|
public required VoiceWork VoiceWork { get; init; }
|
|
public EnglishVoiceWork? EnglishVoiceWork { get; init; }
|
|
public required Circle Circle { get; init; }
|
|
//public VoiceWorkLocalization? VoiceWorkLocalization { get; init; }
|
|
//public VoiceWorkSearch? VoiceWorkSearch { get; init; }
|
|
}
|
|
|
|
public class VoiceWorkSearchProvider(AppDbContext context, IVoiceWorkFullTextSearch fullTextSearch) : SearchProvider<VoiceWorkSearchResult, VoiceWorkSearchCriteria, VoiceWorkSortField, VoiceWorkQuery>, IVoiceWorkSearchProvider
|
|
{
|
|
protected override bool UseSelectIdQuery => true;
|
|
|
|
protected override IQueryable<VoiceWorkQuery> GetBaseQuery()
|
|
{
|
|
return
|
|
from voiceWork in context.VoiceWorks.AsNoTracking()
|
|
join englishVoiceWork in context.EnglishVoiceWorks.AsNoTracking() on voiceWork.VoiceWorkId equals englishVoiceWork.VoiceWorkId into ps
|
|
from englishVoiceWork in ps.DefaultIfEmpty()
|
|
join circle in context.Circles.AsNoTracking() on voiceWork.CircleId equals circle.CircleId into cs
|
|
from circle in cs.DefaultIfEmpty()
|
|
//join voiceWorkLocalization in context.VoiceWorkLocalizations on voiceWork.VoiceWorkId equals voiceWorkLocalization.VoiceWorkId into vwl
|
|
//from voiceWorkLocalization in vwl.DefaultIfEmpty()
|
|
//join voiceWorkSearch in context.VoiceWorkSearches on voiceWork.VoiceWorkId equals voiceWorkSearch.VoiceWorkId into vws
|
|
//from voiceWorkSearch in vws.DefaultIfEmpty()
|
|
select new VoiceWorkQuery
|
|
{
|
|
VoiceWork = voiceWork,
|
|
EnglishVoiceWork = englishVoiceWork,
|
|
Circle = circle
|
|
};
|
|
}
|
|
|
|
protected override IQueryable<VoiceWorkQuery> ApplyFilters(IQueryable<VoiceWorkQuery> query, VoiceWorkSearchCriteria criteria)
|
|
{
|
|
IQueryable<VoiceWorkQuery> filteredQuery = query;
|
|
|
|
filteredQuery = ApplyKeywordsFilter(filteredQuery, criteria);
|
|
|
|
switch (criteria.SaleStatus)
|
|
{
|
|
case SaleStatus.Available:
|
|
filteredQuery = filteredQuery.Where(x => x.VoiceWork.SalesDate != null);
|
|
break;
|
|
case SaleStatus.Upcoming:
|
|
filteredQuery = filteredQuery.Where(x => x.VoiceWork.ExpectedDate != null);
|
|
break;
|
|
}
|
|
|
|
if (criteria.SupportedLanguages.Length > 0)
|
|
filteredQuery = filteredQuery.Where(x => criteria.SupportedLanguages.Contains((Language)x.VoiceWork.SubtitleLanguage));
|
|
|
|
filteredQuery = ApplyCircleStatusFilter(filteredQuery, criteria);
|
|
filteredQuery = ApplyTagStatusFilter(filteredQuery, criteria);
|
|
filteredQuery = ApplyCreatorStatusFilter(filteredQuery, criteria);
|
|
filteredQuery = ApplyTagIdsFilter(filteredQuery, criteria);
|
|
filteredQuery = ApplyCreatorIdsFilter(filteredQuery, criteria);
|
|
|
|
if (criteria.Status is not null)
|
|
filteredQuery = filteredQuery.Where(x => x.VoiceWork.Status == (byte)criteria.Status);
|
|
|
|
if (criteria.ScheduledReleaseDateStart is not null)
|
|
filteredQuery = filteredQuery.Where(x => x.VoiceWork.PlannedReleaseDate >= criteria.ScheduledReleaseDateStart.Value.ToDateTime(TimeOnly.MinValue));
|
|
|
|
if (criteria.ScheduledReleaseDateEnd is not null)
|
|
filteredQuery = filteredQuery.Where(x => x.VoiceWork.PlannedReleaseDate <= criteria.ScheduledReleaseDateEnd.Value.ToDateTime(TimeOnly.MinValue));
|
|
|
|
if (criteria.ReleaseDateStart is not null)
|
|
filteredQuery = filteredQuery.Where(x => x.VoiceWork.SalesDate >= criteria.ReleaseDateStart.Value.ToDateTime(TimeOnly.MinValue));
|
|
|
|
if (criteria.ReleaseDateEnd is not null)
|
|
filteredQuery = filteredQuery.Where(x => x.VoiceWork.SalesDate <= criteria.ReleaseDateEnd.Value.ToDateTime(TimeOnly.MinValue));
|
|
|
|
if (criteria.AgeRatings.Length > 0)
|
|
filteredQuery = filteredQuery.Where(x => criteria.AgeRatings.Contains((AgeRating)x.VoiceWork.Rating));
|
|
|
|
//if (criteria.SupportedLanguages.Length > 0)
|
|
// filteredQuery = filteredQuery.Where(x => criteria.SupportedLanguages.Contains((Language)x.VoiceWork.SubtitleLanguage));
|
|
|
|
if (criteria.AIGenerationOptions.Length > 0)
|
|
filteredQuery = filteredQuery.Where(x => criteria.AIGenerationOptions.Contains((AIGeneration)x.VoiceWork.AIGeneration));
|
|
|
|
if (criteria.ShowFavoriteVoiceWorks)
|
|
filteredQuery = filteredQuery.Where(x => x.VoiceWork.Favorite);
|
|
|
|
if (criteria.ShowInvalidVoiceWorks)
|
|
filteredQuery = filteredQuery.Where(x => x.VoiceWork.IsValid != true);
|
|
|
|
if (criteria.MinDownloads is not null)
|
|
filteredQuery = filteredQuery.Where(x => x.VoiceWork.Downloads >= criteria.MinDownloads.Value);
|
|
|
|
if (criteria.MaxDownloads is not null)
|
|
filteredQuery = filteredQuery.Where(x => x.VoiceWork.Downloads <= criteria.MaxDownloads.Value);
|
|
|
|
return filteredQuery;
|
|
}
|
|
|
|
private IQueryable<VoiceWorkQuery> ApplyKeywordsFilter(IQueryable<VoiceWorkQuery> query, VoiceWorkSearchCriteria criteria)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(criteria.Keywords))
|
|
return query;
|
|
|
|
var voiceWorkIds = fullTextSearch.MatchingIds(context, criteria.Keywords);
|
|
|
|
return query.Where(x => voiceWorkIds.Contains(x.VoiceWork.VoiceWorkId));
|
|
}
|
|
|
|
private IQueryable<VoiceWorkQuery> ApplyCircleStatusFilter(IQueryable<VoiceWorkQuery> query, VoiceWorkSearchCriteria criteria)
|
|
{
|
|
if (criteria.CircleStatus is null)
|
|
return query;
|
|
|
|
switch (criteria.CircleStatus)
|
|
{
|
|
case CircleStatus.NotBlacklisted:
|
|
query = query.Where(q =>
|
|
!context.Circles.Any(c => c.CircleId == q.VoiceWork.CircleId && c.Blacklisted));
|
|
break;
|
|
|
|
case CircleStatus.Blacklisted:
|
|
query = query.Where(q =>
|
|
context.Circles.Any(c => c.CircleId == q.VoiceWork.CircleId && c.Blacklisted));
|
|
break;
|
|
|
|
case CircleStatus.Favorited:
|
|
query = query.Where(q =>
|
|
context.Circles.Any(c => c.CircleId == q.VoiceWork.CircleId && c.Favorite));
|
|
break;
|
|
}
|
|
|
|
//switch (criteria.CircleStatus)
|
|
//{
|
|
// case CircleStatus.NotBlacklisted:
|
|
// query = query.Where(x => x.Circle.Blacklisted == false);
|
|
// break;
|
|
// case CircleStatus.Favorited:
|
|
// query = query.Where(x => x.Circle.Favorite);
|
|
// break;
|
|
// case CircleStatus.Blacklisted:
|
|
// query = query.Where(x => x.Circle.Blacklisted);
|
|
// break;
|
|
//}
|
|
|
|
return query;
|
|
}
|
|
|
|
private IQueryable<VoiceWorkQuery> ApplyTagStatusFilter(IQueryable<VoiceWorkQuery> query, VoiceWorkSearchCriteria criteria)
|
|
{
|
|
if (criteria.TagStatus is null)
|
|
return query;
|
|
|
|
// Handy local predicates that translate to EXISTS subqueries
|
|
bool HasFav(int voiceWorkId) =>
|
|
context.VoiceWorkTags
|
|
.Join(context.Tags, vwt => vwt.TagId, t => t.TagId, (vwt, t) => new { vwt, t })
|
|
.Any(x => x.vwt.VoiceWorkId == voiceWorkId && x.t.Favorite);
|
|
|
|
bool HasBlk(int voiceWorkId) =>
|
|
context.VoiceWorkTags
|
|
.Join(context.Tags, vwt => vwt.TagId, t => t.TagId, (vwt, t) => new { vwt, t })
|
|
.Any(x => x.vwt.VoiceWorkId == voiceWorkId && x.t.Blacklisted);
|
|
|
|
return criteria.TagStatus switch
|
|
{
|
|
TagStatus.NotBlacklisted =>
|
|
query.Where(q => !context.VoiceWorkTags
|
|
.Join(context.Tags, vwt => vwt.TagId, t => t.TagId, (vwt, t) => new { vwt, t })
|
|
.Any(x => x.vwt.VoiceWorkId == q.VoiceWork.VoiceWorkId && x.t.Blacklisted)),
|
|
|
|
TagStatus.Blacklisted =>
|
|
query.Where(q => context.VoiceWorkTags
|
|
.Join(context.Tags, vwt => vwt.TagId, t => t.TagId, (vwt, t) => new { vwt, t })
|
|
.Any(x => x.vwt.VoiceWorkId == q.VoiceWork.VoiceWorkId && x.t.Blacklisted)),
|
|
|
|
TagStatus.FavoriteIncludeBlacklist =>
|
|
query.Where(q => context.VoiceWorkTags
|
|
.Join(context.Tags, vwt => vwt.TagId, t => t.TagId, (vwt, t) => new { vwt, t })
|
|
.Any(x => x.vwt.VoiceWorkId == q.VoiceWork.VoiceWorkId && x.t.Favorite)),
|
|
|
|
TagStatus.FavoriteExcludeBlacklist =>
|
|
query.Where(q =>
|
|
context.VoiceWorkTags
|
|
.Join(context.Tags, vwt => vwt.TagId, t => t.TagId, (vwt, t) => new { vwt, t })
|
|
.Any(x => x.vwt.VoiceWorkId == q.VoiceWork.VoiceWorkId && x.t.Favorite)
|
|
&&
|
|
!context.VoiceWorkTags
|
|
.Join(context.Tags, vwt => vwt.TagId, t => t.TagId, (vwt, t) => new { vwt, t })
|
|
.Any(x => x.vwt.VoiceWorkId == q.VoiceWork.VoiceWorkId && x.t.Blacklisted)
|
|
),
|
|
|
|
_ => query
|
|
};
|
|
}
|
|
|
|
private IQueryable<VoiceWorkQuery> ApplyCreatorStatusFilter(IQueryable<VoiceWorkQuery> query, VoiceWorkSearchCriteria criteria)
|
|
{
|
|
if (criteria.CreatorStatus is null)
|
|
return query;
|
|
|
|
// Handy local predicates that translate to EXISTS subqueries
|
|
bool HasFav(int voiceWorkId) =>
|
|
context.VoiceWorkCreators
|
|
.Join(context.Creators, vwc => vwc.CreatorId, c => c.CreatorId, (vwc, c) => new { vwc, c })
|
|
.Any(x => x.vwc.VoiceWorkId == voiceWorkId && x.c.Favorite);
|
|
|
|
bool HasBlk(int voiceWorkId) =>
|
|
context.VoiceWorkCreators
|
|
.Join(context.Creators, vwc => vwc.CreatorId, c => c.CreatorId, (vwc, c) => new { vwc, c })
|
|
.Any(x => x.vwc.VoiceWorkId == voiceWorkId && x.c.Blacklisted);
|
|
|
|
return criteria.CreatorStatus switch
|
|
{
|
|
CreatorStatus.NotBlacklisted =>
|
|
query.Where(q => !context.VoiceWorkCreators
|
|
.Join(context.Creators, vwc => vwc.CreatorId, c => c.CreatorId, (vwc, c) => new { vwc, c })
|
|
.Any(x => x.vwc.VoiceWorkId == q.VoiceWork.VoiceWorkId && x.c.Blacklisted)),
|
|
|
|
CreatorStatus.Blacklisted =>
|
|
query.Where(q => context.VoiceWorkCreators
|
|
.Join(context.Creators, vwc => vwc.CreatorId, c => c.CreatorId, (vwc, c) => new { vwc, c })
|
|
.Any(x => x.vwc.VoiceWorkId == q.VoiceWork.VoiceWorkId && x.c.Blacklisted)),
|
|
|
|
CreatorStatus.FavoriteIncludeBlacklist =>
|
|
query.Where(q => context.VoiceWorkCreators
|
|
.Join(context.Creators, vwc => vwc.CreatorId, c => c.CreatorId, (vwc, c) => new { vwc, c })
|
|
.Any(x => x.vwc.VoiceWorkId == q.VoiceWork.VoiceWorkId && x.c.Favorite)),
|
|
|
|
CreatorStatus.FavoriteExcludeBlacklist =>
|
|
query.Where(q =>
|
|
context.VoiceWorkCreators
|
|
.Join(context.Creators, vwc => vwc.CreatorId, c => c.CreatorId, (vwc, c) => new { vwc, c })
|
|
.Any(x => x.vwc.VoiceWorkId == q.VoiceWork.VoiceWorkId && x.c.Favorite)
|
|
&&
|
|
!context.VoiceWorkCreators
|
|
.Join(context.Creators, vwc => vwc.CreatorId, c => c.CreatorId, (vwc, c) => new { vwc, c })
|
|
.Any(x => x.vwc.VoiceWorkId == q.VoiceWork.VoiceWorkId && x.c.Blacklisted)
|
|
),
|
|
|
|
_ => query
|
|
};
|
|
}
|
|
|
|
private IQueryable<VoiceWorkQuery> ApplyTagIdsFilter(IQueryable<VoiceWorkQuery> filteredQuery, VoiceWorkSearchCriteria criteria)
|
|
{
|
|
if (criteria.TagIds.Length == 0)
|
|
return filteredQuery;
|
|
|
|
if (criteria.IncludeAllTags == false)
|
|
{
|
|
var tagQuery =
|
|
from voiceWorkTag in context.VoiceWorkTags.AsNoTracking()
|
|
where criteria.TagIds.Contains(voiceWorkTag.TagId)
|
|
select new { voiceWorkTag };
|
|
|
|
var finalTagQuery = tagQuery.Select(x => x.voiceWorkTag.VoiceWorkId);
|
|
|
|
filteredQuery = filteredQuery.Where(x => finalTagQuery.Contains(x.VoiceWork.VoiceWorkId));
|
|
}
|
|
else
|
|
{
|
|
foreach (int tagId in criteria.TagIds)
|
|
{
|
|
var tagIdQuery =
|
|
from voiceWorkTag in context.VoiceWorkTags.AsNoTracking()
|
|
where voiceWorkTag.TagId == tagId
|
|
select voiceWorkTag.VoiceWorkId;
|
|
|
|
filteredQuery =
|
|
from query in filteredQuery
|
|
join voiceWorkId in tagIdQuery on query.VoiceWork.VoiceWorkId equals voiceWorkId
|
|
select new VoiceWorkQuery
|
|
{
|
|
VoiceWork = query.VoiceWork,
|
|
EnglishVoiceWork = query.EnglishVoiceWork,
|
|
Circle = query.Circle
|
|
};
|
|
}
|
|
}
|
|
|
|
return filteredQuery;
|
|
}
|
|
|
|
private IQueryable<VoiceWorkQuery> ApplyCreatorIdsFilter(IQueryable<VoiceWorkQuery> filteredQuery, VoiceWorkSearchCriteria criteria)
|
|
{
|
|
if (criteria.CreatorIds.Length == 0)
|
|
return filteredQuery;
|
|
|
|
if (criteria.IncludeAllCreators == false)
|
|
{
|
|
var creatorQuery =
|
|
from voiceWorkCreator in context.VoiceWorkCreators.AsNoTracking()
|
|
where criteria.CreatorIds.Contains(voiceWorkCreator.CreatorId)
|
|
select new { voiceWorkCreator };
|
|
|
|
var finalCreatorQuery = creatorQuery.Select(x => x.voiceWorkCreator.VoiceWorkId);
|
|
|
|
filteredQuery = filteredQuery.Where(x => finalCreatorQuery.Contains(x.VoiceWork.VoiceWorkId));
|
|
}
|
|
else
|
|
{
|
|
foreach (int creatorId in criteria.CreatorIds)
|
|
{
|
|
var creatorIdQuery =
|
|
from voiceWorkCreator in context.VoiceWorkCreators.AsNoTracking()
|
|
where voiceWorkCreator.CreatorId == creatorId
|
|
select voiceWorkCreator.VoiceWorkId;
|
|
|
|
filteredQuery =
|
|
from query in filteredQuery
|
|
join voiceWorkId in creatorIdQuery on query.VoiceWork.VoiceWorkId equals voiceWorkId
|
|
select new VoiceWorkQuery
|
|
{
|
|
VoiceWork = query.VoiceWork,
|
|
EnglishVoiceWork = query.EnglishVoiceWork,
|
|
Circle = query.Circle
|
|
};
|
|
}
|
|
}
|
|
|
|
return filteredQuery;
|
|
}
|
|
|
|
protected override IEnumerable<(Expression<Func<VoiceWorkQuery, object>> Selector, SortDirection Dir)> GetDefaultSortChain()
|
|
{
|
|
yield return (x => x.VoiceWork.ProductId, SortDirection.Descending);
|
|
}
|
|
|
|
protected override Expression<Func<VoiceWorkQuery, object?>> GetSortExpression(VoiceWorkSortField field) => field switch
|
|
{
|
|
VoiceWorkSortField.ExpectedReleaseDate => x => x.VoiceWork.ExpectedDate,
|
|
VoiceWorkSortField.ScheduledReleaseDate => x => x.VoiceWork.PlannedReleaseDate,
|
|
VoiceWorkSortField.ReleaseDate => x => x.VoiceWork.SalesDate,
|
|
VoiceWorkSortField.AnyReleaseDate => x => x.VoiceWork.SalesDate ?? x.VoiceWork.PlannedReleaseDate ?? x.VoiceWork.ExpectedDate,
|
|
VoiceWorkSortField.Downloads => x => x.VoiceWork.Downloads ?? 0,
|
|
VoiceWorkSortField.WishlistCount => x => x.VoiceWork.WishlistCount ?? 0,
|
|
VoiceWorkSortField.StarRating => x => x.VoiceWork.StarRating ?? 0,
|
|
VoiceWorkSortField.FavoriteCircle => x => !x.Circle.Favorite,
|
|
_ => x => x.VoiceWork.ProductId
|
|
};
|
|
|
|
protected override IQueryable<int> GetSelectIdQuery(IOrderedQueryable<VoiceWorkQuery> query)
|
|
{
|
|
return query.Select(x => x.VoiceWork.VoiceWorkId);
|
|
}
|
|
|
|
protected override IQueryable<VoiceWorkSearchResult> GetSelectQuery(IOrderedQueryable<VoiceWorkQuery> query)
|
|
{
|
|
var result =
|
|
from q in query
|
|
let voiceWork = q.VoiceWork
|
|
let englishVoiceWork = q.EnglishVoiceWork
|
|
let circle = q.Circle
|
|
let productLinkPage = voiceWork.SalesDate.HasValue ? "work" : "announce"
|
|
select new VoiceWorkSearchResult()
|
|
{
|
|
VoiceWorkId = voiceWork.VoiceWorkId,
|
|
ProductId = voiceWork.ProductId,
|
|
OriginalProductId = voiceWork.OriginalProductId,
|
|
ProductName = englishVoiceWork != null ? englishVoiceWork.ProductName : voiceWork.ProductName,
|
|
ProductUrl = "http://www.dlsite.com/maniax/" + productLinkPage + "/=/product_id/" + voiceWork.ProductId + ".html",
|
|
Description = englishVoiceWork != null ? englishVoiceWork.Description : voiceWork.Description,
|
|
Favorite = voiceWork.Favorite,
|
|
HasImage = voiceWork.HasImage,
|
|
Maker = circle.Name,
|
|
MakerId = circle.MakerId,
|
|
ExpectedDate = voiceWork.ExpectedDate,
|
|
SalesDate = voiceWork.SalesDate,
|
|
PlannedReleaseDate = voiceWork.PlannedReleaseDate,
|
|
Downloads = voiceWork.Downloads,
|
|
WishlistCount = voiceWork.WishlistCount,
|
|
Status = voiceWork.Status,
|
|
SubtitleLanguage = voiceWork.SubtitleLanguage,
|
|
HasTrial = voiceWork.HasTrial,
|
|
HasChobit = voiceWork.HasChobit,
|
|
IsValid = voiceWork.IsValid
|
|
};
|
|
|
|
return result;
|
|
}
|
|
|
|
protected override async Task<Dictionary<int, VoiceWorkSearchResult>> GetItems(int[] ids)
|
|
{
|
|
var result =
|
|
from voiceWork in context.VoiceWorks.AsNoTracking()
|
|
join englishVoiceWork in context.EnglishVoiceWorks.AsNoTracking() on voiceWork.VoiceWorkId equals englishVoiceWork.VoiceWorkId into ps
|
|
from englishVoiceWork in ps.DefaultIfEmpty()
|
|
join circle in context.Circles.AsNoTracking() on voiceWork.CircleId equals circle.CircleId into cs
|
|
from circle in cs.DefaultIfEmpty()
|
|
let productLinkPage = voiceWork.SalesDate.HasValue ? "work" : "announce"
|
|
where ids.Contains(voiceWork.VoiceWorkId)
|
|
select new VoiceWorkSearchResult()
|
|
{
|
|
VoiceWorkId = voiceWork.VoiceWorkId,
|
|
ProductId = voiceWork.ProductId,
|
|
OriginalProductId = voiceWork.OriginalProductId,
|
|
ProductName = englishVoiceWork != null ? englishVoiceWork.ProductName : voiceWork.ProductName,
|
|
ProductUrl = "http://www.dlsite.com/maniax/" + productLinkPage + "/=/product_id/" + voiceWork.ProductId + ".html",
|
|
Description = englishVoiceWork != null ? englishVoiceWork.Description : voiceWork.Description,
|
|
Favorite = voiceWork.Favorite,
|
|
HasImage = voiceWork.HasImage,
|
|
Maker = circle.Name,
|
|
MakerId = circle.MakerId,
|
|
ExpectedDate = voiceWork.ExpectedDate,
|
|
SalesDate = voiceWork.SalesDate,
|
|
PlannedReleaseDate = voiceWork.PlannedReleaseDate,
|
|
Downloads = voiceWork.Downloads,
|
|
WishlistCount = voiceWork.WishlistCount,
|
|
Status = voiceWork.Status,
|
|
SubtitleLanguage = voiceWork.SubtitleLanguage,
|
|
HasTrial = voiceWork.HasTrial,
|
|
HasChobit = voiceWork.HasChobit,
|
|
IsValid = voiceWork.IsValid
|
|
};
|
|
|
|
return await result.ToDictionaryAsync(x => x.VoiceWorkId);
|
|
}
|
|
|
|
protected override async Task PostLoadAsync(IList<VoiceWorkSearchResult> items, CancellationToken cancellationToken)
|
|
{
|
|
if (items.Count == 0)
|
|
return;
|
|
|
|
int[] voiceWorkIds = [.. items.Select(i => i.VoiceWorkId)];
|
|
|
|
Dictionary<int, VoiceWorkTagItem[]> tagsByVw = await GetTagsAsync(voiceWorkIds, cancellationToken);
|
|
Dictionary<int, VoiceWorkCreatorItem[]> creatorsByVw = await GetCreatorsAsync(voiceWorkIds, cancellationToken);
|
|
|
|
foreach (VoiceWorkSearchResult item in items)
|
|
{
|
|
if (tagsByVw.TryGetValue(item.VoiceWorkId, out VoiceWorkTagItem[]? tags))
|
|
item.Tags = tags;
|
|
|
|
if (creatorsByVw.TryGetValue(item.VoiceWorkId, out VoiceWorkCreatorItem[]? creators))
|
|
item.Creators = creators;
|
|
}
|
|
}
|
|
|
|
private async Task<Dictionary<int, VoiceWorkTagItem[]>> GetTagsAsync(int[] voiceWorkIds, CancellationToken cancellationToken)
|
|
{
|
|
var tagRows = await (
|
|
from voiceWorkTag in context.VoiceWorkTags.AsNoTracking()
|
|
join tag in context.Tags.AsNoTracking() on voiceWorkTag.TagId equals tag.TagId
|
|
join englishTag in context.EnglishTags.AsNoTracking() on tag.TagId equals englishTag.TagId into et
|
|
from englishTag in et.DefaultIfEmpty()
|
|
where voiceWorkIds.Contains(voiceWorkTag.VoiceWorkId)
|
|
orderby voiceWorkTag.VoiceWorkId, voiceWorkTag.Position
|
|
select new { voiceWorkTag.VoiceWorkId, voiceWorkTag.TagId, tag.Name, EnglishName = englishTag.Name }
|
|
).ToListAsync(cancellationToken);
|
|
|
|
return tagRows
|
|
.GroupBy(r => r.VoiceWorkId)
|
|
.ToDictionary(
|
|
g => g.Key,
|
|
g => g.Select(r => new VoiceWorkTagItem { TagId = r.TagId, Name = r.EnglishName ?? r.Name }).ToArray()
|
|
);
|
|
}
|
|
|
|
private async Task<Dictionary<int, VoiceWorkCreatorItem[]>> GetCreatorsAsync(int[] voiceWorkIds, CancellationToken cancellationToken)
|
|
{
|
|
var creatorRows = await (
|
|
from voiceWorkCreator in context.VoiceWorkCreators.AsNoTracking()
|
|
join creator in context.Creators.AsNoTracking() on voiceWorkCreator.CreatorId equals creator.CreatorId
|
|
where voiceWorkIds.Contains(voiceWorkCreator.VoiceWorkId)
|
|
orderby voiceWorkCreator.VoiceWorkId, voiceWorkCreator.Position
|
|
select new { voiceWorkCreator.VoiceWorkId, creator.CreatorId, creator.Name }
|
|
).ToListAsync(cancellationToken);
|
|
|
|
return creatorRows
|
|
.GroupBy(r => r.VoiceWorkId)
|
|
.ToDictionary(
|
|
g => g.Key,
|
|
g => g.Select(r => new VoiceWorkCreatorItem { CreatorId = r.CreatorId, Name = r.Name }).ToArray()
|
|
);
|
|
}
|
|
} |