diff --git a/phase05/FullTextSearch.Controller/DocumentController/Abstraction/IDocumentBuilder.cs b/phase05/FullTextSearch.Controller/DocumentController/Abstraction/IDocumentBuilder.cs new file mode 100644 index 0000000..107f740 --- /dev/null +++ b/phase05/FullTextSearch.Controller/DocumentController/Abstraction/IDocumentBuilder.cs @@ -0,0 +1,14 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.DocumentController.Abstraction; + +public interface IDocumentBuilder +{ + + void BuildName(string name); + void BuildPath(string path); + void BuildText(string text); + void DeleteExtraSpace(); + void BuildWords(); + Document GetDocument(); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/DocumentController/Abstraction/IDocumentDirector.cs b/phase05/FullTextSearch.Controller/DocumentController/Abstraction/IDocumentDirector.cs new file mode 100644 index 0000000..0b32ce2 --- /dev/null +++ b/phase05/FullTextSearch.Controller/DocumentController/Abstraction/IDocumentDirector.cs @@ -0,0 +1,6 @@ +namespace FullTextSearch.Controller.DocumentController.Abstraction; + +public interface IDocumentDirector +{ + void Construct(string name, string path, string text, IDocumentBuilder documentBuilder); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/DocumentController/Abstraction/IDocumentFormatter.cs b/phase05/FullTextSearch.Controller/DocumentController/Abstraction/IDocumentFormatter.cs new file mode 100644 index 0000000..6dc13f6 --- /dev/null +++ b/phase05/FullTextSearch.Controller/DocumentController/Abstraction/IDocumentFormatter.cs @@ -0,0 +1,7 @@ +namespace InvertedIndex.Controller.Document; + +public interface IDocumentFormatter +{ + string ToUpper(string text); + IEnumerable Split(string queryText, string regex); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/DocumentController/DocumentBuilder.cs b/phase05/FullTextSearch.Controller/DocumentController/DocumentBuilder.cs new file mode 100644 index 0000000..1531c40 --- /dev/null +++ b/phase05/FullTextSearch.Controller/DocumentController/DocumentBuilder.cs @@ -0,0 +1,47 @@ +using System.Text.RegularExpressions; +using FullTextSearch.Controller.DocumentController.Abstraction; +using FullTextSearch.Core; +using InvertedIndex.Controller.Document; + +namespace FullTextSearch.Controller.DocumentController; + +public class DocumentBuilder : IDocumentBuilder +{ + private readonly Document _document; + private readonly IDocumentFormatter _documentFormatter; + + public DocumentBuilder(IDocumentFormatter documentFormatter) + { + _documentFormatter = documentFormatter ?? throw new ArgumentNullException(nameof(documentFormatter)); + _document = new Document(); + } + + public void BuildName(string name) + { + _document.Name = name; + } + + public void BuildPath(string path) + { + _document.Path = path; + } + + public void BuildText(string text) + { + _document.Text = text; + } + + public void DeleteExtraSpace() + { + _document.Text = Regex.Replace( _document.Text, @"\s+", " "); + } + public void BuildWords() + { + _document.Words = _documentFormatter.Split(_documentFormatter.ToUpper(_document.Text), " "); + } + + public Document GetDocument() + { + return _document; + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/DocumentController/DocumentDirector.cs b/phase05/FullTextSearch.Controller/DocumentController/DocumentDirector.cs new file mode 100644 index 0000000..518b17a --- /dev/null +++ b/phase05/FullTextSearch.Controller/DocumentController/DocumentDirector.cs @@ -0,0 +1,16 @@ +using FullTextSearch.Controller.DocumentController.Abstraction; + +namespace FullTextSearch.Controller.DocumentController; + +public class DocumentDirector : IDocumentDirector +{ + public void Construct(string name, string path, string text, IDocumentBuilder documentBuilder) + { + documentBuilder.BuildName(name); + documentBuilder.BuildPath(path); + documentBuilder.BuildText(text); + documentBuilder.DeleteExtraSpace(); + documentBuilder.BuildWords(); + } + +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/DocumentController/DocumentFormatter.cs b/phase05/FullTextSearch.Controller/DocumentController/DocumentFormatter.cs new file mode 100644 index 0000000..d92a7c6 --- /dev/null +++ b/phase05/FullTextSearch.Controller/DocumentController/DocumentFormatter.cs @@ -0,0 +1,18 @@ +using System.Text.RegularExpressions; +using InvertedIndex.Controller.Document; + +namespace FullTextSearch.Controller.DocumentController; + +public class DocumentFormatter : IDocumentFormatter{ + + public string ToUpper(string text) + { + return text.ToUpper(); + } + + public IEnumerable Split(string queryText, string regex) + { + return Regex.Split(queryText, regex); + } + +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/FullTextSearch.Controller.csproj b/phase05/FullTextSearch.Controller/FullTextSearch.Controller.csproj new file mode 100644 index 0000000..3ba61ca --- /dev/null +++ b/phase05/FullTextSearch.Controller/FullTextSearch.Controller.csproj @@ -0,0 +1,22 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/phase05/FullTextSearch.Controller/InvertedIndexController/Abstraction/IInverIndexMapper.cs b/phase05/FullTextSearch.Controller/InvertedIndexController/Abstraction/IInverIndexMapper.cs new file mode 100644 index 0000000..00b0277 --- /dev/null +++ b/phase05/FullTextSearch.Controller/InvertedIndexController/Abstraction/IInverIndexMapper.cs @@ -0,0 +1,8 @@ +using System.Collections; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.InvertedIndexController; + +public interface IInvertedIndexMapper { + Dictionary> Map(IEnumerable documents); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/InvertedIndexController/InvertedIndexMapper.cs b/phase05/FullTextSearch.Controller/InvertedIndexController/InvertedIndexMapper.cs new file mode 100644 index 0000000..c08e031 --- /dev/null +++ b/phase05/FullTextSearch.Controller/InvertedIndexController/InvertedIndexMapper.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.InvertedIndexController; + +public class InvertedIndexMapper : IInvertedIndexMapper +{ + public Dictionary> Map(IEnumerable documents) + { + var invertedIndex = new Dictionary>(); + + foreach (var document in documents) + { + var words = document.Words.ToList(); + + for (int startIndex = 0; startIndex < words.Count; startIndex++) + { + for (int endIndex = startIndex + 1; endIndex <= Math.Min(words.Count, startIndex + 5); endIndex++) + { + var phrase = string.Join(" ", words.Skip(startIndex).Take(endIndex - startIndex)); + if (!invertedIndex.ContainsKey(phrase)) + invertedIndex[phrase] = new List(); + invertedIndex[phrase].Add(document); + } + } + } + return invertedIndex.ToDictionary(pair => pair.Key, pair => (IEnumerable)pair.Value); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/Program.cs b/phase05/FullTextSearch.Controller/Program.cs new file mode 100644 index 0000000..e5dff12 --- /dev/null +++ b/phase05/FullTextSearch.Controller/Program.cs @@ -0,0 +1,3 @@ +// See https://aka.ms/new-console-template for more information + +Console.WriteLine("Hello, World!"); \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/QueryController/Abstraction/IQueryBuilder.cs b/phase05/FullTextSearch.Controller/QueryController/Abstraction/IQueryBuilder.cs new file mode 100644 index 0000000..1aecab1 --- /dev/null +++ b/phase05/FullTextSearch.Controller/QueryController/Abstraction/IQueryBuilder.cs @@ -0,0 +1,10 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.QueryController.Abstraction; + +public interface IQueryBuilder +{ + void BuildText(string text); + void BuildWordsBySign(IEnumerable signs); + Query GetQuery(); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/QueryController/Abstraction/IQueryDirector.cs b/phase05/FullTextSearch.Controller/QueryController/Abstraction/IQueryDirector.cs new file mode 100644 index 0000000..ac9e4e3 --- /dev/null +++ b/phase05/FullTextSearch.Controller/QueryController/Abstraction/IQueryDirector.cs @@ -0,0 +1,6 @@ +namespace FullTextSearch.Controller.QueryController.Abstraction; + +public interface IQueryDirector +{ + void Construct(IQueryBuilder queryBuilder); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/QueryController/Abstraction/IQueryFormatter.cs b/phase05/FullTextSearch.Controller/QueryController/Abstraction/IQueryFormatter.cs new file mode 100644 index 0000000..c080f05 --- /dev/null +++ b/phase05/FullTextSearch.Controller/QueryController/Abstraction/IQueryFormatter.cs @@ -0,0 +1,9 @@ +namespace FullTextSearch.Controller.QueryController.Abstraction; + +public interface IQueryFormatter +{ + string ToUpper(string text); + IEnumerable Split(string queryText, string regex); + IEnumerable CollectBySign(IEnumerable queryWords, char sign); + IEnumerable RemovePrefix(IEnumerable querySameSignWords); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/QueryController/QueryBuilder.cs b/phase05/FullTextSearch.Controller/QueryController/QueryBuilder.cs new file mode 100644 index 0000000..2194069 --- /dev/null +++ b/phase05/FullTextSearch.Controller/QueryController/QueryBuilder.cs @@ -0,0 +1,66 @@ +using FullTextSearch.Controller.QueryController.Abstraction; +using FullTextSearch.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using FullTextSearch.Controller.TextFormatter.Abstraction; +using InvertedIndex.Controller.Document; + +namespace FullTextSearch.Controller.QueryController; + +public class QueryBuilder : IQueryBuilder +{ + private readonly Query _query; + private readonly IQueryFormatter _queryFormatter; + private readonly ITextFormatter _textFormatter; + public QueryBuilder(IQueryFormatter queryFormatter, ITextFormatter textFormatter) + { + _query = new Query(); + _queryFormatter = queryFormatter ?? throw new ArgumentNullException(nameof(queryFormatter)); + _textFormatter = textFormatter ?? throw new ArgumentNullException(nameof(textFormatter)); + } + + public void BuildText(string text) + { + _query.Text = text; + } + + public void BuildWordsBySign(IEnumerable signs) + { + _query.WordsBySign.Clear(); + + var splittedText = _queryFormatter.Split(_queryFormatter.ToUpper(_query.Text), " ").ToList(); + var quoteIndices = _textFormatter.GetQuoteIndices(splittedText); + var indicesToRemove = _textFormatter.GetIndicesToRemove(quoteIndices); + var filteredWords = _textFormatter.FilterOutIndices(splittedText, indicesToRemove); + var concatenatedQuotes = _textFormatter.ConcatenateQuotedWords(splittedText, quoteIndices); + + ProcessWordsBySign(filteredWords, signs); + ProcessWordsBySign(concatenatedQuotes, signs); + } + private void ProcessWordsBySign(IEnumerable words, IEnumerable signs) + { + + foreach (var sign in signs) + { + if (!_query.WordsBySign.ContainsKey(sign)) + { + _query.WordsBySign[sign] = new List(); + } + var queryWords = words.ToList(); + var texts = _queryFormatter.CollectBySign(queryWords, sign); + words = queryWords.Except(texts).ToList(); + _query.WordsBySign[sign] = _query.WordsBySign[sign].Concat(_queryFormatter.RemovePrefix(texts)).ToList(); + } + if (!_query.WordsBySign.ContainsKey(' ')) + { + _query.WordsBySign[' '] = new List(); + } + _query.WordsBySign[' '] = _query.WordsBySign[' '].Concat(_queryFormatter.RemovePrefix(words)).ToList(); + } + + public Query GetQuery() + { + return _query; + } +} diff --git a/phase05/FullTextSearch.Controller/QueryController/QueryDirector.cs b/phase05/FullTextSearch.Controller/QueryController/QueryDirector.cs new file mode 100644 index 0000000..06c7b16 --- /dev/null +++ b/phase05/FullTextSearch.Controller/QueryController/QueryDirector.cs @@ -0,0 +1,12 @@ +using FullTextSearch.Controller.QueryController.Abstraction; + +namespace FullTextSearch.Controller.QueryController; + +public class QueryDirector +{ + public void Construct(string text, IEnumerable signs, IQueryBuilder queryBuilder) + { + queryBuilder.BuildText(text); + queryBuilder.BuildWordsBySign(signs); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/QueryController/QueryFormatter.cs b/phase05/FullTextSearch.Controller/QueryController/QueryFormatter.cs new file mode 100644 index 0000000..b93807e --- /dev/null +++ b/phase05/FullTextSearch.Controller/QueryController/QueryFormatter.cs @@ -0,0 +1,38 @@ +using System.Text.RegularExpressions; +using FullTextSearch.Controller.QueryController.Abstraction; + +namespace FullTextSearch.Controller.QueryController; + +public class QueryFormatter : IQueryFormatter +{ + public string ToUpper(string text) + { + return text.ToUpper(); + } + + public IEnumerable Split(string queryText, string regex) + { + return Regex.Split(queryText, regex); + } + + public IEnumerable CollectBySign(IEnumerable queryWords, char sign) + { + return queryWords.Where(w => w[0].Equals(sign)); + } + + public IEnumerable RemovePrefix(IEnumerable querySameSignWords) + { + return querySameSignWords.Select(word => + { + if (word.StartsWith("+") || word.StartsWith("-")) + { + word = word.Substring(1); + } + if (word.StartsWith("\"") && word.EndsWith("\"")) + { + word = word.Substring(1, word.Length - 2); + } + return word; + }); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/Read/Abstraction/IDirectory.cs b/phase05/FullTextSearch.Controller/Read/Abstraction/IDirectory.cs new file mode 100644 index 0000000..a28e4b3 --- /dev/null +++ b/phase05/FullTextSearch.Controller/Read/Abstraction/IDirectory.cs @@ -0,0 +1,6 @@ +namespace FullTextSearch.Controller.Read.Abstraction; + +public interface IDirectory +{ + List GetFiles(string dirPath); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/Read/Abstraction/IFileReader.cs b/phase05/FullTextSearch.Controller/Read/Abstraction/IFileReader.cs new file mode 100644 index 0000000..3b9204e --- /dev/null +++ b/phase05/FullTextSearch.Controller/Read/Abstraction/IFileReader.cs @@ -0,0 +1,6 @@ +namespace InvertedIndex.Abstraction.Read; + +public interface IFileReader +{ + Task ReadAsync(string path); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/Read/Abstraction/IPath.cs b/phase05/FullTextSearch.Controller/Read/Abstraction/IPath.cs new file mode 100644 index 0000000..64d6fee --- /dev/null +++ b/phase05/FullTextSearch.Controller/Read/Abstraction/IPath.cs @@ -0,0 +1,6 @@ +namespace FullTextSearch.Controller.Read.Abstraction; + +public interface IPath +{ + string GetFileName(string path); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/Read/Directory.cs b/phase05/FullTextSearch.Controller/Read/Directory.cs new file mode 100644 index 0000000..a1ae1e3 --- /dev/null +++ b/phase05/FullTextSearch.Controller/Read/Directory.cs @@ -0,0 +1,11 @@ +using FullTextSearch.Controller.Read.Abstraction; + +namespace FullTextSearch.Controller.Read; + +public class Directory : IDirectory +{ + public List GetFiles(string dirPath) + { + return System.IO.Directory.GetFiles(dirPath).ToList(); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/Read/Path.cs b/phase05/FullTextSearch.Controller/Read/Path.cs new file mode 100644 index 0000000..97620be --- /dev/null +++ b/phase05/FullTextSearch.Controller/Read/Path.cs @@ -0,0 +1,11 @@ +using FullTextSearch.Controller.Read.Abstraction; + +namespace FullTextSearch.Controller.Read; + +public class Path : IPath +{ + public string GetFileName(string path) + { + return System.IO.Path.GetFileName(path); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/Read/TextFileReader.cs b/phase05/FullTextSearch.Controller/Read/TextFileReader.cs new file mode 100644 index 0000000..ba23675 --- /dev/null +++ b/phase05/FullTextSearch.Controller/Read/TextFileReader.cs @@ -0,0 +1,11 @@ +using InvertedIndex.Abstraction.Read; + +namespace InvertedIndex.Controller.Read; + +public class TextFileReader : IFileReader +{ + public async Task ReadAsync(string path) + { + return await File.ReadAllTextAsync(path); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/ResultController/Abstraction/IFilter.cs b/phase05/FullTextSearch.Controller/ResultController/Abstraction/IFilter.cs new file mode 100644 index 0000000..737d98f --- /dev/null +++ b/phase05/FullTextSearch.Controller/ResultController/Abstraction/IFilter.cs @@ -0,0 +1,7 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController.Abstraction; + +public interface IFilter { + IEnumerable Filter(IEnumerable documents, Dictionary> documentsBySign); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/ResultController/Abstraction/IFilterDriver.cs b/phase05/FullTextSearch.Controller/ResultController/Abstraction/IFilterDriver.cs new file mode 100644 index 0000000..ced7f7f --- /dev/null +++ b/phase05/FullTextSearch.Controller/ResultController/Abstraction/IFilterDriver.cs @@ -0,0 +1,8 @@ +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController; + +public interface IFilterDriver { + void DriveFilterer(IEnumerable filterers, Result result); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/ResultController/Abstraction/IMinusFilter.cs b/phase05/FullTextSearch.Controller/ResultController/Abstraction/IMinusFilter.cs new file mode 100644 index 0000000..bd97d26 --- /dev/null +++ b/phase05/FullTextSearch.Controller/ResultController/Abstraction/IMinusFilter.cs @@ -0,0 +1,10 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController.Abstraction; + +public interface IMinusFilter +{ + IEnumerable Filter(IEnumerable documents, + Dictionary> documentsBySign); + +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/ResultController/Abstraction/INoSignedFilter.cs b/phase05/FullTextSearch.Controller/ResultController/Abstraction/INoSignedFilter.cs new file mode 100644 index 0000000..38f13ff --- /dev/null +++ b/phase05/FullTextSearch.Controller/ResultController/Abstraction/INoSignedFilter.cs @@ -0,0 +1,10 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController.Abstraction; + +public interface INoSignedFilter +{ + IEnumerable Filter(IEnumerable documents, + Dictionary> documentsBySign); + +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/ResultController/Abstraction/IPlusFilter.cs b/phase05/FullTextSearch.Controller/ResultController/Abstraction/IPlusFilter.cs new file mode 100644 index 0000000..65208a9 --- /dev/null +++ b/phase05/FullTextSearch.Controller/ResultController/Abstraction/IPlusFilter.cs @@ -0,0 +1,10 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController.Abstraction; + +public interface IPlusFilter +{ + IEnumerable Filter(IEnumerable documents, + Dictionary> documentsBySign); + +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/ResultController/Abstraction/IResultBuilder.cs b/phase05/FullTextSearch.Controller/ResultController/Abstraction/IResultBuilder.cs new file mode 100644 index 0000000..7e02167 --- /dev/null +++ b/phase05/FullTextSearch.Controller/ResultController/Abstraction/IResultBuilder.cs @@ -0,0 +1,13 @@ +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController.Abstraction; + +public interface IResultBuilder +{ + void BuildDocumentsBySign(IEnumerable searchers, Query query, + Dictionary> invertedIndex); + + void BuildDocuments(IEnumerable filters, Dictionary> invertedIndex); + Result GetResult(); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/ResultController/Abstraction/IResultDirector.cs b/phase05/FullTextSearch.Controller/ResultController/Abstraction/IResultDirector.cs new file mode 100644 index 0000000..4e4a076 --- /dev/null +++ b/phase05/FullTextSearch.Controller/ResultController/Abstraction/IResultDirector.cs @@ -0,0 +1,9 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController.Abstraction; + +public interface IResultDirector +{ + void Construct(IResultBuilder resultBuilder, Query query, + Dictionary> invertedIndex); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/ResultController/FilterDriver.cs b/phase05/FullTextSearch.Controller/ResultController/FilterDriver.cs new file mode 100644 index 0000000..334853e --- /dev/null +++ b/phase05/FullTextSearch.Controller/ResultController/FilterDriver.cs @@ -0,0 +1,12 @@ +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController; + +public class FilterDriver : IFilterDriver { + public void DriveFilterer(IEnumerable filterers, Result result) + { + filterers.ToList().ForEach(f=> result.documents = f.Filter(result.documents, result.documentsBySign)); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/ResultController/MinusFilter.cs b/phase05/FullTextSearch.Controller/ResultController/MinusFilter.cs new file mode 100644 index 0000000..20ec409 --- /dev/null +++ b/phase05/FullTextSearch.Controller/ResultController/MinusFilter.cs @@ -0,0 +1,12 @@ +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController; + +public class MinusFilter : IFilter +{ + public IEnumerable Filter(IEnumerable documents, Dictionary> documentsBySign) + { + return documents.Except(documentsBySign['-']); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/ResultController/NoSignedFilter.cs b/phase05/FullTextSearch.Controller/ResultController/NoSignedFilter.cs new file mode 100644 index 0000000..b58af2e --- /dev/null +++ b/phase05/FullTextSearch.Controller/ResultController/NoSignedFilter.cs @@ -0,0 +1,12 @@ +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController; + +public class NoSignedFilter : IFilter +{ + public IEnumerable Filter(IEnumerable documents, Dictionary> documentsBySign) + { + return documents.Intersect(documentsBySign[' ']); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/ResultController/PlusFilter.cs b/phase05/FullTextSearch.Controller/ResultController/PlusFilter.cs new file mode 100644 index 0000000..7de9f32 --- /dev/null +++ b/phase05/FullTextSearch.Controller/ResultController/PlusFilter.cs @@ -0,0 +1,12 @@ +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController; + +public class PlusFilter : IFilter +{ + public IEnumerable Filter(IEnumerable documents, Dictionary> documentsBySign) + { + return documents.Intersect(documentsBySign['+']); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/ResultController/ResultBuilder.cs b/phase05/FullTextSearch.Controller/ResultController/ResultBuilder.cs new file mode 100644 index 0000000..947dcec --- /dev/null +++ b/phase05/FullTextSearch.Controller/ResultController/ResultBuilder.cs @@ -0,0 +1,37 @@ +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Controller.SearchController.Abstraction; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController; + +public class ResultBuilder : IResultBuilder +{ + + private readonly Result _result; + private readonly ISearcherDriver _searcherDriver; + private readonly IFilterDriver _filterDriver; + + public ResultBuilder(IFilterDriver filterDriver, ISearcherDriver searcherDriver) + { + _result = new Result(); + _filterDriver = filterDriver ?? throw new ArgumentNullException(nameof(filterDriver)); + _searcherDriver = searcherDriver ?? throw new ArgumentNullException(nameof(searcherDriver)); + } + + public void BuildDocumentsBySign(IEnumerable searchers, Query query, Dictionary> invertedIndex) + { + _searcherDriver.DriveSearch(searchers, query, _result, invertedIndex); + } + + public void BuildDocuments(IEnumerable filters, Dictionary> invertedIndex) + { + _result.documents = new UniversalSearch().GetUniversal(invertedIndex); + _filterDriver.DriveFilterer(filters, _result); + } + + public Result GetResult() + { + return _result; + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/ResultController/ResultDirector.cs b/phase05/FullTextSearch.Controller/ResultController/ResultDirector.cs new file mode 100644 index 0000000..6514e24 --- /dev/null +++ b/phase05/FullTextSearch.Controller/ResultController/ResultDirector.cs @@ -0,0 +1,15 @@ +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Core; +using Query = NSubstitute.Core.Query; + +namespace FullTextSearch.Controller.ResultController; + +public class ResultDirector : IResultDirector +{ + public void Construct(IResultBuilder resultBuilder, Core.Query query, Dictionary> invertedIndex) + { + resultBuilder.BuildDocumentsBySign(new List(){new MinusSearcher(), new PlusSearcher(), new NoSignedSearcher()}, query, invertedIndex); + resultBuilder.BuildDocuments(new List(){new NoSignedFilter(), new PlusFilter(), new MinusFilter()},invertedIndex); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/SearchController/Abstraction/ISearcher.cs b/phase05/FullTextSearch.Controller/SearchController/Abstraction/ISearcher.cs new file mode 100644 index 0000000..e60676a --- /dev/null +++ b/phase05/FullTextSearch.Controller/SearchController/Abstraction/ISearcher.cs @@ -0,0 +1,9 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController; + +public interface ISearcher +{ + char Sign {get; init;} + IEnumerable Search(Query query, Dictionary> invertedIndex); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/SearchController/Abstraction/ISearcherDriver.cs b/phase05/FullTextSearch.Controller/SearchController/Abstraction/ISearcherDriver.cs new file mode 100644 index 0000000..28d5a58 --- /dev/null +++ b/phase05/FullTextSearch.Controller/SearchController/Abstraction/ISearcherDriver.cs @@ -0,0 +1,7 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController.Abstraction; + +public interface ISearcherDriver { + void DriveSearch(IEnumerable searchers, Query query, Result result, Dictionary> invertedIndex); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/SearchController/Abstraction/IUniversalSearch.cs b/phase05/FullTextSearch.Controller/SearchController/Abstraction/IUniversalSearch.cs new file mode 100644 index 0000000..3da7da2 --- /dev/null +++ b/phase05/FullTextSearch.Controller/SearchController/Abstraction/IUniversalSearch.cs @@ -0,0 +1,8 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController; + +public interface IUniversalSearch +{ + IEnumerable GetUniversal(Dictionary> dictionary); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/SearchController/MinusSearcher.cs b/phase05/FullTextSearch.Controller/SearchController/MinusSearcher.cs new file mode 100644 index 0000000..9d41f5d --- /dev/null +++ b/phase05/FullTextSearch.Controller/SearchController/MinusSearcher.cs @@ -0,0 +1,19 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController; + +public class MinusSearcher : ISearcher +{ + public char Sign {get; init;} = '-'; + + public IEnumerable Search(Query query, Dictionary> invertedIndex) + { + var documents = query.WordsBySign[Sign] + .Where(s => invertedIndex.ContainsKey(s)) + .SelectMany(s => invertedIndex[s]) + .Distinct() + .ToList(); + + return documents; + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/SearchController/NoSignedSearcher.cs b/phase05/FullTextSearch.Controller/SearchController/NoSignedSearcher.cs new file mode 100644 index 0000000..7a06a77 --- /dev/null +++ b/phase05/FullTextSearch.Controller/SearchController/NoSignedSearcher.cs @@ -0,0 +1,36 @@ + +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController; + +public class NoSignedSearcher : ISearcher +{ + public char Sign {get; init;} = ' '; + public UniversalSearch universalSearch = new UniversalSearch(); + + public IEnumerable Search(Query query, Dictionary> InvertedIndex) + { + IEnumerable ordinaryDocs = new List(); + + if(query.WordsBySign[Sign].ToList().Count == 0) + ordinaryDocs = universalSearch.GetUniversal(InvertedIndex); + + else + { + try + { + ordinaryDocs = query.WordsBySign[Sign] + .Select(w=>InvertedIndex[w]) + .SelectMany(d=>d) + .Distinct() + .ToList(); + } + catch(KeyNotFoundException e) + { + ordinaryDocs.ToList().Clear(); // + } + } + + return ordinaryDocs; + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/SearchController/PlusSearcher.cs b/phase05/FullTextSearch.Controller/SearchController/PlusSearcher.cs new file mode 100644 index 0000000..ac6294d --- /dev/null +++ b/phase05/FullTextSearch.Controller/SearchController/PlusSearcher.cs @@ -0,0 +1,20 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController; + +public class PlusSearcher : ISearcher +{ + public char Sign {get; init;} = '+'; + public UniversalSearch universalSearch = new UniversalSearch(); // static this + public IEnumerable Search(Query query, Dictionary> dictionary) + { + IEnumerable plusDocs = new List(); + if(query.WordsBySign[Sign].ToList().Count == 0) + plusDocs = universalSearch.GetUniversal(dictionary); + else + query.WordsBySign[Sign].Where(s=>dictionary.ContainsKey(s)).ToList() + .ForEach(x=> plusDocs = plusDocs + .Union(dictionary[x]).ToList()); + return plusDocs; + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/SearchController/SearcherDriver.cs b/phase05/FullTextSearch.Controller/SearchController/SearcherDriver.cs new file mode 100644 index 0000000..5bd6f45 --- /dev/null +++ b/phase05/FullTextSearch.Controller/SearchController/SearcherDriver.cs @@ -0,0 +1,15 @@ +using FullTextSearch.Controller.SearchController.Abstraction; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController; + +public class SearcherDriver : ISearcherDriver +{ + public void DriveSearch(IEnumerable searchers, Query query, Result result, + Dictionary> invertedIndex) + { + result.documentsBySign.Clear(); + searchers.ToList() + .ForEach(s => result.documentsBySign.Add(s.Sign, s.Search(query, invertedIndex))); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/SearchController/UniversalSearch.cs b/phase05/FullTextSearch.Controller/SearchController/UniversalSearch.cs new file mode 100644 index 0000000..a4a810d --- /dev/null +++ b/phase05/FullTextSearch.Controller/SearchController/UniversalSearch.cs @@ -0,0 +1,13 @@ + +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController; + +public class UniversalSearch : IUniversalSearch +{ + public IEnumerable GetUniversal(Dictionary> invertedIndex) + { + var universalList = invertedIndex.Values.SelectMany(d => d).Distinct(); + return universalList; + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/TextFormatter/Abstraction/ITextFormatter.cs b/phase05/FullTextSearch.Controller/TextFormatter/Abstraction/ITextFormatter.cs new file mode 100644 index 0000000..f9e2ddf --- /dev/null +++ b/phase05/FullTextSearch.Controller/TextFormatter/Abstraction/ITextFormatter.cs @@ -0,0 +1,9 @@ +namespace FullTextSearch.Controller.TextFormatter.Abstraction; + +public interface ITextFormatter +{ + List GetQuoteIndices(List words); + List GetIndicesToRemove(List quoteIndices); + List FilterOutIndices(List words, List indicesToRemove); + List ConcatenateQuotedWords(List words, List quoteIndices); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Controller/TextFormatter/TextFormatter.cs b/phase05/FullTextSearch.Controller/TextFormatter/TextFormatter.cs new file mode 100644 index 0000000..93f7dd5 --- /dev/null +++ b/phase05/FullTextSearch.Controller/TextFormatter/TextFormatter.cs @@ -0,0 +1,50 @@ +using FullTextSearch.Controller.TextFormatter.Abstraction; + +namespace FullTextSearch.Controller.TextFormatter; + +public class TextFormatter : ITextFormatter +{ + public List GetQuoteIndices(List words) + { + var quoteIndices = new List(); + for (int i = 0; i < words.Count; i++) + if (words[i].StartsWith("\"") || words[i].EndsWith("\"") || (words[i].Length > 1 && words[i][1] == '\"')) + quoteIndices.Add(i); + return quoteIndices; + } + public List GetIndicesToRemove(List quoteIndices) + { + var indicesToRemove = new List(); + for (int i = 0; i < quoteIndices.Count; i += 2) + { + if (i + 1 < quoteIndices.Count) + { + int start = quoteIndices[i]; + int end = quoteIndices[i + 1]; + for (int j = start; j <= end; j++) + { + indicesToRemove.Add(j); + } + } + } + return indicesToRemove; + } + public List FilterOutIndices(List words, List indicesToRemove) + { + return words.Where((word, index) => !indicesToRemove.Contains(index)).ToList(); + } + public List ConcatenateQuotedWords(List words, List quoteIndices) + { + var result = new List(); + for (int i = 0; i < quoteIndices.Count - 1; i += 2) + { + int start = quoteIndices[i]; + int end = quoteIndices[i + 1]; + + var sublist = words.Skip(start).Take(end - start + 1).ToList(); + var concatenated = string.Join(" ", sublist); + result.Add(concatenated); + } + return result; + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Core.sln b/phase05/FullTextSearch.Core.sln new file mode 100644 index 0000000..d39eb41 --- /dev/null +++ b/phase05/FullTextSearch.Core.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FullTextSearch.Model", "FullTextSearch.Model\FullTextSearch.Model.csproj", "{96CADDEB-41D2-4B75-89BA-794681598CE2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FullTextSearch.Service", "FullTextSearch.Service\FullTextSearch.Service.csproj", "{F498DE60-5A62-48C0-B743-D7E825F01994}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FullTextSearch.Controller", "FullTextSearch.Controller\FullTextSearch.Controller.csproj", "{E4BD884F-008F-444C-A69B-2D1FE7049703}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FullTextSearch.Test", "FullTextSearch.Test\FullTextSearch.Test.csproj", "{F90AFE67-456D-4A8D-90DF-86CF0BD1DD99}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {96CADDEB-41D2-4B75-89BA-794681598CE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {96CADDEB-41D2-4B75-89BA-794681598CE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {96CADDEB-41D2-4B75-89BA-794681598CE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {96CADDEB-41D2-4B75-89BA-794681598CE2}.Release|Any CPU.Build.0 = Release|Any CPU + {F498DE60-5A62-48C0-B743-D7E825F01994}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F498DE60-5A62-48C0-B743-D7E825F01994}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F498DE60-5A62-48C0-B743-D7E825F01994}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F498DE60-5A62-48C0-B743-D7E825F01994}.Release|Any CPU.Build.0 = Release|Any CPU + {E4BD884F-008F-444C-A69B-2D1FE7049703}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E4BD884F-008F-444C-A69B-2D1FE7049703}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E4BD884F-008F-444C-A69B-2D1FE7049703}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E4BD884F-008F-444C-A69B-2D1FE7049703}.Release|Any CPU.Build.0 = Release|Any CPU + {F90AFE67-456D-4A8D-90DF-86CF0BD1DD99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F90AFE67-456D-4A8D-90DF-86CF0BD1DD99}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F90AFE67-456D-4A8D-90DF-86CF0BD1DD99}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F90AFE67-456D-4A8D-90DF-86CF0BD1DD99}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/phase05/FullTextSearch.Core.sln.DotSettings.user b/phase05/FullTextSearch.Core.sln.DotSettings.user new file mode 100644 index 0000000..875181a --- /dev/null +++ b/phase05/FullTextSearch.Core.sln.DotSettings.user @@ -0,0 +1,37 @@ + + + + + <SessionState ContinuousTestingMode="0" Name="GetUniversal_WithEmptyIndex_Returns_EmptyList" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Project Location="E:\programming c#\phase04_02\Summer1403-SE-Team01\phase04\FullTextSearch.Test" Presentation="&lt;FullTextSearch.Test&gt;" /> +</SessionState> + <SessionState ContinuousTestingMode="0" Name="Filter_ShouldReturnFilteredDocuments_WhenDocumentsContainMinusWords" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Project Location="C:\Users\amir\Desktop\mohaymen-phase\Summer1403-SE-Team01\phase05\FullTextSearch.Test" Presentation="&lt;FullTextSearch.Test&gt;" /> +</SessionState> + <SessionState ContinuousTestingMode="0" Name="Split_ShouldReturnExpectedResults" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Or> + <TestAncestor> + <TestId>xUnit::F90AFE67-456D-4A8D-90DF-86CF0BD1DD99::net8.0::FullTextSearch.Test.ControllerTest.ResultControllerTest.MinusFilterTest.Filter_ShouldReturnFilteredDocuments_WhenDocumentsContainMinusWords</TestId> + </TestAncestor> + <ProjectFolder>F90AFE67-456D-4A8D-90DF-86CF0BD1DD99/d:ControllerTest/d:DocumentControllerTest</ProjectFolder> + <ProjectFolder>F90AFE67-456D-4A8D-90DF-86CF0BD1DD99/d:ControllerTest/d:InvertedIndexControllerTest</ProjectFolder> + <ProjectFolder>F90AFE67-456D-4A8D-90DF-86CF0BD1DD99/d:ControllerTest/d:QueryControllerTest</ProjectFolder> + <ProjectFolder>F90AFE67-456D-4A8D-90DF-86CF0BD1DD99/d:ControllerTest/d:ResultControllerTest</ProjectFolder> + <ProjectFolder>F90AFE67-456D-4A8D-90DF-86CF0BD1DD99/d:ControllerTest/d:SearchControllerTest</ProjectFolder> + <ProjectFolder>F90AFE67-456D-4A8D-90DF-86CF0BD1DD99/d:ServiceTest/d:SearchServiceTest</ProjectFolder> + <ProjectFile>F90AFE67-456D-4A8D-90DF-86CF0BD1DD99/d:ServiceTest/d:InitializeServiceTest/f:InitializeServiceTest.cs</ProjectFile> + </Or> +</SessionState> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="BuildWordsBySign_Should_OrganizeWordsCorrectly_ForSignedWords" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Or> + <TestAncestor> + <TestId>xUnit::F90AFE67-456D-4A8D-90DF-86CF0BD1DD99::net8.0::FullTextSearch.Test.ControllerTest.ResultControllerTest.MinusFilterTest.Filter_ShouldReturnFilteredDocuments_WhenDocumentsContainMinusWords</TestId> + <TestId>xUnit::F90AFE67-456D-4A8D-90DF-86CF0BD1DD99::net8.0::QueryBuilderTests.BuildText_ShouldSetTextInQuery</TestId> + <TestId>xUnit::F90AFE67-456D-4A8D-90DF-86CF0BD1DD99::net8.0::QueryBuilderTests.BuildWordsBySign_ShouldPopulateWordsBySign</TestId> + </TestAncestor> + <ProjectFolder>F90AFE67-456D-4A8D-90DF-86CF0BD1DD99/d:ControllerTest/d:DocumentControllerTest</ProjectFolder> + <ProjectFolder>F90AFE67-456D-4A8D-90DF-86CF0BD1DD99/d:ControllerTest/d:InvertedIndexControllerTest</ProjectFolder> + <ProjectFolder>F90AFE67-456D-4A8D-90DF-86CF0BD1DD99/d:ControllerTest/d:QueryControllerTest</ProjectFolder> + <ProjectFolder>F90AFE67-456D-4A8D-90DF-86CF0BD1DD99/d:ControllerTest/d:SearchControllerTest</ProjectFolder> + </Or> +</SessionState> \ No newline at end of file diff --git a/phase05/FullTextSearch.Model/Document.cs b/phase05/FullTextSearch.Model/Document.cs new file mode 100644 index 0000000..65f952d --- /dev/null +++ b/phase05/FullTextSearch.Model/Document.cs @@ -0,0 +1,28 @@ +namespace FullTextSearch.Core; + +public class Document +{ + public string Name { get; set; } + public string Path { get; set; } + public string Text { get; set; } + public IEnumerable Words { get; set; } + + public override string ToString() + { + return $"Name:{Name}"; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (obj.GetType() != this.GetType()) return false; + return Equals((Document) obj); + } + + public bool Equals(Document other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return this.Name.Equals(other.Name) && this.Path.Equals(other.Path) && this.Text.Equals(other.Text) && this.Words.SequenceEqual(other.Words); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Model/FullTextSearch.Model.csproj b/phase05/FullTextSearch.Model/FullTextSearch.Model.csproj new file mode 100644 index 0000000..3ad4529 --- /dev/null +++ b/phase05/FullTextSearch.Model/FullTextSearch.Model.csproj @@ -0,0 +1,23 @@ + + + + Exe + net8.0 + enable + enable + FullTextSearch.Core + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/phase05/FullTextSearch.Model/Program.cs b/phase05/FullTextSearch.Model/Program.cs new file mode 100644 index 0000000..e5dff12 --- /dev/null +++ b/phase05/FullTextSearch.Model/Program.cs @@ -0,0 +1,3 @@ +// See https://aka.ms/new-console-template for more information + +Console.WriteLine("Hello, World!"); \ No newline at end of file diff --git a/phase05/FullTextSearch.Model/Query.cs b/phase05/FullTextSearch.Model/Query.cs new file mode 100644 index 0000000..e5f6dd3 --- /dev/null +++ b/phase05/FullTextSearch.Model/Query.cs @@ -0,0 +1,30 @@ +namespace FullTextSearch.Core; + +public class Query +{ + public string Text { get; set; } + public Dictionary> WordsBySign = new Dictionary>(); + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (obj.GetType() != GetType()) return false; + return Equals((Query) obj); + } + + public bool Equals(Query other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + if (!this.Text.Equals(other.Text)) return false; + if(!this.WordsBySign.Count.Equals(other.WordsBySign.Count)) return false; + foreach (var entry in this.WordsBySign) + { + if (!entry.Value.SequenceEqual(other.WordsBySign[entry.Key])) + { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Model/Result.cs b/phase05/FullTextSearch.Model/Result.cs new file mode 100644 index 0000000..daf568c --- /dev/null +++ b/phase05/FullTextSearch.Model/Result.cs @@ -0,0 +1,37 @@ +namespace FullTextSearch.Core; + +public class Result +{ + public Result() + { + documents = new List(); + documentsBySign = new Dictionary>(); + } + public IEnumerable documents { get; set; } + public Dictionary> documentsBySign {get; set;} + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((Result) obj); + } + + public bool Equals(Result other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + if (!this.documents.SequenceEqual(other.documents)) return false; + if(!this.documentsBySign.Count.Equals(other.documentsBySign.Count)) return false; + foreach (var entry in this.documentsBySign) + { + if (!entry.Value.SequenceEqual(other.documentsBySign[entry.Key])) + { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Model/Search.cs b/phase05/FullTextSearch.Model/Search.cs new file mode 100644 index 0000000..f2c73fe --- /dev/null +++ b/phase05/FullTextSearch.Model/Search.cs @@ -0,0 +1,15 @@ +using FullTextSearch.Core; + +namespace InvertedIndex.Model; + +public class Search +{ + public Query query { get; set; } + public Result result { get; set; } + + public Search(Query query, Result result) + { + this.query = query; + this.result = result; + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Service/FullTextSearch.Service.csproj b/phase05/FullTextSearch.Service/FullTextSearch.Service.csproj new file mode 100644 index 0000000..c82b384 --- /dev/null +++ b/phase05/FullTextSearch.Service/FullTextSearch.Service.csproj @@ -0,0 +1,27 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/phase05/FullTextSearch.Service/InitializeService/Abstraction/IInitializeService.cs b/phase05/FullTextSearch.Service/InitializeService/Abstraction/IInitializeService.cs new file mode 100644 index 0000000..5c7d653 --- /dev/null +++ b/phase05/FullTextSearch.Service/InitializeService/Abstraction/IInitializeService.cs @@ -0,0 +1,8 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Service.InitializeService; + +public interface IInitializeService +{ + Task>> Initialize(string directoryPath); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Service/InitializeService/InitializeService.cs b/phase05/FullTextSearch.Service/InitializeService/InitializeService.cs new file mode 100644 index 0000000..30f85e4 --- /dev/null +++ b/phase05/FullTextSearch.Service/InitializeService/InitializeService.cs @@ -0,0 +1,40 @@ +using FullTextSearch.Controller.DocumentController; +using FullTextSearch.Controller.DocumentController.Abstraction; +using FullTextSearch.Controller.InvertedIndexController; +using FullTextSearch.Controller.Read.Abstraction; +using FullTextSearch.Core; +using InvertedIndex.Abstraction.Read; + +namespace FullTextSearch.Service.InitializeService; + +public class InitializeService : IInitializeService +{ + private readonly IFileReader _fileReader; + private readonly IDocumentDirector _documentDirector; + private readonly IInvertedIndexMapper _invertedIndexMapper; + private readonly IDirectory _directory; + private readonly IPath _path; + + public InitializeService(IFileReader fileReader, IDocumentDirector documentDirector, IInvertedIndexMapper invertedIndexMapper, IDirectory directory, IPath path) + { + _fileReader = fileReader; + _documentDirector = documentDirector; + _invertedIndexMapper = invertedIndexMapper; + _directory = directory; + _path = path; + } + + public async Task>> Initialize(string directoryPath) + { + List documents = new List(); + var paths = _directory.GetFiles(directoryPath); + foreach (var p in paths) + { + IDocumentBuilder documentBuilder = + new DocumentBuilder(new DocumentFormatter()); + _documentDirector.Construct(_path.GetFileName(p), p,await _fileReader.ReadAsync(p), documentBuilder); + documents.Add(documentBuilder.GetDocument()); + } + return _invertedIndexMapper.Map(documents); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Service/Program.cs b/phase05/FullTextSearch.Service/Program.cs new file mode 100644 index 0000000..0bfafeb --- /dev/null +++ b/phase05/FullTextSearch.Service/Program.cs @@ -0,0 +1,37 @@ +using System.Net.Security; +using Castle.Core.Logging; +using FullTextSearch.Controller.DocumentController; +using FullTextSearch.Controller.InvertedIndexController; +using FullTextSearch.Controller.QueryController; +using FullTextSearch.Controller.ResultController; +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Controller.TextFormatter; +using FullTextSearch.Core; +using FullTextSearch.Service.InitializeService; +using FullTextSearch.Service.SearchService; +using InvertedIndex.Controller.Read; +using Directory = FullTextSearch.Controller.Read.Directory; +using Path = FullTextSearch.Controller.Read.Path; + + +class Program +{ + static async Task Main(string[] args) + { + // ./Resources/EnglishData + var path = Console.ReadLine(); + var initializeService = new InitializeService(new TextFileReader(), new DocumentDirector(), + new InvertedIndexMapper(), new Directory(), new Path()); + + var invertedIndex = await initializeService.Initialize(path); + + var searchService = new SearchService(new QueryBuilder(new QueryFormatter(), new TextFormatter()), + new ResultBuilder(new FilterDriver(), new SearcherDriver()));// + + string input; + while (!(input = Console.ReadLine()).Equals("end")) + { + searchService.Search(input, invertedIndex).documents.ToList().ForEach(Console.WriteLine); + } + } +}//+cat -reza mohammad \ No newline at end of file diff --git a/phase05/FullTextSearch.Service/SearchService/Abstraction/ISearchService.cs b/phase05/FullTextSearch.Service/SearchService/Abstraction/ISearchService.cs new file mode 100644 index 0000000..8a6a56e --- /dev/null +++ b/phase05/FullTextSearch.Service/SearchService/Abstraction/ISearchService.cs @@ -0,0 +1,8 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Service.SearchService; + +public interface ISearchService +{ + Result Search(string input, Dictionary> invertedIndex); +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Service/SearchService/InitializeService.cs b/phase05/FullTextSearch.Service/SearchService/InitializeService.cs new file mode 100644 index 0000000..e43410b --- /dev/null +++ b/phase05/FullTextSearch.Service/SearchService/InitializeService.cs @@ -0,0 +1,33 @@ +namespace FullTextSearch.Service.SearchService; + +public class InitializeService +{ + private readonly IServiceProvider _serviceProvider; + public static InvertedIndex InvertedIndex { get; private set; } + + public InitializeService(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + using (var scope = _serviceProvider.CreateScope()) + { + var textFileReader = new TextFileReader(); + var documentDirector = new DocumentDirector(); + var invertedIndexMapper = new InvertedIndexMapper(); + var directory = new Directory(); + var path = new Path(); + + var initializeService = new FullTextSearch.Service.InitializeService.InitializeService( + textFileReader, documentDirector, invertedIndexMapper, directory, path); + + // Replace with the actual path or make it configurable + var pathToData = "./Resources/EnglishData"; + InvertedIndex = await initializeService.Initialize(pathToData); + } + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Service/SearchService/SearchService.cs b/phase05/FullTextSearch.Service/SearchService/SearchService.cs new file mode 100644 index 0000000..b282099 --- /dev/null +++ b/phase05/FullTextSearch.Service/SearchService/SearchService.cs @@ -0,0 +1,32 @@ +using FullTextSearch.Controller.QueryController; +using FullTextSearch.Controller.QueryController.Abstraction; +using FullTextSearch.Controller.ResultController; +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Core; + +namespace FullTextSearch.Service.SearchService; + +public class SearchService : ISearchService +{ + private readonly IQueryBuilder _queryBuilder; + private readonly IResultBuilder _resultBuilder; + + + public SearchService(IQueryBuilder queryBuilder, IResultBuilder resultBuilder) + { + _queryBuilder = queryBuilder; + _resultBuilder = resultBuilder; + } + + public Result Search(string input, Dictionary> invertedIndex) + { + new QueryDirector().Construct(input, new List(){'+', '-'}, _queryBuilder); + var query = _queryBuilder.GetQuery(); + + new ResultDirector().Construct(_resultBuilder, query, invertedIndex); + var result = _resultBuilder.GetResult(); + + return result; + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Test/ControllerTest/DocumentControllerTest/DocumentBuilderTest.cs b/phase05/FullTextSearch.Test/ControllerTest/DocumentControllerTest/DocumentBuilderTest.cs new file mode 100644 index 0000000..40aaaf8 --- /dev/null +++ b/phase05/FullTextSearch.Test/ControllerTest/DocumentControllerTest/DocumentBuilderTest.cs @@ -0,0 +1,82 @@ +using FullTextSearch.Controller.DocumentController; +using FullTextSearch.Controller.DocumentController.Abstraction; +using FullTextSearch.Core; +using InvertedIndex.Controller.Document; +using NSubstitute; +using Xunit; +using Assert = Xunit.Assert; + +namespace FullTextSearch.Test.DocumentTest +{ + public class DocumentBuilderTest + { + private readonly IDocumentFormatter _documentFormatter; + private readonly IDocumentBuilder _sut; + + public DocumentBuilderTest() + { + _documentFormatter = Substitute.For(); + _sut = new DocumentBuilder(_documentFormatter); + } + + [Fact] + public void BuildName_ShouldSetDocumentName_WhenGivenName() + { + // Arrange + var name = "Ali"; + + // Act + _sut.BuildName(name); + + // Assert + Assert.Equal(name, _sut.GetDocument().Name); + } + + [Fact] + public void BuildPath_ShouldSetDocumentPath_WhenGivenPath() + { + // Arrange + var path = "/document"; + + // Act + _sut.BuildPath(path); + + // Assert + Assert.Equal(path, _sut.GetDocument().Path); + } + + [Fact] + public void BuildText_ShouldSetDocumentText_WhenGivenText() + { + // Arrange + var text = "Ali is someone!"; + + // Act + _sut.BuildText(text); + + // Assert + Assert.Equal(text, _sut.GetDocument().Text); + } + + [Fact] + public void BuildWords_ShouldSetDocumentWords_WhenTextIsBuilt() + { + // Arrange + var sampleText = "hello world"; + var upperText = sampleText.ToUpper(); + var expectedWords = new List { "HELLO", "WORLD" }; + + _documentFormatter.ToUpper(sampleText).Returns(upperText); + _documentFormatter.Split(upperText, " ").Returns(expectedWords); + + _sut.BuildText(sampleText); + + // Act + _sut.BuildWords(); + var document = _sut.GetDocument(); + + // Assert + Assert.Equal(expectedWords, document.Words); + } + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Test/ControllerTest/DocumentControllerTest/DocumentFormatterTest.cs b/phase05/FullTextSearch.Test/ControllerTest/DocumentControllerTest/DocumentFormatterTest.cs new file mode 100644 index 0000000..07e1aad --- /dev/null +++ b/phase05/FullTextSearch.Test/ControllerTest/DocumentControllerTest/DocumentFormatterTest.cs @@ -0,0 +1,41 @@ +using FullTextSearch.Controller.DocumentController; +using Xunit; +using Assert = Xunit.Assert; + +namespace FullTextSearch.Test +{ + public class DocumentFormatterTest + { + private readonly DocumentFormatter _sut; + + public DocumentFormatterTest() + { + _sut = new DocumentFormatter(); + } + + [Xunit.Theory] + [InlineData("HEY EVERYBODY! WHATSAPP", "Hey Everybody! Whatsapp")] + [InlineData("ALI REZA", "ALI REZA")] + [InlineData("ALI REZA", "ali reza")] + public void ToUpper_ShouldConvertTextToUpperCase_WhenGivenText(string expected, string text) + { + // Act + var result = _sut.ToUpper(text); + + // Assert + Assert.Equal(expected, result); + } + + [Xunit.Theory] + [InlineData("This is a test.", " ", new[] { "This", "is", "a", "test." })] + [InlineData("amir!", " ", new[] { "amir!" })] + public void Split_ShouldReturnExpectedWords_WhenGivenTextAndDelimiter(string queryText, string regex, string[] expected) + { + // Act + IEnumerable result = _sut.Split(queryText, regex); + + // Assert + Assert.Equal(expected, result); + } + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Test/ControllerTest/InvertedIndexControllerTest/InvertedIndexMapperTest.cs b/phase05/FullTextSearch.Test/ControllerTest/InvertedIndexControllerTest/InvertedIndexMapperTest.cs new file mode 100644 index 0000000..4f30b48 --- /dev/null +++ b/phase05/FullTextSearch.Test/ControllerTest/InvertedIndexControllerTest/InvertedIndexMapperTest.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.Linq; +using Xunit; +using FullTextSearch.Controller; +using FullTextSearch.Controller.InvertedIndexController; +using FullTextSearch.Core; +using FullTextSearch.Test.Data; +using Assert = Xunit.Assert; + +namespace FullTextSearch.Test.ControllerTest +{ + public class InvertedIndexMapperTests + { + private readonly IInvertedIndexMapper _sut; + + public InvertedIndexMapperTests() + { + _sut = new InvertedIndexMapper(); // Assuming this is the correct implementation + } + + [Fact] + public void Map_ShouldReturnCorrectInvertedIndex_WhenGivenDocuments() + { + // Arrange + var documentList = DataSample.GetDocuments(); + + Document document1 = documentList[0]; + Document document2 = documentList[1]; + Document document3 = documentList[2]; + + var documents = new List + { + document1, document2, document3 + }; + + var expected = DataSample.GetInvertedIndexMap(document1, + document2, document3); + + // Act + var result = _sut.Map(documents); + + // Assert + Assert.NotNull(result); + Assert.NotNull(expected); + Assert.Equal(expected.Count, result.Count); + + // Check if both dictionaries have the same keys + foreach (var key in expected.Keys) + { + Assert.True(result.TryGetValue(key, out var resultValues), $"Key '{key}' not found in the result dictionary."); + + var expectedValuesSet = new HashSet(expected[key]); + var resultValuesSet = new HashSet(resultValues); + + // Assert that both sets contain the same elements + Assert.Equal(expectedValuesSet, resultValuesSet); + } + } + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Test/ControllerTest/QueryControllerTest/QueryBuilderTest.cs b/phase05/FullTextSearch.Test/ControllerTest/QueryControllerTest/QueryBuilderTest.cs new file mode 100644 index 0000000..3ffcb57 --- /dev/null +++ b/phase05/FullTextSearch.Test/ControllerTest/QueryControllerTest/QueryBuilderTest.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; +using NSubstitute; +using FullTextSearch.Controller.QueryController; +using FullTextSearch.Controller.QueryController.Abstraction; +using FullTextSearch.Controller.TextFormatter.Abstraction; +using FullTextSearch.Core; +using Assert = Xunit.Assert; + +public class QueryBuilderTests +{ + private readonly IQueryFormatter _queryFormatter; + private readonly ITextFormatter _textFormatter; + private readonly QueryBuilder _queryBuilder; + + public QueryBuilderTests() + { + _queryFormatter = Substitute.For(); + _textFormatter = Substitute.For(); + _queryBuilder = new QueryBuilder(_queryFormatter, _textFormatter); + } + + [Fact] + public void BuildText_ShouldSetTextInQuery() + { + // Arrange + var text = "sample text"; + + // Act + _queryBuilder.BuildText(text); + + // Assert + var query = _queryBuilder.GetQuery(); + Assert.Equal(text, query.Text); + } + + [Fact] + public void BuildWordsBySign_ShouldPopulateWordsBySign() + { + // Arrange + var text = "+cat! -z \"ali is\""; + var signs = new[] { '+', '-' }; + var splittedText = new List { "+CAT!" , "-Z", "\'ali", "is\""}; + var quoteIndices = new List(){2, 3}; + var indicesToRemove = new List(){2, 3}; + var filteredWords = new List(){ "+CAT!", "-Z" }; + var concatenatedQuotes = new List(){"\"ali is\""}; + + _queryFormatter.Split(Arg.Any(), " ").Returns(splittedText); + _queryFormatter.ToUpper(Arg.Any()).Returns(callInfo => callInfo.Arg().ToUpper()); + _textFormatter.GetQuoteIndices(Arg.Any>()).Returns(quoteIndices); + _textFormatter.GetIndicesToRemove(Arg.Any>()).Returns(indicesToRemove); + _textFormatter.FilterOutIndices(Arg.Any>(), Arg.Any>()).Returns(filteredWords); + _textFormatter.ConcatenateQuotedWords(Arg.Any>(), Arg.Any>()).Returns(concatenatedQuotes); + + _queryFormatter.CollectBySign( + Arg.Is>(x => x.SequenceEqual(new List { "+CAT!" , "-Z" })), + '+').Returns(new List { "+CAT!" }); + + _queryFormatter.CollectBySign( + Arg.Is>(x => x.SequenceEqual(new List { "-Z" })), + '-').Returns(new List { "-Z" }); + + _queryFormatter.RemovePrefix( + Arg.Is>(x => + x.SequenceEqual(new List { "+CAT!" }))) + .Returns(new List() { "CAT!" }); + + _queryFormatter.RemovePrefix( + Arg.Is>(x => + x.SequenceEqual(new List { "-Z" }))) + .Returns(new List(){"Z"}); + + _queryFormatter.RemovePrefix( + Arg.Is>(x => + x.SequenceEqual(new List { "\"ali is\"" }))) + .Returns(new List(){"ali is"}); + + + _queryFormatter.CollectBySign( + Arg.Is>(x => x.SequenceEqual(new List { "\"ali is\"" })), + '+').Returns(new List { }); + + _queryFormatter.CollectBySign( + Arg.Is>(x => x.SequenceEqual(new List { "\"ali is\"" })), + '-').Returns(new List { }); + + _queryBuilder.BuildText(text); + + // Act + _queryBuilder.BuildWordsBySign(signs); + + // Assert + var query = _queryBuilder.GetQuery(); + + // Expected dictionary + var expected = new Dictionary> + { + { '+', new List { "CAT!" } }, + { '-', new List() {"Z"} }, + { ' ', new List() {"ali is"}} + }; + + // Compare keys + Assert.Equal(expected.Keys, query.WordsBySign.Keys); + + // Compare values for each key + foreach (var key in expected.Keys) + { + Assert.Equal(expected[key], query.WordsBySign[key]); + } + } +} diff --git a/phase05/FullTextSearch.Test/ControllerTest/QueryControllerTest/QueryFormatterTest.cs b/phase05/FullTextSearch.Test/ControllerTest/QueryControllerTest/QueryFormatterTest.cs new file mode 100644 index 0000000..3624b41 --- /dev/null +++ b/phase05/FullTextSearch.Test/ControllerTest/QueryControllerTest/QueryFormatterTest.cs @@ -0,0 +1,101 @@ +using System.Collections; +using FullTextSearch.Controller.QueryController; +using Xunit; +using Assert = Xunit.Assert; + +namespace InvertedIndex.Test.Query +{ + public class QueryFormatterTest + { + private readonly QueryFormatter _sut; + + public QueryFormatterTest() + { + _sut = new QueryFormatter(); + } + + [Xunit.Theory] + [InlineData("SALAM!", "salAm!")] + [InlineData("ALI", "ALI")] + [InlineData("REZA", "reza")] + public void ToUpper_ShouldReturnUppercaseVersionOfText_WhenGivenText(string expected, string text) + { + // Arrange + + // Act + var result = _sut.ToUpper(text); + + // Assert + Assert.Equal(expected, result); + } + + [Xunit.Theory] + [InlineData("This is a test.", " ", new[] { "This", "is", "a", "test." })] + [InlineData("amir!", " ", new[] { "amir!" })] + public void Split_ShouldReturnExpectedResults_WhenGivenTextAndDelimiter(string queryText, string regex, string[] expected) + { + // Arrange + + // Act + var result = _sut.Split(queryText, regex); + + // Assert + Assert.Equal(expected, result); + } + + [Xunit.Theory] + [MemberData(nameof(CollectBySignTestData.Data), MemberType = typeof(CollectBySignTestData))] + public void CollectBySign_ShouldReturnWordsWithSpecifiedSign_WhenGivenListAndSign(string[] expectedArray, string[] listArray, char c) + { + // Arrange + var list = new List(listArray); + var expected = expectedArray == null ? new List() : new List(expectedArray); + + // Act + var result = _sut.CollectBySign(list, c).ToList(); + + // Assert + Assert.Equal(expected, result); + } + + [Xunit.Theory] + [ClassData(typeof(RemovePrefixTestData))] + public void RemovePrefix_ShouldReturnWordsWithoutPrefix_WhenGivenList(string[] expectedArray, string[] listArray) + { + // Arrange + var list = new List(listArray); + var expected = new List(expectedArray); + + // Act + var result = _sut.RemovePrefix(list).ToList(); + + // Assert + Assert.Equal(expected, result); + } + } + + public class CollectBySignTestData : IEnumerable + { + public static IEnumerable Data => new List + { + new object[] { new[] { "+ali", "+mohammad" }, new[] { "+ali", "-reza", "zahra", "+mohammad" }, '+' }, + new object[] { new[] { "-reza" }, new[] { "+ali", "-reza", "zahra", "+mohammad" }, '-' }, + new object[] { null, new[] { "+ali", "-reza", "zahra", "+mohammad" }, '*' } + }; + + public IEnumerator GetEnumerator() => Data.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class RemovePrefixTestData : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { new[] { "ali", "reza", "zahra", "mohammad" }, new[] { "+ali", "+reza", "+zahra", "+mohammad" } }; + yield return new object[] { new[] { "ali", "reza", "zahra", "mohammad" }, new[] { "+ali", "-reza", "zahra", "+mohammad" } }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Test/ControllerTest/ResultControllerTest/MinusFilterTest.cs b/phase05/FullTextSearch.Test/ControllerTest/ResultControllerTest/MinusFilterTest.cs new file mode 100644 index 0000000..f9308d9 --- /dev/null +++ b/phase05/FullTextSearch.Test/ControllerTest/ResultControllerTest/MinusFilterTest.cs @@ -0,0 +1,46 @@ +using FullTextSearch.Controller.ResultController; +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Core; +using FullTextSearch.Test.Data; +using Xunit; +using Assert = Xunit.Assert; + +namespace FullTextSearch.Test.ControllerTest.ResultControllerTest +{ + public class MinusFilterTest + { + private readonly IFilter _sut; + + public MinusFilterTest() + { + _sut = new MinusFilter(); + } + + [Fact] + public void Filter_ShouldReturnFilteredDocuments_WhenDocumentsContainMinusWords() + { + // Arrange + var documentList = DataSample.GetDocuments(); + + Document document1 = documentList[0]; + Document document2 = documentList[1]; + Document document3 = documentList[2]; + + IEnumerable documents = new List { document1, document2, document3 }; + + var documentsBySign = new Dictionary> + { + { '+', new List { document1, document2 } }, + { '-', new List { document1, document2, document3 } }, + { ' ', new List { document3 } } + }; + var expected = documents.Except(documentsBySign['-']); + + // Act + var actual = _sut.Filter(documents, documentsBySign); + + // Assert + Assert.True(actual.SequenceEqual(expected)); + } + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Test/ControllerTest/ResultControllerTest/NoSignedFilterTest.cs b/phase05/FullTextSearch.Test/ControllerTest/ResultControllerTest/NoSignedFilterTest.cs new file mode 100644 index 0000000..bab91c9 --- /dev/null +++ b/phase05/FullTextSearch.Test/ControllerTest/ResultControllerTest/NoSignedFilterTest.cs @@ -0,0 +1,45 @@ +using FullTextSearch.Controller.ResultController; +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Core; +using FullTextSearch.Test.Data; +using Xunit; +using Assert = Xunit.Assert; + +namespace FullTextSearch.Test.ControllerTest.ResultControllerTest; + +public class NoSignedFilterTest +{ + private readonly IFilter _sut; + + public NoSignedFilterTest() + { + _sut = new NoSignedFilter(); + } + [Fact] + public void Filter_ShouldReturnDocumentsWithoutSignedWords_WhenDocumentsContainNoSignedWords() + { + // Arrange + var documentList = DataSample.GetDocuments(); + + Document document1 = documentList[0]; + Document document2 = documentList[1]; + Document document3 = documentList[2]; + + + IEnumerable documents = new List() { document1, document2, document3 }; + + var documentsBySign = new Dictionary> + { + { '+', new List { document1, document2 } }, + { '-', new List { document1, document2, document3 } }, + { ' ', new List { document3 } } + }; + var expected = documents.Intersect(documentsBySign[' ']); + + // Act + var actual = _sut.Filter(documents, documentsBySign); + + // Assert + Assert.True(actual.SequenceEqual(expected)); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Test/ControllerTest/ResultControllerTest/PlusFilterTest.cs b/phase05/FullTextSearch.Test/ControllerTest/ResultControllerTest/PlusFilterTest.cs new file mode 100644 index 0000000..fe7b029 --- /dev/null +++ b/phase05/FullTextSearch.Test/ControllerTest/ResultControllerTest/PlusFilterTest.cs @@ -0,0 +1,45 @@ +using FullTextSearch.Controller.ResultController; +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Core; +using FullTextSearch.Test.Data; +using Xunit; +using Assert = Xunit.Assert; + +namespace FullTextSearch.Test.ControllerTest.ResultControllerTest; + +public class PlusFilterTest +{ + private readonly IFilter _sut; + + public PlusFilterTest() + { + _sut = new PlusFilter(); + } + [Fact] + public void Filter_ShouldReturnDocumentsContainingPlusWords_WhenFilteringWithPlusSign() + { + // Arrange + var documentList = DataSample.GetDocuments(); + + Document document1 = documentList[0]; + Document document2 = documentList[1]; + Document document3 = documentList[2]; + + + IEnumerable documents = new List() { document1, document2, document3 }; + + var documentsBySign = new Dictionary> + { + { '+', new List { document1, document2 } }, + { '-', new List { document1, document2, document3 } }, + { ' ', new List { document3 } } + }; + var expected = documents.Intersect(documentsBySign['+']); + + // Act + var actual = _sut.Filter(documents, documentsBySign); + + // Assert + Assert.True(actual.SequenceEqual(expected)); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Test/ControllerTest/ResultControllerTest/ResultBuilderTest.cs b/phase05/FullTextSearch.Test/ControllerTest/ResultControllerTest/ResultBuilderTest.cs new file mode 100644 index 0000000..293aad1 --- /dev/null +++ b/phase05/FullTextSearch.Test/ControllerTest/ResultControllerTest/ResultBuilderTest.cs @@ -0,0 +1,89 @@ +using FullTextSearch.Controller.ResultController; +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Controller.SearchController.Abstraction; +using FullTextSearch.Core; +using FullTextSearch.Test.Data; +using NSubstitute; +using Xunit; +using Assert = Xunit.Assert; + +namespace FullTextSearch.Test.ControllerTest.ResultControllerTest; + +public class ResultBuilderTest +{ + private readonly ISearcherDriver _searcherDriver; + private readonly IFilterDriver _filterDriver; + private readonly ResultBuilder _sut; + + public ResultBuilderTest() + { + _searcherDriver = Substitute.For(); + _filterDriver = Substitute.For(); + _sut = new ResultBuilder(_filterDriver, _searcherDriver); + } + + [Fact] + public void BuildDocumentsBySign_ShouldFillDocumentsBySign_WhenGivenSearchersAndQuery() + { + // Arrange + IEnumerable searchers = new List() {new MinusSearcher(), new PlusSearcher(), new NoSignedSearcher()}; + + Query query = new Query(); + query.Text = "cat +reza -demand"; + query.WordsBySign = new Dictionary>() + { + {'+', new List() {"reza"}}, + {'-', new List() {"demand"}}, + {' ', new List() {"cat"}} + }; + Result result = new Result(); + + var documentList = DataSample.GetDocuments(); + + Document document1 = documentList[0]; + Document document2 = documentList[1]; + Document document3 = documentList[2]; + + var invertedIndexMap = DataSample.GetInvertedIndexMap(document1, + document2, document3); + + _searcherDriver.DriveSearch(searchers, query, result, invertedIndexMap); + + // Act + _sut.BuildDocumentsBySign(searchers, query, invertedIndexMap); + + // Assert + Assert.Equal(result.documentsBySign.Keys.Count, _sut.GetResult().documentsBySign.Keys.Count); + foreach (var entry in _sut.GetResult().documentsBySign) + { + Assert.True(entry.Value.SequenceEqual(result.documentsBySign[entry.Key])); + } + } + + [Test] + public void BuildDocuments_ShouldFillResultDocuments_WhenGivenFiltersAndInvertedIndex() + { + // Arrange + IEnumerable filters = new List() {new NoSignedFilter(), new MinusFilter(), new PlusFilter()}; + Result result = new Result(); + + var documentList = DataSample.GetDocuments(); + + Document document1 = documentList[0]; + Document document2 = documentList[1]; + Document document3 = documentList[2]; + + var invertedIndexMap = DataSample.GetInvertedIndexMap(document1, + document2, document3); + + result.documents = new UniversalSearch().GetUniversal(invertedIndexMap); + _filterDriver.DriveFilterer(filters, result); + + // Act + _sut.BuildDocuments(filters, invertedIndexMap); + + // Assert + Assert.True(result.documents.SequenceEqual(_sut.GetResult().documents)); + } +} diff --git a/phase05/FullTextSearch.Test/ControllerTest/SearchControllerTest/MinusSearcherTest.cs b/phase05/FullTextSearch.Test/ControllerTest/SearchControllerTest/MinusSearcherTest.cs new file mode 100644 index 0000000..b47e48b --- /dev/null +++ b/phase05/FullTextSearch.Test/ControllerTest/SearchControllerTest/MinusSearcherTest.cs @@ -0,0 +1,49 @@ +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Core; +using FullTextSearch.Test.Data; +using Xunit; +using Assert = Xunit.Assert; + +namespace FullTextSearch.Test.ControllerTest.SearchControllerTest; + +public class MinusSearcherTest +{ + private readonly ISearcher _sut; + + public MinusSearcherTest() + { + _sut = new MinusSearcher(); + } + + [Fact] + public void Search_ShouldReturnDocumentsExcludingMinusWords_WhenGivenQueryAndInvertedIndex() + { + // Arrange + Query query = new Query(); + query.Text = "cat +reza -demand!"; + + query.WordsBySign = new Dictionary>() + { + { '+', new List() { "reza" } }, + { '-', new List() { "demand" } }, + { ' ', new List() { "cat" } }, + }; + + var documentList = DataSample.GetDocuments(); + + Document document1 = documentList[0]; + Document document2 = documentList[1]; + Document document3 = documentList[2]; + + var invertedIndexMap = DataSample.GetInvertedIndexMap(document1, + document2, document3); + + var expected = new List() {}; + + // Act + var actual = _sut.Search(query, invertedIndexMap); + + // Assert + Assert.True(actual.SequenceEqual(expected)); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Test/ControllerTest/SearchControllerTest/NoSignedSearcherTest.cs b/phase05/FullTextSearch.Test/ControllerTest/SearchControllerTest/NoSignedSearcherTest.cs new file mode 100644 index 0000000..9587455 --- /dev/null +++ b/phase05/FullTextSearch.Test/ControllerTest/SearchControllerTest/NoSignedSearcherTest.cs @@ -0,0 +1,48 @@ +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Core; +using FullTextSearch.Test.Data; +using Xunit; +using Assert = Xunit.Assert; + +namespace FullTextSearch.Test.ControllerTest.SearchControllerTest; + +public class NoSignedSearcherTest +{ + private readonly ISearcher _sut; + + public NoSignedSearcherTest() + { + _sut = new NoSignedSearcher(); + } + [Fact] + public void Search_ShouldReturnDocumentsContainingNoSignedWords_WhenGivenQueryAndInvertedIndex() + { + // Arrange + Query query = new Query(); + query.Text = "cat +reza -demand!"; + + query.WordsBySign = new Dictionary>() + { + {'+', new List(){"reza"}}, + {'-', new List(){"demand"}}, + {' ', new List(){"cat"}}, + }; + + var documentList = DataSample.GetDocuments(); + + Document document1 = documentList[0]; + Document document2 = documentList[1]; + Document document3 = documentList[2]; + + var invertedIndexMap = DataSample.GetInvertedIndexMap(document1, + document2, document3); + + var expected = new List() { }; + + // Act + var actual = _sut.Search(query, invertedIndexMap); + + // Assert + Assert.True(actual.SequenceEqual(expected)); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Test/ControllerTest/SearchControllerTest/PlusSearcherTest.cs b/phase05/FullTextSearch.Test/ControllerTest/SearchControllerTest/PlusSearcherTest.cs new file mode 100644 index 0000000..165afbd --- /dev/null +++ b/phase05/FullTextSearch.Test/ControllerTest/SearchControllerTest/PlusSearcherTest.cs @@ -0,0 +1,48 @@ +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Core; +using FullTextSearch.Test.Data; +using Xunit; +using Assert = Xunit.Assert; + +namespace FullTextSearch.Test.ControllerTest.SearchControllerTest; + +public class PlusSearcherTest +{ + private readonly ISearcher _sut; + + public PlusSearcherTest() + { + _sut = new PlusSearcher(); + } + [Fact] + public void Search_ShouldReturnDocumentsContainingPlusWords_WhenGivenQueryAndInvertedIndex() + { + // Arrange + Query query = new Query(); + query.Text = "cat +reza -demand!"; + + query.WordsBySign = new Dictionary>() + { + {'+', new List(){"reza"}}, + {'-', new List(){"demand"}}, + {' ', new List(){"cat"}}, + }; + + var documentList = DataSample.GetDocuments(); + + Document document1 = documentList[0]; + Document document2 = documentList[1]; + Document document3 = documentList[2]; + + var invertedIndexMap = DataSample.GetInvertedIndexMap(document1, + document2, document3); + + var expected = new List() {document1, document2, document3}; + + // Act + var actual = _sut.Search(query, invertedIndexMap); + + // Assert + Assert.True(actual.SequenceEqual(expected)); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Test/ControllerTest/SearchControllerTest/UniversalSearchTest.cs b/phase05/FullTextSearch.Test/ControllerTest/SearchControllerTest/UniversalSearchTest.cs new file mode 100644 index 0000000..62d5a00 --- /dev/null +++ b/phase05/FullTextSearch.Test/ControllerTest/SearchControllerTest/UniversalSearchTest.cs @@ -0,0 +1,57 @@ +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Core; +using FullTextSearch.Test.Data; +using Xunit; +using Assert = Xunit.Assert; + +namespace FullTextSearch.Test.SearchControllerTest; + +public class UniversalSearchTest +{ + private readonly IUniversalSearch _sut; + + public UniversalSearchTest() + { + _sut = new UniversalSearch(); + } + + [Fact] + public void GetUniversal_ShouldReturnAllDocuments_WhenGivenInvertedIndex() + { + // Arrange + var documentList = DataSample.GetDocuments(); + + Document document1 = documentList[0]; + Document document2 = documentList[1]; + Document document3 = documentList[2]; + + var invertedIndexMap = DataSample.GetInvertedIndexMap(document1, + document2, document3); + + + var expectedDocuments = new List { document1, document2, document3 }; + + // Act + var result = _sut.GetUniversal(invertedIndexMap).ToList(); + + // Assert + Assert.Equal(expectedDocuments.Count, result.Count); + foreach (var doc in expectedDocuments) + { + Assert.Contains(result, d => d.Equals(doc)); + } + } + + [Fact] + public void GetUniversal_ShouldReturnEmptyList_WhenGivenEmptyIndex() + { + // Arrange + var invertedIndexMap = new Dictionary>(); + + // Act + var result = _sut.GetUniversal(invertedIndexMap); + + // Assert + Assert.Empty(result); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Test/Data/DataSample.cs b/phase05/FullTextSearch.Test/Data/DataSample.cs new file mode 100644 index 0000000..2c40440 --- /dev/null +++ b/phase05/FullTextSearch.Test/Data/DataSample.cs @@ -0,0 +1,56 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Test.Data +{ + public static class DataSample + { + + public static List GetDocuments() + { + Document document1 = new Document() + { + Name = "DOC1", + Path = "./ResourcesTest/Doc1", + Text = "reza ali hello", + Words = new List { "reza", "ali", "hello" }, + }; + + Document document2 = new Document() + { + Name = "DOC2", + Path = "./ResourcesTest/Doc2", + Text = "reza mohammad hello", + Words = new List { "reza", "mohammad", "hello" }, + }; + + Document document3 = new Document() + { + Name = "DOC3", + Path = "./ResourcesTest/Doc3", + Text = "reza ali mohammad", + Words = new List { "reza", "ali", "mohammad" }, + }; + + return new List { document1, document2, document3 }; + } + public static Dictionary> GetInvertedIndexMap(Document document1, Document document2, Document document3) + { + return new Dictionary> + { + { "reza", new List { document1, document2, document3 } }, + { "mohammad", new List { document2, document3 } }, + { "ali", new List { document1, document3 } }, + { "hello", new List { document1, document2 } }, + { "reza ali", new List { document3, document1 } }, + { "reza ali hello", new List { document1 } }, + { "ali hello", new List { document1 } }, + { "reza mohammad", new List { document2 } }, + { "reza mohammad hello", new List { document2 } }, + { "mohammad hello", new List { document2 } }, + { "reza ali mohammad", new List { document3 } }, + { "ali mohammad", new List { document3 } }, + }; + } + + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Test/FullTextSearch.Test.csproj b/phase05/FullTextSearch.Test/FullTextSearch.Test.csproj new file mode 100644 index 0000000..a27c1c6 --- /dev/null +++ b/phase05/FullTextSearch.Test/FullTextSearch.Test.csproj @@ -0,0 +1,40 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + diff --git a/phase05/FullTextSearch.Test/ServiceTest/InitializeServiceTest/InitializeServiceTest.cs b/phase05/FullTextSearch.Test/ServiceTest/InitializeServiceTest/InitializeServiceTest.cs new file mode 100644 index 0000000..e2cfb97 --- /dev/null +++ b/phase05/FullTextSearch.Test/ServiceTest/InitializeServiceTest/InitializeServiceTest.cs @@ -0,0 +1,72 @@ +using FullTextSearch.Controller.DocumentController; +using FullTextSearch.Controller.DocumentController.Abstraction; +using FullTextSearch.Controller.InvertedIndexController; +using FullTextSearch.Controller.Read.Abstraction; +using FullTextSearch.Core; +using FullTextSearch.Service.InitializeService; +using FullTextSearch.Test.Data; +using InvertedIndex.Abstraction.Read; +using NSubstitute; +using Xunit; +using Assert = Xunit.Assert; + +namespace FullTextSearch.Test.ServiceTest.InitializeServiceTest; + +public class InitializeServiceTest +{ + private readonly IFileReader _fileReader; + private readonly IDocumentDirector _documentDirector; + private readonly IInvertedIndexMapper _invertedIndexMapper; + private readonly IDirectory _directory; + private readonly IPath _path; + private readonly InitializeService _sut; + + public InitializeServiceTest() + { + _fileReader = Substitute.For(); + _documentDirector = Substitute.For(); + _invertedIndexMapper = Substitute.For(); + _directory = Substitute.For(); + _path = Substitute.For(); + _sut = new InitializeService(_fileReader, _documentDirector, _invertedIndexMapper, _directory, _path); + } + + [Fact] + public async Task Initialize_ShouldInitializeDocumentsFromFilesInGivenDirectory_WhenDirectoryExists() + { + // Arrange + var directoryPath = "./Resources/EnglishData"; + var paths = new List { "./Resources/EnglishData/57110", "./Resources/EnglishData/58043", "./Resources/EnglishData/558044" }; + var names = new List { "57110", "58043", "558044" }; + var texts = new List { "", "", "" }; + var documentFormatter = new DocumentFormatter(); + var documentBuilders = new List(); + + _directory.GetFiles(directoryPath).Returns(paths); + + for (int i = 0; i < paths.Count; i++) + { + _path.GetFileName(paths[i]).Returns(names[i]); + _fileReader.ReadAsync(paths[i]).Returns(texts[i]); + var documentBuilder = new DocumentBuilder(documentFormatter); + _documentDirector.Construct(names[i], paths[i], texts[i], documentBuilder); + documentBuilders.Add(documentBuilder); + } + + var documents = documentBuilders.Select(builder => builder.GetDocument()).ToList(); + var documentList = DataSample.GetDocuments(); + var expected = DataSample.GetInvertedIndexMap(documentList[0], documentList[1], documentList[2]); + + _invertedIndexMapper.Map(documents).Returns(expected); + + // Act + var result = await _sut.Initialize(directoryPath); + + // Assert + Assert.Equal(result.Keys.Count, expected.Keys.Count); + foreach (var entry in expected) + { + Assert.True(entry.Value.SequenceEqual(result[entry.Key])); + } + } +} diff --git a/phase05/FullTextSearch.Test/ServiceTest/SearchServiceTest/SearchServiceTest.cs b/phase05/FullTextSearch.Test/ServiceTest/SearchServiceTest/SearchServiceTest.cs new file mode 100644 index 0000000..fe2d9c2 --- /dev/null +++ b/phase05/FullTextSearch.Test/ServiceTest/SearchServiceTest/SearchServiceTest.cs @@ -0,0 +1,68 @@ +using FullTextSearch.Controller.QueryController.Abstraction; +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Core; +using FullTextSearch.Service.SearchService; +using FullTextSearch.Test.Data; +using NSubstitute; +using Assert = Xunit.Assert; + +namespace FullTextSearch.Test.ServiceTest.SearchServiceTest; + +public class SearchServiceTest +{ + private readonly IQueryBuilder _queryBuilder; + private readonly IResultBuilder _resultBuilder; + private readonly ISearchService _sut; + + public SearchServiceTest() + { + _queryBuilder = Substitute.For(); + _resultBuilder = Substitute.For(); + _sut = new SearchService(_queryBuilder, _resultBuilder); + } + + [Test] + public void Search_ShouldReturnResultForPassesQuery() + { + // Arrange + string input = "cat +reza -demand"; + IEnumerable signs = new List() {'+', '-'}; + + var query = new Query(); + query.Text = input; + query.WordsBySign = new Dictionary>() + { + {'+', new List() {"reza"}}, + {'-', new List() {"demand"}}, + {' ', new List() {"cat"}} + }; + + var documentList = DataSample.GetDocuments(); + + Document document1 = documentList[0]; + Document document2 = documentList[1]; + Document document3 = documentList[2]; + + var expected = new Result(); + expected.documents = new List() { document1 }; + expected.documentsBySign = new Dictionary> + { + { '+', new List { document1, document2} }, + { '-', new List { document2} }, + { ' ', new List { document1, document2, document3 } } + }; + + var invertedIndexMap = DataSample.GetInvertedIndexMap(document1, + document2, document3); + + + _queryBuilder.GetQuery().Returns(query); + _resultBuilder.GetResult().Returns(expected); + + // Act + var actual = _sut.Search(input, invertedIndexMap); + + // Assert + Assert.Equal(actual, expected); + } +} \ No newline at end of file diff --git a/phase05/FullTextSearch.Test/newInvertedIndexMapTest.cs b/phase05/FullTextSearch.Test/newInvertedIndexMapTest.cs new file mode 100644 index 0000000..9efd372 --- /dev/null +++ b/phase05/FullTextSearch.Test/newInvertedIndexMapTest.cs @@ -0,0 +1,61 @@ +using FullTextSearch.Controller.InvertedIndexController; +using FullTextSearch.Core; +using FullTextSearch.Test.Data; +using Xunit; +using Assert = Xunit.Assert; + +namespace FullTextSearch.Test.ControllerTest.InvertedIndexControllerTest +{ + public class NewInvertedIndexMapTests + { + private readonly IInvertedIndexMapper _sut; + + public NewInvertedIndexMapTests() + { + _sut = new InvertedIndexMapper(); + } + + [Fact] + public void Map_ShouldReturnCorrectInvertedIndex_WhenGivenDocuments() + { + // Arrange + var documentList = DataSample.GetDocuments(); + + Document document1 = documentList[0]; + Document document2 = documentList[1]; + Document document3 = documentList[2]; + + var documents = new List + { + document1, document2, document3 + }; + + var expected = new Dictionary> + { + { "reza", new List { documents[0], documents[1], documents[2]} }, + {"reza ali", new List(){documents[0], documents[1], documents[2]}}, + {"reza ali hello", new List(){documents[0]}}, + { "ali", new List { documents[0], documents[2] } }, + { "ali hello", new List { documents[0] } }, + {"hello", new List(){documents[0]}}, + {"mohammad", new List(){documents[1], documents[2]}}, + {"mohammad reza", new List(){documents[1], documents[2]}}, + {"mohammad reza ali", new List(){documents[1], documents[2]}} + }; + + //var expected = DataSample.GetInvertedIndexMap(document1, + // document2, document3); + + // Act + var actual = _sut.Map(documents); + + // Assert + Assert.NotNull(expected); + Assert.Equal(expected.Keys.Count, actual.Keys.Count); + foreach (var entry in expected) + { + Assert.True(expected[entry.Key].SequenceEqual(actual[entry.Key])); + } + } + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI.sln b/phase06/InvertedIndexAPI/InvertedIndexAPI.sln new file mode 100644 index 0000000..48193fc --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InvertedIndexAPI", "InvertedIndexAPI\InvertedIndexAPI.csproj", "{F22FA597-2947-46F2-BCD5-1AF33A912BBA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F22FA597-2947-46F2-BCD5-1AF33A912BBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F22FA597-2947-46F2-BCD5-1AF33A912BBA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F22FA597-2947-46F2-BCD5-1AF33A912BBA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F22FA597-2947-46F2-BCD5-1AF33A912BBA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Controllers/SearchController.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Controllers/SearchController.cs new file mode 100644 index 0000000..a75c5b5 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Controllers/SearchController.cs @@ -0,0 +1,29 @@ +using FullTextSearch.Service.InitializeService; +using FullTextSearch.Service.SearchService; +using Microsoft.AspNetCore.Mvc; + +namespace InvertedIndexAPI.Controllers; + + +[ApiController] +[Route("[controller]/[action]")] +public class SearchController : ControllerBase +{ + + private readonly ISearchService _searchService; + + public SearchController(IInitializeServices2 initializeServices2, ISearchService searchService) + { + initializeServices2.Drive(); + _searchService = searchService; + } + + + [HttpGet("{input}")] + public IActionResult ResponseQuery([FromRoute]string input) + { + var result = _searchService.Search(input, InitializeServicesDriver.InvertedIndex); + return Ok(result.documents.Count()); + } + +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/InvertedIndexAPI.csproj b/phase06/InvertedIndexAPI/InvertedIndexAPI/InvertedIndexAPI.csproj new file mode 100644 index 0000000..45e6268 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/InvertedIndexAPI.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/InvertedIndexAPI.http b/phase06/InvertedIndexAPI/InvertedIndexAPI/InvertedIndexAPI.http new file mode 100644 index 0000000..7d76397 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/InvertedIndexAPI.http @@ -0,0 +1,6 @@ +@InvertedIndexAPI_HostAddress = http://localhost:5135 + +POST {{InvertedIndexAPI_HostAddress}}/Search/ResponseQuery/ +Accept: application/json + +### diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Models/Document.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Models/Document.cs new file mode 100644 index 0000000..65f952d --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Models/Document.cs @@ -0,0 +1,28 @@ +namespace FullTextSearch.Core; + +public class Document +{ + public string Name { get; set; } + public string Path { get; set; } + public string Text { get; set; } + public IEnumerable Words { get; set; } + + public override string ToString() + { + return $"Name:{Name}"; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (obj.GetType() != this.GetType()) return false; + return Equals((Document) obj); + } + + public bool Equals(Document other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return this.Name.Equals(other.Name) && this.Path.Equals(other.Path) && this.Text.Equals(other.Text) && this.Words.SequenceEqual(other.Words); + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Models/Query.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Models/Query.cs new file mode 100644 index 0000000..e5f6dd3 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Models/Query.cs @@ -0,0 +1,30 @@ +namespace FullTextSearch.Core; + +public class Query +{ + public string Text { get; set; } + public Dictionary> WordsBySign = new Dictionary>(); + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (obj.GetType() != GetType()) return false; + return Equals((Query) obj); + } + + public bool Equals(Query other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + if (!this.Text.Equals(other.Text)) return false; + if(!this.WordsBySign.Count.Equals(other.WordsBySign.Count)) return false; + foreach (var entry in this.WordsBySign) + { + if (!entry.Value.SequenceEqual(other.WordsBySign[entry.Key])) + { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Models/Result.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Models/Result.cs new file mode 100644 index 0000000..daf568c --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Models/Result.cs @@ -0,0 +1,37 @@ +namespace FullTextSearch.Core; + +public class Result +{ + public Result() + { + documents = new List(); + documentsBySign = new Dictionary>(); + } + public IEnumerable documents { get; set; } + public Dictionary> documentsBySign {get; set;} + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((Result) obj); + } + + public bool Equals(Result other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + if (!this.documents.SequenceEqual(other.documents)) return false; + if(!this.documentsBySign.Count.Equals(other.documentsBySign.Count)) return false; + foreach (var entry in this.documentsBySign) + { + if (!entry.Value.SequenceEqual(other.documentsBySign[entry.Key])) + { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Models/Search.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Models/Search.cs new file mode 100644 index 0000000..f2c73fe --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Models/Search.cs @@ -0,0 +1,15 @@ +using FullTextSearch.Core; + +namespace InvertedIndex.Model; + +public class Search +{ + public Query query { get; set; } + public Result result { get; set; } + + public Search(Query query, Result result) + { + this.query = query; + this.result = result; + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Program.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Program.cs new file mode 100644 index 0000000..ffc17ef --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Program.cs @@ -0,0 +1,49 @@ +using FullTextSearch.Controller.QueryController; +using FullTextSearch.Controller.QueryController.Abstraction; +using FullTextSearch.Controller.ResultController; +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Controller.SearchController.Abstraction; +using FullTextSearch.Controller.TextFormatter; +using FullTextSearch.Controller.TextFormatter.Abstraction; +using FullTextSearch.Service.InitializeService; +using FullTextSearch.Service.SearchService; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +builder.Services.AddControllers(); + + + +//builder.Services.AddSingleton<>(); + + +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + + +//builder.Services.AddScoped(); +builder.Services.AddSingleton(); + + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); +app.MapControllers(); + +app.Run(); + diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Properties/launchSettings.json b/phase06/InvertedIndexAPI/InvertedIndexAPI/Properties/launchSettings.json new file mode 100644 index 0000000..be89872 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:44718", + "sslPort": 44365 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5135", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7027;http://localhost:5135", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Resources/EnglishData/57110 b/phase06/InvertedIndexAPI/InvertedIndexAPI/Resources/EnglishData/57110 new file mode 100644 index 0000000..28d15b8 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Resources/EnglishData/57110 @@ -0,0 +1 @@ +cat I have a 42 yr old male reza friend, upa misdiagnosed as havin osteopporosis for payampardaz two years, who recently found out that hi illness is the rare Gaucher's disease.Gaucher's disease symptoms include: brittle bones (he lost 9 inches off his hieght); enlarged liver and spleen; interna bleeding; and fatigue (all the time). The problem (in Type 1) i attributed to a genetic mutation where there is a lack of th enzyme glucocerebroside in macrophages so the cells swell up This will eventually cause deathEnyzme replacement therapy has been successfully developed an approved by the FDA in the last few years so that those patient administered with this drug (called Ceredase) report a remarkabl improvement in their condition. Ceredase, which is manufacture by biotech biggy company--Genzyme--costs the patient $380,00 per year. Gaucher\'s disease has justifyably been called "the mos expensive disease in the world"NEED INFOI have researched Gaucher's disease at the library but am relyin on netlanders to provide me with any additional information**news, stories, report**people you know with this diseas**ideas, articles about Genzyme Corp, how to get a hold o enough cat money to buy some, programs available to help wit costs**Basically ANY HELP YOU CAN OFFEThanks so very muchDeborah \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Resources/EnglishData/58043 b/phase06/InvertedIndexAPI/InvertedIndexAPI/Resources/EnglishData/58043 new file mode 100644 index 0000000..2eb5a87 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Resources/EnglishData/58043 @@ -0,0 +1 @@ +three free outreach speakersPLEASE >This wouldn't happen to be the same thing as chiggers, would it>A truly awful parasitic affliction, as I understand it. Tiny bug>dig deeply into the skin, burying themselves. Yuck! They have thes>things in OklahomaClose. My mother comes from Gainesville Tex, right UPA across the borderThey claim to be the chigger capitol of the world, and I believe themWhen I grew up in Fort Worth it was bad enough, but in Gainesvillin the summer an attack was guaranteedDoug McDonal \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Resources/EnglishData/58044 b/phase06/InvertedIndexAPI/InvertedIndexAPI/Resources/EnglishData/58044 new file mode 100644 index 0000000..1cbdaa8 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Resources/EnglishData/58044 @@ -0,0 +1 @@ +cat Announcing. . . Announcing. . . Announcing. . .Announcing. . CELEBRATE LIBERTY 1993 LIBERTARIAN PARTY NATIONAL CONVENTIO AND POLITICAL EXP THE MARRIOTT HOTEL AND THE SALT PALAC SALT LAKE CITY, UTA INCLUDES INFORMATION ON DELEGATE DEALS (Back by Popular Demand! The convention will be held at the Salt Palace Convention Center and thMarriott Hotel, Salt Lake City, Utah. The business sessions, Karl HesInstitute, and Political Expo are at the Salt Palace; breakfasts, parties, anbanquet are at the Marriott HotelMarriott Hotel room rates are $79.00 night, plus 10.5% tax ($87.17 total).This rate is good for one to four persons room occupancy. Double is onor two beds; 3 or 4 people is 2 beds. You can make your reservationdirect with the hotel (801-531-0800), or you can purchase your roothrough one of MGP's payment plans. MGP will provide assistance imatching roommates if requestedAugust 30, 31, Sept. 1: Everything You Always Wanted t Know About Winning Elections, bu Didn't Know Where to AskThree days of intensive campaign training conducted by Sal Guzzetta, 25 year veteran of more than 200 campaigns. Students receive 990 pageof professional campaign manuals. Everything from strategy antargeting to opposition research, fundraising, and field operationsPrice: $150 if purchased by May 1, 199 $175 thereafteAugust 31 and Sept. 1: Platform, Bylaws, Credentials an National committee meetingsShoot out in Salt Lake! PLEDGE versus Committee for a LibertariaMajority. Will the party's membership and platform definitions change?Is compromise possible? The Platform and Bylaws committees arresponsible for making recommendations to the convention concerninchanges in those documents. At this convention, the party will onlconsider deletions to the platform. The Convention Rules would have tbe amended by a 2/3 vote to change this ruleThe meetings are open to the public. There is no charge for attending.Sept. 2-5, 1993: Celebrate Liberty! Begin Political Expo OpenSept. 2, 1993: 9 AM -- Credentials Committee report to the delegates 10:30 -- Gala Opening Ceremony and Keynote Addres by Russell Means. 1:00 -- After lunch break, convention business continue (see "Standing Order of Business" from the "Conventio Rules of the Libertarian Party" at the end of thi document Karl Hess Institute of Libertarian Politics Begins, runs i tandem with the business sessionsSept. 3, 1993: Dawns Early Light, Green Dragon Inn (morning an evening), with Karl Hess Institute and conventio business in betweenSept. 4, 1993: Dawns Early Light, Freedom Rock '93, Karl Hes Institute, convention businessSept. 5, 1993: Dawns Early Light, Convention Banquet, Karl Hes Institute, convention business, Joyful Noise ACTIVITY DESCRIPTIONSDAWNS EARLYLIGHT Three great convention breakfasts to start your day right, featuring science fiction author L. Neil Smith psychiatrist and author Dr. Thomas Szasz, and Sout African Libertarian leader Frances KendallGREEN DRAGONINN "Opening night" party, named after the famous in where Sam Adams and his crowd plotted trouble for th British over pints of ale and beer. Music, food, drink and comedyFREEDOMROCK '93 Free downtown rock concert Friday night, with dru circle, comic Tim Slagle, Middle Eastern dancer, reggae and local classic rock-n-roll bands. Will be widel publicized in the local area. Major outreach opportunityBANQUET Vivaldi and Mozart, fine dining, in the elegant Marriot Grand Ballroom (black tie optional). Dancing followsPOLITICALEXPO Exhibits and vendors. FREE admission. Event will b widely publicized in local area for maximum draw. Major Outreach opportunity KARL HESS INSTITUTE OF LIBERTARIAN POLITIC Workshops, speakers, roundtable discussions in these areasLIBERTY: NEXGENERATION High school and college age Libertarians tal about what matters to them and the 20 something generationAGENDA 2000 Considers key issues of the 1990s. Environment. Health Care. 21st Century Economics. Dru War. Second Amendment. Social Services. Foreign Policy. Crime & Violence. AIDSTHE GREAT DEBATE LP Strategy and tactics. Media. Ballot Access. Initiatives. Feminist Issues. Presidentia Campaigns. LP Elected Officials. Grassroots. Early look at the 1996 presidential nominationVALUES FORTHE 90s Community. Children. Abundance. Hom Schooling. Religion and Liberty. RaceCAMPUS FOCUS Organizing. Academia. Blue Collar Youth CONVENTION PACKAGE DESCRIPTIONS AND PRICETOTAL EVENT: All activities, Aug. 30-Sept. 5, $400, including day candidate traininFull Celebration: All convention activities, Sept. 2-5, $30Late Riser No breakfasts, everything else Sept. 2-5, $25Thrift No breakfasts or banquet, $15Issues Focus Karl Hess Institute, $12Basic Convention packet, souvenirs, two Karl Hes Institute speakerFree Political Expo, Access to convention hall Keynote Address, Joyful Noise, Freedom Roc '93, three free outreach speakersPLEASE NOTE-- PRICES INCREASE MAY 1, 199-- Special student prices are available to anyone under 25 years o age or who is enrolled in a college or university-- Six and seven month payment plans are available which ca include housing (if requested)-- To add the three day candidate training to any package belo (except "Total Event"), add $150 to the price-- All prices are in U.S. dollars-- Advertising is available in the convention program; exhibits an sponsorships are available for the Political Expo. Free Politica Expo admission and MGP promotions will draw visitors from th surrounding community (one million people live within a 3 minute drive of the Expo)-- If your special interest group, organization, committee, or caus would like to schedule space for a presentation, contact us-- MGP conducts a drawing each month and gives away FRE hotel nights. The sooner you register, the more chances you hav to win-- Roommate match service available upon requestOTHER EVENTS"Anti-Federalist Two" MGP sponsored writing contest. Jun submission deadline. Contact MGP fo prospectus"The LibertarianGames" Friendly competition -- marksmanship, compute programming, chess, maybe moreLibertarians for Gay Lesbian Concerns Business meeting, social night, sponsored b LGLC??? YOUR EVENT CAN BE LISTED HERE. Contac MGP for details ATTENTION COLLEGE STUDENTSSpecial discounts are available for college and high school students. Wwill work on casual housing opportunities for the "Poverty Caucus".College Libertarians will meet at Celebrate Liberty! and discuss the futurof their movement on campuses. Contact MGP for more details\t\t\t LIST OF SPEAKER\t\t (as of March 14, 1993)Dean Ahmad\t\tJim Hudler\t\tSheldon RichmaKaren Allard\t\tJeff Hummel\t\tKathleen RichmaRick Arnold\t\tAlexander Joseph\tDan RosenthaDr. George Ayittey\tFrances Kendall\t\tDr. Mary RuwarAlan Boch\t\tMartin Luther King\tDagny SharoRichard Boddie\t\tMe-Me King\t\tJane ShaGus Dizerega\t\tHenry Lamb\t\tSandy ShaLarry Dodge\t\tAmy Lassen\t\tL. Neil SmitDr. Richard Ebeling\tScott Lieberman\t\tEric SterlinDon Ernsberger\t\tDr. Nancy Lord\t\tDr. Richard StrouBill Evers\t\tRussell Means\t\tDr. Thomas SzasBonnie Flickenger\tVince Miller\t\tMichael TanneJohn Fund\t\tMaury Modine\t\tSojourner TrutDoris Gordon\t\tDavid Nolan\t\tYuri TuviLeon Hadar\t\tRandall O'Toole\t\tBob WaldroPatrick Henry\t\tJames Ostrowski\t\tTerree WasleKarl Hess\t\tDirk Pearson\t\tPerry WilliDr. Karl Hess Jr.\tBob Poole\t\tRichard WingeJacob Honrberger\tCarole Ann Rand\t\tJarret Wollstei\t\t\t\t\t\tBrigham Youn\t\t UPCOMING CONVENTION DEVELOPMENTSOn May 1st, prices increase for convention packages, candidate trainingand exhibits/advertisingNew prices for convention packages will beTotal Event: $45Full Celebration: $35Late Riser: $27Thrift: $17Issues Focus: $15Basic: $3Free: $These prices good through July 2, 1993 BACK BY POPULAR DEMAND! ANNOUNCING THE DELEGATE DEALS Available May 1, 199I: Business Focus: All convention activities except Karl Hes Institute -- $27II: Delegate Celebration, includes a complete set of Karl Hes Institute audio tapes instead of institute tickets -- $35 STANDING ORDER OF BUSINESS FO A LIBERTARIAN PARTY CONVENTIO 1. Call to orde 2. Credentials Committee repor 3. Adoption of agend 4. Treasurer's repor 5. Bylaws and Rules Committee report (Non-nominating convention only 6. Platform Committee report (At non-Presidential nominatin conventions only deletions may be considered. 7. Nomination of Party candidates for President an Vice-President (in appropriate years 8. Election of Party Officers and at-large member of the National Committe 9. Election of Judicial Committe 10. Resolution 11. Other busines FOR QUESTIONS OR COMMENTS GRUMBLES OR GRINS SUGGESTIONS OR CRITICISM AN TO REGISTER CONTACT MORNING GLORY PRODUCTIONS, INC P.O. Box 52617 Salt Lake City, UT 8415 801.582.331 E-mail: Bob.Waldrop@f418.n104.z1.fidonet.or Make Checks Payable to Morning Glory Productions, Inc--\t\t Don't blame me; I voted LibertarianDisclaimer: I speak for myself, except as noted; Copyright 1993 Rich ThomsoUUCP: ...!uunet!dsd.es.com!rthomson\t\t\tRich ThomsoInternet: rthomson@dsd.es.com\tIRC: _Rich_\t\tPEXt Programme \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/Abstraction/IDocumentBuilder.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/Abstraction/IDocumentBuilder.cs new file mode 100644 index 0000000..107f740 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/Abstraction/IDocumentBuilder.cs @@ -0,0 +1,14 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.DocumentController.Abstraction; + +public interface IDocumentBuilder +{ + + void BuildName(string name); + void BuildPath(string path); + void BuildText(string text); + void DeleteExtraSpace(); + void BuildWords(); + Document GetDocument(); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/Abstraction/IDocumentDirector.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/Abstraction/IDocumentDirector.cs new file mode 100644 index 0000000..0b32ce2 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/Abstraction/IDocumentDirector.cs @@ -0,0 +1,6 @@ +namespace FullTextSearch.Controller.DocumentController.Abstraction; + +public interface IDocumentDirector +{ + void Construct(string name, string path, string text, IDocumentBuilder documentBuilder); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/Abstraction/IDocumentFormatter.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/Abstraction/IDocumentFormatter.cs new file mode 100644 index 0000000..6dc13f6 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/Abstraction/IDocumentFormatter.cs @@ -0,0 +1,7 @@ +namespace InvertedIndex.Controller.Document; + +public interface IDocumentFormatter +{ + string ToUpper(string text); + IEnumerable Split(string queryText, string regex); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/DocumentBuilder.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/DocumentBuilder.cs new file mode 100644 index 0000000..1531c40 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/DocumentBuilder.cs @@ -0,0 +1,47 @@ +using System.Text.RegularExpressions; +using FullTextSearch.Controller.DocumentController.Abstraction; +using FullTextSearch.Core; +using InvertedIndex.Controller.Document; + +namespace FullTextSearch.Controller.DocumentController; + +public class DocumentBuilder : IDocumentBuilder +{ + private readonly Document _document; + private readonly IDocumentFormatter _documentFormatter; + + public DocumentBuilder(IDocumentFormatter documentFormatter) + { + _documentFormatter = documentFormatter ?? throw new ArgumentNullException(nameof(documentFormatter)); + _document = new Document(); + } + + public void BuildName(string name) + { + _document.Name = name; + } + + public void BuildPath(string path) + { + _document.Path = path; + } + + public void BuildText(string text) + { + _document.Text = text; + } + + public void DeleteExtraSpace() + { + _document.Text = Regex.Replace( _document.Text, @"\s+", " "); + } + public void BuildWords() + { + _document.Words = _documentFormatter.Split(_documentFormatter.ToUpper(_document.Text), " "); + } + + public Document GetDocument() + { + return _document; + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/DocumentDirector.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/DocumentDirector.cs new file mode 100644 index 0000000..518b17a --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/DocumentDirector.cs @@ -0,0 +1,16 @@ +using FullTextSearch.Controller.DocumentController.Abstraction; + +namespace FullTextSearch.Controller.DocumentController; + +public class DocumentDirector : IDocumentDirector +{ + public void Construct(string name, string path, string text, IDocumentBuilder documentBuilder) + { + documentBuilder.BuildName(name); + documentBuilder.BuildPath(path); + documentBuilder.BuildText(text); + documentBuilder.DeleteExtraSpace(); + documentBuilder.BuildWords(); + } + +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/DocumentFormatter.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/DocumentFormatter.cs new file mode 100644 index 0000000..d92a7c6 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/DocumentServices/DocumentFormatter.cs @@ -0,0 +1,18 @@ +using System.Text.RegularExpressions; +using InvertedIndex.Controller.Document; + +namespace FullTextSearch.Controller.DocumentController; + +public class DocumentFormatter : IDocumentFormatter{ + + public string ToUpper(string text) + { + return text.ToUpper(); + } + + public IEnumerable Split(string queryText, string regex) + { + return Regex.Split(queryText, regex); + } + +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/Abstraction/ISearcher.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/Abstraction/ISearcher.cs new file mode 100644 index 0000000..e60676a --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/Abstraction/ISearcher.cs @@ -0,0 +1,9 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController; + +public interface ISearcher +{ + char Sign {get; init;} + IEnumerable Search(Query query, Dictionary> invertedIndex); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/Abstraction/ISearcherDriver.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/Abstraction/ISearcherDriver.cs new file mode 100644 index 0000000..28d5a58 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/Abstraction/ISearcherDriver.cs @@ -0,0 +1,7 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController.Abstraction; + +public interface ISearcherDriver { + void DriveSearch(IEnumerable searchers, Query query, Result result, Dictionary> invertedIndex); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/Abstraction/IUniversalSearch.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/Abstraction/IUniversalSearch.cs new file mode 100644 index 0000000..3da7da2 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/Abstraction/IUniversalSearch.cs @@ -0,0 +1,8 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController; + +public interface IUniversalSearch +{ + IEnumerable GetUniversal(Dictionary> dictionary); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/MinusSearcher.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/MinusSearcher.cs new file mode 100644 index 0000000..9d41f5d --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/MinusSearcher.cs @@ -0,0 +1,19 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController; + +public class MinusSearcher : ISearcher +{ + public char Sign {get; init;} = '-'; + + public IEnumerable Search(Query query, Dictionary> invertedIndex) + { + var documents = query.WordsBySign[Sign] + .Where(s => invertedIndex.ContainsKey(s)) + .SelectMany(s => invertedIndex[s]) + .Distinct() + .ToList(); + + return documents; + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/NoSignedSearcher.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/NoSignedSearcher.cs new file mode 100644 index 0000000..7a06a77 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/NoSignedSearcher.cs @@ -0,0 +1,36 @@ + +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController; + +public class NoSignedSearcher : ISearcher +{ + public char Sign {get; init;} = ' '; + public UniversalSearch universalSearch = new UniversalSearch(); + + public IEnumerable Search(Query query, Dictionary> InvertedIndex) + { + IEnumerable ordinaryDocs = new List(); + + if(query.WordsBySign[Sign].ToList().Count == 0) + ordinaryDocs = universalSearch.GetUniversal(InvertedIndex); + + else + { + try + { + ordinaryDocs = query.WordsBySign[Sign] + .Select(w=>InvertedIndex[w]) + .SelectMany(d=>d) + .Distinct() + .ToList(); + } + catch(KeyNotFoundException e) + { + ordinaryDocs.ToList().Clear(); // + } + } + + return ordinaryDocs; + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/PlusSearcher.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/PlusSearcher.cs new file mode 100644 index 0000000..ac6294d --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/PlusSearcher.cs @@ -0,0 +1,20 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController; + +public class PlusSearcher : ISearcher +{ + public char Sign {get; init;} = '+'; + public UniversalSearch universalSearch = new UniversalSearch(); // static this + public IEnumerable Search(Query query, Dictionary> dictionary) + { + IEnumerable plusDocs = new List(); + if(query.WordsBySign[Sign].ToList().Count == 0) + plusDocs = universalSearch.GetUniversal(dictionary); + else + query.WordsBySign[Sign].Where(s=>dictionary.ContainsKey(s)).ToList() + .ForEach(x=> plusDocs = plusDocs + .Union(dictionary[x]).ToList()); + return plusDocs; + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/SearcherDriver.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/SearcherDriver.cs new file mode 100644 index 0000000..5bd6f45 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/SearcherDriver.cs @@ -0,0 +1,15 @@ +using FullTextSearch.Controller.SearchController.Abstraction; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController; + +public class SearcherDriver : ISearcherDriver +{ + public void DriveSearch(IEnumerable searchers, Query query, Result result, + Dictionary> invertedIndex) + { + result.documentsBySign.Clear(); + searchers.ToList() + .ForEach(s => result.documentsBySign.Add(s.Sign, s.Search(query, invertedIndex))); + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/UniversalSearch.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/UniversalSearch.cs new file mode 100644 index 0000000..a4a810d --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/HelperSearchServices/UniversalSearch.cs @@ -0,0 +1,13 @@ + +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.SearchController; + +public class UniversalSearch : IUniversalSearch +{ + public IEnumerable GetUniversal(Dictionary> invertedIndex) + { + var universalList = invertedIndex.Values.SelectMany(d => d).Distinct(); + return universalList; + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InitializeServices/Abstraction/IInitializeService.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InitializeServices/Abstraction/IInitializeService.cs new file mode 100644 index 0000000..9e0d23d --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InitializeServices/Abstraction/IInitializeService.cs @@ -0,0 +1,8 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Service.InitializeService; + +public interface IInitializeService +{ + Dictionary> Initialize(string directoryPath); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InitializeServices/Abstraction/IInitializeServices2.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InitializeServices/Abstraction/IInitializeServices2.cs new file mode 100644 index 0000000..09f21f7 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InitializeServices/Abstraction/IInitializeServices2.cs @@ -0,0 +1,6 @@ +namespace FullTextSearch.Service.InitializeService; + +public interface IInitializeServices2 +{ + public void Drive(); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InitializeServices/InitializeService.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InitializeServices/InitializeService.cs new file mode 100644 index 0000000..3807701 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InitializeServices/InitializeService.cs @@ -0,0 +1,40 @@ +using FullTextSearch.Controller.DocumentController; +using FullTextSearch.Controller.DocumentController.Abstraction; +using FullTextSearch.Controller.InvertedIndexController; +using FullTextSearch.Controller.Read.Abstraction; +using FullTextSearch.Core; +using InvertedIndex.Abstraction.Read; + +namespace FullTextSearch.Service.InitializeService; + +public class InitializeService : IInitializeService +{ + private readonly IFileReader _fileReader; + private readonly IDocumentDirector _documentDirector; + private readonly IInvertedIndexMapper _invertedIndexMapper; + private readonly IDirectory _directory; + private readonly IPath _path; + + public InitializeService(IFileReader fileReader, IDocumentDirector documentDirector, IInvertedIndexMapper invertedIndexMapper, IDirectory directory, IPath path) + { + _fileReader = fileReader; + _documentDirector = documentDirector; + _invertedIndexMapper = invertedIndexMapper; + _directory = directory; + _path = path; + } + + public Dictionary> Initialize(string directoryPath) + { + List documents = new List(); + var paths = _directory.GetFiles(directoryPath); + foreach (var p in paths) + { + IDocumentBuilder documentBuilder = + new DocumentBuilder(new DocumentFormatter()); + _documentDirector.Construct(_path.GetFileName(p), p, _fileReader.ReadAsync(p), documentBuilder); + documents.Add(documentBuilder.GetDocument()); + } + return _invertedIndexMapper.Map(documents); + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InitializeServices/InitializeServicesDriver.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InitializeServices/InitializeServicesDriver.cs new file mode 100644 index 0000000..3747774 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InitializeServices/InitializeServicesDriver.cs @@ -0,0 +1,36 @@ +using FullTextSearch.Controller.DocumentController; +using FullTextSearch.Controller.InvertedIndexController; +using InvertedIndex.Controller.Read; +using Directory = FullTextSearch.Controller.Read.Directory; +using Document = FullTextSearch.Core.Document; +using Path = FullTextSearch.Controller.Read.Path; + +namespace FullTextSearch.Service.InitializeService +{ + public class InitializeServicesDriver : IInitializeServices2 + { + private readonly IConfiguration _configuration; + + public static Dictionary> InvertedIndex { get; set; } + + public InitializeServicesDriver(IConfiguration configuration) + { + _configuration = configuration; + } + + public void Drive() + { + var textFileReader = new TextFileReader(); + var documentDirector = new DocumentDirector(); + var invertedIndexMapper = new InvertedIndexMapper(); + var directory = new Directory(); + var path = new Path(); + + var initializeService = new InitializeService( + textFileReader, documentDirector, invertedIndexMapper, directory, path); + + var pathToData = _configuration["FilePaths:DataPath"]; + InvertedIndex = initializeService.Initialize(pathToData); + } + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InvertedIndexServices/Abstraction/IInverIndexMapper.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InvertedIndexServices/Abstraction/IInverIndexMapper.cs new file mode 100644 index 0000000..00b0277 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InvertedIndexServices/Abstraction/IInverIndexMapper.cs @@ -0,0 +1,8 @@ +using System.Collections; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.InvertedIndexController; + +public interface IInvertedIndexMapper { + Dictionary> Map(IEnumerable documents); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InvertedIndexServices/InvertedIndexMapper.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InvertedIndexServices/InvertedIndexMapper.cs new file mode 100644 index 0000000..c08e031 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/InvertedIndexServices/InvertedIndexMapper.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.InvertedIndexController; + +public class InvertedIndexMapper : IInvertedIndexMapper +{ + public Dictionary> Map(IEnumerable documents) + { + var invertedIndex = new Dictionary>(); + + foreach (var document in documents) + { + var words = document.Words.ToList(); + + for (int startIndex = 0; startIndex < words.Count; startIndex++) + { + for (int endIndex = startIndex + 1; endIndex <= Math.Min(words.Count, startIndex + 5); endIndex++) + { + var phrase = string.Join(" ", words.Skip(startIndex).Take(endIndex - startIndex)); + if (!invertedIndex.ContainsKey(phrase)) + invertedIndex[phrase] = new List(); + invertedIndex[phrase].Add(document); + } + } + } + return invertedIndex.ToDictionary(pair => pair.Key, pair => (IEnumerable)pair.Value); + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/Abstraction/IQueryBuilder.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/Abstraction/IQueryBuilder.cs new file mode 100644 index 0000000..1aecab1 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/Abstraction/IQueryBuilder.cs @@ -0,0 +1,10 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.QueryController.Abstraction; + +public interface IQueryBuilder +{ + void BuildText(string text); + void BuildWordsBySign(IEnumerable signs); + Query GetQuery(); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/Abstraction/IQueryDirector.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/Abstraction/IQueryDirector.cs new file mode 100644 index 0000000..ac9e4e3 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/Abstraction/IQueryDirector.cs @@ -0,0 +1,6 @@ +namespace FullTextSearch.Controller.QueryController.Abstraction; + +public interface IQueryDirector +{ + void Construct(IQueryBuilder queryBuilder); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/Abstraction/IQueryFormatter.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/Abstraction/IQueryFormatter.cs new file mode 100644 index 0000000..c080f05 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/Abstraction/IQueryFormatter.cs @@ -0,0 +1,9 @@ +namespace FullTextSearch.Controller.QueryController.Abstraction; + +public interface IQueryFormatter +{ + string ToUpper(string text); + IEnumerable Split(string queryText, string regex); + IEnumerable CollectBySign(IEnumerable queryWords, char sign); + IEnumerable RemovePrefix(IEnumerable querySameSignWords); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/QueryBuilder.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/QueryBuilder.cs new file mode 100644 index 0000000..2194069 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/QueryBuilder.cs @@ -0,0 +1,66 @@ +using FullTextSearch.Controller.QueryController.Abstraction; +using FullTextSearch.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using FullTextSearch.Controller.TextFormatter.Abstraction; +using InvertedIndex.Controller.Document; + +namespace FullTextSearch.Controller.QueryController; + +public class QueryBuilder : IQueryBuilder +{ + private readonly Query _query; + private readonly IQueryFormatter _queryFormatter; + private readonly ITextFormatter _textFormatter; + public QueryBuilder(IQueryFormatter queryFormatter, ITextFormatter textFormatter) + { + _query = new Query(); + _queryFormatter = queryFormatter ?? throw new ArgumentNullException(nameof(queryFormatter)); + _textFormatter = textFormatter ?? throw new ArgumentNullException(nameof(textFormatter)); + } + + public void BuildText(string text) + { + _query.Text = text; + } + + public void BuildWordsBySign(IEnumerable signs) + { + _query.WordsBySign.Clear(); + + var splittedText = _queryFormatter.Split(_queryFormatter.ToUpper(_query.Text), " ").ToList(); + var quoteIndices = _textFormatter.GetQuoteIndices(splittedText); + var indicesToRemove = _textFormatter.GetIndicesToRemove(quoteIndices); + var filteredWords = _textFormatter.FilterOutIndices(splittedText, indicesToRemove); + var concatenatedQuotes = _textFormatter.ConcatenateQuotedWords(splittedText, quoteIndices); + + ProcessWordsBySign(filteredWords, signs); + ProcessWordsBySign(concatenatedQuotes, signs); + } + private void ProcessWordsBySign(IEnumerable words, IEnumerable signs) + { + + foreach (var sign in signs) + { + if (!_query.WordsBySign.ContainsKey(sign)) + { + _query.WordsBySign[sign] = new List(); + } + var queryWords = words.ToList(); + var texts = _queryFormatter.CollectBySign(queryWords, sign); + words = queryWords.Except(texts).ToList(); + _query.WordsBySign[sign] = _query.WordsBySign[sign].Concat(_queryFormatter.RemovePrefix(texts)).ToList(); + } + if (!_query.WordsBySign.ContainsKey(' ')) + { + _query.WordsBySign[' '] = new List(); + } + _query.WordsBySign[' '] = _query.WordsBySign[' '].Concat(_queryFormatter.RemovePrefix(words)).ToList(); + } + + public Query GetQuery() + { + return _query; + } +} diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/QueryDirector.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/QueryDirector.cs new file mode 100644 index 0000000..06c7b16 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/QueryDirector.cs @@ -0,0 +1,12 @@ +using FullTextSearch.Controller.QueryController.Abstraction; + +namespace FullTextSearch.Controller.QueryController; + +public class QueryDirector +{ + public void Construct(string text, IEnumerable signs, IQueryBuilder queryBuilder) + { + queryBuilder.BuildText(text); + queryBuilder.BuildWordsBySign(signs); + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/QueryFormatter.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/QueryFormatter.cs new file mode 100644 index 0000000..b93807e --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/QueryServices/QueryFormatter.cs @@ -0,0 +1,38 @@ +using System.Text.RegularExpressions; +using FullTextSearch.Controller.QueryController.Abstraction; + +namespace FullTextSearch.Controller.QueryController; + +public class QueryFormatter : IQueryFormatter +{ + public string ToUpper(string text) + { + return text.ToUpper(); + } + + public IEnumerable Split(string queryText, string regex) + { + return Regex.Split(queryText, regex); + } + + public IEnumerable CollectBySign(IEnumerable queryWords, char sign) + { + return queryWords.Where(w => w[0].Equals(sign)); + } + + public IEnumerable RemovePrefix(IEnumerable querySameSignWords) + { + return querySameSignWords.Select(word => + { + if (word.StartsWith("+") || word.StartsWith("-")) + { + word = word.Substring(1); + } + if (word.StartsWith("\"") && word.EndsWith("\"")) + { + word = word.Substring(1, word.Length - 2); + } + return word; + }); + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Abstraction/IDirectory.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Abstraction/IDirectory.cs new file mode 100644 index 0000000..a28e4b3 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Abstraction/IDirectory.cs @@ -0,0 +1,6 @@ +namespace FullTextSearch.Controller.Read.Abstraction; + +public interface IDirectory +{ + List GetFiles(string dirPath); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Abstraction/IFileReader.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Abstraction/IFileReader.cs new file mode 100644 index 0000000..54d1a5b --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Abstraction/IFileReader.cs @@ -0,0 +1,6 @@ +namespace InvertedIndex.Abstraction.Read; + +public interface IFileReader +{ + string ReadAsync(string path); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Abstraction/IPath.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Abstraction/IPath.cs new file mode 100644 index 0000000..64d6fee --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Abstraction/IPath.cs @@ -0,0 +1,6 @@ +namespace FullTextSearch.Controller.Read.Abstraction; + +public interface IPath +{ + string GetFileName(string path); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Directory.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Directory.cs new file mode 100644 index 0000000..a1ae1e3 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Directory.cs @@ -0,0 +1,11 @@ +using FullTextSearch.Controller.Read.Abstraction; + +namespace FullTextSearch.Controller.Read; + +public class Directory : IDirectory +{ + public List GetFiles(string dirPath) + { + return System.IO.Directory.GetFiles(dirPath).ToList(); + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Path.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Path.cs new file mode 100644 index 0000000..97620be --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/Path.cs @@ -0,0 +1,11 @@ +using FullTextSearch.Controller.Read.Abstraction; + +namespace FullTextSearch.Controller.Read; + +public class Path : IPath +{ + public string GetFileName(string path) + { + return System.IO.Path.GetFileName(path); + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/TextFileReader.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/TextFileReader.cs new file mode 100644 index 0000000..24d1d5e --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/Read/TextFileReader.cs @@ -0,0 +1,11 @@ +using InvertedIndex.Abstraction.Read; + +namespace InvertedIndex.Controller.Read; + +public class TextFileReader : IFileReader +{ + public string ReadAsync(string path) + { + return File.ReadAllText(path); + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IFilter.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IFilter.cs new file mode 100644 index 0000000..737d98f --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IFilter.cs @@ -0,0 +1,7 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController.Abstraction; + +public interface IFilter { + IEnumerable Filter(IEnumerable documents, Dictionary> documentsBySign); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IFilterDriver.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IFilterDriver.cs new file mode 100644 index 0000000..ced7f7f --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IFilterDriver.cs @@ -0,0 +1,8 @@ +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController; + +public interface IFilterDriver { + void DriveFilterer(IEnumerable filterers, Result result); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IMinusFilter.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IMinusFilter.cs new file mode 100644 index 0000000..bd97d26 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IMinusFilter.cs @@ -0,0 +1,10 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController.Abstraction; + +public interface IMinusFilter +{ + IEnumerable Filter(IEnumerable documents, + Dictionary> documentsBySign); + +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/INoSignedFilter.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/INoSignedFilter.cs new file mode 100644 index 0000000..38f13ff --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/INoSignedFilter.cs @@ -0,0 +1,10 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController.Abstraction; + +public interface INoSignedFilter +{ + IEnumerable Filter(IEnumerable documents, + Dictionary> documentsBySign); + +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IPlusFilter.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IPlusFilter.cs new file mode 100644 index 0000000..65208a9 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IPlusFilter.cs @@ -0,0 +1,10 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController.Abstraction; + +public interface IPlusFilter +{ + IEnumerable Filter(IEnumerable documents, + Dictionary> documentsBySign); + +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IResultBuilder.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IResultBuilder.cs new file mode 100644 index 0000000..7e02167 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IResultBuilder.cs @@ -0,0 +1,13 @@ +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController.Abstraction; + +public interface IResultBuilder +{ + void BuildDocumentsBySign(IEnumerable searchers, Query query, + Dictionary> invertedIndex); + + void BuildDocuments(IEnumerable filters, Dictionary> invertedIndex); + Result GetResult(); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IResultDirector.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IResultDirector.cs new file mode 100644 index 0000000..4e4a076 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/Abstraction/IResultDirector.cs @@ -0,0 +1,9 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController.Abstraction; + +public interface IResultDirector +{ + void Construct(IResultBuilder resultBuilder, Query query, + Dictionary> invertedIndex); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/FilterDriver.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/FilterDriver.cs new file mode 100644 index 0000000..334853e --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/FilterDriver.cs @@ -0,0 +1,12 @@ +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController; + +public class FilterDriver : IFilterDriver { + public void DriveFilterer(IEnumerable filterers, Result result) + { + filterers.ToList().ForEach(f=> result.documents = f.Filter(result.documents, result.documentsBySign)); + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/MinusFilter.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/MinusFilter.cs new file mode 100644 index 0000000..20ec409 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/MinusFilter.cs @@ -0,0 +1,12 @@ +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController; + +public class MinusFilter : IFilter +{ + public IEnumerable Filter(IEnumerable documents, Dictionary> documentsBySign) + { + return documents.Except(documentsBySign['-']); + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/NoSignedFilter.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/NoSignedFilter.cs new file mode 100644 index 0000000..b58af2e --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/NoSignedFilter.cs @@ -0,0 +1,12 @@ +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController; + +public class NoSignedFilter : IFilter +{ + public IEnumerable Filter(IEnumerable documents, Dictionary> documentsBySign) + { + return documents.Intersect(documentsBySign[' ']); + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/PlusFilter.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/PlusFilter.cs new file mode 100644 index 0000000..7de9f32 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/PlusFilter.cs @@ -0,0 +1,12 @@ +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController; + +public class PlusFilter : IFilter +{ + public IEnumerable Filter(IEnumerable documents, Dictionary> documentsBySign) + { + return documents.Intersect(documentsBySign['+']); + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/ResultBuilder.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/ResultBuilder.cs new file mode 100644 index 0000000..947dcec --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/ResultBuilder.cs @@ -0,0 +1,37 @@ +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Controller.SearchController.Abstraction; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController; + +public class ResultBuilder : IResultBuilder +{ + + private readonly Result _result; + private readonly ISearcherDriver _searcherDriver; + private readonly IFilterDriver _filterDriver; + + public ResultBuilder(IFilterDriver filterDriver, ISearcherDriver searcherDriver) + { + _result = new Result(); + _filterDriver = filterDriver ?? throw new ArgumentNullException(nameof(filterDriver)); + _searcherDriver = searcherDriver ?? throw new ArgumentNullException(nameof(searcherDriver)); + } + + public void BuildDocumentsBySign(IEnumerable searchers, Query query, Dictionary> invertedIndex) + { + _searcherDriver.DriveSearch(searchers, query, _result, invertedIndex); + } + + public void BuildDocuments(IEnumerable filters, Dictionary> invertedIndex) + { + _result.documents = new UniversalSearch().GetUniversal(invertedIndex); + _filterDriver.DriveFilterer(filters, _result); + } + + public Result GetResult() + { + return _result; + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/ResultDirector.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/ResultDirector.cs new file mode 100644 index 0000000..61ea8ca --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/ResultServices/ResultDirector.cs @@ -0,0 +1,14 @@ +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Core; + +namespace FullTextSearch.Controller.ResultController; + +public class ResultDirector : IResultDirector +{ + public void Construct(IResultBuilder resultBuilder, Core.Query query, Dictionary> invertedIndex) + { + resultBuilder.BuildDocumentsBySign(new List(){new MinusSearcher(), new PlusSearcher(), new NoSignedSearcher()}, query, invertedIndex); + resultBuilder.BuildDocuments(new List(){new NoSignedFilter(), new PlusFilter(), new MinusFilter()},invertedIndex); + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/SearchService/Abstraction/ISearchService.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/SearchService/Abstraction/ISearchService.cs new file mode 100644 index 0000000..8a6a56e --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/SearchService/Abstraction/ISearchService.cs @@ -0,0 +1,8 @@ +using FullTextSearch.Core; + +namespace FullTextSearch.Service.SearchService; + +public interface ISearchService +{ + Result Search(string input, Dictionary> invertedIndex); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/SearchService/SearchService.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/SearchService/SearchService.cs new file mode 100644 index 0000000..b282099 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/SearchService/SearchService.cs @@ -0,0 +1,32 @@ +using FullTextSearch.Controller.QueryController; +using FullTextSearch.Controller.QueryController.Abstraction; +using FullTextSearch.Controller.ResultController; +using FullTextSearch.Controller.ResultController.Abstraction; +using FullTextSearch.Controller.SearchController; +using FullTextSearch.Core; + +namespace FullTextSearch.Service.SearchService; + +public class SearchService : ISearchService +{ + private readonly IQueryBuilder _queryBuilder; + private readonly IResultBuilder _resultBuilder; + + + public SearchService(IQueryBuilder queryBuilder, IResultBuilder resultBuilder) + { + _queryBuilder = queryBuilder; + _resultBuilder = resultBuilder; + } + + public Result Search(string input, Dictionary> invertedIndex) + { + new QueryDirector().Construct(input, new List(){'+', '-'}, _queryBuilder); + var query = _queryBuilder.GetQuery(); + + new ResultDirector().Construct(_resultBuilder, query, invertedIndex); + var result = _resultBuilder.GetResult(); + + return result; + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/TextFormatterServices/Abstraction/ITextFormatter.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/TextFormatterServices/Abstraction/ITextFormatter.cs new file mode 100644 index 0000000..f9e2ddf --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/TextFormatterServices/Abstraction/ITextFormatter.cs @@ -0,0 +1,9 @@ +namespace FullTextSearch.Controller.TextFormatter.Abstraction; + +public interface ITextFormatter +{ + List GetQuoteIndices(List words); + List GetIndicesToRemove(List quoteIndices); + List FilterOutIndices(List words, List indicesToRemove); + List ConcatenateQuotedWords(List words, List quoteIndices); +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/TextFormatterServices/TextFormatter.cs b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/TextFormatterServices/TextFormatter.cs new file mode 100644 index 0000000..93f7dd5 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/Services/TextFormatterServices/TextFormatter.cs @@ -0,0 +1,50 @@ +using FullTextSearch.Controller.TextFormatter.Abstraction; + +namespace FullTextSearch.Controller.TextFormatter; + +public class TextFormatter : ITextFormatter +{ + public List GetQuoteIndices(List words) + { + var quoteIndices = new List(); + for (int i = 0; i < words.Count; i++) + if (words[i].StartsWith("\"") || words[i].EndsWith("\"") || (words[i].Length > 1 && words[i][1] == '\"')) + quoteIndices.Add(i); + return quoteIndices; + } + public List GetIndicesToRemove(List quoteIndices) + { + var indicesToRemove = new List(); + for (int i = 0; i < quoteIndices.Count; i += 2) + { + if (i + 1 < quoteIndices.Count) + { + int start = quoteIndices[i]; + int end = quoteIndices[i + 1]; + for (int j = start; j <= end; j++) + { + indicesToRemove.Add(j); + } + } + } + return indicesToRemove; + } + public List FilterOutIndices(List words, List indicesToRemove) + { + return words.Where((word, index) => !indicesToRemove.Contains(index)).ToList(); + } + public List ConcatenateQuotedWords(List words, List quoteIndices) + { + var result = new List(); + for (int i = 0; i < quoteIndices.Count - 1; i += 2) + { + int start = quoteIndices[i]; + int end = quoteIndices[i + 1]; + + var sublist = words.Skip(start).Take(end - start + 1).ToList(); + var concatenated = string.Join(" ", sublist); + result.Add(concatenated); + } + return result; + } +} \ No newline at end of file diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/appsettings.Development.json b/phase06/InvertedIndexAPI/InvertedIndexAPI/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/phase06/InvertedIndexAPI/InvertedIndexAPI/appsettings.json b/phase06/InvertedIndexAPI/InvertedIndexAPI/appsettings.json new file mode 100644 index 0000000..4318281 --- /dev/null +++ b/phase06/InvertedIndexAPI/InvertedIndexAPI/appsettings.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "FilePaths": { + "DataPath": "./Resources/EnglishData" + } +}