diff --git a/ErsatzTV.Infrastructure/Search/CustomMultiFieldQueryParser.cs b/ErsatzTV.Infrastructure/Search/CustomMultiFieldQueryParser.cs new file mode 100644 index 000000000..a75e366b1 --- /dev/null +++ b/ErsatzTV.Infrastructure/Search/CustomMultiFieldQueryParser.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using Lucene.Net.Analysis; +using Lucene.Net.QueryParsers.Classic; +using Lucene.Net.Search; +using Lucene.Net.Util; + +namespace ErsatzTV.Infrastructure.Search +{ + public class CustomMultiFieldQueryParser : MultiFieldQueryParser + { + public CustomMultiFieldQueryParser( + LuceneVersion matchVersion, + string[] fields, + Analyzer analyzer, + IDictionary boosts) : base(matchVersion, fields, analyzer, boosts) + { + } + + public CustomMultiFieldQueryParser(LuceneVersion matchVersion, string[] fields, Analyzer analyzer) : base( + matchVersion, + fields, + analyzer) + { + } + + protected override Query GetFieldQuery(string field, string queryText, bool quoted) + { + if (field == "released_onthisday") + { + var todayString = DateTime.Today.ToString("*MMdd"); + return base.GetWildcardQuery("release_date", todayString); + } + + return base.GetFieldQuery(field, queryText, quoted); + } + + protected override Query GetFieldQuery(string field, string queryText, int slop) + { + if (field == "released_inthelast" && CustomQueryParser.ParseStart(queryText, out DateTime start)) + { + var todayString = DateTime.Today.ToString("yyyyMMdd"); + var dateString = start.ToString("yyyyMMdd"); + + return base.GetRangeQuery("release_date", dateString, todayString, true, true); + } + + if (field == "released_notinthelast" && CustomQueryParser.ParseStart(queryText, out DateTime finish)) + { + var dateString = finish.ToString("yyyyMMdd"); + + return base.GetRangeQuery("release_date", "00000000", dateString, false, false); + } + + return base.GetFieldQuery(field, queryText, slop); + } + } +} diff --git a/ErsatzTV.Infrastructure/Search/RelativeDateQueryParser.cs b/ErsatzTV.Infrastructure/Search/CustomQueryParser.cs similarity index 65% rename from ErsatzTV.Infrastructure/Search/RelativeDateQueryParser.cs rename to ErsatzTV.Infrastructure/Search/CustomQueryParser.cs index 0d8c96306..c6fbbe91c 100644 --- a/ErsatzTV.Infrastructure/Search/RelativeDateQueryParser.cs +++ b/ErsatzTV.Infrastructure/Search/CustomQueryParser.cs @@ -6,19 +6,30 @@ using Lucene.Net.Util; namespace ErsatzTV.Infrastructure.Search { - public class RelativeDateQueryParser : QueryParser + public class CustomQueryParser : QueryParser { - public RelativeDateQueryParser(LuceneVersion matchVersion, string f, Analyzer a) : base(matchVersion, f, a) + public CustomQueryParser(LuceneVersion matchVersion, string f, Analyzer a) : base(matchVersion, f, a) { } - protected internal RelativeDateQueryParser(ICharStream stream) : base(stream) + protected internal CustomQueryParser(ICharStream stream) : base(stream) { } - protected RelativeDateQueryParser(QueryParserTokenManager tm) : base(tm) + protected CustomQueryParser(QueryParserTokenManager tm) : base(tm) { } + + protected override Query GetFieldQuery(string field, string queryText, bool quoted) + { + if (field == "released_onthisday") + { + var todayString = DateTime.Today.ToString("*MMdd"); + return base.GetWildcardQuery("release_date", todayString); + } + + return base.GetFieldQuery(field, queryText, quoted); + } protected override Query GetFieldQuery(string field, string queryText, int slop) { @@ -29,6 +40,13 @@ namespace ErsatzTV.Infrastructure.Search return base.GetRangeQuery("release_date", dateString, todayString, true, true); } + + if (field == "released_notinthelast" && ParseStart(queryText, out DateTime finish)) + { + var dateString = finish.ToString("yyyyMMdd"); + + return base.GetRangeQuery("release_date", "00000000", dateString, false, false); + } return base.GetFieldQuery(field, queryText, slop); } diff --git a/ErsatzTV.Infrastructure/Search/RelativeDateMultiFieldQueryParser.cs b/ErsatzTV.Infrastructure/Search/RelativeDateMultiFieldQueryParser.cs deleted file mode 100644 index e164dfc8d..000000000 --- a/ErsatzTV.Infrastructure/Search/RelativeDateMultiFieldQueryParser.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using Lucene.Net.Analysis; -using Lucene.Net.QueryParsers.Classic; -using Lucene.Net.Search; -using Lucene.Net.Util; - -namespace ErsatzTV.Infrastructure.Search -{ - public class RelativeDateMultiFieldQueryParser : MultiFieldQueryParser - { - public RelativeDateMultiFieldQueryParser( - LuceneVersion matchVersion, - string[] fields, - Analyzer analyzer, - IDictionary boosts) : base(matchVersion, fields, analyzer, boosts) - { - } - - public RelativeDateMultiFieldQueryParser(LuceneVersion matchVersion, string[] fields, Analyzer analyzer) : base( - matchVersion, - fields, - analyzer) - { - } - - protected override Query GetFieldQuery(string field, string queryText, int slop) - { - if (field == "released_inthelast" && RelativeDateQueryParser.ParseStart(queryText, out DateTime start)) - { - var todayString = DateTime.Today.ToString("yyyyMMdd"); - var dateString = start.ToString("yyyyMMdd"); - - return base.GetRangeQuery("release_date", dateString, todayString, true, true); - } - - return base.GetFieldQuery(field, queryText, slop); - } - } -} diff --git a/ErsatzTV.Infrastructure/Search/SearchIndex.cs b/ErsatzTV.Infrastructure/Search/SearchIndex.cs index 2cb18a639..21ad31c6c 100644 --- a/ErsatzTV.Infrastructure/Search/SearchIndex.cs +++ b/ErsatzTV.Infrastructure/Search/SearchIndex.cs @@ -148,8 +148,8 @@ namespace ErsatzTV.Infrastructure.Search }; using var analyzerWrapper = new PerFieldAnalyzerWrapper(analyzer, customAnalyzers); QueryParser parser = !string.IsNullOrWhiteSpace(searchField) - ? new RelativeDateQueryParser(AppLuceneVersion, searchField, analyzerWrapper) - : new RelativeDateMultiFieldQueryParser(AppLuceneVersion, new[] { TitleField }, analyzerWrapper); + ? new CustomQueryParser(AppLuceneVersion, searchField, analyzerWrapper) + : new CustomMultiFieldQueryParser(AppLuceneVersion, new[] { TitleField }, analyzerWrapper); parser.AllowLeadingWildcard = true; Query query = ParseQuery(searchQuery, parser); var filter = new DuplicateFilter(TitleAndYearField); diff --git a/docs/user-guide/search.md b/docs/user-guide/search.md index 0507e4350..f44268b8b 100644 --- a/docs/user-guide/search.md +++ b/docs/user-guide/search.md @@ -24,7 +24,6 @@ The following fields are available for searching movies: - `content_rating`: The movie content rating (case-sensitive) - `language`: The movie audio stream language - `release_date`: The movie release date (YYYYMMDD) -- `released_inthelast`: A range for the movie release date (days, weeks, months, years) - `type`: Always `movie` ### Shows @@ -41,7 +40,6 @@ The following fields are available for searching shows: - `content_rating`: The show content rating (case-sensitive) - `language`: The show audio stream language - `release_date`: The show release date (YYYYMMDD) -- `released_inthelast`: A range for the show release date (days, weeks, months, years) - `type`: Always `show` ### Episodes @@ -55,7 +53,6 @@ The following fields are available for searching episodes: - `library_name`: The name of the library that contains the episode - `language`: The episode audio stream language - `release_date`: The episode release date (YYYYMMDD) -- `released_inthelast`: A range for the episode release date (days, weeks, months, years) - `type`: Always `episode` ### Artists @@ -78,9 +75,14 @@ The following fields are available for searching music videos: - `library_name`: The name of the library that contains the music video - `language`: The music video audio stream language - `release_date`: The music video release date (YYYYMMDD) -- `released_inthelast`: A range for the music video release date (days, weeks, months, years) - `type`: Always `music_video` +## Special Search Fields + +- `released_inthelast`: For any media type that supports `release_date`, `released_inthelast` takes a number and a unit (days, weeks, months, years) and returns items released between the specified time ago and now +- `released_notinthelast`: For any media type that supports `release_date`, `released_notinthelast` takes a number and a unit (days, weeks, months, years) and returns items released before the specified time ago +- `released_onthisday`: For any media type that supports `release_date`, `released_onthisday` takes any value (ignored) and will return items released on this month number and day number in previous years + ## Sample Searches ### Christmas @@ -106,3 +108,11 @@ The following fields are available for searching music videos: ### Episodes from the past week `type:episode AND released_inthelast:"1 week"` + +### Episodes older than the past week + +`type:episode AND released_notinthelast:"1 week"` + +### Episodes released on this day + +`type:episode AND released_onthisday:1` \ No newline at end of file