Files
caliverse_server/UGQDataAccess/Repository/Query/QuestContentQuery.cs
2025-05-01 07:20:41 +09:00

564 lines
22 KiB
C#

using System.Linq.Expressions;
using MongoDB.Bson;
using MongoDB.Driver;
using UGQDataAccess.Repository.Models;
using UGQDatabase.Models;
namespace UGQDataAccess.Repository.Query;
public static class QuestContentQuery
{
public class QuestBoardQueryJoin : QuestContentEntity
{
public IEnumerable<LikeEntity> Likes { get; set; } = null!;
public int LikeCount { get; set; } = 0;
public IEnumerable<BookmarkEntity> Bookmarks { get; set; } = null!;
public int BookmarkCount { get; set; } = 0;
public List<QuestAcceptedEntity> QuestAccepteds { get; set; } = null!;
public int QuestAcceptedCount { get; set; } = 0;
public List<QuestAcceptedEntity> QuestCompleteds { get; set; } = null!;
public int QuestCompletedCount { get; set; } = 0;
}
public class QuestSummariesQueryJoin : QuestContentEntity
{
public IEnumerable<LikeEntity> Likes { get; set; } = null!;
public int LikeCount { get; set; } = 0;
public IEnumerable<BookmarkEntity> Bookmarks { get; set; } = null!;
public int BookmarkCount { get; set; } = 0;
public List<QuestAcceptedEntity> QuestAccepteds { get; set; } = null!;
public int QuestAcceptedCount { get; set; } = 0;
public List<QuestAcceptedEntity> QuestCompleteds { get; set; } = null!;
public int QuestCompletedCount { get; set; } = 0;
public List<QuestAcceptedEntity> QuestAborteds { get; set; } = null!;
public int QuestAbortedCount { get; set; } = 0;
public List<ReportEntity> Reports { get; set; } = null!;
public int ReporCount { get; set; } = 0;
public List<CreatorPointHistoryEntity> ProfitHistories { get; set; } = null!;
public double TotalProfit { get; set; } = 0;
}
public class QuestProfitStatsQueryJoin : QuestContentEntity
{
public List<QuestAcceptedEntity> QuestCompleteds { get; set; } = null!;
public int QuestCompletedCount { get; set; } = 0;
public List<CreatorPointHistoryEntity> ProfitHistories { get; set; } = null!;
public double TotalProfit { get; set; } = 0;
}
public class QuestAcceptedLookup : QuestAcceptedEntity
{
}
public static Expression<Func<QuestBoardQueryJoin, QuestBoardItemResult>> QuestBoardItemResultProjection =
x => new QuestBoardItemResult
{
QuestId = x.QuestId,
Revision = x.Revision,
Author = x.Author,
Title = x.Title,
Languages = x.Langs,
Cost = x.Cost,
GradeType = x.GradeType,
TitleImagePath = x.TitleImagePath,
BannerImagePath = x.BannerImagePath,
UpdatedAt = x.UpdatedAt,
LikeCount = x.LikeCount,
BookmarkCount = x.BookmarkCount,
};
public static Expression<Func<QuestSummariesQueryJoin, SummaryItemResult>> SummaryItemResultProjection =
x => new SummaryItemResult
{
QuestContentId = x.Id.ToString(),
QuestId = x.QuestId,
Revision = x.Revision,
BeaconId = x.BeaconId,
UserGuid = x.UserGuid,
Author = x.Author,
UgcBeaconGuid = x.UgcBeaconGuid,
UgcBeaconNickname = x.UgcBeaconNickname,
GradeType = x.GradeType,
Title = x.Title,
Languages = x.Langs,
Description = x.Description,
Cost = x.Cost,
TitleImagePath = x.TitleImagePath,
BannerImagePath = x.BannerImagePath,
State = x.State,
UpdatedAt = x.UpdatedAt,
CreatedAt = x.CreatedAt,
AcceptedCount = x.QuestAcceptedCount,
CompletedCount = x.QuestCompletedCount,
LikeCount = x.LikeCount,
BookmarkCount = x.BookmarkCount,
ReportCount = x.ReporCount,
TotalProfit = x.TotalProfit,
Savelanguage = x.Savelanguage,
};
public static Expression<Func<QuestProfitStatsQueryJoin, QuestProfitStatsItemResult>> QuestProfitStatsResultProjection =
x => new QuestProfitStatsItemResult
{
QuestContentId = x.Id.ToString(),
QuestId = x.QuestId,
Revision = x.Revision,
Title = x.Title,
Languages = x.Langs,
TitleImagePath = x.TitleImagePath,
BannerImagePath = x.BannerImagePath,
CompletedCount = x.QuestCompletedCount,
TotalProfit = x.TotalProfit,
};
static List<UgqGradeType> getGradeTypes(UgqUICategoryGradeType gradeType)
{
switch (gradeType)
{
case UgqUICategoryGradeType.Amateur:
return [UgqGradeType.Amature];
case UgqUICategoryGradeType.RisingStar:
return [UgqGradeType.RisingStar];
case UgqUICategoryGradeType.Master:
return [UgqGradeType.Master1, UgqGradeType.Master2, UgqGradeType.Master3];
}
return [];
}
static async Task<List<long>> getBookmarks(IMongoCollection<BookmarkEntity> bookmarkCollection, string userGuid)
{
var filterBuilder = Builders<BookmarkEntity>.Filter;
var filter = filterBuilder.Eq(x => x.UserGuid, userGuid);
var list = await bookmarkCollection.Find(filter).ToListAsync();
return list.Select(x => x.QuestId).ToList();
}
static async Task<List<int>> searchNpcs(IMongoCollection<NpcNameEntity> npcNameCollection, string searchText)
{
var filterBuilder = Builders<NpcNameEntity>.Filter;
var filter = filterBuilder.Empty;
if (string.IsNullOrEmpty(searchText) == false)
{
filter &= (filterBuilder.Regex(x => x.NpcName.Kr, searchText) |
filterBuilder.Regex(x => x.NpcName.En, searchText) |
filterBuilder.Regex(x => x.NpcName.Jp, searchText));
}
var list = await npcNameCollection.Find(filter).ToListAsync();
return list.Select(x => x.NpcId).ToList();
}
static FilterDefinition<QuestContentEntity> searchTitle(FilterDefinitionBuilder<QuestContentEntity> filterBuilder, string searchText)
{
return filterBuilder.Regex(x => x.Title.Kr, searchText) |
filterBuilder.Regex(x => x.Title.En, searchText) |
filterBuilder.Regex(x => x.Title.Jp, searchText);
}
public static async Task<FilterDefinition<QuestContentEntity>> summariesFilter(string? userGuid,
UgqSearchType searchType, string? searchText, QuestContentState state,
IMongoCollection<AccountEntity> accountCollection,
IMongoCollection<NpcNameEntity> npcNameCollection)
{
var filterBuilder = Builders<QuestContentEntity>.Filter;
var filter = filterBuilder.Eq(x => x.IsDeleted, false);
if (state != QuestContentState.None)
{
if(state == QuestContentState.Uncomplate ||
state == QuestContentState.Editable)
{
var UncomplateOrFilter = filterBuilder.Eq(x => x.State, QuestContentState.Uncomplate);
var EditableOrFilter = filterBuilder.Eq(x => x.State, QuestContentState.Editable);
filter &= filterBuilder.Or(UncomplateOrFilter, EditableOrFilter);
}
else
{
filter &= filterBuilder.Eq(x => x.State, state);
}
}
if (string.IsNullOrEmpty(userGuid) == false)
filter &= filterBuilder.Eq(x => x.UserGuid, userGuid);
if (string.IsNullOrEmpty(searchText) == false)
{
switch (searchType)
{
case UgqSearchType.Title:
filter &= searchTitle(filterBuilder, searchText);
break;
case UgqSearchType.Beacon:
var npcIds = await searchNpcs(npcNameCollection, searchText);
filter &= (filterBuilder.In(x => x.BeaconId, npcIds) |
filterBuilder.Regex(x => x.UgcBeaconNickname, searchText));
break;
}
}
return filter;
}
public static async Task<FilterDefinition<QuestContentEntity>> questBoardFilter(string? userGuid,
UgqUICategoryGradeType gradeType, UgqSearchType searchType, string? searchText,
IMongoCollection<AccountEntity> accountCollection,
IMongoCollection<NpcNameEntity> npcNameCollection)
{
var filterBuilder = Builders<QuestContentEntity>.Filter;
var filter = filterBuilder.Eq(x => x.IsDeleted, false);
filter &= filterBuilder.Eq(x => x.State, QuestContentState.Live);
if (string.IsNullOrEmpty(userGuid) == false)
filter &= filterBuilder.Eq(x => x.UserGuid, userGuid);
if(gradeType != UgqUICategoryGradeType.None)
{
var gradeTypes = getGradeTypes(gradeType);
filter &= filterBuilder.In(x => x.GradeType, gradeTypes);
}
if (string.IsNullOrEmpty(searchText) == false)
{
switch (searchType)
{
case UgqSearchType.Title:
filter &= searchTitle(filterBuilder, searchText);
break;
case UgqSearchType.Beacon:
var npcIds = await searchNpcs(npcNameCollection, searchText);
filter &= (filterBuilder.In(x => x.BeaconId, npcIds) |
filterBuilder.Regex(x => x.UgcBeaconNickname, searchText));
break;
}
}
return filter;
}
public static async Task<FilterDefinition<QuestContentEntity>> questBoardBookmarkFilter(string userGuid,
UgqUICategoryGradeType gradeType, UgqSearchType searchType, string? searchText,
IMongoCollection<NpcNameEntity> npcNameCollection,
IMongoCollection<BookmarkEntity> bookmarkCollection)
{
var filterBuilder = Builders<QuestContentEntity>.Filter;
var filter = filterBuilder.Eq(x => x.IsDeleted, false);
filter &= filterBuilder.Eq(x => x.State, QuestContentState.Live);
if (gradeType != UgqUICategoryGradeType.None)
{
var gradeTypes = getGradeTypes(gradeType);
filter &= filterBuilder.In(x => x.GradeType, gradeTypes);
}
var bookmarkQuestIds = await getBookmarks(bookmarkCollection, userGuid);
filter &= filterBuilder.In(x => x.QuestId, bookmarkQuestIds);
if (string.IsNullOrEmpty(searchText) == false)
{
switch (searchType)
{
case UgqSearchType.Title:
filter &= searchTitle(filterBuilder, searchText);
break;
case UgqSearchType.Beacon:
var npcIds = await searchNpcs(npcNameCollection, searchText);
filter &= (filterBuilder.In(x => x.BeaconId, npcIds) |
filterBuilder.Regex(x => x.UgcBeaconNickname, searchText));
break;
}
}
return filter;
}
public static SortDefinition<T> questBoardSort<T>(UgqSortType sortType)
{
var sortBuilder = Builders<T>.Sort;
SortDefinition<T> sort;
switch (sortType)
{
case UgqSortType.New:
sort = Builders<T>.Sort.Descending("UpdatedAt");
break;
case UgqSortType.Like:
sort = Builders<T>.Sort.Descending("LikeCount");
break;
case UgqSortType.Bookmark:
sort = Builders<T>.Sort.Descending("BookmarkCount");
break;
default:
sort = Builders<T>.Sort.Descending("UpdatedAt");
break;
}
return sort;
}
public static PipelineDefinition<QuestContentEntity, T> summariesPipeline<T>(
IMongoCollection<LikeEntity> likeCollection,
IMongoCollection<BookmarkEntity> bookmarkCollection,
FilterDefinition<QuestContentEntity> filter,
Expression<Func<QuestSummariesQueryJoin, T>> projection)
{
var lookupStage1 = new BsonDocument("$lookup",
new BsonDocument
{
{ "from", "QuestAccepted" },
{ "localField", "QuestId" },
{ "foreignField", "QuestId" },
{ "pipeline",
new BsonArray
{
new BsonDocument("$match",
new BsonDocument("Reason", "Player"))
} },
{ "as", "QuestAccepteds" }
});
var addFields1 = new BsonDocument("$addFields",
new BsonDocument("QuestAcceptedCount",
new BsonDocument("$size", "$QuestAccepteds")));
var lookupStage2 = new BsonDocument("$lookup",
new BsonDocument
{
{ "from", "QuestCompleted" },
{ "localField", "QuestId" },
{ "foreignField", "QuestId" },
{ "as", "QuestCompleteds" }
});
var addFields2 = new BsonDocument("$addFields",
new BsonDocument("QuestCompletedCount",
new BsonDocument("$size", "$QuestCompleteds")));
var lookupStage3 = new BsonDocument("$lookup",
new BsonDocument
{
{ "from", "QuestAborted" },
{ "localField", "QuestId" },
{ "foreignField", "QuestId" },
{ "as", "QuestAborteds" }
});
var addFields3 = new BsonDocument("$addFields",
new BsonDocument("QuestAbortedCount",
new BsonDocument("$size", "$QuestAborteds")));
var lookupStage4 = new BsonDocument("$lookup",
new BsonDocument
{
{ "from", "CreatorPointHistory" },
{ "localField", "QuestId" },
{ "foreignField", "QuestId" },
{ "pipeline",
new BsonArray
{
new BsonDocument("$match",
new BsonDocument("Kind", "QuestProfit"))
} },
{ "as", "ProfitHistories" }
});
var addFields4 = new BsonDocument("$addFields",
new BsonDocument("TotalProfit",
new BsonDocument("$sum", "$ProfitHistories.Amount")));
var lookupStage5 = new BsonDocument("$lookup",
new BsonDocument
{
{ "from", "Report" },
{ "localField", "QuestId" },
{ "foreignField", "QuestId" },
{ "as", "Reports" }
});
var addFields5 = new BsonDocument("$addFields",
new BsonDocument("ReporCount",
new BsonDocument("$size", "$Reports")));
var pipeline = new EmptyPipelineDefinition<QuestContentEntity>()
.Match(filter)
.Lookup<QuestContentEntity, QuestContentEntity, LikeEntity, QuestSummariesQueryJoin>(
likeCollection,
x => x.QuestId,
x => x.QuestId,
x => x.Likes)
.Lookup<QuestContentEntity, QuestSummariesQueryJoin, BookmarkEntity, QuestSummariesQueryJoin>(
bookmarkCollection,
x => x.QuestId,
x => x.QuestId,
x => x.Bookmarks)
.AppendStage<QuestContentEntity, QuestSummariesQueryJoin, QuestSummariesQueryJoin>("{$addFields: {LikeCount: {$size:'$Likes'}}}")
.AppendStage<QuestContentEntity, QuestSummariesQueryJoin, QuestSummariesQueryJoin>("{$addFields: {BookmarkCount: {$size:'$Bookmarks'}}}")
.AppendStage<QuestContentEntity, QuestSummariesQueryJoin, QuestSummariesQueryJoin>(lookupStage1)
.AppendStage<QuestContentEntity, QuestSummariesQueryJoin, QuestSummariesQueryJoin>(addFields1)
.AppendStage<QuestContentEntity, QuestSummariesQueryJoin, QuestSummariesQueryJoin>(lookupStage2)
.AppendStage<QuestContentEntity, QuestSummariesQueryJoin, QuestSummariesQueryJoin>(addFields2)
.AppendStage<QuestContentEntity, QuestSummariesQueryJoin, QuestSummariesQueryJoin>(lookupStage3)
.AppendStage<QuestContentEntity, QuestSummariesQueryJoin, QuestSummariesQueryJoin>(addFields3)
.AppendStage<QuestContentEntity, QuestSummariesQueryJoin, QuestSummariesQueryJoin>(lookupStage4)
.AppendStage<QuestContentEntity, QuestSummariesQueryJoin, QuestSummariesQueryJoin>(addFields4)
.AppendStage<QuestContentEntity, QuestSummariesQueryJoin, QuestSummariesQueryJoin>(lookupStage5)
.AppendStage<QuestContentEntity, QuestSummariesQueryJoin, QuestSummariesQueryJoin>(addFields5)
.Project(projection);
return pipeline;
}
public static PipelineDefinition<QuestContentEntity, T> questProfitStatsPipeline<T>(
FilterDefinition<QuestContentEntity> filter,
Expression<Func<QuestProfitStatsQueryJoin, T>> projection)
{
var lookupStage1 = new BsonDocument("$lookup",
new BsonDocument
{
{ "from", "QuestCompleted" },
{ "localField", "QuestId" },
{ "foreignField", "QuestId" },
{ "as", "QuestCompleteds" }
});
var addFields1 = new BsonDocument("$addFields",
new BsonDocument("QuestCompletedCount",
new BsonDocument("$size", "$QuestCompleteds")));
var lookupStage2 = new BsonDocument("$lookup",
new BsonDocument
{
{ "from", "CreatorPointHistory" },
{ "localField", "QuestId" },
{ "foreignField", "QuestId" },
{ "pipeline",
new BsonArray
{
new BsonDocument("$match",
new BsonDocument("Kind", "QuestProfit"))
} },
{ "as", "ProfitHistories" }
});
var addFields2 = new BsonDocument("$addFields",
new BsonDocument("TotalProfit",
new BsonDocument("$sum", "$ProfitHistories.Amount")));
var pipeline = new EmptyPipelineDefinition<QuestContentEntity>()
.Match(filter)
.AppendStage<QuestContentEntity, QuestContentEntity, QuestProfitStatsQueryJoin>(lookupStage1)
.AppendStage<QuestContentEntity, QuestProfitStatsQueryJoin, QuestProfitStatsQueryJoin>(addFields1)
.AppendStage<QuestContentEntity, QuestProfitStatsQueryJoin, QuestProfitStatsQueryJoin>(lookupStage2)
.AppendStage<QuestContentEntity, QuestProfitStatsQueryJoin, QuestProfitStatsQueryJoin>(addFields2)
.Project(projection);
return pipeline;
}
public static PipelineDefinition<QuestContentEntity, T> questBoardPipeline<T>(
IMongoCollection<LikeEntity> likeCollection,
IMongoCollection<BookmarkEntity> bookmarkCollection,
FilterDefinition<QuestContentEntity> filter,
Expression<Func<QuestBoardQueryJoin, T>> projection)
{
var pipeline = new EmptyPipelineDefinition<QuestContentEntity>()
.Match(filter)
.Lookup<QuestContentEntity, QuestContentEntity, LikeEntity, QuestBoardQueryJoin>(
likeCollection,
x => x.QuestId,
x => x.QuestId,
x => x.Likes)
.Lookup<QuestContentEntity, QuestBoardQueryJoin, BookmarkEntity, QuestBoardQueryJoin>(
bookmarkCollection,
x => x.QuestId,
x => x.QuestId,
x => x.Bookmarks)
.AppendStage<QuestContentEntity, QuestBoardQueryJoin, QuestBoardQueryJoin>("{$addFields: {LikeCount: {$size:'$Likes'}}}")
.AppendStage<QuestContentEntity, QuestBoardQueryJoin, QuestBoardQueryJoin>("{$addFields: {BookmarkCount: {$size:'$Bookmarks'}}}")
.Project(projection);
return pipeline;
}
public static PipelineDefinition<QuestContentEntity, T> questBoardDetailPipeline<T>(
IMongoCollection<LikeEntity> likeCollection,
IMongoCollection<BookmarkEntity> bookmarkCollection,
FilterDefinition<QuestContentEntity> filter,
Expression<Func<QuestBoardQueryJoin, T>> projection)
{
var lookupStage1 = new BsonDocument("$lookup",
new BsonDocument
{
{ "from", "QuestAccepted" },
{ "localField", "QuestId" },
{ "foreignField", "QuestId" },
{ "pipeline",
new BsonArray
{
new BsonDocument("$match",
new BsonDocument("Reason", "Player"))
} },
{ "as", "QuestAccepteds" }
});
var lookupStage2 = new BsonDocument("$lookup",
new BsonDocument
{
{ "from", "QuestCompleted" },
{ "localField", "QuestId" },
{ "foreignField", "QuestId" },
{ "as", "QuestCompleteds" }
});
var pipeline = new EmptyPipelineDefinition<QuestContentEntity>()
.Match(filter)
.Lookup<QuestContentEntity, QuestContentEntity, LikeEntity, QuestBoardQueryJoin>(
likeCollection,
x => x.QuestId,
x => x.QuestId,
x => x.Likes)
.Lookup<QuestContentEntity, QuestBoardQueryJoin, BookmarkEntity, QuestBoardQueryJoin>(
bookmarkCollection,
x => x.QuestId,
x => x.QuestId,
x => x.Bookmarks)
.AppendStage<QuestContentEntity, QuestBoardQueryJoin, QuestBoardQueryJoin>(lookupStage1)
.AppendStage<QuestContentEntity, QuestBoardQueryJoin, QuestBoardQueryJoin>(lookupStage2)
.AppendStage<QuestContentEntity, QuestBoardQueryJoin, QuestBoardQueryJoin>("{$addFields: {LikeCount: {$size:'$Likes'}}}")
.AppendStage<QuestContentEntity, QuestBoardQueryJoin, QuestBoardQueryJoin>("{$addFields: {BookmarkCount: {$size:'$Bookmarks'}}}")
.AppendStage<QuestContentEntity, QuestBoardQueryJoin, QuestBoardQueryJoin>("{$addFields: {QuestAcceptedCount: {$size:'$QuestAccepteds'}}}")
.AppendStage<QuestContentEntity, QuestBoardQueryJoin, QuestBoardQueryJoin>("{$addFields: {QuestCompletedCount: {$size:'$QuestCompleteds'}}}")
.Project(projection);
return pipeline;
}
}