Refilter on search update

This commit is contained in:
Michael Bucari-Tovo 2022-12-31 18:41:55 -07:00
parent 6d7b3bd5f0
commit 28802c8279
5 changed files with 89 additions and 88 deletions

View File

@ -43,28 +43,18 @@ namespace ApplicationServices
else else
{ {
foreach (var book in books) foreach (var book in books)
{ UpdateUserDefinedItems(book);
UpdateLiberatedStatus(book);
UpdateBookTags(book);
UpdateUserRatings(book);
}
} }
} }
public static void FullReIndex() => performSafeCommand(e => public static void FullReIndex() => performSafeCommand(fullReIndex);
fullReIndex(e)
);
internal static void UpdateLiberatedStatus(Book book) => performSafeCommand(e => internal static void UpdateUserDefinedItems(Book book) => performSafeCommand(e =>
e.UpdateLiberatedStatus(book) {
); e.UpdateLiberatedStatus(book);
e.UpdateTags(book.AudibleProductId, book.UserDefinedItem.Tags);
internal static void UpdateBookTags(Book book) => performSafeCommand(e => e.UpdateUserRatings(book);
e.UpdateTags(book.AudibleProductId, book.UserDefinedItem.Tags) }
);
internal static void UpdateUserRatings(Book book) => performSafeCommand(e =>
e.UpdateUserRatings(book)
); );
private static void performSafeCommand(Action<SearchEngine> action) private static void performSafeCommand(Action<SearchEngine> action)
@ -92,7 +82,6 @@ namespace ApplicationServices
isUpdating = true; isUpdating = true;
action(new SearchEngine()); action(new SearchEngine());
if (!prevIsUpdating) if (!prevIsUpdating)
SearchEngineUpdated?.Invoke(null, null); SearchEngineUpdated?.Invoke(null, null);
} }

View File

@ -27,7 +27,8 @@ namespace LibationAvalonia.Controls
IsEnabled = false IsEnabled = false
}; };
ToolTip.SetTip(cell, "Click to change ratings"); if (ToolTip.GetTip(cell) is null)
ToolTip.SetTip(cell, "Click to change ratings");
if (Binding != null) if (Binding != null)
{ {

View File

@ -41,6 +41,7 @@ namespace LibationAvalonia.ViewModels
public ProductsDisplayViewModel() public ProductsDisplayViewModel()
{ {
SearchEngineCommands.SearchEngineUpdated += SearchEngineCommands_SearchEngineUpdated;
GridEntries = new(SOURCE); GridEntries = new(SOURCE);
GridEntries.Filter = CollectionFilter; GridEntries.Filter = CollectionFilter;
@ -166,6 +167,18 @@ namespace LibationAvalonia.ViewModels
return booksFilteredIn.Concat(seriesFilteredIn).ToList(); return booksFilteredIn.Concat(seriesFilteredIn).ToList();
} }
private async void SearchEngineCommands_SearchEngineUpdated(object sender, EventArgs e)
{
var filterResults = QueryResults(SOURCE, FilterString);
if (filterResults.Except(FilteredInGridEntries).Any())
{
FilteredInGridEntries = filterResults;
GridEntries.CommitEdit();
await Dispatcher.UIThread.InvokeAsync(GridEntries.Refresh);
}
}
#endregion #endregion
#region Scan and Remove Books #region Scan and Remove Books

View File

@ -150,10 +150,10 @@ namespace LibationSearchEngine
stringIndexRules[ALL_NARRATOR_NAMES], stringIndexRules[ALL_NARRATOR_NAMES],
stringIndexRules[ALL_SERIES_NAMES] stringIndexRules[ALL_SERIES_NAMES]
}; };
#endregion #endregion
#region get search fields. used for display in help #region get search fields. used for display in help
public static IEnumerable<string> GetSearchIdFields() public static IEnumerable<string> GetSearchIdFields()
{ {
foreach (var key in idIndexRules.Keys) foreach (var key in idIndexRules.Keys)
yield return key; yield return key;
@ -176,29 +176,29 @@ namespace LibationSearchEngine
foreach (var key in numberIndexRules.Keys) foreach (var key in numberIndexRules.Keys)
yield return key; yield return key;
} }
#endregion #endregion
#region create and update index #region create and update index
/// <summary>create new. ie: full re-index</summary> /// <summary>create new. ie: full re-index</summary>
public void CreateNewIndex(IEnumerable<LibraryBook> library, bool overwrite = true) public void CreateNewIndex(IEnumerable<LibraryBook> library, bool overwrite = true)
{ {
// location of index/create the index // location of index/create the index
using var index = getIndex(); using var index = getIndex();
var exists = IndexReader.IndexExists(index); var exists = IndexReader.IndexExists(index);
var createNewIndex = overwrite || !exists; var createNewIndex = overwrite || !exists;
// analyzer for tokenizing text. same analyzer should be used for indexing and searching // analyzer for tokenizing text. same analyzer should be used for indexing and searching
using var analyzer = new StandardAnalyzer(Version); using var analyzer = new StandardAnalyzer(Version);
using var ixWriter = new IndexWriter(index, analyzer, createNewIndex, IndexWriter.MaxFieldLength.UNLIMITED); using var ixWriter = new IndexWriter(index, analyzer, createNewIndex, IndexWriter.MaxFieldLength.UNLIMITED);
foreach (var libraryBook in library) foreach (var libraryBook in library)
{ {
var doc = createBookIndexDocument(libraryBook); var doc = createBookIndexDocument(libraryBook);
ixWriter.AddDocument(doc); ixWriter.AddDocument(doc);
} }
} }
/// <summary>Long running. Use await Task.Run(() => UpdateBook(productId))</summary> /// <summary>Long running. Use await Task.Run(() => UpdateBook(productId))</summary>
public void UpdateBook(LibationContext context, string productId) public void UpdateBook(LibationContext context, string productId)
{ {
var libraryBook = context.GetLibraryBook_Flat_NoTracking(productId); var libraryBook = context.GetLibraryBook_Flat_NoTracking(productId);
var term = new Term(_ID_, productId); var term = new Term(_ID_, productId);
@ -206,12 +206,12 @@ namespace LibationSearchEngine
var document = createBookIndexDocument(libraryBook); var document = createBookIndexDocument(libraryBook);
var createNewIndex = false; var createNewIndex = false;
using var index = getIndex(); using var index = getIndex();
using var analyzer = new StandardAnalyzer(Version); using var analyzer = new StandardAnalyzer(Version);
using var ixWriter = new IndexWriter(index, analyzer, createNewIndex, IndexWriter.MaxFieldLength.UNLIMITED); using var ixWriter = new IndexWriter(index, analyzer, createNewIndex, IndexWriter.MaxFieldLength.UNLIMITED);
ixWriter.DeleteDocuments(term); ixWriter.DeleteDocuments(term);
ixWriter.AddDocument(document); ixWriter.AddDocument(document);
} }
private static Document createBookIndexDocument(LibraryBook libraryBook) private static Document createBookIndexDocument(LibraryBook libraryBook)
{ {
@ -262,7 +262,7 @@ namespace LibationSearchEngine
{ {
// fields are key value pairs. MULTIPLE FIELDS CAN POTENTIALLY HAVE THE SAME KEY. // fields are key value pairs. MULTIPLE FIELDS CAN POTENTIALLY HAVE THE SAME KEY.
// ie: must remove old before adding new else will create unwanted duplicates. // ie: must remove old before adding new else will create unwanted duplicates.
d.RemoveField(fieldName); d.RemoveField(fieldName.ToLower());
d.AddAnalyzed(fieldName, newValue); d.AddAnalyzed(fieldName, newValue);
}); });
@ -279,18 +279,18 @@ namespace LibationSearchEngine
// fields are key value pairs. MULTIPLE FIELDS CAN POTENTIALLY HAVE THE SAME KEY. // fields are key value pairs. MULTIPLE FIELDS CAN POTENTIALLY HAVE THE SAME KEY.
// ie: must remove old before adding new else will create unwanted duplicates. // ie: must remove old before adding new else will create unwanted duplicates.
var v1 = isLiberated(book); var v1 = isLiberated(book);
d.RemoveField("IsLiberated"); d.RemoveField("isliberated");
d.AddBool("IsLiberated", v1); d.AddBool("IsLiberated", v1);
d.RemoveField("Liberated"); d.RemoveField("liberated");
d.AddBool("Liberated", v1); d.AddBool("Liberated", v1);
var v2 = liberatedError(book); var v2 = liberatedError(book);
d.RemoveField("LiberatedError"); d.RemoveField("liberatederror");
d.AddBool("LiberatedError", v2); d.AddBool("LiberatedError", v2);
}); });
public void UpdateUserRatings(Book book) public void UpdateUserRatings(Book book)
=> updateDocument( =>updateDocument(
book.AudibleProductId, book.AudibleProductId,
d => d =>
{ {
@ -301,9 +301,9 @@ namespace LibationSearchEngine
// fields are key value pairs. MULTIPLE FIELDS CAN POTENTIALLY HAVE THE SAME KEY. // fields are key value pairs. MULTIPLE FIELDS CAN POTENTIALLY HAVE THE SAME KEY.
// ie: must remove old before adding new else will create unwanted duplicates. // ie: must remove old before adding new else will create unwanted duplicates.
var v1 = userOverallRating(book); var v1 = userOverallRating(book);
d.RemoveField("UserRating"); d.RemoveField("userrating");
d.AddNotAnalyzed("UserRating", v1); d.AddNotAnalyzed("UserRating", v1);
d.RemoveField("MyRating"); d.RemoveField("myrating");
d.AddNotAnalyzed("MyRating", v1); d.AddNotAnalyzed("MyRating", v1);
}); });
@ -333,12 +333,12 @@ namespace LibationSearchEngine
using var ixWriter = new IndexWriter(index, analyzer, createNewIndex, IndexWriter.MaxFieldLength.UNLIMITED); using var ixWriter = new IndexWriter(index, analyzer, createNewIndex, IndexWriter.MaxFieldLength.UNLIMITED);
ixWriter.UpdateDocument(productTerm, document, analyzer); ixWriter.UpdateDocument(productTerm, document, analyzer);
} }
#endregion #endregion
// the workaround which allows displaying all books when query is empty // the workaround which allows displaying all books when query is empty
public const string ALL_QUERY = "*:*"; public const string ALL_QUERY = "*:*";
#region search #region search
public SearchResultSet Search(string searchString) public SearchResultSet Search(string searchString)
{ {
Serilog.Log.Logger.Debug("original search string: {@DebugInfo}", new { searchString }); Serilog.Log.Logger.Debug("original search string: {@DebugInfo}", new { searchString });
@ -371,8 +371,8 @@ namespace LibationSearchEngine
return searchString; return searchString;
} }
#region format query string #region format query string
private static string parseTag(string tagSearchString) private static string parseTag(string tagSearchString)
{ {
var allMatches = LuceneRegex var allMatches = LuceneRegex
.TagRegex .TagRegex
@ -437,33 +437,33 @@ namespace LibationSearchEngine
{ {
var defaultField = ALL; var defaultField = ALL;
using var index = getIndex(); using var index = getIndex();
using var searcher = new IndexSearcher(index); using var searcher = new IndexSearcher(index);
using var analyzer = new StandardAnalyzer(Version); using var analyzer = new StandardAnalyzer(Version);
var query = analyzer.GetQuery(defaultField, searchString); var query = analyzer.GetQuery(defaultField, searchString);
// lucene doesn't allow only negations. eg this returns nothing: // lucene doesn't allow only negations. eg this returns nothing:
// -tags:hidden // -tags:hidden
// work arounds: https://kb.ucla.edu/articles/pure-negation-query-in-lucene // work arounds: https://kb.ucla.edu/articles/pure-negation-query-in-lucene
// HOWEVER, doing this to any other type of query can cause EVERYTHING to be a match unless "Occur" is carefully set // HOWEVER, doing this to any other type of query can cause EVERYTHING to be a match unless "Occur" is carefully set
// this should really check that all leaf nodes are MUST_NOT // this should really check that all leaf nodes are MUST_NOT
if (query is BooleanQuery boolQuery) if (query is BooleanQuery boolQuery)
{ {
var occurs = getOccurs_recurs(boolQuery); var occurs = getOccurs_recurs(boolQuery);
if (occurs.Any() && occurs.All(o => o == Occur.MUST_NOT)) if (occurs.Any() && occurs.All(o => o == Occur.MUST_NOT))
boolQuery.Add(new MatchAllDocsQuery(), Occur.MUST); boolQuery.Add(new MatchAllDocsQuery(), Occur.MUST);
} }
var docs = searcher var docs = searcher
.Search(query, searcher.MaxDoc + 1) .Search(query, searcher.MaxDoc + 1)
.ScoreDocs .ScoreDocs
.Select(ds => new ScoreDocExplicit(searcher.Doc(ds.Doc), ds.Score)) .Select(ds => new ScoreDocExplicit(searcher.Doc(ds.Doc), ds.Score))
.ToList(); .ToList();
var queryString = query.ToString(); var queryString = query.ToString();
Serilog.Log.Logger.Debug("query: {@DebugInfo}", new { queryString }); Serilog.Log.Logger.Debug("query: {@DebugInfo}", new { queryString });
return new SearchResultSet(queryString, docs); return new SearchResultSet(queryString, docs);
} }
private IEnumerable<Occur> getOccurs_recurs(BooleanQuery query) private IEnumerable<Occur> getOccurs_recurs(BooleanQuery query)
{ {
@ -495,9 +495,9 @@ namespace LibationSearchEngine
// Serilog.Log.Logger.Debug($" [{f.Name}]={f.StringValue}"); // Serilog.Log.Logger.Debug($" [{f.Name}]={f.StringValue}");
//} //}
} }
#endregion #endregion
private static Directory getIndex() => FSDirectory.Open(SearchEngineDirectory); private static Directory getIndex() => FSDirectory.Open(SearchEngineDirectory);
// not customizable. don't move to config // not customizable. don't move to config
private static string SearchEngineDirectory { get; } private static string SearchEngineDirectory { get; }

View File

@ -14,7 +14,8 @@ namespace LibationWinForms.GridView
private const string HOLLOW_STAR = "☆"; private const string HOLLOW_STAR = "☆";
private Rating _rating; private Rating _rating;
public Rating Rating { public Rating Rating
{
get => _rating; get => _rating;
set set
{ {
@ -32,6 +33,7 @@ namespace LibationWinForms.GridView
star.Tag = star.Text = _rating.StoryRating > rating++ ? SOLID_STAR : HOLLOW_STAR; star.Tag = star.Text = _rating.StoryRating > rating++ ? SOLID_STAR : HOLLOW_STAR;
} }
} }
public MyRatingCellEditor() public MyRatingCellEditor()
{ {
InitializeComponent(); InitializeComponent();
@ -102,15 +104,11 @@ namespace LibationWinForms.GridView
#region IDataGridViewEditingControl #region IDataGridViewEditingControl
DataGridView dataGridView; public DataGridView EditingControlDataGridView { get; set; }
private bool valueChanged = false; public int EditingControlRowIndex { get; set; }
int rowIndex; public bool EditingControlValueChanged { get; set; }
public DataGridView EditingControlDataGridView { get => dataGridView; set => dataGridView = value; }
public int EditingControlRowIndex { get => rowIndex; set => rowIndex = value; }
public bool EditingControlValueChanged { get => valueChanged; set => valueChanged = value; }
public object EditingControlFormattedValue { get => Rating; set => Rating = (Rating)value; } public object EditingControlFormattedValue { get => Rating; set => Rating = (Rating)value; }
public Cursor EditingPanelCursor => base.Cursor; public Cursor EditingPanelCursor => Cursor;
public bool RepositionEditingControlOnValueChange => false; public bool RepositionEditingControlOnValueChange => false;
public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle) public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)