1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 08:13:31 +08:00

Merge pull request #22782 from OliBomby/optimise-search

Optimise carousel beatmap filter method
This commit is contained in:
Bartłomiej Dach 2023-03-07 22:43:02 +01:00 committed by GitHub
commit 9479b7c19f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 196 additions and 22 deletions

View File

@ -0,0 +1,123 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using BenchmarkDotNet.Attributes;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
namespace osu.Game.Benchmarks
{
public class BenchmarkCarouselFilter : BenchmarkTest
{
private BeatmapInfo getExampleBeatmap() => new BeatmapInfo
{
Ruleset = new RulesetInfo
{
ShortName = "osu",
OnlineID = 0
},
StarRating = 4.0d,
Difficulty = new BeatmapDifficulty
{
ApproachRate = 5.0f,
DrainRate = 3.0f,
CircleSize = 2.0f,
},
Metadata = new BeatmapMetadata
{
Artist = "The Artist",
ArtistUnicode = "check unicode too",
Title = "Title goes here",
TitleUnicode = "Title goes here",
Author = { Username = "The Author" },
Source = "unit tests",
Tags = "look for tags too",
},
DifficultyName = "version as well",
Length = 2500,
BPM = 160,
BeatDivisor = 12,
Status = BeatmapOnlineStatus.Loved
};
private CarouselBeatmap carouselBeatmap = null!;
private FilterCriteria criteria1 = null!;
private FilterCriteria criteria2 = null!;
private FilterCriteria criteria3 = null!;
private FilterCriteria criteria4 = null!;
private FilterCriteria criteria5 = null!;
private FilterCriteria criteria6 = null!;
public override void SetUp()
{
var beatmap = getExampleBeatmap();
beatmap.OnlineID = 20201010;
beatmap.BeatmapSet = new BeatmapSetInfo { OnlineID = 1535 };
carouselBeatmap = new CarouselBeatmap(beatmap);
criteria1 = new FilterCriteria();
criteria2 = new FilterCriteria
{
Ruleset = new RulesetInfo { ShortName = "catch" }
};
criteria3 = new FilterCriteria
{
Ruleset = new RulesetInfo { OnlineID = 6 },
AllowConvertedBeatmaps = true,
BPM = new FilterCriteria.OptionalRange<double>
{
IsUpperInclusive = false,
Max = 160d
}
};
criteria4 = new FilterCriteria
{
Ruleset = new RulesetInfo { OnlineID = 6 },
AllowConvertedBeatmaps = true,
SearchText = "an artist"
};
criteria5 = new FilterCriteria
{
Creator = new FilterCriteria.OptionalTextFilter { SearchTerm = "the author AND then something else" }
};
criteria6 = new FilterCriteria { SearchText = "20201010" };
}
[Benchmark]
public void CarouselBeatmapFilter()
{
carouselBeatmap.Filter(criteria1);
}
[Benchmark]
public void CriteriaMatchingSpecificRuleset()
{
carouselBeatmap.Filter(criteria2);
}
[Benchmark]
public void CriteriaMatchingRangeMax()
{
carouselBeatmap.Filter(criteria3);
}
[Benchmark]
public void CriteriaMatchingTerms()
{
carouselBeatmap.Filter(criteria4);
}
[Benchmark]
public void CriteriaMatchingCreator()
{
carouselBeatmap.Filter(criteria5);
}
[Benchmark]
public void CriteriaMatchingBeatmapIDs()
{
carouselBeatmap.Filter(criteria6);
}
}
}

View File

@ -1,7 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Linq; using System.Collections.Generic;
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
@ -29,10 +29,21 @@ namespace osu.Game.Beatmaps
return new RomanisableString($"{metadata.GetPreferred(true)}".Trim(), $"{metadata.GetPreferred(false)}".Trim()); return new RomanisableString($"{metadata.GetPreferred(true)}".Trim(), $"{metadata.GetPreferred(false)}".Trim());
} }
public static string[] GetSearchableTerms(this IBeatmapInfo beatmapInfo) => new[] public static List<string> GetSearchableTerms(this IBeatmapInfo beatmapInfo)
{ {
beatmapInfo.DifficultyName var termsList = new List<string>(BeatmapMetadataInfoExtensions.MAX_SEARCHABLE_TERM_COUNT + 1);
}.Concat(beatmapInfo.Metadata.GetSearchableTerms()).Where(s => !string.IsNullOrEmpty(s)).ToArray();
addIfNotNull(beatmapInfo.DifficultyName);
BeatmapMetadataInfoExtensions.CollectSearchableTerms(beatmapInfo.Metadata, termsList);
return termsList;
void addIfNotNull(string? s)
{
if (!string.IsNullOrEmpty(s))
termsList.Add(s);
}
}
private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]"; private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]";
} }

View File

@ -3,7 +3,7 @@
#nullable disable #nullable disable
using System.Linq; using System.Collections.Generic;
using osu.Framework.Localisation; using osu.Framework.Localisation;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
@ -13,16 +13,31 @@ namespace osu.Game.Beatmaps
/// <summary> /// <summary>
/// An array of all searchable terms provided in contained metadata. /// An array of all searchable terms provided in contained metadata.
/// </summary> /// </summary>
public static string[] GetSearchableTerms(this IBeatmapMetadataInfo metadataInfo) => new[] public static string[] GetSearchableTerms(this IBeatmapMetadataInfo metadataInfo)
{ {
metadataInfo.Author.Username, var termsList = new List<string>(MAX_SEARCHABLE_TERM_COUNT);
metadataInfo.Artist, CollectSearchableTerms(metadataInfo, termsList);
metadataInfo.ArtistUnicode, return termsList.ToArray();
metadataInfo.Title, }
metadataInfo.TitleUnicode,
metadataInfo.Source, internal const int MAX_SEARCHABLE_TERM_COUNT = 7;
metadataInfo.Tags
}.Where(s => !string.IsNullOrEmpty(s)).ToArray(); internal static void CollectSearchableTerms(IBeatmapMetadataInfo metadataInfo, IList<string> termsList)
{
addIfNotNull(metadataInfo.Author.Username);
addIfNotNull(metadataInfo.Artist);
addIfNotNull(metadataInfo.ArtistUnicode);
addIfNotNull(metadataInfo.Title);
addIfNotNull(metadataInfo.TitleUnicode);
addIfNotNull(metadataInfo.Source);
addIfNotNull(metadataInfo.Tags);
void addIfNotNull(string s)
{
if (!string.IsNullOrEmpty(s))
termsList.Add(s);
}
}
/// <summary> /// <summary>
/// A user-presentable display title representing this metadata. /// A user-presentable display title representing this metadata.

View File

@ -26,6 +26,11 @@ namespace osu.Game.Screens.Select.Carousel
{ {
base.Filter(criteria); base.Filter(criteria);
Filtered.Value = !checkMatch(criteria);
}
private bool checkMatch(FilterCriteria criteria)
{
bool match = bool match =
criteria.Ruleset == null || criteria.Ruleset == null ||
BeatmapInfo.Ruleset.ShortName == criteria.Ruleset.ShortName || BeatmapInfo.Ruleset.ShortName == criteria.Ruleset.ShortName ||
@ -34,8 +39,7 @@ namespace osu.Game.Screens.Select.Carousel
if (BeatmapInfo.BeatmapSet?.Equals(criteria.SelectedBeatmapSet) == true) if (BeatmapInfo.BeatmapSet?.Equals(criteria.SelectedBeatmapSet) == true)
{ {
// only check ruleset equality or convertability for selected beatmap // only check ruleset equality or convertability for selected beatmap
Filtered.Value = !match; return match;
return;
} }
match &= !criteria.StarDifficulty.HasFilter || criteria.StarDifficulty.IsInRange(BeatmapInfo.StarRating); match &= !criteria.StarDifficulty.HasFilter || criteria.StarDifficulty.IsInRange(BeatmapInfo.StarRating);
@ -49,18 +53,38 @@ namespace osu.Game.Screens.Select.Carousel
match &= !criteria.BeatDivisor.HasFilter || criteria.BeatDivisor.IsInRange(BeatmapInfo.BeatDivisor); match &= !criteria.BeatDivisor.HasFilter || criteria.BeatDivisor.IsInRange(BeatmapInfo.BeatDivisor);
match &= !criteria.OnlineStatus.HasFilter || criteria.OnlineStatus.IsInRange(BeatmapInfo.Status); match &= !criteria.OnlineStatus.HasFilter || criteria.OnlineStatus.IsInRange(BeatmapInfo.Status);
if (!match) return false;
match &= !criteria.Creator.HasFilter || criteria.Creator.Matches(BeatmapInfo.Metadata.Author.Username); match &= !criteria.Creator.HasFilter || criteria.Creator.Matches(BeatmapInfo.Metadata.Author.Username);
match &= !criteria.Artist.HasFilter || criteria.Artist.Matches(BeatmapInfo.Metadata.Artist) || match &= !criteria.Artist.HasFilter || criteria.Artist.Matches(BeatmapInfo.Metadata.Artist) ||
criteria.Artist.Matches(BeatmapInfo.Metadata.ArtistUnicode); criteria.Artist.Matches(BeatmapInfo.Metadata.ArtistUnicode);
match &= !criteria.UserStarDifficulty.HasFilter || criteria.UserStarDifficulty.IsInRange(BeatmapInfo.StarRating); match &= !criteria.UserStarDifficulty.HasFilter || criteria.UserStarDifficulty.IsInRange(BeatmapInfo.StarRating);
if (match && criteria.SearchTerms.Length > 0) if (!match) return false;
if (criteria.SearchTerms.Length > 0)
{ {
string[] terms = BeatmapInfo.GetSearchableTerms(); var terms = BeatmapInfo.GetSearchableTerms();
foreach (string criteriaTerm in criteria.SearchTerms) foreach (string criteriaTerm in criteria.SearchTerms)
match &= terms.Any(term => term.Contains(criteriaTerm, StringComparison.InvariantCultureIgnoreCase)); {
bool any = false;
// ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
foreach (string term in terms)
{
if (!term.Contains(criteriaTerm, StringComparison.InvariantCultureIgnoreCase)) continue;
any = true;
break;
}
if (any) continue;
match = false;
break;
}
// if a match wasn't found via text matching of terms, do a second catch-all check matching against online IDs. // if a match wasn't found via text matching of terms, do a second catch-all check matching against online IDs.
// this should be done after text matching so we can prioritise matching numbers in metadata. // this should be done after text matching so we can prioritise matching numbers in metadata.
@ -71,13 +95,14 @@ namespace osu.Game.Screens.Select.Carousel
} }
} }
if (match) if (!match) return false;
match &= criteria.CollectionBeatmapMD5Hashes?.Contains(BeatmapInfo.MD5Hash) ?? true; match &= criteria.CollectionBeatmapMD5Hashes?.Contains(BeatmapInfo.MD5Hash) ?? true;
if (match && criteria.RulesetCriteria != null) if (match && criteria.RulesetCriteria != null)
match &= criteria.RulesetCriteria.Matches(BeatmapInfo); match &= criteria.RulesetCriteria.Matches(BeatmapInfo);
Filtered.Value = !match; return match;
} }
public override int CompareTo(FilterCriteria criteria, CarouselItem other) public override int CompareTo(FilterCriteria criteria, CarouselItem other)