diff --git a/Source/ApplicationServices/SearchEngineCommands.cs b/Source/ApplicationServices/SearchEngineCommands.cs index ad4b3fa8..b87326f8 100644 --- a/Source/ApplicationServices/SearchEngineCommands.cs +++ b/Source/ApplicationServices/SearchEngineCommands.cs @@ -43,28 +43,18 @@ namespace ApplicationServices else { foreach (var book in books) - { - UpdateLiberatedStatus(book); - UpdateBookTags(book); - UpdateUserRatings(book); - } + UpdateUserDefinedItems(book); } } - public static void FullReIndex() => performSafeCommand(e => - fullReIndex(e) - ); + public static void FullReIndex() => performSafeCommand(fullReIndex); - internal static void UpdateLiberatedStatus(Book book) => performSafeCommand(e => - e.UpdateLiberatedStatus(book) - ); - - internal static void UpdateBookTags(Book book) => performSafeCommand(e => - e.UpdateTags(book.AudibleProductId, book.UserDefinedItem.Tags) - ); - - internal static void UpdateUserRatings(Book book) => performSafeCommand(e => - e.UpdateUserRatings(book) + internal static void UpdateUserDefinedItems(Book book) => performSafeCommand(e => + { + e.UpdateLiberatedStatus(book); + e.UpdateTags(book.AudibleProductId, book.UserDefinedItem.Tags); + e.UpdateUserRatings(book); + } ); private static void performSafeCommand(Action action) @@ -92,7 +82,6 @@ namespace ApplicationServices isUpdating = true; action(new SearchEngine()); - if (!prevIsUpdating) SearchEngineUpdated?.Invoke(null, null); } diff --git a/Source/LibationAvalonia/Controls/MyRatingGridColumn.axaml.cs b/Source/LibationAvalonia/Controls/MyRatingGridColumn.axaml.cs index ff9b83c9..d85d669a 100644 --- a/Source/LibationAvalonia/Controls/MyRatingGridColumn.axaml.cs +++ b/Source/LibationAvalonia/Controls/MyRatingGridColumn.axaml.cs @@ -27,7 +27,8 @@ namespace LibationAvalonia.Controls 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) { diff --git a/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs b/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs index 8f12349a..adb3aa1b 100644 --- a/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs +++ b/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs @@ -41,6 +41,7 @@ namespace LibationAvalonia.ViewModels public ProductsDisplayViewModel() { + SearchEngineCommands.SearchEngineUpdated += SearchEngineCommands_SearchEngineUpdated; GridEntries = new(SOURCE); GridEntries.Filter = CollectionFilter; @@ -164,7 +165,19 @@ namespace LibationAvalonia.ViewModels var seriesFilteredIn = entries.SeriesEntries().Where(s => s.Children.Join(SearchResults.Docs, lbe => lbe.AudibleProductId, d => d.ProductId, (lbe, d) => lbe).Any()); 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 diff --git a/Source/LibationSearchEngine/SearchEngine.cs b/Source/LibationSearchEngine/SearchEngine.cs index 260bf14c..3be589cb 100644 --- a/Source/LibationSearchEngine/SearchEngine.cs +++ b/Source/LibationSearchEngine/SearchEngine.cs @@ -150,10 +150,10 @@ namespace LibationSearchEngine stringIndexRules[ALL_NARRATOR_NAMES], stringIndexRules[ALL_SERIES_NAMES] }; - #endregion + #endregion - #region get search fields. used for display in help - public static IEnumerable GetSearchIdFields() + #region get search fields. used for display in help + public static IEnumerable GetSearchIdFields() { foreach (var key in idIndexRules.Keys) yield return key; @@ -176,29 +176,29 @@ namespace LibationSearchEngine foreach (var key in numberIndexRules.Keys) yield return key; } - #endregion + #endregion - #region create and update index + #region create and update index /// create new. ie: full re-index public void CreateNewIndex(IEnumerable library, bool overwrite = true) { - // location of index/create the index - using var index = getIndex(); + // location of index/create the index + using var index = getIndex(); var exists = IndexReader.IndexExists(index); var createNewIndex = overwrite || !exists; - // analyzer for tokenizing text. same analyzer should be used for indexing and searching - using var analyzer = new StandardAnalyzer(Version); - using var ixWriter = new IndexWriter(index, analyzer, createNewIndex, IndexWriter.MaxFieldLength.UNLIMITED); - foreach (var libraryBook in library) - { - var doc = createBookIndexDocument(libraryBook); - ixWriter.AddDocument(doc); - } + // analyzer for tokenizing text. same analyzer should be used for indexing and searching + using var analyzer = new StandardAnalyzer(Version); + using var ixWriter = new IndexWriter(index, analyzer, createNewIndex, IndexWriter.MaxFieldLength.UNLIMITED); + foreach (var libraryBook in library) + { + var doc = createBookIndexDocument(libraryBook); + ixWriter.AddDocument(doc); + } } - /// Long running. Use await Task.Run(() => UpdateBook(productId)) - public void UpdateBook(LibationContext context, string productId) + /// Long running. Use await Task.Run(() => UpdateBook(productId)) + public void UpdateBook(LibationContext context, string productId) { var libraryBook = context.GetLibraryBook_Flat_NoTracking(productId); var term = new Term(_ID_, productId); @@ -206,12 +206,12 @@ namespace LibationSearchEngine var document = createBookIndexDocument(libraryBook); var createNewIndex = false; - using var index = getIndex(); - using var analyzer = new StandardAnalyzer(Version); - using var ixWriter = new IndexWriter(index, analyzer, createNewIndex, IndexWriter.MaxFieldLength.UNLIMITED); - ixWriter.DeleteDocuments(term); - ixWriter.AddDocument(document); - } + using var index = getIndex(); + using var analyzer = new StandardAnalyzer(Version); + using var ixWriter = new IndexWriter(index, analyzer, createNewIndex, IndexWriter.MaxFieldLength.UNLIMITED); + ixWriter.DeleteDocuments(term); + ixWriter.AddDocument(document); + } 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. // ie: must remove old before adding new else will create unwanted duplicates. - d.RemoveField(fieldName); + d.RemoveField(fieldName.ToLower()); d.AddAnalyzed(fieldName, newValue); }); @@ -279,18 +279,18 @@ namespace LibationSearchEngine // 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. var v1 = isLiberated(book); - d.RemoveField("IsLiberated"); + d.RemoveField("isliberated"); d.AddBool("IsLiberated", v1); - d.RemoveField("Liberated"); + d.RemoveField("liberated"); d.AddBool("Liberated", v1); var v2 = liberatedError(book); - d.RemoveField("LiberatedError"); + d.RemoveField("liberatederror"); d.AddBool("LiberatedError", v2); }); public void UpdateUserRatings(Book book) - => updateDocument( + =>updateDocument( book.AudibleProductId, d => { @@ -301,9 +301,9 @@ namespace LibationSearchEngine // 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. var v1 = userOverallRating(book); - d.RemoveField("UserRating"); + d.RemoveField("userrating"); d.AddNotAnalyzed("UserRating", v1); - d.RemoveField("MyRating"); + d.RemoveField("myrating"); d.AddNotAnalyzed("MyRating", v1); }); @@ -333,12 +333,12 @@ namespace LibationSearchEngine using var ixWriter = new IndexWriter(index, analyzer, createNewIndex, IndexWriter.MaxFieldLength.UNLIMITED); ixWriter.UpdateDocument(productTerm, document, analyzer); } - #endregion + #endregion // the workaround which allows displaying all books when query is empty public const string ALL_QUERY = "*:*"; - #region search + #region search public SearchResultSet Search(string searchString) { Serilog.Log.Logger.Debug("original search string: {@DebugInfo}", new { searchString }); @@ -371,8 +371,8 @@ namespace LibationSearchEngine return searchString; } - #region format query string - private static string parseTag(string tagSearchString) + #region format query string + private static string parseTag(string tagSearchString) { var allMatches = LuceneRegex .TagRegex @@ -437,33 +437,33 @@ namespace LibationSearchEngine { var defaultField = ALL; - using var index = getIndex(); - using var searcher = new IndexSearcher(index); - using var analyzer = new StandardAnalyzer(Version); - var query = analyzer.GetQuery(defaultField, searchString); + using var index = getIndex(); + using var searcher = new IndexSearcher(index); + using var analyzer = new StandardAnalyzer(Version); + var query = analyzer.GetQuery(defaultField, searchString); - // lucene doesn't allow only negations. eg this returns nothing: - // -tags:hidden - // 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 - // this should really check that all leaf nodes are MUST_NOT - if (query is BooleanQuery boolQuery) - { - var occurs = getOccurs_recurs(boolQuery); - if (occurs.Any() && occurs.All(o => o == Occur.MUST_NOT)) - boolQuery.Add(new MatchAllDocsQuery(), Occur.MUST); - } + // lucene doesn't allow only negations. eg this returns nothing: + // -tags:hidden + // 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 + // this should really check that all leaf nodes are MUST_NOT + if (query is BooleanQuery boolQuery) + { + var occurs = getOccurs_recurs(boolQuery); + if (occurs.Any() && occurs.All(o => o == Occur.MUST_NOT)) + boolQuery.Add(new MatchAllDocsQuery(), Occur.MUST); + } - var docs = searcher - .Search(query, searcher.MaxDoc + 1) - .ScoreDocs - .Select(ds => new ScoreDocExplicit(searcher.Doc(ds.Doc), ds.Score)) - .ToList(); + var docs = searcher + .Search(query, searcher.MaxDoc + 1) + .ScoreDocs + .Select(ds => new ScoreDocExplicit(searcher.Doc(ds.Doc), ds.Score)) + .ToList(); var queryString = query.ToString(); Serilog.Log.Logger.Debug("query: {@DebugInfo}", new { queryString }); - return new SearchResultSet(queryString, docs); - } + return new SearchResultSet(queryString, docs); + } private IEnumerable getOccurs_recurs(BooleanQuery query) { @@ -495,9 +495,9 @@ namespace LibationSearchEngine // 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 private static string SearchEngineDirectory { get; } diff --git a/Source/LibationWinForms/GridView/MyRatingCellEditor.cs b/Source/LibationWinForms/GridView/MyRatingCellEditor.cs index d6803126..1a322cc4 100644 --- a/Source/LibationWinForms/GridView/MyRatingCellEditor.cs +++ b/Source/LibationWinForms/GridView/MyRatingCellEditor.cs @@ -14,7 +14,8 @@ namespace LibationWinForms.GridView private const string HOLLOW_STAR = "☆"; private Rating _rating; - public Rating Rating { + public Rating Rating + { get => _rating; set { @@ -32,6 +33,7 @@ namespace LibationWinForms.GridView star.Tag = star.Text = _rating.StoryRating > rating++ ? SOLID_STAR : HOLLOW_STAR; } } + public MyRatingCellEditor() { InitializeComponent(); @@ -102,15 +104,11 @@ namespace LibationWinForms.GridView #region IDataGridViewEditingControl - DataGridView dataGridView; - private bool valueChanged = false; - int rowIndex; - - 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 DataGridView EditingControlDataGridView { get; set; } + public int EditingControlRowIndex { get; set; } + public bool EditingControlValueChanged { get; set; } public object EditingControlFormattedValue { get => Rating; set => Rating = (Rating)value; } - public Cursor EditingPanelCursor => base.Cursor; + public Cursor EditingPanelCursor => Cursor; public bool RepositionEditingControlOnValueChange => false; public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)