From ad2685fb2ec30b0849993164e0602063c558458f Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Fri, 10 Sep 2021 21:11:14 -0500 Subject: [PATCH] add released_inthelast queries (#357) --- CHANGELOG.md | 3 + .../RelativeDateMultiFieldQueryParser.cs | 40 ++++++++++ .../Search/RelativeDateQueryParser.cs | 77 +++++++++++++++++++ ErsatzTV.Infrastructure/Search/SearchIndex.cs | 4 +- docs/user-guide/search.md | 9 ++- 5 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 ErsatzTV.Infrastructure/Search/RelativeDateMultiFieldQueryParser.cs create mode 100644 ErsatzTV.Infrastructure/Search/RelativeDateQueryParser.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index a3e78749c..03a1397b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Added +- Add `released_inthelast` search field for relative release date queries + - Syntax is a number and a unit (days, weeks, months, years) like `1 week` or `2 years` ## [0.0.56-alpha] - 2021-09-10 ### Added diff --git a/ErsatzTV.Infrastructure/Search/RelativeDateMultiFieldQueryParser.cs b/ErsatzTV.Infrastructure/Search/RelativeDateMultiFieldQueryParser.cs new file mode 100644 index 000000000..e164dfc8d --- /dev/null +++ b/ErsatzTV.Infrastructure/Search/RelativeDateMultiFieldQueryParser.cs @@ -0,0 +1,40 @@ +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/RelativeDateQueryParser.cs b/ErsatzTV.Infrastructure/Search/RelativeDateQueryParser.cs new file mode 100644 index 000000000..0d8c96306 --- /dev/null +++ b/ErsatzTV.Infrastructure/Search/RelativeDateQueryParser.cs @@ -0,0 +1,77 @@ +using System; +using Lucene.Net.Analysis; +using Lucene.Net.QueryParsers.Classic; +using Lucene.Net.Search; +using Lucene.Net.Util; + +namespace ErsatzTV.Infrastructure.Search +{ + public class RelativeDateQueryParser : QueryParser + { + public RelativeDateQueryParser(LuceneVersion matchVersion, string f, Analyzer a) : base(matchVersion, f, a) + { + } + + protected internal RelativeDateQueryParser(ICharStream stream) : base(stream) + { + } + + protected RelativeDateQueryParser(QueryParserTokenManager tm) : base(tm) + { + } + + protected override Query GetFieldQuery(string field, string queryText, int slop) + { + if (field == "released_inthelast" && 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); + } + + internal static bool ParseStart(string text, out DateTime start) + { + start = DateTime.MinValue; + + try + { + if (int.TryParse(text.Split(" ")[0], out int number)) + { + if (text.Contains("day")) + { + start = DateTime.Today.AddDays(number * -1); + return true; + } + + if (text.Contains("week")) + { + start = DateTime.Today.AddDays(number * -7); + return true; + } + + if (text.Contains("month")) + { + start = DateTime.Today.AddMonths(number * -1); + return true; + } + + if (text.Contains("year")) + { + start = DateTime.Today.AddYears(number * -1); + return true; + } + } + } + catch + { + // do nothing + } + + return false; + } + } +} diff --git a/ErsatzTV.Infrastructure/Search/SearchIndex.cs b/ErsatzTV.Infrastructure/Search/SearchIndex.cs index 6ba6cfd22..2cb18a639 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 QueryParser(AppLuceneVersion, searchField, analyzerWrapper) - : new MultiFieldQueryParser(AppLuceneVersion, new[] { TitleField }, analyzerWrapper); + ? new RelativeDateQueryParser(AppLuceneVersion, searchField, analyzerWrapper) + : new RelativeDateMultiFieldQueryParser(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 7aaf02871..9754ecc04 100644 --- a/docs/user-guide/search.md +++ b/docs/user-guide/search.md @@ -24,6 +24,7 @@ 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 @@ -40,6 +41,7 @@ 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 @@ -53,6 +55,7 @@ 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 @@ -96,4 +99,8 @@ The following fields are available for searching music videos: ### Lush Music -`mood:lush` \ No newline at end of file +`mood:lush` + +### Episodes from the past week + +`type:episode AND released_inthelast:"1 week"` \ No newline at end of file