[backend/core] Add in:interactions search query filter, allow multiple from: filters
This commit is contained in:
parent
af5e5752d5
commit
1d02bd7119
4 changed files with 49 additions and 9 deletions
|
@ -4,6 +4,25 @@ namespace Iceshrimp.Backend.Core.Extensions;
|
|||
|
||||
public static class ExpressionExtensions
|
||||
{
|
||||
public static Expression<Func<T, bool>> True<T>() => f => true;
|
||||
public static Expression<Func<T, bool>> False<T>() => f => false;
|
||||
|
||||
public static Expression<Func<T, bool>> Or<T>(
|
||||
this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2
|
||||
)
|
||||
{
|
||||
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters);
|
||||
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
|
||||
}
|
||||
|
||||
public static Expression<Func<T, bool>> And<T>(
|
||||
this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2
|
||||
)
|
||||
{
|
||||
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters);
|
||||
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
|
||||
}
|
||||
|
||||
public static Expression<Func<TFirstParam, TResult>> Compose<TFirstParam, TIntermediate, TResult>(
|
||||
this Expression<Func<TFirstParam, TIntermediate>> first,
|
||||
Expression<Func<TIntermediate, TResult>> second
|
||||
|
|
|
@ -21,14 +21,16 @@ public static class QueryableFtsExtensions
|
|||
var caseSensitivity = parsed.OfType<CaseFilter>().LastOrDefault()?.Value ?? CaseFilterType.Insensitive;
|
||||
var matchType = parsed.OfType<MatchFilter>().LastOrDefault()?.Value ?? MatchFilterType.Substring;
|
||||
|
||||
query = query.ApplyFromFilters(parsed.OfType<FromFilter>().ToList(), config, db);
|
||||
|
||||
return parsed.Aggregate(query, (current, filter) => filter switch
|
||||
{
|
||||
CaseFilter => current,
|
||||
MatchFilter => current,
|
||||
FromFilter => current,
|
||||
AfterFilter afterFilter => current.ApplyAfterFilter(afterFilter),
|
||||
AttachmentFilter attachmentFilter => current.ApplyAttachmentFilter(attachmentFilter),
|
||||
BeforeFilter beforeFilter => current.ApplyBeforeFilter(beforeFilter),
|
||||
FromFilter fromFilter => current.ApplyFromFilter(fromFilter, config, db),
|
||||
InFilter inFilter => current.ApplyInFilter(inFilter, user, db),
|
||||
InstanceFilter instanceFilter => current.ApplyInstanceFilter(instanceFilter, config),
|
||||
MentionFilter mentionFilter => current.ApplyMentionFilter(mentionFilter, config, db),
|
||||
|
@ -75,10 +77,16 @@ public static class QueryableFtsExtensions
|
|||
this IQueryable<Note> query, MultiWordFilter filter, CaseFilterType caseSensitivity, MatchFilterType matchType
|
||||
) => query.Where(p => p.FtsQueryOneOf(filter.Values, caseSensitivity, matchType));
|
||||
|
||||
[Projectable]
|
||||
private static IQueryable<Note> ApplyFromFilter(
|
||||
this IQueryable<Note> query, FromFilter filter, Config.InstanceSection config, DatabaseContext db
|
||||
) => query.Where(p => p.User.UserSubqueryMatches(filter.Value, filter.Negated, config, db));
|
||||
private static IQueryable<Note> ApplyFromFilters(
|
||||
this IQueryable<Note> query, List<FromFilter> filters, Config.InstanceSection config, DatabaseContext db
|
||||
)
|
||||
{
|
||||
if (filters.Count == 0) return query;
|
||||
var expr = ExpressionExtensions.False<Note>();
|
||||
expr = filters.Aggregate(expr, (current, filter) => current
|
||||
.Or(p => p.User.UserSubqueryMatches(filter.Value, filter.Negated, config, db)));
|
||||
return query.Where(expr);
|
||||
}
|
||||
|
||||
[Projectable]
|
||||
private static IQueryable<Note> ApplyInstanceFilter(
|
||||
|
@ -105,7 +113,9 @@ public static class QueryableFtsExtensions
|
|||
? query.ApplyInBookmarksFilter(user, filter.Negated, db)
|
||||
: filter.Value.Equals(InFilterType.Likes)
|
||||
? query.ApplyInLikesFilter(user, filter.Negated, db)
|
||||
: query.ApplyInReactionsFilter(user, filter.Negated, db);
|
||||
: filter.Value.Equals(InFilterType.Reactions)
|
||||
? query.ApplyInReactionsFilter(user, filter.Negated, db)
|
||||
: query.ApplyInInteractionsFilter(user, filter.Negated, db);
|
||||
|
||||
[Projectable]
|
||||
[SuppressMessage("ReSharper", "EntityFramework.UnsupportedServerSideFunctionCall", Justification = "Projectables")]
|
||||
|
@ -131,6 +141,14 @@ public static class QueryableFtsExtensions
|
|||
? !db.Users.First(u => u == user).HasReacted(p)
|
||||
: db.Users.First(u => u == user).HasReacted(p));
|
||||
|
||||
[Projectable]
|
||||
[SuppressMessage("ReSharper", "EntityFramework.UnsupportedServerSideFunctionCall", Justification = "Projectables")]
|
||||
internal static IQueryable<Note> ApplyInInteractionsFilter(
|
||||
this IQueryable<Note> query, User user, bool negated, DatabaseContext db
|
||||
) => query.Where(p => negated
|
||||
? !db.Users.First(u => u == user).HasInteractedWith(p)
|
||||
: db.Users.First(u => u == user).HasInteractedWith(p));
|
||||
|
||||
[Projectable]
|
||||
private static IQueryable<Note> ApplyMiscFilter(
|
||||
this IQueryable<Note> query, MiscFilter filter, User user
|
||||
|
|
|
@ -63,6 +63,7 @@ module SearchQueryFilters =
|
|||
| Bookmarks
|
||||
| Likes
|
||||
| Reactions
|
||||
| Interactions
|
||||
|
||||
type InFilter(neg: bool, value: string) =
|
||||
inherit Filter()
|
||||
|
@ -75,6 +76,7 @@ module SearchQueryFilters =
|
|||
| "favorites" -> Likes
|
||||
| "favourites" -> Likes
|
||||
| "reactions" -> Reactions
|
||||
| "interactions" -> Interactions
|
||||
| _ -> failwith $"Invalid type: {value}"
|
||||
|
||||
|
||||
|
@ -204,7 +206,7 @@ module private SearchQueryParser =
|
|||
<| fun n v -> MiscFilter(n.IsSome, v) :> Filter
|
||||
|
||||
let inFilter =
|
||||
negKeyFilter [ "in" ] [ "bookmarks"; "favorites"; "favourites"; "reactions"; "likes" ]
|
||||
negKeyFilter [ "in" ] [ "bookmarks"; "favorites"; "favourites"; "reactions"; "likes"; "interactions" ]
|
||||
<| fun n v -> InFilter(n.IsSome, v) :> Filter
|
||||
|
||||
let attachmentFilter =
|
||||
|
|
|
@ -138,7 +138,7 @@ public class SearchQueryTests
|
|||
public void TestParseIn(bool negated)
|
||||
{
|
||||
var key = negated ? "-in" : "in";
|
||||
List<string> candidates = ["bookmarks", "likes", "favorites", "favourites", "reactions"];
|
||||
List<string> candidates = ["bookmarks", "likes", "favorites", "favourites", "reactions", "interactions"];
|
||||
var results = candidates.Select(v => $"{key}:{v}").SelectMany(SearchQuery.parse).ToList();
|
||||
List<Filter> expectedResults =
|
||||
[
|
||||
|
@ -146,7 +146,8 @@ public class SearchQueryTests
|
|||
new InFilter(negated, "likes"),
|
||||
new InFilter(negated, "likes"),
|
||||
new InFilter(negated, "likes"),
|
||||
new InFilter(negated, "reactions")
|
||||
new InFilter(negated, "reactions"),
|
||||
new InFilter(negated, "interactions")
|
||||
];
|
||||
results.Should()
|
||||
.HaveCount(expectedResults.Count)
|
||||
|
|
Loading…
Add table
Reference in a new issue