Implemented Fulltext search across tags and linebreaks for letters

This commit is contained in:
schnulller
2022-06-19 00:01:14 +02:00
parent 078c4b75b8
commit 4ef0e260e0
24 changed files with 510 additions and 148 deletions

View File

@@ -4,16 +4,19 @@ using HaWeb.Models;
using HaWeb.FileHelpers;
using HaDocument.Interfaces;
using HaDocument.Models;
using HaXMLReader.Interfaces;
using System.Collections.Specialized;
namespace HaWeb.Controllers;
public class SucheController : Controller {
private IHaDocumentWrappper _lib;
private IReaderService _readerService;
private int _lettersForPage;
public SucheController(IHaDocumentWrappper lib, IConfiguration config) {
public SucheController(IHaDocumentWrappper lib, IReaderService readerService, IConfiguration config) {
_lib = lib;
_readerService = readerService;
_lettersForPage = config.GetValue<int>("LettersOnPage");
}
@@ -48,21 +51,45 @@ public class SucheController : Controller {
[Route("Suche")]
// Order of actions:
// Filter, sort by year, paginate, sort by Meta.Sort & .Order, parse
public IActionResult Index(string? person, int page = 0) {
public IActionResult Index(string? search, int page = 0) {
var lib = _lib.GetLibrary();
List<IGrouping<int, Meta>>? metasbyyear = null;
if (person != null) {
var letters = lib.Metas
.Where(x => x.Value.Senders.Contains(person) || x.Value.Receivers.Contains(person))
.Select(x => x.Value);
if (letters == null) return _error404();
metasbyyear = letters.ToLookup(x => x.Sort.Year).OrderBy(x => x.Key).ToList();
} else {
metasbyyear = lib.MetasByYear.OrderBy(x => x.Key).ToList();
if (search != null) {
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
var res = _lib.SearchLetters(search, _readerService);
if (res == null || !res.Any()) return _error404();
var ret = res.ToDictionary(
x => x.Index,
x => x.Results
.Select(y => new SearchResult(search, x.Index) { Page = y.Page, Line = y.Line, Preview = y.Preview})
.ToList()
);
var keys = res.Select(x => x.Index).Where(x => lib.Metas.ContainsKey(x)).Select(x => lib.Metas[x]);
var letters = keys.ToLookup(x => x.Sort.Year).OrderBy(x => x.Key).ToList();
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
return _paginateSend(lib, page, letters, null, null, null, search, ret);
}
metasbyyear = lib.MetasByYear.OrderBy(x => x.Key).ToList();
return _paginateSend(lib, page, metasbyyear);
}
[Route("Suche/Person/{person}")]
public IActionResult Person(string person, int page = 0) {
if (String.IsNullOrWhiteSpace(person)) return _error404();
var lib = _lib.GetLibrary();
List<IGrouping<int, Meta>>? metasbyyear = null;
var letters = lib.Metas
.Where(x => x.Value.Senders.Contains(person) || x.Value.Receivers.Contains(person))
.Select(x => x.Value);
if (letters == null) return _error404();
metasbyyear = letters.ToLookup(x => x.Sort.Year).OrderBy(x => x.Key).ToList();
return _paginateSend(lib, page, metasbyyear, person);
}
private List<(string Key, string Person)> _getAvailablePersons(ILibrary lib) {
return lib.Persons
.OrderBy(x => x.Value.Surname)
@@ -106,7 +133,15 @@ public class SucheController : Controller {
return res;
}
private IActionResult _paginateSend(ILibrary lib, int page, List<IGrouping<int, Meta>>? metasbyyear, string? person = null, string? zhvolume = null, string? zhpage = null) {
private IActionResult _paginateSend(
ILibrary lib,
int page,
List<IGrouping<int, Meta>> metasbyyear,
string? person = null,
string? zhvolume = null,
string? zhpage = null,
string? activeSearch = null,
Dictionary<string, List<SearchResult>>? searchResults = null) {
var pages = _paginate(metasbyyear);
if (pages != null && page >= pages.Count) return _error404();
if (pages == null && page > 0) return _error404();
@@ -123,7 +158,7 @@ public class SucheController : Controller {
List<(string Volume, List<string> Pages)>? availablePages = null;
availablePages = lib.Structure.Select(x => (x.Key, x.Value.Select(x => x.Key).ToList())).ToList();
zhvolume = zhvolume == null ? "1" : zhvolume;
var model = new SucheViewModel(letters, page, pages, _getAvailablePersons(lib), availablePages.OrderBy(x => x.Volume).ToList(), zhvolume, zhpage);
var model = new SucheViewModel(letters, page, pages, _getAvailablePersons(lib), availablePages.OrderBy(x => x.Volume).ToList(), zhvolume, zhpage, activeSearch, searchResults);
if (person != null) model.ActivePerson = person;
return View("Index", model);
}

View File

@@ -2,6 +2,11 @@ namespace HaWeb.FileHelpers;
using HaDocument.Interfaces;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.FileProviders;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using HaXMLReader.Interfaces;
using HaWeb.SearchHelpers;
using System.Text;
public class HaDocumentWrapper : IHaDocumentWrappper {
private ILibrary Library;
@@ -10,6 +15,8 @@ public class HaDocumentWrapper : IHaDocumentWrappper {
public int StartYear { get; private set; }
public int EndYear { get; private set; }
public List<SearchHelpers.SeachableItem>? SearchableLetters { get; private set; }
public HaDocumentWrapper(IXMLProvider xmlProvider, IConfiguration configuration) {
_xmlProvider = xmlProvider;
StartYear = configuration.GetValue<int>("AvailableStartYear");
@@ -41,9 +48,45 @@ public class HaDocumentWrapper : IHaDocumentWrappper {
return null;
}
var searchableletters = new ConcurrentBag<SearchHelpers.SeachableItem>();
var letters = Library.Letters.Values;
Parallel.ForEach(letters, letter => {
var o = new SearchHelpers.SeachableItem(letter.Index, _prepareSearch(letter));
searchableletters.Add(o);
});
this.SearchableLetters = searchableletters.ToList();
return Library;
}
public List<(string Index, List<(string Page, string Line, string Preview)> Results)>? SearchLetters(string searchword, IReaderService reader) {
if (SearchableLetters == null) return null;
var res = new ConcurrentBag<(string Index, List<(string Page, string Line, string preview)> Results)>();
var sw = StringHelpers.NormalizeWhiteSpace(searchword.Trim());
Parallel.ForEach(SearchableLetters, (letter) => {
var state = new SearchState(sw);
var rd = reader.RequestStringReader(letter.SearchText);
var parser = new HaWeb.HTMLParser.LineXMLHelper<SearchState>(state, rd, new StringBuilder(), null, null, null, SearchRules.TextRules, SearchRules.WhitespaceRules);
rd.Read();
if (state.Results != null)
res.Add((
letter.Index,
state.Results.Select(x => (
x.Page,
x.Line,
parser.Lines != null ?
parser.Lines
.Where(y => y.Page == x.Page && y.Line == x.Line)
.Select(x => x.Text)
.FirstOrDefault(string.Empty)
: ""
)).ToList()));
});
return res.ToList();
}
public ILibrary GetLibrary() {
return Library;
}
@@ -57,4 +100,8 @@ public class HaDocumentWrapper : IHaDocumentWrappper {
}
}
}
private string _prepareSearch(HaDocument.Interfaces.ISearchable objecttoseach) {
return SearchHelpers.StringHelpers.NormalizeWhiteSpace(objecttoseach.Element, ' ', false);
}
}

View File

@@ -1,9 +1,11 @@
namespace HaWeb.FileHelpers;
using HaDocument.Interfaces;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using HaXMLReader.Interfaces;
public interface IHaDocumentWrappper {
public ILibrary ResetLibrary();
public ILibrary? SetLibrary(string filepath, ModelStateDictionary ModelState);
public ILibrary GetLibrary();
public List<(string Index, List<(string Page, string Line, string Preview)> Results)>? SearchLetters(string searchword, IReaderService reader);
}

View File

@@ -53,7 +53,7 @@ public static class CommentHelpers {
sb.Append(HTMLHelpers.TagHelpers.CreateEndElement(DEFAULTELEMENT));
}
sb.Append(HTMLHelpers.TagHelpers.CreateEndElement(DEFAULTELEMENT));
rd = readerService.RequestStringReader(comment.Entry);
rd = readerService.RequestStringReader(comment.Element);
new HTMLParser.XMLHelper<CommentState>(commentState, rd, sb, CommentRules.OTagRules, CommentRules.STagRules, CommentRules.CTagRules, CommentRules.TextRules, CommentRules.WhitespaceRules);
new HTMLHelpers.LinkHelper(lib, rd, sb);
rd.Read();

View File

@@ -0,0 +1,170 @@
namespace HaWeb.HTMLParser;
using HaXMLReader.Interfaces;
using HaXMLReader.EvArgs;
using System.Text;
using System.Collections.Generic;
using System;
public class LineXMLHelper<T> {
protected IReader _in;
protected StringBuilder _target;
protected List<(Func<Tag, LineXMLHelper<T>, bool>, Action<StringBuilder, Tag, LineXMLHelper<T>>)>? _OTag_Funcs;
protected List<(Func<Tag, LineXMLHelper<T>, bool>, Action<StringBuilder, Tag, LineXMLHelper<T>>)>? _STag_Funcs;
protected List<(Func<Tag, LineXMLHelper<T>, bool>, Action<StringBuilder, Tag, LineXMLHelper<T>>)>? _CTag_Funcs;
protected List<(Func<Text, LineXMLHelper<T>, bool>, Action<StringBuilder, Text, LineXMLHelper<T>>)>? _Text_Funcs;
protected List<(Func<Whitespace, LineXMLHelper<T>, bool>, Action<StringBuilder, Whitespace, LineXMLHelper<T>>)>? _WS_Funcs;
public T State;
public Stack<Tag> OpenTags;
public Dictionary<string, List<Tag>> LastSingleTags;
public StringBuilder LastText;
public string CurrentPage { get; private set; }
public string CurrentLine { get; private set; }
public List<(string Page, string Line, string Text)>? Lines { get; private set; }
public (string Page, string Line)? CatchPageLine { get; private set; }
private bool _newpage;
private bool _firstline;
private StringBuilder _currentText;
public LineXMLHelper(
T state,
IReader input,
StringBuilder target,
List<(Func<Tag, LineXMLHelper<T>, bool>, Action<StringBuilder, Tag, LineXMLHelper<T>>)>? OTag_Funcs = null,
List<(Func<Tag, LineXMLHelper<T>, bool>, Action<StringBuilder, Tag, LineXMLHelper<T>>)>? STag_Funcs = null,
List<(Func<Tag, LineXMLHelper<T>, bool>, Action<StringBuilder, Tag, LineXMLHelper<T>>)>? CTag_Funcs = null,
List<(Func<Text, LineXMLHelper<T>, bool>, Action<StringBuilder, Text, LineXMLHelper<T>>)>? Text_Funcs = null,
List<(Func<Whitespace, LineXMLHelper<T>, bool>, Action<StringBuilder, Whitespace, LineXMLHelper<T>>)>? WS_Funcs = null,
string startPage = "-1",
string startLine = "-1",
// Set if only a specific line should be appended to the output
(string Page, string Line)? CatchPageLine = null
) {
if (input == null || target == null || state == null) throw new ArgumentNullException();
State = state;
_in = input;
_target = target;
_OTag_Funcs = OTag_Funcs;
_STag_Funcs = STag_Funcs;
_CTag_Funcs = CTag_Funcs;
_Text_Funcs = Text_Funcs;
_WS_Funcs = WS_Funcs;
OpenTags = new Stack<Tag>();
LastSingleTags = new Dictionary<string, List<Tag>>();
LastText = new StringBuilder();
CurrentPage = startPage;
CurrentLine = startLine;
_currentText = new StringBuilder();
input.SingleTag += _setPageLine;
input.ReadingStop += _pushLine;
_newpage = false;
_firstline = true;
if (_OTag_Funcs != null)
_in.OpenTag += OnOTag;
if (_STag_Funcs != null)
_in.SingleTag += OnSTag;
if (_CTag_Funcs != null)
_in.CloseTag += OnCTag;
if (_Text_Funcs != null)
_in.Text += OnText;
if (_WS_Funcs != null)
_in.Whitespace += OnWS;
}
private void _pushLine(object? _, EventArgs _empty) {
if (Lines == null) Lines = new List<(string Page, string Line, string Text)>();
Lines.Add((CurrentPage, CurrentLine, _currentText.ToString()));
_currentText.Clear();
}
private void _setPageLine(object? _, Tag tag) {
if (tag.Name == "page" && !String.IsNullOrWhiteSpace(tag["index"])) {
_pushLine(_, EventArgs.Empty);
CurrentPage = tag["index"];
_newpage = true;
}
if (tag.Name == "line" && !String.IsNullOrWhiteSpace(tag["index"])) {
if (!_newpage && !_firstline) {
_pushLine(_, EventArgs.Empty);
}
_firstline = false;
_newpage = false;
CurrentLine = tag["index"];
}
}
protected virtual void OnText(object? _, Text text) {
LastText.Append(text.Value);
if (_Text_Funcs != null)
if (CatchPageLine == null || (CurrentPage == CatchPageLine.Value.Page && CurrentLine == CatchPageLine.Value.Line)) {
_currentText.Append(text.Value);
foreach (var entry in _Text_Funcs)
if (entry.Item1(text, this)) entry.Item2(_target, text, this);
}
}
protected virtual void OnWS(object? _, Whitespace ws) {
LastText.Append(ws.Value);
if (_WS_Funcs != null)
if (CatchPageLine == null || (CurrentPage == CatchPageLine.Value.Page && CurrentLine == CatchPageLine.Value.Line)) {
_currentText.Append(ws.Value);
foreach (var entry in _WS_Funcs)
if (entry.Item1(ws, this)) entry.Item2(_target, ws, this);
}
}
protected virtual void OnOTag(object? _, Tag tag) {
OpenTags.Push(tag);
if (_OTag_Funcs != null)
foreach (var entry in _OTag_Funcs)
if (entry.Item1(tag, this)) entry.Item2(_target, tag, this);
}
protected virtual void OnSTag(object? _, Tag tag) {
if (!LastSingleTags.ContainsKey(tag.Name))
LastSingleTags.Add(tag.Name, new List<Tag>());
LastSingleTags[tag.Name].Add(tag);
if (_STag_Funcs != null)
foreach (var entry in _STag_Funcs)
if (entry.Item1(tag, this)) entry.Item2(_target, tag, this);
}
protected virtual void OnCTag(object? _, Tag tag) {
OpenTags.Pop();
if (_CTag_Funcs != null)
foreach (var entry in _CTag_Funcs)
if (entry.Item1(tag, this)) entry.Item2(_target, tag, this);
}
internal void Dispose() {
OpenTags.Clear();
LastSingleTags.Clear();
LastText.Clear();
if (_in != null) {
if (_OTag_Funcs != null)
_in.OpenTag -= OnOTag;
if (_STag_Funcs != null)
_in.SingleTag -= OnSTag;
if (_CTag_Funcs != null)
_in.CloseTag -= OnCTag;
if (_Text_Funcs != null)
_in.Text -= OnText;
if (_WS_Funcs != null)
_in.Whitespace -= OnWS;
}
}
~LineXMLHelper() {
Dispose();
}
}

View File

@@ -1,5 +0,0 @@
namespace HaWeb.HTMLParser;
class GenericParser<T> where T : IState {
}

View File

@@ -7,15 +7,13 @@ using System;
public class XMLHelper<T> where T : IState
{
private IReader _in;
private StringBuilder _target;
private List<(Func<Tag, XMLHelper<T>, bool>, Action<StringBuilder, Tag, XMLHelper<T>>)>? _OTag_Funcs;
private List<(Func<Tag, XMLHelper<T>, bool>, Action<StringBuilder, Tag, XMLHelper<T>>)>? _STag_Funcs;
private List<(Func<Tag, XMLHelper<T>, bool>, Action<StringBuilder, Tag, XMLHelper<T>>)>? _CTag_Funcs;
private List<(Func<Text, XMLHelper<T>, bool>, Action<StringBuilder, Text, XMLHelper<T>>)>? _Text_Funcs;
private List<(Func<Whitespace, XMLHelper<T>, bool>, Action<StringBuilder, Whitespace, XMLHelper<T>>)>? _WS_Funcs;
private bool _deleteLeadingWS;
private bool _deleteTrailingWS;
protected IReader _in;
protected StringBuilder _target;
protected List<(Func<Tag, XMLHelper<T>, bool>, Action<StringBuilder, Tag, XMLHelper<T>>)>? _OTag_Funcs;
protected List<(Func<Tag, XMLHelper<T>, bool>, Action<StringBuilder, Tag, XMLHelper<T>>)>? _STag_Funcs;
protected List<(Func<Tag, XMLHelper<T>, bool>, Action<StringBuilder, Tag, XMLHelper<T>>)>? _CTag_Funcs;
protected List<(Func<Text, XMLHelper<T>, bool>, Action<StringBuilder, Text, XMLHelper<T>>)>? _Text_Funcs;
protected List<(Func<Whitespace, XMLHelper<T>, bool>, Action<StringBuilder, Whitespace, XMLHelper<T>>)>? _WS_Funcs;
public T State;
public Stack<Tag> OpenTags;
@@ -30,9 +28,7 @@ public class XMLHelper<T> where T : IState
List<(Func<Tag, XMLHelper<T>, bool>, Action<StringBuilder, Tag, XMLHelper<T>>)>? STag_Funcs = null,
List<(Func<Tag, XMLHelper<T>, bool>, Action<StringBuilder, Tag, XMLHelper<T>>)>? CTag_Funcs = null,
List<(Func<Text, XMLHelper<T>, bool>, Action<StringBuilder, Text, XMLHelper<T>>)>? Text_Funcs = null,
List<(Func<Whitespace, XMLHelper<T>, bool>, Action<StringBuilder, Whitespace, XMLHelper<T>>)>? WS_Funcs = null,
bool deleteLeadingWS = false,
bool deleteTrailingWS = false
List<(Func<Whitespace, XMLHelper<T>, bool>, Action<StringBuilder, Whitespace, XMLHelper<T>>)>? WS_Funcs = null
)
{
if (input == null || target == null || state == null) throw new ArgumentNullException();
@@ -40,8 +36,6 @@ public class XMLHelper<T> where T : IState
State = state;
_in = input;
_target = target;
_deleteLeadingWS = deleteLeadingWS;
_deleteTrailingWS = deleteTrailingWS;
_OTag_Funcs = OTag_Funcs;
_STag_Funcs = STag_Funcs;
@@ -65,7 +59,7 @@ public class XMLHelper<T> where T : IState
_in.Whitespace += OnWS;
}
void OnOTag(object _, Tag tag)
protected virtual void OnOTag(object? _, Tag tag)
{
OpenTags.Push(tag);
if (_OTag_Funcs != null)
@@ -73,17 +67,15 @@ public class XMLHelper<T> where T : IState
if (entry.Item1(tag, this)) entry.Item2(_target, tag, this);
}
void OnText(object _, Text text)
protected virtual void OnText(object? _, Text text)
{
if (_deleteLeadingWS) text.Value = text.Value.TrimStart();
if (_deleteTrailingWS) text.Value = text.Value.TrimEnd();
LastText.Append(text.Value);
if (_Text_Funcs != null)
foreach (var entry in _Text_Funcs)
if (entry.Item1(text, this)) entry.Item2(_target, text, this);
}
void OnSTag(object _, Tag tag)
protected virtual void OnSTag(object? _, Tag tag)
{
if (!LastSingleTags.ContainsKey(tag.Name))
LastSingleTags.Add(tag.Name, new List<Tag>());
@@ -93,7 +85,7 @@ public class XMLHelper<T> where T : IState
if (entry.Item1(tag, this)) entry.Item2(_target, tag, this);
}
void OnCTag(object _, Tag tag)
protected virtual void OnCTag(object? _, Tag tag)
{
OpenTags.Pop();
if(_CTag_Funcs != null)
@@ -101,7 +93,7 @@ public class XMLHelper<T> where T : IState
if (entry.Item1(tag, this)) entry.Item2(_target, tag, this);
}
void OnWS(object _, Whitespace ws)
protected virtual void OnWS(object? _, Whitespace ws)
{
LastText.Append(ws.Value);
if (_WS_Funcs != null)
@@ -111,9 +103,9 @@ public class XMLHelper<T> where T : IState
internal void Dispose()
{
OpenTags = null;
LastSingleTags = null;
LastText = null;
OpenTags.Clear();
LastSingleTags.Clear();
LastText.Clear();
if (_in != null)
{
if (_OTag_Funcs != null)

View File

@@ -4,31 +4,17 @@ using HaDocument.Comparers;
using HaDocument.Interfaces;
using System.Collections.Generic;
public class DocumentSearchResult {
public Meta MetaData { get; }
public List<DocumentResult> Results { get; }
public class SearchResult {
public string Search { get; private set; }
public string Index { get; private set; }
public string? Page { get; set; }
public string? Line { get; set; }
public string? Preview { get; set; }
// TODO:
public string? ParsedPreview { get; set; }
public DocumentSearchResult(Meta meta) {
MetaData = meta;
Results = new List<DocumentResult>(4);
}
}
public class DocumentResult {
public string PreviewString { get; }
public string Page { get; }
public string Line { get; }
public DocumentResult(string previewstring, string page, string line) {
PreviewString = previewstring;
Page = page;
Line = line;
}
}
public class LetterComparer : IComparer<DocumentSearchResult> {
public int Compare(DocumentSearchResult first, DocumentSearchResult second) {
var cmp = new DefaultComparer();
return cmp.Compare(first.MetaData, second.MetaData);
public SearchResult(string search, string index) {
Search = search;
Index = index;
}
}

View File

@@ -1,4 +1,5 @@
namespace HaWeb.Models;
using HaDocument.Models;
public class SucheViewModel {
public List<(int Year, List<BriefeMetaViewModel> LetterList)>? Letters { get; private set; }
@@ -11,6 +12,7 @@ public class SucheViewModel {
public string? ActiveVolume { get; private set; }
public string? ActivePage { get; private set; }
public string? ActiveSearch { get; private set; }
public Dictionary<string, List<SearchResult>>? SearchResults { get; private set; }
public SucheViewModel(
List<(int Year, List<BriefeMetaViewModel> LetterList)>? letters,
@@ -19,7 +21,9 @@ public class SucheViewModel {
List<(string Key, string Name)>? availablePersons,
List<(string Volume, List<string> Pages)>? availablePages,
string? activeVolume,
string? activePage
string? activePage,
string? activeSearch,
Dictionary<string, List<SearchResult>>? searchResults
) {
Letters = letters;
if (letters != null)
@@ -32,5 +36,7 @@ public class SucheViewModel {
AvailablePages = availablePages;
ActiveVolume = activeVolume;
ActivePage = activePage;
ActiveSearch = activeSearch;
SearchResults = searchResults;
}
}

View File

@@ -73,4 +73,6 @@ TODO Abhärten des Konstruktors von XMLRootDokument für von außerhalb platzier
TODO XML-Check im Client
TODO Lock für die Liste, Bzw ConcurretBag
TODO 516A david friedlaender in den traditions
TODO 3 Zeilen marginal schließt perfekt an 2 zeilen text an
TODO 3 Zeilen marginal schließt perfekt an 2 zeilen text an
TODO Einfügungszeichen zerstört suchergebnisse

View File

@@ -1,76 +1,76 @@
namespace HaWeb;
using HaDocument.Models;
using HaDocument.Interfaces;
using HaDocument.Comparers;
using HaXMLReader.Interfaces;
using HaWeb.Models;
using HaXMLReader.EvArgs;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using System.Linq;
using System;
// namespace HaWeb;
// using HaDocument.Models;
// using HaDocument.Interfaces;
// using HaDocument.Comparers;
// using HaXMLReader.Interfaces;
// using HaWeb.Models;
// using HaXMLReader.EvArgs;
// using System.Collections.Concurrent;
// using System.Collections.Generic;
// using System.Collections.Immutable;
// using System.Threading.Tasks;
// using System.Linq;
// using System;
internal class DocumentSearch {
internal DocumentSearchResult _searchResult { get; }
private IReader _reader { get; }
private string _search { get; }
// internal class DocumentSearch {
// internal DocumentSearchResult _searchResult { get; }
// private IReader _reader { get; }
// private string _search { get; }
internal DocumentSearch(DocumentSearchResult res, IReader reader, string search) {
_searchResult = res;
_reader = reader;
_search = search;
_pg = "";
}
// internal DocumentSearch(DocumentSearchResult res, IReader reader, string search) {
// _searchResult = res;
// _reader = reader;
// _search = search;
// _pg = "";
// }
internal DocumentSearchResult Act() {
_reader.Text += OnText;
_reader.SingleTag += OnSTag;
_reader.Read();
return _searchResult;
}
// internal DocumentSearchResult Act() {
// _reader.Text += OnText;
// _reader.SingleTag += OnSTag;
// _reader.Read();
// return _searchResult;
// }
internal void OnText(object _, Text text) {
if (text.Value.ToLower().Contains(_search.ToLower())) {
_searchResult.Results.Add(new DocumentResult(text.Value, _pg, _ln));
}
}
// internal void OnText(object _, Text text) {
// if (text.Value.ToLower().Contains(_search.ToLower())) {
// _searchResult.Results.Add(new DocumentResult(text.Value, _pg, _ln));
// }
// }
private string _pg;
private string _ln;
// private string _pg;
// private string _ln;
internal void OnSTag(object _, Tag tag) {
if (tag.Name == "page")
_pg = tag["index"];
else if (tag.Name == "line")
_ln = tag["index"];
}
}
// internal void OnSTag(object _, Tag tag) {
// if (tag.Name == "page")
// _pg = tag["index"];
// else if (tag.Name == "line")
// _ln = tag["index"];
// }
// }
internal class RegisterSearch {
internal Comment _searchResult { get; }
private IReader _reader { get; }
private string _search { get; }
private bool found;
// internal class RegisterSearch {
// internal Comment _searchResult { get; }
// private IReader _reader { get; }
// private string _search { get; }
// private bool found;
internal RegisterSearch(Comment res, IReader reader, string search) {
_searchResult = res;
_reader = reader;
_search = search;
found = false;
}
// internal RegisterSearch(Comment res, IReader reader, string search) {
// _searchResult = res;
// _reader = reader;
// _search = search;
// found = false;
// }
internal bool Act() {
_reader.Text += OnText;
_reader.Read();
return found;
}
// internal bool Act() {
// _reader.Text += OnText;
// _reader.Read();
// return found;
// }
internal void OnText(object _, Text text) {
if (text.Value.ToLower().Contains(_search.ToLower())) {
found = true;
}
}
}
// internal void OnText(object _, Text text) {
// if (text.Value.ToLower().Contains(_search.ToLower())) {
// found = true;
// }
// }
// }

View File

@@ -0,0 +1,6 @@
namespace HaWeb.SearchHelpers;
public interface ISearchable {
public string Index { get; }
public string SearchText { get; }
}

View File

@@ -0,0 +1,46 @@
namespace HaWeb.SearchHelpers;
using System.Text;
using System.Web;
using TagFuncList = List<(Func<HaXMLReader.EvArgs.Tag, HaWeb.HTMLParser.LineXMLHelper<SearchState>, bool>, Action<System.Text.StringBuilder, HaXMLReader.EvArgs.Tag, HaWeb.HTMLParser.LineXMLHelper<SearchState>>)>;
using TextFuncList = List<(Func<HaXMLReader.EvArgs.Text, HaWeb.HTMLParser.LineXMLHelper<SearchState>, bool>, Action<System.Text.StringBuilder, HaXMLReader.EvArgs.Text, HaWeb.HTMLParser.LineXMLHelper<SearchState>>)>;
using WhitespaceFuncList = List<(Func<HaXMLReader.EvArgs.Whitespace, HaWeb.HTMLParser.LineXMLHelper<SearchState>, bool>, Action<System.Text.StringBuilder, HaXMLReader.EvArgs.Whitespace, HaWeb.HTMLParser.LineXMLHelper<SearchState>>)>;
public class SearchRules {
public static readonly TextFuncList TextRules = new TextFuncList() {
( (x, _) => true, (sb, text, reader) => {
var t = text.Value;
if (reader.State.Normalize)
t = HaWeb.SearchHelpers.StringHelpers.NormalizeWhiteSpace(t);
sb.Append(t);
var sw = reader.State.SearchWord;
if (sb.Length >= sw.Length) {
if (sb.ToString().ToLower().Contains(sw)) {
if (reader.State.Results == null)
reader.State.Results = new List<(string Page, string Line)>();
reader.State.Results.Add((reader.CurrentPage, reader.CurrentLine));
}
sb.Remove(0, sb.Length - sw.Length);
}
})
};
public static readonly WhitespaceFuncList WhitespaceRules= new WhitespaceFuncList() {
( (x, _) => true, (sb, text, reader) => {
var t = text.Value;
if (reader.State.Normalize)
t = HaWeb.SearchHelpers.StringHelpers.NormalizeWhiteSpace(t);
sb.Append(t);
var sw = reader.State.SearchWord;
if (sb.Length >= sw.Length) {
if (sb.ToString().Contains(sw)) {
if (reader.State.Results == null)
reader.State.Results = new List<(string Page, string Line)>();
reader.State.Results.Add((reader.CurrentPage, reader.CurrentLine));
}
sb.Remove(0, sb.Length - sw.Length);
}
})
};
}

View File

@@ -0,0 +1,15 @@
namespace HaWeb.SearchHelpers;
using System.Text;
public class SearchState : HaWeb.HTMLParser.IState {
internal string SearchWord;
internal bool Normalize;
internal List<(string Page, string Line)>? Results;
public SearchState(string searchword, bool normalize = false) {
Normalize = normalize;
SearchWord = searchword;
}
public void SetupState() {}
}

View File

@@ -0,0 +1,11 @@
namespace HaWeb.SearchHelpers;
public class SeachableItem : ISearchable {
public string Index { get; private set; }
public string SearchText { get; private set; }
public SeachableItem(string index, string searchtext) {
this.Index = index;
this.SearchText = searchtext;
}
}

View File

@@ -0,0 +1,39 @@
namespace HaWeb.SearchHelpers;
using System.Text;
public static class StringHelpers {
public static string NormalizeWhiteSpace(string input, char normalizeTo = ' ', bool toLower = true) {
if (string.IsNullOrEmpty(input)) {
return string.Empty;
}
StringBuilder output = new StringBuilder();
// TODO: what about punctuation (char.IsPunctuation()) ? what about spaces?
// Remove all whitespace, search becomes whitespace insensitive
// foreach (var c in input)
// if (!char.IsWhiteSpace(c)) {
// if (toLower) output.Append(char.ToLower(c));
// else output.Append(c);
// }
// Collapse all whitespace into a single whitespace:
bool skipped = false;
foreach (char c in input) {
if (char.IsWhiteSpace(c)) {
if (!skipped) {
output.Append(normalizeTo);
skipped = true;
}
} else {
skipped = false;
if (toLower) output.Append(char.ToLower(c));
else output.Append(c);
}
}
return output.ToString();
}
}

View File

@@ -14,7 +14,7 @@
<div class="ha-searchnav">
@if (Model.AvailableYears != null && Model.AvailableYears.Any()) {
@for(var i = 0; i < Model.AvailableYears.Count; i++) {
<a class="@(Model.ActiveYear == i ? "active" : "")" asp-route-person="@Model.ActivePerson" asp-controller="Suche" asp-action="Index" asp-route-page="@i">
<a class="@(Model.ActiveYear == i ? "active" : "")" asp-route-person="@Model.ActivePerson" asp-route-search="@Model.ActiveSearch" asp-controller="Suche" asp-route-page="@i">
@if (Model.AvailableYears[i].StartYear != Model.AvailableYears[i].EndYear) {
<span>
@Model.AvailableYears[i].StartYear-@Model.AvailableYears[i].EndYear
@@ -41,6 +41,12 @@
<a class="ha-letterlistentry" asp-controller="Briefe" asp-action="Index" asp-route-id="@letter.Meta.Autopsic">
@await Html.PartialAsync("/Views/Shared/_LetterHead.cshtml", (letter, true))
</a>
@if (Model.SearchResults != null && Model.SearchResults.ContainsKey(letter.Meta.Index)) {
@foreach (var item in Model.SearchResults[letter.Meta.Index])
{
<p>@item.Page / @item.Line: @item.Preview</p>
}
}
}
}
</div>
@@ -148,7 +154,7 @@
}
</div>
<form class="ha-searchform" id="ha-searchform">
<input id="ha-searchformtext" type="text" placeholder="Suchbegriff"/>
<input id="ha-searchformtext" type="text" placeholder="Suchbegriff" value="@Model.ActiveSearch"/>
<button id="ha-searchformsubmit" type="submit">Suchen</button>
</form>
</div>
@@ -164,7 +170,7 @@
const SUBMITSEARCH = function(filter) {
let f = filter.value;
window.location.href = "/Suche/" + f;
window.location.href = "/Suche?search=" + f;
}
window.addEventListener("load", () => {
@@ -192,7 +198,7 @@
<div class="ha-personlist">
<a class="ha-personlistperson @(Model.ActivePerson == null ? "active" : "")" asp-controller="Suche" asp-action="Index">Alle</a>
@foreach (var person in Model.AvailablePersons) {
<a class="ha-personlistperson @(Model.ActivePerson == person.Key ? "active" : "")" asp-controller="Suche" asp-action="Index" asp-route-person="@person.Key" asp-route-page="@null">
<a class="ha-personlistperson @(Model.ActivePerson == person.Key ? "active" : "")" asp-controller="Suche" asp-action="Person" asp-route-person="@person.Key" asp-route-page="@null">
@person.Name
</a>
}