Refilter on search update
This commit is contained in:
parent
6d7b3bd5f0
commit
28802c8279
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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; }
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user