mirror of
https://github.com/Theodor-Springmann-Stiftung/hamann-ausgabe-core.git
synced 2025-10-29 17:25:32 +00:00
Implemented Fulltext search across tags and linebreaks for letters
This commit is contained in:
6
HaDocumentV6/Interfaces/ISearchable.cs
Normal file
6
HaDocumentV6/Interfaces/ISearchable.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace HaDocument.Interfaces;
|
||||||
|
|
||||||
|
public interface ISearchable {
|
||||||
|
public string Element { get; }
|
||||||
|
public string Index { get; }
|
||||||
|
}
|
||||||
@@ -2,9 +2,9 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
namespace HaDocument.Models {
|
namespace HaDocument.Models{
|
||||||
public class Comment{
|
public class Comment : HaDocument.Interfaces.ISearchable {
|
||||||
public string Entry { get; } = "";
|
public string Element { get; } = "";
|
||||||
public string Index { get; } = "";
|
public string Index { get; } = "";
|
||||||
public string Type { get; } = "";
|
public string Type { get; } = "";
|
||||||
public string Lemma { get; } = "";
|
public string Lemma { get; } = "";
|
||||||
@@ -21,7 +21,7 @@ namespace HaDocument.Models {
|
|||||||
SortedDictionary<string, Comment> subComments,
|
SortedDictionary<string, Comment> subComments,
|
||||||
string parent=""
|
string parent=""
|
||||||
) {
|
) {
|
||||||
Entry = entry;
|
Element = entry;
|
||||||
Index = index;
|
Index = index;
|
||||||
Type = type;
|
Type = type;
|
||||||
Lemma = lemma;
|
Lemma = lemma;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace HaDocument.Models {
|
namespace HaDocument.Models {
|
||||||
public class Editreason {
|
public class Editreason : HaDocument.Interfaces.ISearchable {
|
||||||
public string Index { get; } = "";
|
public string Index { get; } = "";
|
||||||
public string Element { get; } = "";
|
public string Element { get; } = "";
|
||||||
public string Letter { get; } = "";
|
public string Letter { get; } = "";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace HaDocument.Models {
|
namespace HaDocument.Models {
|
||||||
public class Letter : HaModel {
|
public class Letter : HaModel, HaDocument.Interfaces.ISearchable {
|
||||||
public string Index { get; } = "";
|
public string Index { get; } = "";
|
||||||
public string Element { get; } = "";
|
public string Element { get; } = "";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace HaDocument.Models {
|
namespace HaDocument.Models {
|
||||||
public class Marginal {
|
public class Marginal : HaDocument.Interfaces.ISearchable {
|
||||||
public string Index { get; } = "";
|
public string Index { get; } = "";
|
||||||
public string Letter { get; } = "";
|
public string Letter { get; } = "";
|
||||||
public string Page { get; } = "";
|
public string Page { get; } = "";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace HaDocument.Models {
|
namespace HaDocument.Models {
|
||||||
public class Tradition {
|
public class Tradition : HaDocument.Interfaces.ISearchable {
|
||||||
public string Index { get; } = "";
|
public string Index { get; } = "";
|
||||||
public string Element { get; } = "";
|
public string Element { get; } = "";
|
||||||
|
|
||||||
|
|||||||
@@ -156,8 +156,6 @@ namespace HaDocument.Reactors {
|
|||||||
(_availableVolumes == null && _availableYearRange.Item1 == 0 && _availableYearRange.Item2 == 0)
|
(_availableVolumes == null && _availableYearRange.Item1 == 0 && _availableYearRange.Item2 == 0)
|
||||||
) {
|
) {
|
||||||
var ZHInfo = !inZH ? null : new ZHInfo(AltLineNumbering, dateChanged, Volume, Page);
|
var ZHInfo = !inZH ? null : new ZHInfo(AltLineNumbering, dateChanged, Volume, Page);
|
||||||
if (Autopsic == "0")
|
|
||||||
System.Diagnostics.Debugger.Break();
|
|
||||||
var meta = new Meta(
|
var meta = new Meta(
|
||||||
Index,
|
Index,
|
||||||
Autopsic,
|
Autopsic,
|
||||||
|
|||||||
@@ -4,16 +4,19 @@ using HaWeb.Models;
|
|||||||
using HaWeb.FileHelpers;
|
using HaWeb.FileHelpers;
|
||||||
using HaDocument.Interfaces;
|
using HaDocument.Interfaces;
|
||||||
using HaDocument.Models;
|
using HaDocument.Models;
|
||||||
|
using HaXMLReader.Interfaces;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
|
||||||
namespace HaWeb.Controllers;
|
namespace HaWeb.Controllers;
|
||||||
|
|
||||||
public class SucheController : Controller {
|
public class SucheController : Controller {
|
||||||
private IHaDocumentWrappper _lib;
|
private IHaDocumentWrappper _lib;
|
||||||
|
private IReaderService _readerService;
|
||||||
private int _lettersForPage;
|
private int _lettersForPage;
|
||||||
|
|
||||||
public SucheController(IHaDocumentWrappper lib, IConfiguration config) {
|
public SucheController(IHaDocumentWrappper lib, IReaderService readerService, IConfiguration config) {
|
||||||
_lib = lib;
|
_lib = lib;
|
||||||
|
_readerService = readerService;
|
||||||
_lettersForPage = config.GetValue<int>("LettersOnPage");
|
_lettersForPage = config.GetValue<int>("LettersOnPage");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,21 +51,45 @@ public class SucheController : Controller {
|
|||||||
[Route("Suche")]
|
[Route("Suche")]
|
||||||
// Order of actions:
|
// Order of actions:
|
||||||
// Filter, sort by year, paginate, sort by Meta.Sort & .Order, parse
|
// 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();
|
var lib = _lib.GetLibrary();
|
||||||
List<IGrouping<int, Meta>>? metasbyyear = null;
|
List<IGrouping<int, Meta>>? metasbyyear = null;
|
||||||
if (person != null) {
|
if (search != null) {
|
||||||
var letters = lib.Metas
|
var sw = new System.Diagnostics.Stopwatch();
|
||||||
.Where(x => x.Value.Senders.Contains(person) || x.Value.Receivers.Contains(person))
|
sw.Start();
|
||||||
.Select(x => x.Value);
|
var res = _lib.SearchLetters(search, _readerService);
|
||||||
if (letters == null) return _error404();
|
if (res == null || !res.Any()) return _error404();
|
||||||
metasbyyear = letters.ToLookup(x => x.Sort.Year).OrderBy(x => x.Key).ToList();
|
var ret = res.ToDictionary(
|
||||||
} else {
|
x => x.Index,
|
||||||
metasbyyear = lib.MetasByYear.OrderBy(x => x.Key).ToList();
|
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);
|
return _paginateSend(lib, page, metasbyyear, person);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private List<(string Key, string Person)> _getAvailablePersons(ILibrary lib) {
|
private List<(string Key, string Person)> _getAvailablePersons(ILibrary lib) {
|
||||||
return lib.Persons
|
return lib.Persons
|
||||||
.OrderBy(x => x.Value.Surname)
|
.OrderBy(x => x.Value.Surname)
|
||||||
@@ -106,7 +133,15 @@ public class SucheController : Controller {
|
|||||||
return res;
|
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);
|
var pages = _paginate(metasbyyear);
|
||||||
if (pages != null && page >= pages.Count) return _error404();
|
if (pages != null && page >= pages.Count) return _error404();
|
||||||
if (pages == null && page > 0) 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;
|
List<(string Volume, List<string> Pages)>? availablePages = null;
|
||||||
availablePages = lib.Structure.Select(x => (x.Key, x.Value.Select(x => x.Key).ToList())).ToList();
|
availablePages = lib.Structure.Select(x => (x.Key, x.Value.Select(x => x.Key).ToList())).ToList();
|
||||||
zhvolume = zhvolume == null ? "1" : zhvolume;
|
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;
|
if (person != null) model.ActivePerson = person;
|
||||||
return View("Index", model);
|
return View("Index", model);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ namespace HaWeb.FileHelpers;
|
|||||||
using HaDocument.Interfaces;
|
using HaDocument.Interfaces;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.Extensions.FileProviders;
|
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 {
|
public class HaDocumentWrapper : IHaDocumentWrappper {
|
||||||
private ILibrary Library;
|
private ILibrary Library;
|
||||||
@@ -10,6 +15,8 @@ public class HaDocumentWrapper : IHaDocumentWrappper {
|
|||||||
public int StartYear { get; private set; }
|
public int StartYear { get; private set; }
|
||||||
public int EndYear { get; private set; }
|
public int EndYear { get; private set; }
|
||||||
|
|
||||||
|
public List<SearchHelpers.SeachableItem>? SearchableLetters { get; private set; }
|
||||||
|
|
||||||
public HaDocumentWrapper(IXMLProvider xmlProvider, IConfiguration configuration) {
|
public HaDocumentWrapper(IXMLProvider xmlProvider, IConfiguration configuration) {
|
||||||
_xmlProvider = xmlProvider;
|
_xmlProvider = xmlProvider;
|
||||||
StartYear = configuration.GetValue<int>("AvailableStartYear");
|
StartYear = configuration.GetValue<int>("AvailableStartYear");
|
||||||
@@ -41,9 +48,45 @@ public class HaDocumentWrapper : IHaDocumentWrappper {
|
|||||||
return null;
|
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;
|
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() {
|
public ILibrary GetLibrary() {
|
||||||
return Library;
|
return Library;
|
||||||
}
|
}
|
||||||
@@ -57,4 +100,8 @@ public class HaDocumentWrapper : IHaDocumentWrappper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string _prepareSearch(HaDocument.Interfaces.ISearchable objecttoseach) {
|
||||||
|
return SearchHelpers.StringHelpers.NormalizeWhiteSpace(objecttoseach.Element, ' ', false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
namespace HaWeb.FileHelpers;
|
namespace HaWeb.FileHelpers;
|
||||||
using HaDocument.Interfaces;
|
using HaDocument.Interfaces;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using HaXMLReader.Interfaces;
|
||||||
|
|
||||||
public interface IHaDocumentWrappper {
|
public interface IHaDocumentWrappper {
|
||||||
public ILibrary ResetLibrary();
|
public ILibrary ResetLibrary();
|
||||||
public ILibrary? SetLibrary(string filepath, ModelStateDictionary ModelState);
|
public ILibrary? SetLibrary(string filepath, ModelStateDictionary ModelState);
|
||||||
public ILibrary GetLibrary();
|
public ILibrary GetLibrary();
|
||||||
|
public List<(string Index, List<(string Page, string Line, string Preview)> Results)>? SearchLetters(string searchword, IReaderService reader);
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ public static class CommentHelpers {
|
|||||||
sb.Append(HTMLHelpers.TagHelpers.CreateEndElement(DEFAULTELEMENT));
|
sb.Append(HTMLHelpers.TagHelpers.CreateEndElement(DEFAULTELEMENT));
|
||||||
}
|
}
|
||||||
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 HTMLParser.XMLHelper<CommentState>(commentState, rd, sb, CommentRules.OTagRules, CommentRules.STagRules, CommentRules.CTagRules, CommentRules.TextRules, CommentRules.WhitespaceRules);
|
||||||
new HTMLHelpers.LinkHelper(lib, rd, sb);
|
new HTMLHelpers.LinkHelper(lib, rd, sb);
|
||||||
rd.Read();
|
rd.Read();
|
||||||
|
|||||||
170
HaWeb/HTMLParser/LineXMLHelper.cs
Normal file
170
HaWeb/HTMLParser/LineXMLHelper.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
namespace HaWeb.HTMLParser;
|
|
||||||
|
|
||||||
class GenericParser<T> where T : IState {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -7,15 +7,13 @@ using System;
|
|||||||
|
|
||||||
public class XMLHelper<T> where T : IState
|
public class XMLHelper<T> where T : IState
|
||||||
{
|
{
|
||||||
private IReader _in;
|
protected IReader _in;
|
||||||
private StringBuilder _target;
|
protected StringBuilder _target;
|
||||||
private 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>>)>? _OTag_Funcs;
|
||||||
private 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>>)>? _STag_Funcs;
|
||||||
private List<(Func<Tag, XMLHelper<T>, bool>, Action<StringBuilder, Tag, XMLHelper<T>>)>? _CTag_Funcs;
|
protected 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;
|
protected 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;
|
protected List<(Func<Whitespace, XMLHelper<T>, bool>, Action<StringBuilder, Whitespace, XMLHelper<T>>)>? _WS_Funcs;
|
||||||
private bool _deleteLeadingWS;
|
|
||||||
private bool _deleteTrailingWS;
|
|
||||||
|
|
||||||
public T State;
|
public T State;
|
||||||
public Stack<Tag> OpenTags;
|
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>>)>? STag_Funcs = null,
|
||||||
List<(Func<Tag, XMLHelper<T>, bool>, Action<StringBuilder, Tag, XMLHelper<T>>)>? CTag_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<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,
|
List<(Func<Whitespace, XMLHelper<T>, bool>, Action<StringBuilder, Whitespace, XMLHelper<T>>)>? WS_Funcs = null
|
||||||
bool deleteLeadingWS = false,
|
|
||||||
bool deleteTrailingWS = false
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (input == null || target == null || state == null) throw new ArgumentNullException();
|
if (input == null || target == null || state == null) throw new ArgumentNullException();
|
||||||
@@ -40,8 +36,6 @@ public class XMLHelper<T> where T : IState
|
|||||||
State = state;
|
State = state;
|
||||||
_in = input;
|
_in = input;
|
||||||
_target = target;
|
_target = target;
|
||||||
_deleteLeadingWS = deleteLeadingWS;
|
|
||||||
_deleteTrailingWS = deleteTrailingWS;
|
|
||||||
|
|
||||||
_OTag_Funcs = OTag_Funcs;
|
_OTag_Funcs = OTag_Funcs;
|
||||||
_STag_Funcs = STag_Funcs;
|
_STag_Funcs = STag_Funcs;
|
||||||
@@ -65,7 +59,7 @@ public class XMLHelper<T> where T : IState
|
|||||||
_in.Whitespace += OnWS;
|
_in.Whitespace += OnWS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnOTag(object _, Tag tag)
|
protected virtual void OnOTag(object? _, Tag tag)
|
||||||
{
|
{
|
||||||
OpenTags.Push(tag);
|
OpenTags.Push(tag);
|
||||||
if (_OTag_Funcs != null)
|
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);
|
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);
|
LastText.Append(text.Value);
|
||||||
if (_Text_Funcs != null)
|
if (_Text_Funcs != null)
|
||||||
foreach (var entry in _Text_Funcs)
|
foreach (var entry in _Text_Funcs)
|
||||||
if (entry.Item1(text, this)) entry.Item2(_target, text, this);
|
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))
|
if (!LastSingleTags.ContainsKey(tag.Name))
|
||||||
LastSingleTags.Add(tag.Name, new List<Tag>());
|
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);
|
if (entry.Item1(tag, this)) entry.Item2(_target, tag, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnCTag(object _, Tag tag)
|
protected virtual void OnCTag(object? _, Tag tag)
|
||||||
{
|
{
|
||||||
OpenTags.Pop();
|
OpenTags.Pop();
|
||||||
if(_CTag_Funcs != null)
|
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);
|
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);
|
LastText.Append(ws.Value);
|
||||||
if (_WS_Funcs != null)
|
if (_WS_Funcs != null)
|
||||||
@@ -111,9 +103,9 @@ public class XMLHelper<T> where T : IState
|
|||||||
|
|
||||||
internal void Dispose()
|
internal void Dispose()
|
||||||
{
|
{
|
||||||
OpenTags = null;
|
OpenTags.Clear();
|
||||||
LastSingleTags = null;
|
LastSingleTags.Clear();
|
||||||
LastText = null;
|
LastText.Clear();
|
||||||
if (_in != null)
|
if (_in != null)
|
||||||
{
|
{
|
||||||
if (_OTag_Funcs != null)
|
if (_OTag_Funcs != null)
|
||||||
|
|||||||
@@ -4,31 +4,17 @@ using HaDocument.Comparers;
|
|||||||
using HaDocument.Interfaces;
|
using HaDocument.Interfaces;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
public class DocumentSearchResult {
|
public class SearchResult {
|
||||||
public Meta MetaData { get; }
|
public string Search { get; private set; }
|
||||||
public List<DocumentResult> Results { get; }
|
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) {
|
public SearchResult(string search, string index) {
|
||||||
MetaData = meta;
|
Search = search;
|
||||||
Results = new List<DocumentResult>(4);
|
Index = index;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
namespace HaWeb.Models;
|
namespace HaWeb.Models;
|
||||||
|
using HaDocument.Models;
|
||||||
|
|
||||||
public class SucheViewModel {
|
public class SucheViewModel {
|
||||||
public List<(int Year, List<BriefeMetaViewModel> LetterList)>? Letters { get; private set; }
|
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? ActiveVolume { get; private set; }
|
||||||
public string? ActivePage { get; private set; }
|
public string? ActivePage { get; private set; }
|
||||||
public string? ActiveSearch { get; private set; }
|
public string? ActiveSearch { get; private set; }
|
||||||
|
public Dictionary<string, List<SearchResult>>? SearchResults { get; private set; }
|
||||||
|
|
||||||
public SucheViewModel(
|
public SucheViewModel(
|
||||||
List<(int Year, List<BriefeMetaViewModel> LetterList)>? letters,
|
List<(int Year, List<BriefeMetaViewModel> LetterList)>? letters,
|
||||||
@@ -19,7 +21,9 @@ public class SucheViewModel {
|
|||||||
List<(string Key, string Name)>? availablePersons,
|
List<(string Key, string Name)>? availablePersons,
|
||||||
List<(string Volume, List<string> Pages)>? availablePages,
|
List<(string Volume, List<string> Pages)>? availablePages,
|
||||||
string? activeVolume,
|
string? activeVolume,
|
||||||
string? activePage
|
string? activePage,
|
||||||
|
string? activeSearch,
|
||||||
|
Dictionary<string, List<SearchResult>>? searchResults
|
||||||
) {
|
) {
|
||||||
Letters = letters;
|
Letters = letters;
|
||||||
if (letters != null)
|
if (letters != null)
|
||||||
@@ -32,5 +36,7 @@ public class SucheViewModel {
|
|||||||
AvailablePages = availablePages;
|
AvailablePages = availablePages;
|
||||||
ActiveVolume = activeVolume;
|
ActiveVolume = activeVolume;
|
||||||
ActivePage = activePage;
|
ActivePage = activePage;
|
||||||
|
ActiveSearch = activeSearch;
|
||||||
|
SearchResults = searchResults;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,4 +73,6 @@ TODO Abhärten des Konstruktors von XMLRootDokument für von außerhalb platzier
|
|||||||
TODO XML-Check im Client
|
TODO XML-Check im Client
|
||||||
TODO Lock für die Liste, Bzw ConcurretBag
|
TODO Lock für die Liste, Bzw ConcurretBag
|
||||||
TODO 516A david friedlaender in den traditions
|
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
|
||||||
130
HaWeb/Search.cs
130
HaWeb/Search.cs
@@ -1,76 +1,76 @@
|
|||||||
namespace HaWeb;
|
// namespace HaWeb;
|
||||||
using HaDocument.Models;
|
// using HaDocument.Models;
|
||||||
using HaDocument.Interfaces;
|
// using HaDocument.Interfaces;
|
||||||
using HaDocument.Comparers;
|
// using HaDocument.Comparers;
|
||||||
using HaXMLReader.Interfaces;
|
// using HaXMLReader.Interfaces;
|
||||||
using HaWeb.Models;
|
// using HaWeb.Models;
|
||||||
using HaXMLReader.EvArgs;
|
// using HaXMLReader.EvArgs;
|
||||||
using System.Collections.Concurrent;
|
// using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
// using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
// using System.Collections.Immutable;
|
||||||
using System.Threading.Tasks;
|
// using System.Threading.Tasks;
|
||||||
using System.Linq;
|
// using System.Linq;
|
||||||
using System;
|
// using System;
|
||||||
|
|
||||||
internal class DocumentSearch {
|
// internal class DocumentSearch {
|
||||||
internal DocumentSearchResult _searchResult { get; }
|
// internal DocumentSearchResult _searchResult { get; }
|
||||||
private IReader _reader { get; }
|
// private IReader _reader { get; }
|
||||||
private string _search { get; }
|
// private string _search { get; }
|
||||||
|
|
||||||
internal DocumentSearch(DocumentSearchResult res, IReader reader, string search) {
|
// internal DocumentSearch(DocumentSearchResult res, IReader reader, string search) {
|
||||||
_searchResult = res;
|
// _searchResult = res;
|
||||||
_reader = reader;
|
// _reader = reader;
|
||||||
_search = search;
|
// _search = search;
|
||||||
_pg = "";
|
// _pg = "";
|
||||||
}
|
// }
|
||||||
|
|
||||||
internal DocumentSearchResult Act() {
|
// internal DocumentSearchResult Act() {
|
||||||
_reader.Text += OnText;
|
// _reader.Text += OnText;
|
||||||
_reader.SingleTag += OnSTag;
|
// _reader.SingleTag += OnSTag;
|
||||||
_reader.Read();
|
// _reader.Read();
|
||||||
return _searchResult;
|
// return _searchResult;
|
||||||
}
|
// }
|
||||||
|
|
||||||
internal void OnText(object _, Text text) {
|
// internal void OnText(object _, Text text) {
|
||||||
if (text.Value.ToLower().Contains(_search.ToLower())) {
|
// if (text.Value.ToLower().Contains(_search.ToLower())) {
|
||||||
_searchResult.Results.Add(new DocumentResult(text.Value, _pg, _ln));
|
// _searchResult.Results.Add(new DocumentResult(text.Value, _pg, _ln));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
private string _pg;
|
// private string _pg;
|
||||||
private string _ln;
|
// private string _ln;
|
||||||
|
|
||||||
internal void OnSTag(object _, Tag tag) {
|
// internal void OnSTag(object _, Tag tag) {
|
||||||
if (tag.Name == "page")
|
// if (tag.Name == "page")
|
||||||
_pg = tag["index"];
|
// _pg = tag["index"];
|
||||||
else if (tag.Name == "line")
|
// else if (tag.Name == "line")
|
||||||
_ln = tag["index"];
|
// _ln = tag["index"];
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
internal class RegisterSearch {
|
// internal class RegisterSearch {
|
||||||
internal Comment _searchResult { get; }
|
// internal Comment _searchResult { get; }
|
||||||
private IReader _reader { get; }
|
// private IReader _reader { get; }
|
||||||
private string _search { get; }
|
// private string _search { get; }
|
||||||
private bool found;
|
// private bool found;
|
||||||
|
|
||||||
internal RegisterSearch(Comment res, IReader reader, string search) {
|
// internal RegisterSearch(Comment res, IReader reader, string search) {
|
||||||
_searchResult = res;
|
// _searchResult = res;
|
||||||
_reader = reader;
|
// _reader = reader;
|
||||||
_search = search;
|
// _search = search;
|
||||||
found = false;
|
// found = false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
internal bool Act() {
|
// internal bool Act() {
|
||||||
_reader.Text += OnText;
|
// _reader.Text += OnText;
|
||||||
_reader.Read();
|
// _reader.Read();
|
||||||
return found;
|
// return found;
|
||||||
}
|
// }
|
||||||
|
|
||||||
internal void OnText(object _, Text text) {
|
// internal void OnText(object _, Text text) {
|
||||||
if (text.Value.ToLower().Contains(_search.ToLower())) {
|
// if (text.Value.ToLower().Contains(_search.ToLower())) {
|
||||||
found = true;
|
// found = true;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
6
HaWeb/SearchHelpers/ISearchable.cs
Normal file
6
HaWeb/SearchHelpers/ISearchable.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace HaWeb.SearchHelpers;
|
||||||
|
|
||||||
|
public interface ISearchable {
|
||||||
|
public string Index { get; }
|
||||||
|
public string SearchText { get; }
|
||||||
|
}
|
||||||
46
HaWeb/SearchHelpers/SearchRules.cs
Normal file
46
HaWeb/SearchHelpers/SearchRules.cs
Normal 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);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
15
HaWeb/SearchHelpers/SearchState.cs
Normal file
15
HaWeb/SearchHelpers/SearchState.cs
Normal 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() {}
|
||||||
|
}
|
||||||
11
HaWeb/SearchHelpers/SearchableItem.cs
Normal file
11
HaWeb/SearchHelpers/SearchableItem.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
39
HaWeb/SearchHelpers/StringHelpers.cs
Normal file
39
HaWeb/SearchHelpers/StringHelpers.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<div class="ha-searchnav">
|
<div class="ha-searchnav">
|
||||||
@if (Model.AvailableYears != null && Model.AvailableYears.Any()) {
|
@if (Model.AvailableYears != null && Model.AvailableYears.Any()) {
|
||||||
@for(var i = 0; i < Model.AvailableYears.Count; i++) {
|
@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) {
|
@if (Model.AvailableYears[i].StartYear != Model.AvailableYears[i].EndYear) {
|
||||||
<span>
|
<span>
|
||||||
@Model.AvailableYears[i].StartYear-@Model.AvailableYears[i].EndYear
|
@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">
|
<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))
|
@await Html.PartialAsync("/Views/Shared/_LetterHead.cshtml", (letter, true))
|
||||||
</a>
|
</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>
|
</div>
|
||||||
@@ -148,7 +154,7 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<form class="ha-searchform" id="ha-searchform">
|
<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>
|
<button id="ha-searchformsubmit" type="submit">Suchen</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -164,7 +170,7 @@
|
|||||||
|
|
||||||
const SUBMITSEARCH = function(filter) {
|
const SUBMITSEARCH = function(filter) {
|
||||||
let f = filter.value;
|
let f = filter.value;
|
||||||
window.location.href = "/Suche/" + f;
|
window.location.href = "/Suche?search=" + f;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("load", () => {
|
window.addEventListener("load", () => {
|
||||||
@@ -192,7 +198,7 @@
|
|||||||
<div class="ha-personlist">
|
<div class="ha-personlist">
|
||||||
<a class="ha-personlistperson @(Model.ActivePerson == null ? "active" : "")" asp-controller="Suche" asp-action="Index">Alle</a>
|
<a class="ha-personlistperson @(Model.ActivePerson == null ? "active" : "")" asp-controller="Suche" asp-action="Index">Alle</a>
|
||||||
@foreach (var person in Model.AvailablePersons) {
|
@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
|
@person.Name
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user