├── .gitattributes ├── .gitignore ├── API ├── .gitignore ├── API.sln └── API │ ├── API.csproj │ ├── App_Start │ ├── FilterConfig.cs │ ├── RouteConfig.cs │ ├── UnityConfig.cs │ └── WebApiConfig.cs │ ├── Controllers │ ├── IndexController.cs │ └── QueryController.cs │ ├── Global.asax │ ├── Global.asax.cs │ ├── Models │ ├── Document.cs │ ├── Query.cs │ └── Startup.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Web.config │ ├── favicon.ico │ └── packages.config ├── ClientApp ├── .env ├── .gitignore ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json └── src │ ├── assets │ ├── add.svg │ ├── bg.svg │ ├── bg2.jpg │ ├── close-btn.svg │ ├── coloured │ │ ├── document.svg │ │ ├── excel.svg │ │ ├── html.svg │ │ ├── json.svg │ │ ├── pdf.svg │ │ ├── powerpoint.svg │ │ └── word.svg │ ├── document.svg │ ├── error.svg │ ├── excel.svg │ ├── html.svg │ ├── info-notification.svg │ ├── info.svg │ ├── json.svg │ ├── logo.svg │ ├── logo1.png │ ├── pdf (2).svg │ ├── pdf.svg │ ├── powerpoint.svg │ ├── search.svg │ ├── success.svg │ ├── team-logo.svg │ ├── txt.svg │ ├── undraw_File_searching_re_3evy.svg │ ├── undraw_Web_search_re_efla.svg │ ├── unilag.png │ ├── warning.svg │ ├── word.svg │ └── xls.svg │ ├── App.js │ ├── App.test.js │ ├── components │ ├── Counter.js │ ├── FetchData.js │ ├── Layout.css │ ├── Layout.js │ ├── Loader │ │ ├── loader.css │ │ └── loader.js │ ├── NavMenu.css │ ├── NavMenu.js │ ├── Search │ │ ├── Search.css │ │ └── Search.js │ ├── SearchResult │ │ ├── SearchResult.css │ │ └── SearchResult.js │ └── Toast Notification │ │ ├── Notify.css │ │ └── Notify.js │ ├── index.css │ ├── index.js │ ├── pages │ ├── Home │ │ ├── Home.css │ │ └── Home.js │ ├── Homepage.js │ ├── Results Page │ │ ├── Results.css │ │ └── Results.js │ └── Upload Page │ │ ├── Upload.css │ │ └── Upload.js │ └── registerServiceWorker.js ├── Engine.Test ├── Engine.Test.sln ├── Engine.Test.sln.DotSettings.user └── Engine.Test │ ├── BaseDocumentTest.cs │ ├── ConnectorTest.cs │ ├── DbDocumentTest.cs │ ├── Engine.Test.csproj │ ├── IndexTest.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── TokenTest.cs │ ├── fixtures │ ├── CSC326.docx │ └── fat.txt │ └── packages.config ├── Engine ├── Engine.sln └── Engine │ ├── BaseDocument.cs │ ├── Connector.cs │ ├── DbDocument.cs │ ├── Engine.csproj │ ├── Index.cs │ ├── Indexer.cs │ ├── PositionPointer.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── Querier.cs │ ├── ScoreDocumentNode.cs │ ├── SearchIndex.cs │ ├── Stemmer.cs │ ├── Token.cs │ ├── TokenItem.cs │ ├── TokenPointer.cs │ ├── UML Diagram.cd │ ├── UML.pdf │ ├── Utils.cs │ └── packages.config ├── GUI ├── GUI.sln ├── GUI.sln.DotSettings.user └── Gui │ ├── App.config │ ├── App.xaml │ ├── App.xaml.cs │ ├── Components │ ├── MenuButton.xaml │ ├── SearchButton.xaml │ └── SearchInput.xaml │ ├── Core │ ├── ObservableObject.cs │ ├── RelayCommand.cs │ └── UploadFile.cs │ ├── Gui.csproj │ ├── Images │ ├── Add Button.png │ ├── Home Button.png │ ├── Info Button.png │ ├── bg.jpg │ ├── excel.png │ ├── html.png │ ├── json.png │ ├── logo.png │ ├── pdf.png │ ├── powerpoint.png │ └── word.png │ ├── MVVM │ ├── View │ │ ├── About.xaml │ │ ├── About.xaml.cs │ │ ├── Homepage.xaml │ │ ├── Homepage.xaml.cs │ │ ├── UploadContent.xaml │ │ └── UploadContent.xaml.cs │ └── ViewModel │ │ ├── AboutViewModel.cs │ │ ├── HomepageViewModel.cs │ │ ├── MainViewModel.cs │ │ └── UploadContentViewModel.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings │ ├── SearchResult.cs │ └── packages.config ├── README.md └── package-lock.json /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Build results 5 | [Dd]ebug/ 6 | [Dd]ebugPublic/ 7 | [Rr]elease/ 8 | [Rr]eleases/ 9 | x64/ 10 | x86/ 11 | build/ 12 | bld/ 13 | [Bb]in/ 14 | [Oo]bj/ 15 | 16 | # Visual Studio 2015 cache/options directory 17 | .vs/ 18 | 19 | #Jetbrains Rider ide folder 20 | .idea/ 21 | 22 | # Visual C++ cache files 23 | ipch/ 24 | *.aps 25 | *.ncb 26 | *.opensdf 27 | *.sdf 28 | *.cachefile 29 | 30 | # Visual Studio profiler 31 | *.psess 32 | *.vsp 33 | *.vspx 34 | # NuGet Packages 35 | *.nupkg 36 | # The packages folder can be ignored because of Package Restore 37 | **/packages/* 38 | # except build/, which is used as an MSBuild target. 39 | !**/packages/build/ 40 | 41 | # Visual Studio cache files 42 | # files ending in .cache can be ignored 43 | *.[Cc]ache 44 | # but keep track of directories ending in .cache 45 | !*.[Cc]ache/ 46 | 47 | #Ignore mongo files 48 | *.dll 49 | *.dylib 50 | *.so -------------------------------------------------------------------------------- /API/.gitignore: -------------------------------------------------------------------------------- 1 | packages/ 2 | *.user 3 | .idea/ 4 | bin/ 5 | obj/ 6 | *.bin 7 | *.json -------------------------------------------------------------------------------- /API/API.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API", "API\API.csproj", "{53F18513-374E-4A3E-B215-BF4A057F730C}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Any CPU = Debug|Any CPU 8 | Release|Any CPU = Release|Any CPU 9 | EndGlobalSection 10 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 11 | {53F18513-374E-4A3E-B215-BF4A057F730C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 12 | {53F18513-374E-4A3E-B215-BF4A057F730C}.Debug|Any CPU.Build.0 = Debug|Any CPU 13 | {53F18513-374E-4A3E-B215-BF4A057F730C}.Release|Any CPU.ActiveCfg = Release|Any CPU 14 | {53F18513-374E-4A3E-B215-BF4A057F730C}.Release|Any CPU.Build.0 = Release|Any CPU 15 | EndGlobalSection 16 | EndGlobal 17 | -------------------------------------------------------------------------------- /API/API/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | namespace API 4 | { 5 | public class FilterConfig 6 | { 7 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 8 | { 9 | filters.Add(new HandleErrorAttribute()); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /API/API/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Routing; 7 | 8 | namespace API 9 | { 10 | public class RouteConfig 11 | { 12 | public static void RegisterRoutes(RouteCollection routes) 13 | { 14 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 15 | 16 | routes.MapRoute( 17 | "Default", 18 | "{controller}/{action}/{id}", 19 | new { controller = "Home", action = "Index", id = UrlParameter.Optional } 20 | ); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /API/API/App_Start/UnityConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | using API.Controllers; 3 | using Hangfire; 4 | using Unity; 5 | using Unity.WebApi; 6 | using GlobalConfiguration = System.Web.Http.GlobalConfiguration; 7 | 8 | namespace API 9 | { 10 | public static class UnityConfig 11 | { 12 | public static void RegisterComponents() 13 | { 14 | var container = new UnityContainer(); 15 | 16 | container.RegisterType(); 17 | container.RegisterType(); 18 | 19 | GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /API/API/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Serialization; 2 | 3 | using System.Web.Http; 4 | using System.Web.Http.Cors; 5 | 6 | namespace API 7 | { 8 | public static class WebApiConfig 9 | { 10 | public static void Register(HttpConfiguration config) 11 | { 12 | // Web API configuration and services 13 | var cors = new EnableCorsAttribute("*", "*", "*"); 14 | config.EnableCors(cors); 15 | 16 | // Web API routes 17 | config.MapHttpAttributeRoutes(); 18 | 19 | // Set JSON formatter as default one and remove XmlFormatter 20 | var jsonFormatter = config.Formatters.JsonFormatter; 21 | jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 22 | config.Formatters.Remove(config.Formatters.XmlFormatter); 23 | jsonFormatter.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /API/API/Controllers/IndexController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Net; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | using System.Web; 7 | using System.Web.Http; 8 | using System.Web.Http.ModelBinding; 9 | using System.Web.Http.Results; 10 | using System.Web.Http.ValueProviders; 11 | using API.Models; 12 | using Hangfire; 13 | 14 | namespace API.Controllers 15 | { 16 | public class IndexController : ApiController { 17 | private readonly BackgroundJobClient _client = new BackgroundJobClient(); 18 | 19 | // GET: api/ 20 | [Route("api/")] 21 | [HttpGet] 22 | public IHttpActionResult Root() 23 | { 24 | return Ok("https://www.youtube.com/watch?v=rEq1Z0bjdwc"); 25 | } 26 | 27 | // POST: api/index/ 28 | [Route("api/index/")] 29 | [HttpPost] 30 | public IHttpActionResult Index(Document[] data) 31 | { 32 | if (ModelState.IsValid) 33 | { 34 | foreach (var document in data) { 35 | _client.Enqueue(() => Engine.DbDocument.IndexDocument(document.Name, document.Url)); 36 | } 37 | 38 | return new ResponseMessageResult(Request.CreateResponse(HttpStatusCode.Accepted, "document queued for indexing")); 39 | } 40 | 41 | return BadRequest(); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /API/API/Controllers/QueryController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Net; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | using System.Web; 7 | using System.Web.Http; 8 | using System.Web.Http.ModelBinding; 9 | using System.Web.Http.Results; 10 | using System.Web.Http.ValueProviders; 11 | using API.Models; 12 | using Engine; 13 | using Hangfire; 14 | using Newtonsoft.Json; 15 | 16 | namespace API.Controllers { 17 | public class QueryController : ApiController { 18 | // POST: api/query 19 | [Route("api/query")] 20 | [HttpGet] 21 | public async Task Root(string query) { 22 | var querier = new Querier(); 23 | BaseDocument [] documents = await querier.Search(query); 24 | JsonConvert.SerializeObject(documents); 25 | return Ok(documents); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /API/API/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="API.WebApiApplication" Language="C#" %> -------------------------------------------------------------------------------- /API/API/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | using System.Web.Mvc; 3 | using System.Web.Routing; 4 | 5 | namespace API 6 | { 7 | public class WebApiApplication : System.Web.HttpApplication 8 | { 9 | protected void Application_Start() 10 | { 11 | AreaRegistration.RegisterAllAreas(); 12 | UnityConfig.RegisterComponents(); 13 | GlobalConfiguration.Configure(WebApiConfig.Register); 14 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 15 | RouteConfig.RegisterRoutes(RouteTable.Routes); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /API/API/Models/Document.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace API.Models 4 | { 5 | public class Document 6 | { 7 | [JsonRequired] 8 | [JsonProperty("url")] 9 | public string Url { get; set; } 10 | 11 | [JsonRequired] 12 | [JsonProperty("name")] 13 | public string Name { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /API/API/Models/Query.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace API.Models { 4 | public class Query { 5 | [JsonRequired] 6 | [JsonProperty("query")] 7 | public string QueryString { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /API/API/Models/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Owin; 4 | using Engine; 5 | using Hangfire; 6 | using Hangfire.MemoryStorage; 7 | 8 | namespace API 9 | { 10 | public class Startup 11 | { 12 | public void Configuration(IAppBuilder app) 13 | { 14 | Connector.GenerateDb(); 15 | Console.WriteLine("db initialized"); 16 | 17 | GlobalConfiguration.Configuration.UseMemoryStorage(); 18 | 19 | app.UseHangfireDashboard(); 20 | app.UseHangfireServer(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /API/API/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("IndexerService")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("IndexerService")] 13 | [assembly: AssemblyCopyright("Copyright © 2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("53F18513-374E-4A3E-B215-BF4A057F730C")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /API/API/Web.Debug.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /API/API/Web.Release.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /API/API/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /API/API/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/API/API/favicon.ico -------------------------------------------------------------------------------- /ClientApp/.env: -------------------------------------------------------------------------------- 1 | BROWSER=none -------------------------------------------------------------------------------- /ClientApp/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /ClientApp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Search_Engine", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "bootstrap": "^3.4.1", 7 | "react": "^16.0.0", 8 | "react-bootstrap": "^0.31.5", 9 | "react-dom": "^16.0.0", 10 | "react-router-bootstrap": "^0.24.4", 11 | "react-router-dom": "^4.2.2", 12 | "react-scripts": "1.0.17", 13 | "rimraf": "^2.6.2" 14 | }, 15 | "scripts": { 16 | "start": "rimraf ./build && react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test --env=jsdom", 19 | "eject": "react-scripts eject" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ClientApp/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/ClientApp/public/favicon.ico -------------------------------------------------------------------------------- /ClientApp/public/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 23 | 404Engine 24 | 25 | 26 | 29 |
30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ClientApp/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Search_Engine", 3 | "name": "Search_Engine", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/bg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/ClientApp/src/ assets/bg2.jpg -------------------------------------------------------------------------------- /ClientApp/src/ assets/close-btn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/coloured/document.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/coloured/excel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/coloured/html.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/coloured/json.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/coloured/powerpoint.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/coloured/word.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/document.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/excel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/html.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/info-notification.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/json.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/ClientApp/src/ assets/logo1.png -------------------------------------------------------------------------------- /ClientApp/src/ assets/pdf (2).svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/pdf.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/powerpoint.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/success.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/txt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/undraw_Web_search_re_efla.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/unilag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/ClientApp/src/ assets/unilag.png -------------------------------------------------------------------------------- /ClientApp/src/ assets/warning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/word.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ClientApp/src/ assets/xls.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ClientApp/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import { Route } from 'react-router'; 4 | import { Layout } from './components/Layout'; 5 | import { Home } from './pages/Home/Home'; 6 | import { ResultPage } from './pages/Results Page/Results'; 7 | import { UploadPage } from './pages/Upload Page/Upload'; 8 | 9 | export default class App extends Component { 10 | displayName = App.name 11 | 12 | render() { 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ClientApp/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /ClientApp/src/components/Counter.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | export class Counter extends Component { 4 | displayName = Counter.name 5 | 6 | constructor(props) { 7 | super(props); 8 | this.state = { currentCount: 0 }; 9 | this.incrementCounter = this.incrementCounter.bind(this); 10 | } 11 | 12 | incrementCounter() { 13 | this.setState({ 14 | currentCount: this.state.currentCount + 1 15 | }); 16 | } 17 | 18 | render() { 19 | return ( 20 |
21 |

Counter

22 | 23 |

This is a simple example of a React component.

24 | 25 |

Current count: {this.state.currentCount}

26 | 27 | 28 |
29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ClientApp/src/components/FetchData.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | export class FetchData extends Component { 4 | displayName = FetchData.name 5 | 6 | constructor(props) { 7 | super(props); 8 | this.state = { forecasts: [], loading: true }; 9 | 10 | fetch('api/SampleData/WeatherForecasts') 11 | .then(response => response.json()) 12 | .then(data => { 13 | this.setState({ forecasts: data, loading: false }); 14 | }); 15 | } 16 | 17 | static renderForecastsTable(forecasts) { 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {forecasts.map(forecast => 30 | 31 | 32 | 33 | 34 | 35 | 36 | )} 37 | 38 |
DateTemp. (C)Temp. (F)Summary
{forecast.dateFormatted}{forecast.temperatureC}{forecast.temperatureF}{forecast.summary}
39 | ); 40 | } 41 | 42 | render() { 43 | let contents = this.state.loading 44 | ?

Loading...

45 | : FetchData.renderForecastsTable(this.state.forecasts); 46 | 47 | return ( 48 |
49 |

Weather forecast

50 |

This component demonstrates fetching data from the server.

51 | {contents} 52 |
53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ClientApp/src/components/Layout.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Mulish:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,200&display=swap'); 2 | 3 | body, 4 | html, 5 | * { 6 | margin: 0; 7 | padding: 0; 8 | box-sizing: border-box; 9 | font-family: 'Mulish', sans-serif; 10 | } 11 | 12 | 13 | .layout { 14 | height: 100%; 15 | background: linear-gradient(0deg, rgba(0, 0, 0, 0.35), rgba(0, 0, 0, 0.35)), url("../ assets/bg2.jpg"); 16 | background-attachment: fixed; 17 | background-size: cover; 18 | } 19 | 20 | 21 | 22 | footer{ 23 | display: flex; 24 | flex-direction: column; 25 | align-items: center; 26 | } 27 | 28 | footer > p { 29 | text-align: center; 30 | color: rgba(0, 0, 0, 0.4); 31 | } 32 | 33 | footer .links{ 34 | margin: 15px 0; 35 | } 36 | 37 | .github-link, 38 | .doc-link{ 39 | color: #000; 40 | font-size: 16px; 41 | text-decoration: none; 42 | margin: 0 2px; 43 | padding: 0 5px; 44 | } 45 | 46 | .github-link { 47 | border-left: 1.5px solid #000; 48 | } -------------------------------------------------------------------------------- /ClientApp/src/components/Layout.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Col, Grid, Row } from 'react-bootstrap'; 3 | import "./Layout.css"; 4 | 5 | export class Layout extends Component { 6 | displayName = Layout.name 7 | 8 | render() { 9 | 10 | 11 | return ( 12 |
13 | 14 | 15 | 16 | {this.props.children} 17 | 18 | 19 | 26 | 27 |
28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ClientApp/src/components/Loader/loader.css: -------------------------------------------------------------------------------- 1 | .loading-screen { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | justify-content: center; 6 | flex: 1; 7 | width: 100%; 8 | height: 95vh; 9 | } 10 | 11 | .lds-facebook { 12 | display: inline-block; 13 | position: relative; 14 | width: 80px; 15 | height: 120px; 16 | } 17 | 18 | .lds-facebook div { 19 | display: inline-block; 20 | position: absolute; 21 | left: 8px; 22 | width: 16px; 23 | background: #003CB0; 24 | animation: lds-facebook 1.2s cubic-bezier(0, 0.5, 0.5, 1) infinite; 25 | } 26 | 27 | .lds-facebook div:nth-child(1) { 28 | left: 8px; 29 | animation-delay: -0.24s; 30 | } 31 | 32 | .lds-facebook div:nth-child(2) { 33 | left: 32px; 34 | animation-delay: -0.12s; 35 | } 36 | 37 | .lds-facebook div:nth-child(3) { 38 | left: 56px; 39 | animation-delay: 0; 40 | } 41 | 42 | .loading-text{ 43 | text-align: center; 44 | } 45 | 46 | @keyframes lds-facebook { 47 | 0% { 48 | top: 8px; 49 | height: 64px; 50 | } 51 | 52 | 50%, 100% { 53 | top: 24px; 54 | height: 32px; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ClientApp/src/components/Loader/loader.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import "./loader.css" 3 | 4 | const Loader = () => { 5 | return ( 6 |
7 |
8 |
9 |
10 |

Fetching data

11 |
12 | ) 13 | } 14 | 15 | export default Loader; -------------------------------------------------------------------------------- /ClientApp/src/components/NavMenu.css: -------------------------------------------------------------------------------- 1 | .navbar li .glyphicon { 2 | margin-right: 10px; 3 | } 4 | 5 | /* Highlighting rules for nav menu items */ 6 | .navbar .navbar-nav .active a, 7 | .navbar .navbar-nav .active a:hover, 8 | .navbar .navbar-nav .active a:focus { 9 | background-image: none; 10 | background-color: #4189C7; 11 | color: white; 12 | } 13 | 14 | @media (min-width: 768px) { 15 | /* On large screens, convert the nav menu to a vertical sidebar */ 16 | .navbar { 17 | height: 100%; 18 | width: calc(25% - 20px); 19 | } 20 | .navbar { 21 | border-radius: 0; 22 | border-width: 0; 23 | height: 100%; 24 | } 25 | .navbar-header { 26 | float: none; 27 | } 28 | .navbar .navbar-collapse { 29 | border-top: 1px solid #444; 30 | padding: 0; 31 | } 32 | .navbar .container-fluid { 33 | padding: 0; 34 | margin: 0; 35 | } 36 | .navbar .container-fluid .navbar-brand { 37 | margin: 0; 38 | } 39 | .navbar ul { 40 | float: none; 41 | } 42 | .navbar li { 43 | float: none; 44 | font-size: 15px; 45 | margin: 6px; 46 | } 47 | .navbar li a { 48 | padding: 10px 16px; 49 | border-radius: 4px; 50 | } 51 | .navbar a { 52 | /* If a menu item's text is too long, truncate it */ 53 | width: 100%; 54 | white-space: nowrap; 55 | overflow: hidden; 56 | text-overflow: ellipsis; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ClientApp/src/components/NavMenu.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { Glyphicon, Nav, Navbar, NavItem } from 'react-bootstrap'; 4 | import { LinkContainer } from 'react-router-bootstrap'; 5 | import './NavMenu.css'; 6 | 7 | export class NavMenu extends Component { 8 | displayName = NavMenu.name 9 | 10 | render() { 11 | return ( 12 | 13 | 14 | 15 | Search_Engine 16 | 17 | 18 | 19 | 20 | 37 | 38 | 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ClientApp/src/components/Search/Search.css: -------------------------------------------------------------------------------- 1 | .search-component { 2 | display: flex; 3 | flex-direction: column; 4 | position: relative; 5 | max-width: 570px; 6 | } 7 | 8 | .search-form { 9 | display: flex; 10 | flex-direction: row; 11 | justify-content: space-between; 12 | border: 2px solid #003CB0; 13 | border-radius: 30px; 14 | padding: 7px 10px; 15 | background: rgba(255, 255, 255, 0.8); 16 | transition: 0.3s; 17 | } 18 | 19 | 20 | .search-input { 21 | border: none; 22 | padding: 2px; 23 | width: 100%; 24 | font-size: 18px; 25 | background: none; 26 | } 27 | 28 | .search-input:focus { 29 | outline: 0.1px dotted #808080; 30 | } 31 | 32 | 33 | 34 | .search-submit-btn { 35 | background: none; 36 | border: none; 37 | border-left: 1px solid #003CB0; 38 | padding: 6px 5px; 39 | min-width: 40px; 40 | } 41 | 42 | .search-suggestions { 43 | display: none; 44 | flex-direction: column; 45 | position: absolute; 46 | box-shadow: 0 6px 12px rgba(0,0,0,0.2); 47 | width: 82%; 48 | background: rgba(255, 255, 255, 0.8); 49 | top: 60px; 50 | left: 22px; 51 | align-self:flex-start; 52 | padding: 10px 5px; 53 | } 54 | 55 | .search-suggestion{ 56 | line-height: 30px; 57 | } -------------------------------------------------------------------------------- /ClientApp/src/components/Search/Search.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import "./Search.css"; 4 | 5 | export class Search extends Component { 6 | displayName = Search.name 7 | 8 | render() { 9 | return ( 10 |
11 |
12 | 13 | 14 |
15 |
16 | Why is Charles GOATED? 17 | Why is Nifemi the great dev alive? 18 | Lorem ipsum 19 |
20 |
21 | ) 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /ClientApp/src/components/SearchResult/SearchResult.css: -------------------------------------------------------------------------------- 1 | .search-result { 2 | display: flex; 3 | flex-direction: row; 4 | align-content: center; 5 | justify-content: flex-start; 6 | margin: 14px 0; 7 | padding: 8px; 8 | } 9 | 10 | .search-result:hover .search-result-img { 11 | opacity: 1; 12 | } 13 | 14 | .search-result-img { 15 | max-width: 50px; 16 | margin: 0 0 0 10px; 17 | opacity: 0.4; 18 | transition: 0.2s; 19 | } 20 | 21 | .search-result-img > img{ 22 | width: 100%; 23 | } 24 | 25 | .search-result-info{ 26 | display:flex; 27 | flex-direction: row; 28 | justify-content:space-between; 29 | width:100%; 30 | margin: 0 2px; 31 | } 32 | 33 | .search-result-info .title{ 34 | font-size: 19px; 35 | font-weight: 600; 36 | cursor:pointer; 37 | } 38 | 39 | .search-result-info .description { 40 | color: rgba(0, 0, 0, 0.4); 41 | margin-left: 4px; 42 | 43 | } 44 | 45 | .search-result-info .date-added { 46 | font-size: 14px; 47 | color: rgba(0, 0, 0, 0.4); 48 | align-self: flex-start; 49 | } -------------------------------------------------------------------------------- /ClientApp/src/components/SearchResult/SearchResult.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import "./SearchResult.css" 3 | 4 | 5 | export class SearchResult extends Component { 6 | displayName = SearchResult.name 7 | 8 | constructor(props) { 9 | super(props); 10 | this.state = { docIcon: doc-type, type: props.type } 11 | } 12 | 13 | componentDidMount() { 14 | switch (this.state.type) { 15 | case "pdf": 16 | this.setState({ docIcon: doc-type }); 17 | break; 18 | case "html": 19 | this.setState({ docIcon: doc-type }); 20 | break; 21 | case "ppt": 22 | this.setState({ docIcon: doc-type }); 23 | break; 24 | case "txt": 25 | this.setState({ docIcon: doc-type }); 26 | break; 27 | case "json": 28 | this.setState({ docIcon: doc-type }); 29 | break; 30 | case "xls": 31 | this.setState({ docIcon: doc-type }); 32 | break; 33 | case "word": 34 | this.setState({ docIcon: doc-type }); 35 | break; 36 | default: 37 | this.setState({ docIcon: doc-type }); 38 | } 39 | } 40 | 41 | render() { 42 | return ( 43 |
44 |
45 | { this.state.docIcon} 46 |
47 |
48 |
49 | Title 50 |

Desc

51 |
52 |

17/08/2021

53 |
54 |
55 | ) 56 | } 57 | } -------------------------------------------------------------------------------- /ClientApp/src/components/Toast Notification/Notify.css: -------------------------------------------------------------------------------- 1 | .notify-container { 2 | display: none; 3 | flex-direction: row; 4 | align-items: center; 5 | position: fixed; 6 | background: #ffffff; 7 | right: 5px; 8 | box-shadow: 24px 24px 54px 0 rgba(25,25,25, 0.2); 9 | border-radius: 4px; 10 | padding: 4px 6px; 11 | transition: 0.3s; 12 | } 13 | 14 | .show-notify{ 15 | display:flex; 16 | } 17 | 18 | .hide-notify{ 19 | display: none; 20 | } 21 | 22 | .notify-container .icon { 23 | max-width: 45px; 24 | } 25 | 26 | .notify-container .icon img{ 27 | width:100%; 28 | } 29 | 30 | .message { 31 | margin: 0 20px; 32 | } 33 | 34 | .notify-container button{ 35 | background:none; 36 | border:none; 37 | align-self: flex-start; 38 | max-width:19px; 39 | } 40 | 41 | .notify-container button img{ 42 | max-width: 100%; 43 | } -------------------------------------------------------------------------------- /ClientApp/src/components/Toast Notification/Notify.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import "./Notify.css"; 3 | 4 | export class Notify extends Component { 5 | displayName = Notify.name 6 | 7 | constructor(props) { 8 | super(props); 9 | this.state = { 10 | notificationIcon: notify-type, 11 | type: props.type, 12 | message: props.message, 13 | showNotification: true 14 | } 15 | } 16 | 17 | componentDidMount() { 18 | switch (this.state.type) { 19 | case "success": 20 | this.setState({ notificationIcon: notify-type }); 21 | break; 22 | case "error": 23 | this.setState({ notificationIcon: notify-type }); 24 | break; 25 | case "warning": 26 | this.setState({ notificationIcon: notify-type }); 27 | break; 28 | default: 29 | this.setState({ notificationIcon: notify-type }); 30 | break; 31 | } 32 | 33 | 34 | } 35 | 36 | componentDidUpdate() { 37 | if (this.state.showNotification === true) { 38 | setTimeout(() => { 39 | this.setState({ showNotification: false }) 40 | }, 4000) 41 | } 42 | } 43 | 44 | componentWillReceiveProps() { 45 | if (this.state.message !== "" && this.state.type !== "") { 46 | this.setState({ showNotification: true }) 47 | } else { 48 | this.setState({ showNotification: false }) 49 | } 50 | } 51 | 52 | render() { 53 | return ( 54 |
55 |
56 | {this.state.notificationIcon} 57 |
58 |
59 | {this.state.message} 60 |
61 | 64 |
65 | ) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /ClientApp/src/index.css: -------------------------------------------------------------------------------- 1 | @media (max-width: 767px) { 2 | /* On small screens, the nav menu spans the full width of the screen. Leave a space for it. */ 3 | body { 4 | padding-top: 50px; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ClientApp/src/index.js: -------------------------------------------------------------------------------- 1 | import 'bootstrap/dist/css/bootstrap.css'; 2 | import 'bootstrap/dist/css/bootstrap-theme.css'; 3 | import './index.css'; 4 | import React from 'react'; 5 | import ReactDOM from 'react-dom'; 6 | import { BrowserRouter } from 'react-router-dom'; 7 | import App from './App'; 8 | import registerServiceWorker from './registerServiceWorker'; 9 | 10 | const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href'); 11 | const rootElement = document.getElementById('root'); 12 | 13 | ReactDOM.render( 14 | 15 | 16 | , 17 | rootElement); 18 | 19 | registerServiceWorker(); 20 | -------------------------------------------------------------------------------- /ClientApp/src/pages/Home/Home.css: -------------------------------------------------------------------------------- 1 | .landing-page { 2 | padding: 20px; 3 | width: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | align-items:center; 7 | } 8 | 9 | .home-navbar{ 10 | display:flex; 11 | flex-direction:row; 12 | align-items:flex-end; 13 | justify-content:flex-end; 14 | width: 100%; 15 | } 16 | 17 | .navbar-option { 18 | max-width: 25px; 19 | margin: 0 15px; 20 | transition: 0.3s; 21 | } 22 | 23 | .navbar-option img{ 24 | width:100%; 25 | transition: 0.3s; 26 | opacity: 0.5; 27 | } 28 | 29 | .navbar-option img:hover { 30 | opacity: 1; 31 | } 32 | 33 | .page-header { 34 | display: flex; 35 | flex-direction: column; 36 | margin-top: 100px; 37 | margin-bottom: 50px; 38 | padding: 20px; 39 | border-bottom: none; 40 | } 41 | 42 | .logo{ 43 | max-width: 600px; 44 | } 45 | 46 | .logo-homepage { 47 | max-width: unset; 48 | width: 100%; 49 | } 50 | 51 | .page-subtitle { 52 | margin: 20px 30px 30px 30px; 53 | text-align: center; 54 | color: rgba(0, 0, 0, 0.7); 55 | font-size: 20px; 56 | } 57 | 58 | .supported-content { 59 | display:flex; 60 | flex-direction: column; 61 | align-items:center; 62 | border-top: 1px solid #eeeeee; 63 | } 64 | 65 | .supported-content > h3{ 66 | font-size: 18px; 67 | color: #000; 68 | } 69 | 70 | .supported-items{ 71 | display: flex; 72 | flex-direction:row; 73 | } 74 | 75 | .supported-item{ 76 | margin: 10px; 77 | padding:5px; 78 | max-width: 40px; 79 | } 80 | 81 | .supported-item > img{ 82 | width: 100%; 83 | } 84 | 85 | .supported-content p{ 86 | font-size: 18px; 87 | } -------------------------------------------------------------------------------- /ClientApp/src/pages/Home/Home.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from "react-router-dom" 3 | import './Home.css'; 4 | import { Search } from '../../components/Search/Search'; 5 | 6 | export class Home extends Component { 7 | displayName = Home.name 8 | 9 | render() { 10 | return ( 11 |
12 |
13 | 14 | Add document 15 | 16 | 17 | Project description 18 | 19 |
20 |
21 |
22 | Team logo 23 |
24 |

A custom search engine built with React & C#

25 | 26 |
27 |
28 |

This search engine allows you search for

29 |
30 |
31 | excel 32 |
33 |
34 | txt 35 |
36 |
37 | powerpoint 38 |
39 |
40 | word 41 |
42 |
43 | json 44 |
45 |
46 |

and more

47 |
48 |
49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ClientApp/src/pages/Homepage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Homepage = () => { 4 | return ( 5 |
6 | Home 7 |
8 | ) 9 | } 10 | 11 | export default Homepage; -------------------------------------------------------------------------------- /ClientApp/src/pages/Results Page/Results.css: -------------------------------------------------------------------------------- 1 | 2 | .results-page-header { 3 | display: flex; 4 | flex-direction: column; 5 | border-bottom: 1.5px solid #eeeeee; 6 | } 7 | 8 | .top-nav{ 9 | display: flex; 10 | flex-direction:row; 11 | justify-content: space-between; 12 | margin-top: 20px; 13 | } 14 | 15 | .logo { 16 | max-width: 220px; 17 | margin: 10px; 18 | align-self: flex-start; 19 | } 20 | 21 | .logo img{ 22 | width: 100%; 23 | } 24 | 25 | .result-info{ 26 | align-self:flex-start; 27 | margin: 10px 28 | } 29 | 30 | .result-info > h2{ 31 | font-size: 35px; 32 | } 33 | 34 | .matches-found, .response-time { 35 | color: rgba(0, 0, 0, 0.5); 36 | } 37 | 38 | .results-body { 39 | 40 | background-color: rgba(255, 255, 255, 0.4); 41 | padding: 0 5px; 42 | border-radius: 8px; 43 | } -------------------------------------------------------------------------------- /ClientApp/src/pages/Results Page/Results.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import { Link } from "react-router-dom"; 3 | import Loader from "../../components/Loader/loader" 4 | import './Results.css' 5 | import { SearchResult } from "../../components/SearchResult/SearchResult"; 6 | import { Search } from "../../components/Search/Search"; 7 | 8 | export class ResultPage extends Component { 9 | displayName = ResultPage.name 10 | 11 | constructor(props) { 12 | super(props); 13 | this.state = { loadingState: false }; 14 | } 15 | 16 | render() { 17 | return ( 18 | < div className="results-page" > 19 | { 20 | this.state.loadingState === true ? : 21 |
22 |
23 |
24 | 25 | Team logo 26 | 27 | 28 |
29 | 30 |
31 |

Search results for ""

32 |
20 match(es) found
33 |

Response time:

34 |
35 |
36 |
37 | 38 | 39 | 40 | 41 | 42 |
43 |
44 | } 45 | 46 | ) 47 | } 48 | } 49 | 50 | export default ResultPage; -------------------------------------------------------------------------------- /ClientApp/src/pages/Upload Page/Upload.css: -------------------------------------------------------------------------------- 1 | .upload-page { 2 | display: flex; 3 | flex-direction: column; 4 | min-height: 100vh; 5 | background: rgba(0,0,0,0.18); 6 | padding: 8px; 7 | } 8 | 9 | .upload-page-header{ 10 | display: flex; 11 | flex-direction: column; 12 | } 13 | 14 | .upload-page-header logo{ 15 | max-width: 200px; 16 | } 17 | 18 | .upload-page-header .upload-page-info{ 19 | margin: 10px 0; 20 | } 21 | 22 | .upload-page-header .upload-page-info .title{ 23 | font-size: 38px 24 | } 25 | 26 | .upload-page-main { 27 | display: flex; 28 | flex-direction: column; 29 | margin: 15px 0; 30 | max-width: 800px; 31 | } 32 | 33 | .upload-page-main form{ 34 | padding: 5px; 35 | margin-bottom: 30px; 36 | } 37 | 38 | .upload-form-input{ 39 | display: flex; 40 | flex-direction: column; 41 | margin: 25px 5px; 42 | width: 100%; 43 | } 44 | 45 | .upload-form-input #document-title, 46 | .upload-form-input #document-desc { 47 | padding: 8px 4px; 48 | background: #f1f1f1; 49 | border: 1px solid #003CB0; 50 | } 51 | 52 | .upload-form-input #document-title:focus, 53 | .upload-form-input #document-desc:focus { 54 | outline: 2px solid #003CB0; 55 | } 56 | 57 | .upload-form-input #document-desc { 58 | resize: none; 59 | } 60 | 61 | .upload-page-main form .submit-btn { 62 | padding: 10px 25px; 63 | background: #fff; 64 | border: 2px solid #003CB0; 65 | color: #003CB0; 66 | font-weight: 500; 67 | font-size: 18px; 68 | transition: 0.3s; 69 | margin: 10px 0; 70 | border-radius: 5px; 71 | } 72 | 73 | .upload-page-main form .submit-btn:hover { 74 | background: #003CB0; 75 | border: 2px solid #fff; 76 | color: #fff; 77 | } 78 | 79 | .notice { 80 | margin-top: 30px; 81 | font-style: italic; 82 | font-weight: 600; 83 | color: #fff; 84 | } 85 | 86 | -------------------------------------------------------------------------------- /ClientApp/src/pages/Upload Page/Upload.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import { Link } from "react-router-dom" 3 | import { Notify } from "../../components/Toast Notification/Notify"; 4 | import "./Upload.css" 5 | 6 | export class UploadPage extends Component { 7 | displayName = UploadPage.name 8 | 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | loadingState: false, 13 | title: "", 14 | description: "", 15 | url: "", 16 | document: "", 17 | notificationType: "", 18 | notificationMsg:"" 19 | }; 20 | } 21 | 22 | 23 | handleInputChange = (e) => { 24 | const { name, value } = e.target; 25 | this.setState({ 26 | [name]: value 27 | }) 28 | } 29 | 30 | handleFileUpload = () => { 31 | const uploadLink = "https://api.cloudinary.com/v1_1/dpgdjfckl/upload"; 32 | const data = new FormData(); 33 | data.append("file", this.state.document) 34 | data.append("upload_preset", "search-engine") 35 | 36 | 37 | fetch(uploadLink, { 38 | method: "POST", 39 | body: data 40 | }) 41 | .then(response => { 42 | if (response.status === 200) { 43 | response.json() 44 | } else { 45 | throw new Error("Something went wrong"); 46 | } 47 | 48 | }) 49 | .then(data => this.setState({ url: data.url })) 50 | .catch(error => console.log(error.message)) 51 | } 52 | 53 | handleSubmit = () => { 54 | this.handleFileUpload(); 55 | 56 | if (this.state.url !== "") { 57 | const formData = { name: this.state.title, url: this.state.url, description: this.state.description }; 58 | 59 | } 60 | } 61 | 62 | render() { 63 | 64 | return ( 65 |
66 |
67 | 68 | team logo 69 | 70 |
71 |

Upload content

72 |

Fill the form with the appropriate information required

73 |
74 |
75 |
76 |
77 |
78 | 79 | 80 |
81 |
82 | 83 | 84 |
85 |
86 | 87 | 88 |
89 | 90 |
91 |

92 | N.B: This information will be available on the search engine about 2 hours after upload 93 |

94 |
95 | < Notify type={this.state.notificationType} message={this.state.notificationMsg} /> 96 |
97 | ) 98 | } 99 | 100 | } 101 | 102 | -------------------------------------------------------------------------------- /ClientApp/src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | } else { 39 | // Is not local host. Just register service worker 40 | registerValidSW(swUrl); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | function registerValidSW(swUrl) { 47 | navigator.serviceWorker 48 | .register(swUrl) 49 | .then(registration => { 50 | registration.onupdatefound = () => { 51 | const installingWorker = registration.installing; 52 | installingWorker.onstatechange = () => { 53 | if (installingWorker.state === 'installed') { 54 | if (navigator.serviceWorker.controller) { 55 | // At this point, the old content will have been purged and 56 | // the fresh content will have been added to the cache. 57 | // It's the perfect time to display a "New content is 58 | // available; please refresh." message in your web app. 59 | console.log('New content is available; please refresh.'); 60 | } else { 61 | // At this point, everything has been precached. 62 | // It's the perfect time to display a 63 | // "Content is cached for offline use." message. 64 | console.log('Content is cached for offline use.'); 65 | } 66 | } 67 | }; 68 | }; 69 | }) 70 | .catch(error => { 71 | console.error('Error during service worker registration:', error); 72 | }); 73 | } 74 | 75 | function checkValidServiceWorker(swUrl) { 76 | // Check if the service worker can be found. If it can't reload the page. 77 | fetch(swUrl) 78 | .then(response => { 79 | // Ensure service worker exists, and that we really are getting a JS file. 80 | if ( 81 | response.status === 404 || 82 | response.headers.get('content-type').indexOf('javascript') === -1 83 | ) { 84 | // No service worker found. Probably a different app. Reload the page. 85 | navigator.serviceWorker.ready.then(registration => { 86 | registration.unregister().then(() => { 87 | window.location.reload(); 88 | }); 89 | }); 90 | } else { 91 | // Service worker found. Proceed as normal. 92 | registerValidSW(swUrl); 93 | } 94 | }) 95 | .catch(() => { 96 | console.log( 97 | 'No internet connection found. App is running in offline mode.' 98 | ); 99 | }); 100 | } 101 | 102 | export function unregister() { 103 | if ('serviceWorker' in navigator) { 104 | navigator.serviceWorker.ready.then(registration => { 105 | registration.unregister(); 106 | }); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Engine.Test/Engine.Test.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Test", "Engine.Test\Engine.Test.csproj", "{B79759F3-2D38-4F20-A539-BBBEB519B993}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Any CPU = Debug|Any CPU 8 | Release|Any CPU = Release|Any CPU 9 | EndGlobalSection 10 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 11 | {B79759F3-2D38-4F20-A539-BBBEB519B993}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 12 | {B79759F3-2D38-4F20-A539-BBBEB519B993}.Debug|Any CPU.Build.0 = Debug|Any CPU 13 | {B79759F3-2D38-4F20-A539-BBBEB519B993}.Release|Any CPU.ActiveCfg = Release|Any CPU 14 | {B79759F3-2D38-4F20-A539-BBBEB519B993}.Release|Any CPU.Build.0 = Release|Any CPU 15 | EndGlobalSection 16 | EndGlobal 17 | -------------------------------------------------------------------------------- /Engine.Test/Engine.Test.sln.DotSettings.user: -------------------------------------------------------------------------------- 1 |  2 | True 3 | <SessionState ContinuousTestingMode="0" Name="BaseDocumentTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> 4 | <TestAncestor> 5 | <TestId>xUnit::B79759F3-2D38-4F20-A539-BBBEB519B993::.NETFramework,Version=v4.8::Engine.Test.BaseDocumentTest</TestId> 6 | <TestId>xUnit::B79759F3-2D38-4F20-A539-BBBEB519B993::.NETFramework,Version=v4.8::Engine.Test.DbDocumentTest.IndexDocument</TestId> 7 | </TestAncestor> 8 | </SessionState> 9 | <SessionState ContinuousTestingMode="0" IsActive="True" Name="IndexDocument" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> 10 | <TestAncestor> 11 | <TestId>xUnit::B79759F3-2D38-4F20-A539-BBBEB519B993::.NETFramework,Version=v4.8::Engine.Test.DbDocumentTest.IndexDocument</TestId> 12 | <TestId>xUnit::B79759F3-2D38-4F20-A539-BBBEB519B993::.NETFramework,Version=v4.8::Engine.Test.QuerierTest.SearchTest</TestId> 13 | </TestAncestor> 14 | </SessionState> 15 | <SessionState ContinuousTestingMode="0" Name="IndexDocument #4" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> 16 | <TestAncestor> 17 | <TestId>xUnit::B79759F3-2D38-4F20-A539-BBBEB519B993::.NETFramework,Version=v4.8::Engine.Test.IndexTest.IndexDocument</TestId> 18 | </TestAncestor> 19 | </SessionState> 20 | <SessionState ContinuousTestingMode="0" Name="documentCreation" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> 21 | <TestAncestor> 22 | <TestId>xUnit::B79759F3-2D38-4F20-A539-BBBEB519B993::.NETFramework,Version=v4.8::Engine.Test.ConnectorTest.ConnectorCreation</TestId> 23 | <TestId>xUnit::B79759F3-2D38-4F20-A539-BBBEB519B993::.NETFramework,Version=v4.8::Engine.Test.BaseDocumentTest</TestId> 24 | </TestAncestor> 25 | </SessionState> 26 | <SessionState ContinuousTestingMode="0" Name="IndexDocument #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> 27 | <TestAncestor> 28 | <TestId>xUnit::B79759F3-2D38-4F20-A539-BBBEB519B993::.NETFramework,Version=v4.8::Engine.Test.DbDocumentTest</TestId> 29 | <TestId>xUnit::B79759F3-2D38-4F20-A539-BBBEB519B993::.NETFramework,Version=v4.8::Engine.Test.IndexTest.IndexDocument</TestId> 30 | <TestId>xUnit::B79759F3-2D38-4F20-A539-BBBEB519B993::.NETFramework,Version=v4.8::Engine.Test.QuerierTest.SearchTest</TestId> 31 | </TestAncestor> 32 | </SessionState> 33 | <SessionState ContinuousTestingMode="0" Name="IndexDocument #3" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> 34 | <TestAncestor> 35 | <TestId>xUnit::B79759F3-2D38-4F20-A539-BBBEB519B993::.NETFramework,Version=v4.8::Engine.Test.DbDocumentTest.IndexDocument</TestId> 36 | <TestId>xUnit::B79759F3-2D38-4F20-A539-BBBEB519B993::.NETFramework,Version=v4.8::Engine.Test.QuerierTest.SearchTest</TestId> 37 | </TestAncestor> 38 | </SessionState> -------------------------------------------------------------------------------- /Engine.Test/Engine.Test/BaseDocumentTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using Xunit.Abstractions; 4 | using Engine; 5 | 6 | namespace Engine.Test 7 | { 8 | public class BaseDocumentTest 9 | { 10 | private readonly ITestOutputHelper _output; 11 | 12 | public BaseDocumentTest(ITestOutputHelper output){ 13 | _output = output; 14 | } 15 | 16 | [Fact] 17 | public void DocumentCreation() 18 | { 19 | const string name = "Report on Search Engine Project"; 20 | const string url = "https://test.com"; 21 | const string documentId = "1"; 22 | 23 | var doc1 = new BaseDocument(documentId); 24 | var doc2 = new BaseDocument(name, url); 25 | var doc3 = new BaseDocument(name, url, documentId); 26 | 27 | _output.WriteLine("Test running."); 28 | 29 | Assert.True(doc1.DocumentId == documentId); 30 | Assert.True(doc2.Name == name && doc2.Url == url); 31 | Assert.True(doc3.Name == name && doc2.Url == url && doc3.DocumentId == documentId); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Engine.Test/Engine.Test/ConnectorTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Xunit; 4 | using Xunit.Abstractions; 5 | using Engine; 6 | using MongoDB.Bson; 7 | using MongoDB.Driver; 8 | 9 | namespace Engine.Test 10 | { 11 | public class ConnectorTest 12 | { 13 | private readonly ITestOutputHelper _output; 14 | 15 | public ConnectorTest(ITestOutputHelper output){ 16 | _output = output; 17 | } 18 | 19 | [Fact] 20 | public void ConnectorCreation() 21 | { 22 | var filters = Builders.Filter.Empty; 23 | var documents = Connector.GetDocumentsCollection().Find(filters).ToList(); 24 | var tokens = Connector.GetTokensCollection().Find(filters).ToList(); 25 | 26 | Assert.IsType>(tokens); 27 | Assert.IsType>(documents); 28 | Assert.Empty(tokens); 29 | Assert.Empty(documents); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Engine.Test/Engine.Test/DbDocumentTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using MongoDB.Bson; 6 | using MongoDB.Bson.Serialization; 7 | using MongoDB.Driver; 8 | using Xunit; 9 | using Xunit.Abstractions; 10 | using TikaOnDotNet.TextExtraction; 11 | 12 | namespace Engine.Test 13 | { 14 | public class DbDocumentTest 15 | { 16 | private static readonly string _name = "Report for testing"; 17 | private readonly string _url = "../../fixtures/CSC326.docx"; 18 | FilterDefinition _filter = Builders.Filter.Eq("name", _name); 19 | 20 | private readonly ITestOutputHelper _output; 21 | 22 | public DbDocumentTest(ITestOutputHelper output){ 23 | _output = output; 24 | } 25 | 26 | public void SetupDb() 27 | { 28 | Connector.SetTestMode(); 29 | DbDocument.IndexDocument(_name, _url); 30 | Thread.Sleep(5000); 31 | } 32 | public void TearDown() 33 | { 34 | var dColl = Connector.GetDocumentsCollection(); 35 | var docs = dColl.AsQueryable().ToList(); 36 | _output.WriteLine($"Before teardown, no of docs: {docs.Count}"); 37 | dColl.DeleteMany(new BsonDocument()); 38 | docs = dColl.Find(_filter).ToList(); 39 | _output.WriteLine($"Ended, no of docs: {docs.Count}"); 40 | } 41 | [Fact] 42 | public void IndexDocument() 43 | { 44 | SetupDb(); 45 | var doc = Connector.GetDocumentsCollection().Find(_filter).SingleOrDefault(); 46 | _output.WriteLine($"Here's your doc: {doc}"); 47 | Assert.NotNull(doc); 48 | TearDown(); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Engine.Test/Engine.Test/IndexTest.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading; 3 | using MongoDB.Bson; 4 | using MongoDB.Bson.Serialization; 5 | using MongoDB.Driver; 6 | using Xunit; 7 | using Xunit.Abstractions; 8 | using Engine; 9 | 10 | namespace Engine.Test 11 | { 12 | public class QuerierTest 13 | { 14 | private static readonly string _name = "Report for testing"; 15 | private static readonly string _url = "https://res.cloudinary.com/dpgdjfckl/raw/upload/v1629749999/fat_dydkjd.txt"; 16 | 17 | private readonly ITestOutputHelper _output; 18 | FilterDefinition _filter = Builders.Filter.Eq("name", _name); 19 | 20 | public QuerierTest(ITestOutputHelper output) 21 | { 22 | _output = output; 23 | } 24 | 25 | private void SetupDb() 26 | { 27 | 28 | Connector.SetTestMode(); 29 | var dColl = Connector.GetDocumentsCollection(); 30 | dColl.DeleteMany(new BsonDocument()); 31 | var docs = dColl.CountDocuments(new BsonDocument()); 32 | 33 | _output.WriteLine($"Before setup, no of docs: {docs}"); 34 | 35 | DbDocument.IndexDocument(_name, _url); 36 | Thread.Sleep(10000); 37 | 38 | docs = dColl.CountDocuments(new BsonDocument()); 39 | _output.WriteLine($"After setup, no of docs: {docs}"); 40 | var tColl = Connector.GetTokensCollection(); 41 | var tokens = tColl.CountDocuments(new BsonDocument()); 42 | _output.WriteLine($"After setup, no of tokens: {tokens}"); 43 | } 44 | 45 | private void TearDown() 46 | { 47 | var dColl = Connector.GetDocumentsCollection(); 48 | var docs = dColl.CountDocuments(new BsonDocument()); 49 | _output.WriteLine($"Before teardown, no of docs: {docs}"); 50 | dColl.DeleteMany(new BsonDocument()); 51 | docs = dColl.CountDocuments(_filter); 52 | 53 | 54 | _output.WriteLine($"Ended, no of docs: {docs}"); 55 | 56 | var tColl = Connector.GetTokensCollection(); 57 | var tokens = tColl.CountDocuments(new BsonDocument()); 58 | _output.WriteLine($"Before teardown, no of tokens: {tokens}"); 59 | tColl.DeleteMany(new BsonDocument()); 60 | tokens = tColl.CountDocuments(new BsonDocument()); 61 | _output.WriteLine($"Ended, no of tokens: {tokens}"); 62 | } 63 | 64 | [Theory] 65 | [InlineData("dog")] 66 | [InlineData("dog fat")] 67 | public async void SearchTest(string query) 68 | { 69 | SetupDb(); 70 | _output.WriteLine("Started search"); 71 | var querier = new Querier(); 72 | var result = await querier.Search(query); 73 | var doc = Connector.GetDocumentsCollection().AsQueryable().ToList()[0]; 74 | 75 | Assert.Single(result); 76 | 77 | if (result.Length > 0) { 78 | Assert.Equal(doc["_id"].ToString(), result[0].DocumentId); 79 | } 80 | TearDown(); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /Engine.Test/Engine.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Engine.Test")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("Engine.Test")] 12 | [assembly: AssemblyCopyright("Copyright © 2021")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("B79759F3-2D38-4F20-A539-BBBEB519B993")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /Engine.Test/Engine.Test/TokenTest.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using MongoDB.Bson; 3 | using MongoDB.Bson.Serialization; 4 | using MongoDB.Driver; 5 | using Xunit; 6 | using Xunit.Abstractions; 7 | using Engine; 8 | 9 | namespace Engine.Test 10 | { 11 | public class TokenTest 12 | { 13 | private static readonly string _name = "Report for testing"; 14 | private static readonly string _url = "../../fixtures/CSC326.docx"; 15 | 16 | private readonly ITestOutputHelper _output; 17 | FilterDefinition _filter = Builders.Filter.Eq("name", _name); 18 | 19 | public TokenTest(ITestOutputHelper output) 20 | { 21 | _output = output; 22 | } 23 | 24 | private void SetupDb() 25 | { 26 | 27 | Connector.SetTestMode(); 28 | var dColl = Connector.GetDocumentsCollection(); 29 | dColl.DeleteMany(new BsonDocument()); 30 | var docs = dColl.CountDocuments(new BsonDocument()); 31 | 32 | _output.WriteLine($"Before setup, no of docs: {docs}"); 33 | 34 | DbDocument.IndexDocument(_name, _url); 35 | Thread.Sleep(5000); 36 | 37 | docs = dColl.CountDocuments(new BsonDocument()); 38 | _output.WriteLine($"After setup, no of docs: {docs}"); 39 | var tColl = Connector.GetTokensCollection(); 40 | var tokens = tColl.CountDocuments(new BsonDocument()); 41 | _output.WriteLine($"After setup, no of tokens: {tokens}"); 42 | } 43 | 44 | private void TearDown() 45 | { 46 | var dColl = Connector.GetDocumentsCollection(); 47 | var docs = dColl.CountDocuments(new BsonDocument()); 48 | _output.WriteLine($"Before teardown, no of docs: {docs}"); 49 | dColl.DeleteMany(new BsonDocument()); 50 | docs = dColl.CountDocuments(_filter); 51 | 52 | 53 | _output.WriteLine($"Ended, no of docs: {docs}"); 54 | 55 | var tColl = Connector.GetTokensCollection(); 56 | var tokens = tColl.CountDocuments(new BsonDocument()); 57 | _output.WriteLine($"Before teardown, no of tokens: {tokens}"); 58 | tColl.DeleteMany(new BsonDocument()); 59 | tokens = tColl.CountDocuments(new BsonDocument()); 60 | _output.WriteLine($"Ended, no of tokens: {tokens}"); 61 | } 62 | 63 | [Fact] 64 | public void TokenCreationTest() 65 | { 66 | SetupDb(); 67 | var dbtoken = Connector.GetTokensCollection().AsQueryable().FirstOrDefault(); 68 | var word = dbtoken["word"].ToString(); 69 | var documents = dbtoken["documents"].AsBsonArray.ToArray(); 70 | var frequency = dbtoken["frequency"].ToInt32(); 71 | var token = new Token(word, documents, frequency); 72 | Assert.Equal(1, 1); 73 | TearDown(); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /Engine.Test/Engine.Test/fixtures/CSC326.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/Engine.Test/Engine.Test/fixtures/CSC326.docx -------------------------------------------------------------------------------- /Engine.Test/Engine.Test/fixtures/fat.txt: -------------------------------------------------------------------------------- 1 | fat dog mendoza witcher is actually a pretty rad cartoon all things considered -------------------------------------------------------------------------------- /Engine.Test/Engine.Test/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Engine/Engine.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine", "Engine\Engine.csproj", "{FD177583-C2ED-4746-8438-15E19BE2D488}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Any CPU = Debug|Any CPU 8 | Release|Any CPU = Release|Any CPU 9 | EndGlobalSection 10 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 11 | {FD177583-C2ED-4746-8438-15E19BE2D488}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 12 | {FD177583-C2ED-4746-8438-15E19BE2D488}.Debug|Any CPU.Build.0 = Debug|Any CPU 13 | {FD177583-C2ED-4746-8438-15E19BE2D488}.Release|Any CPU.ActiveCfg = Release|Any CPU 14 | {FD177583-C2ED-4746-8438-15E19BE2D488}.Release|Any CPU.Build.0 = Release|Any CPU 15 | EndGlobalSection 16 | EndGlobal 17 | -------------------------------------------------------------------------------- /Engine/Engine/BaseDocument.cs: -------------------------------------------------------------------------------- 1 | namespace Engine { 2 | /// 3 | /// BaseDocument class is used to represent a document. 4 | /// 5 | public class BaseDocument { 6 | public string Name; 7 | public string Url; 8 | public readonly string DocumentId; 9 | 10 | /// 11 | /// Constructor for the BaseDocument class 12 | /// 13 | /// UUID of the document in the database 14 | public BaseDocument(string documentId) { 15 | DocumentId = documentId; 16 | } 17 | 18 | /// 19 | /// Constructor for the BaseDocument class 20 | /// 21 | /// Document Name 22 | /// Document URL 23 | public BaseDocument(string name, string url) { 24 | Name = name; 25 | Url = url; 26 | } 27 | /// 28 | /// Constructor for the BaseDocument class 29 | /// 30 | /// Document Name 31 | /// Document URL 32 | /// UUID of the document in the database 33 | public BaseDocument(string name, string url, string documentId) : this(name, url) { 34 | DocumentId = documentId; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Engine/Engine/Connector.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Bson; 2 | using MongoDB.Driver; 3 | 4 | namespace Engine { 5 | /// 6 | /// This provides an interface between the functions and the database 7 | /// 8 | public static class Connector { 9 | private static MongoClient _client; 10 | 11 | private static string _database = "404Db"; 12 | private static string _mongoUrl = 13 | $"mongodb://127.0.0.1:27017/{_database}?retryWrites=true&w=majority"; 14 | 15 | /// 16 | /// This generates a client and stores it on the connector class 17 | /// 18 | public static void GenerateDb() { 19 | _client = new MongoClient(_mongoUrl); 20 | } 21 | 22 | /// 23 | /// Allows the user to set a mongo db url 24 | /// 25 | /// The target cluster url 26 | public static void SetMongoUri(string url) { 27 | _mongoUrl = $"{url}/{_database}?retryWrites=true&w=majority"; 28 | } 29 | 30 | /// 31 | /// Sets database to test db 32 | /// 33 | public static void SetTestMode() 34 | { 35 | _database = "404Db_Test"; 36 | _mongoUrl = $"mongodb://127.0.0.1:27017/{_database}?retryWrites=true&w=majority"; 37 | } 38 | 39 | /// 40 | /// Returns the database to be used by the collection classes 41 | /// 42 | private static IMongoDatabase GetDb() { 43 | if (_client == null) { 44 | _client = new MongoClient(_mongoUrl); 45 | } 46 | 47 | return _client.GetDatabase(_database); 48 | } 49 | 50 | /// 51 | /// Helper function to get collection 52 | /// 53 | /// Documents collection 54 | public static IMongoCollection GetDocumentsCollection() { 55 | var db = GetDb(); 56 | return db.GetCollection("documents"); 57 | } 58 | 59 | /// 60 | /// Helper function to get collection 61 | /// 62 | /// Tokens collection 63 | public static IMongoCollection GetTokensCollection() { 64 | var db = GetDb(); 65 | return db.GetCollection("tokens"); 66 | } 67 | 68 | /// 69 | /// Helper function to get collection 70 | /// 71 | /// Saved queries collection 72 | public static IMongoCollection GetSavedQueriesCollection() { 73 | var db = GetDb(); 74 | return db.GetCollection("savedQueries"); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /Engine/Engine/DbDocument.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using MongoDB.Bson; 6 | using MongoDB.Driver; 7 | 8 | namespace Engine { 9 | /// 10 | /// This extends the base document class to add extra field poition to describe the position of the document in the list of documents 11 | /// Also contains helper functions for indexing documents and saving documents to the database 12 | /// 13 | public class DbDocument : BaseDocument { 14 | public readonly long Position; 15 | private static readonly Queue ToBeIndexed = new Queue(); 16 | 17 | private static readonly Semaphore SaveDocSemaphore = new Semaphore(1, 1); 18 | /// 19 | /// Creates a new instance of 20 | /// 21 | /// Position of document in list of documents 22 | /// Document id 23 | /// Url of document 24 | /// Name of document 25 | private DbDocument(long position, string documentId, string url, string name) : base(name, url, documentId) { 26 | Position = position; 27 | } 28 | 29 | /// 30 | /// This function adds the document to the indexing queue and saves the document to the database 31 | /// 32 | /// Url of document 33 | /// Name of document 34 | public static void IndexDocument(string name, string url) { 35 | ToBeIndexed.Enqueue(new BaseDocument(name, url)); 36 | SaveToDb(); 37 | } 38 | 39 | /// 40 | /// This saves the document to the database 41 | /// 42 | private static async void SaveToDb() { 43 | SaveDocSemaphore.WaitOne(); 44 | var currentDocument = ToBeIndexed.Dequeue(); 45 | if (currentDocument == null) return; 46 | var name = currentDocument.Name; 47 | var url = currentDocument.Url; 48 | 49 | var collection = Connector.GetDocumentsCollection(); 50 | var documentIndex = await collection.CountDocumentsAsync(new BsonDocument()); 51 | var bsonDocument = new BsonDocument { 52 | {"name", name}, 53 | {"url", url}, 54 | {"position", documentIndex} 55 | }; 56 | 57 | await collection.InsertOneAsync(bsonDocument); 58 | 59 | var getDocFilter = Builders.Filter.Eq("name", name); 60 | var createdDoc = collection.Find(getDocFilter).ToList().Last(); 61 | 62 | Console.WriteLine($"Document {name} saved to database"); 63 | 64 | SaveDocSemaphore.Release(); 65 | 66 | //Calls the indexer function to add the document to the queue of items to be indexed 67 | Indexer.TryIndex(new DbDocument(documentIndex, createdDoc["_id"].ToString(), url, createdDoc["name"].ToString())); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /Engine/Engine/Index.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | namespace Engine { 6 | /// 7 | /// This is a representation of the index 8 | /// 9 | public class Index { 10 | /// 11 | /// This created a key value mapping of the word to a 12 | /// 13 | public readonly Dictionary Tokens = new Dictionary(); 14 | 15 | /// 16 | /// This is used to add an instance of a word to the index 17 | /// It checks if the token already contains an instance of the word. 18 | /// If it does it just updates the word 19 | /// Else it creates a new instance of the word and adds that initial position 20 | /// 21 | /// The string word 22 | /// The document the word is in 23 | /// The position of the word in the document 24 | public void AddWord(string word, DbDocument dbDocument, int position) { 25 | Token wordItem; 26 | if (Tokens.ContainsKey(word)) { 27 | wordItem = Tokens[word]; 28 | } 29 | else { 30 | wordItem = new Token(word); 31 | Tokens[word] = wordItem; 32 | } 33 | 34 | wordItem.AddItem(dbDocument, position); 35 | } 36 | 37 | /// 38 | /// Saves the tokens in the document to the database 39 | /// 40 | public async Task SaveToDb() { 41 | var saveActions = Tokens.Select(token => token.Value.SaveSelfToDb()).ToList(); 42 | 43 | await Task.WhenAll(saveActions); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Engine/Engine/Indexer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using TikaOnDotNet.TextExtraction; 5 | using Exception = System.Exception; 6 | 7 | 8 | namespace Engine { 9 | /// 10 | /// Indexer class used to index documents 11 | /// 12 | public static class Indexer { 13 | private static readonly Queue ToBeIndexed = new Queue(); 14 | 15 | private static readonly Semaphore IndexDocSemaphore = new Semaphore(1, 1); 16 | 17 | /// 18 | /// This is used to extract text from a document url 19 | /// 20 | /// This is the url of the target document 21 | /// The extracted text 22 | private static string ExtractText(string url) { 23 | var textExtractor = new TextExtractor(); 24 | var result = Uri.TryCreate(url, UriKind.Absolute, out var uri) 25 | && (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps); 26 | var contents = result ? textExtractor.Extract(uri) : textExtractor.Extract(url); 27 | return contents.Text; 28 | } 29 | 30 | /// 31 | /// Adds the document to the documents to be indexed and tries to acquire a semaphore for indexing 32 | /// 33 | /// An instance of which is to be indexed 34 | public static void TryIndex(DbDocument dbDocument) { 35 | ToBeIndexed.Enqueue(dbDocument); 36 | IndexDocument(); 37 | } 38 | 39 | /// 40 | /// Tries to acquire semaphore for indexing 41 | /// Calls needed functions for cleaning and indexing the document 42 | /// 43 | private static async void IndexDocument() { 44 | IndexDocSemaphore.WaitOne(); 45 | try { 46 | DbDocument dbDocument = ToBeIndexed.Dequeue(); 47 | 48 | Index newIndex = new Index(); 49 | 50 | Console.WriteLine($"Indexing document {dbDocument.Position}"); 51 | 52 | string text = ExtractText(dbDocument.Url).Trim(); 53 | 54 | string[] words = Utils.CleanAndExtractWords(text); 55 | 56 | for (int i = 0; i < words.Length; i++) { 57 | newIndex.AddWord(words[i], dbDocument, i); 58 | } 59 | 60 | await newIndex.SaveToDb(); 61 | 62 | Console.WriteLine($"Done indexing document {dbDocument.Position}"); 63 | } 64 | catch (Exception e) { 65 | Console.WriteLine(e.StackTrace, e.Message); //TODO: Log to file 66 | } 67 | 68 | IndexDocSemaphore.Release(); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /Engine/Engine/PositionPointer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Engine { 4 | /// 5 | /// This pointer is used for the linear mapping algorithm 6 | /// It stores the positions of the word in the document and contains helper function to move forward through the array 7 | /// 8 | public class PositionPointer { 9 | private readonly List _positions; 10 | private int _index; 11 | public readonly bool EmptyPointer; 12 | public readonly bool IsValid; 13 | 14 | /// 15 | /// Creates a new . Useful for consecutive words to signify empty space when a word in the user query doesn't exist in the database 16 | /// 17 | public PositionPointer() { 18 | EmptyPointer = true; 19 | } 20 | 21 | /// 22 | /// This creates a new 23 | /// 24 | /// The positions of the word in the document 25 | /// Used for specifying whether the current pointer is valid for the current document we're scoring 26 | public PositionPointer(List positions, bool isValid) { 27 | _positions = positions; 28 | IsValid = isValid; 29 | } 30 | 31 | /// 32 | /// This returns the current position that we are in. 33 | /// Useful for linear mapping algorithm 34 | /// 35 | public int CurrentPosition { 36 | get { 37 | if (EmptyPointer) { 38 | return -1; 39 | } 40 | 41 | if (_index < _positions.Count) { 42 | return _positions[_index]; 43 | } 44 | 45 | return -1; 46 | } 47 | } 48 | 49 | /// 50 | /// Moves current position forward 51 | /// 52 | public void MoveForward() { 53 | _index++; 54 | } 55 | 56 | /// 57 | /// Keeps moving current position forward until it is either greater than the target or we have exhausted the possible positions 58 | /// 59 | /// The target position 60 | public void MoveForwardUntilGreaterThanOrEqualTo(int target) { 61 | while (CurrentPosition < target && _index < _positions.Count) { 62 | MoveForward(); 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Engine/Engine/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Engine")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("Engine")] 12 | [assembly: AssemblyCopyright("Copyright © 2021")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("FD177583-C2ED-4746-8438-15E19BE2D488")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /Engine/Engine/ScoreDocumentNode.cs: -------------------------------------------------------------------------------- 1 | using Priority_Queue; 2 | 3 | namespace Engine { 4 | /// 5 | /// ScoreDocumentNode class records the score of a particular document in a queue 6 | /// 7 | public class ScoreDocumentNode : StablePriorityQueueNode { 8 | public readonly string DocumentId; 9 | 10 | public ScoreDocumentNode(string documentId) { 11 | this.DocumentId = documentId; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Engine/Engine/SearchIndex.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MongoDB.Bson; 3 | using MongoDB.Driver; 4 | 5 | namespace Engine { 6 | /// 7 | /// SearchIndex inherits the Index class. It is used for the Querier class. 8 | /// It instantiates itself with a list of words from the database. 9 | /// 10 | public class SearchIndex : Index { 11 | /// 12 | /// Makes a new instance of 13 | /// 14 | /// These are the words that are loaded from the db 15 | public SearchIndex(string[] words) { 16 | var filter = Builders.Filter.In("word", words); 17 | var tokensCollection = Connector.GetTokensCollection(); 18 | var dbTokens = tokensCollection.Find(filter).ToList(); 19 | 20 | foreach (var token in dbTokens) { 21 | var word = token["word"].ToString(); 22 | var documents = token["documents"].AsBsonArray.ToArray(); 23 | var frequency = token["frequency"].ToInt32(); 24 | Tokens[word] = new Token(word, documents, frequency); 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Engine/Engine/TokenItem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using MongoDB.Bson; 3 | 4 | namespace Engine { 5 | /// 6 | /// This class represents a document under a token 7 | /// 8 | public class TokenItem { 9 | /// 10 | /// Positions contains the particular postion a token exists in a document 11 | /// 12 | public readonly List Positions = new List(); 13 | /// 14 | /// DocumentId contains the ID of the particular document 15 | /// 16 | public readonly string DocumentId; 17 | /// 18 | /// DocumentPosition contains the positions where the token is found 19 | /// 20 | public readonly long DocumentPosition; 21 | 22 | /// 23 | /// Creates a new instance of 24 | /// 25 | /// This is the first position of the word in a document 26 | /// This is the id of the document 27 | /// This is the position of the document in the documents repository 28 | public TokenItem(int position, string documentId, long documentPosition) { 29 | Positions.Add(position); 30 | DocumentId = documentId; 31 | DocumentPosition = documentPosition; 32 | } 33 | 34 | /// 35 | /// Creates a new instance of 36 | /// 37 | /// These are the positions of the item in the document 38 | /// This is the id of the document 39 | /// This is the position of the document in the documents repository 40 | public TokenItem(List positions, string documentId, long documentPosition) { 41 | Positions = positions; 42 | DocumentId = documentId; 43 | DocumentPosition = documentPosition; 44 | } 45 | 46 | /// 47 | /// Adds a new position 48 | /// 49 | /// Position of word in document 50 | public void AddPosition(int position) { 51 | Positions.Add(position); 52 | } 53 | 54 | /// 55 | /// Converts positions to bson document 56 | /// 57 | /// Returns of positions 58 | public BsonArray GetBsonPositions() { 59 | var positionsArray = new BsonArray(); 60 | 61 | foreach (var position in Positions) { 62 | positionsArray.Add(position); 63 | } 64 | 65 | return positionsArray; 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /Engine/Engine/TokenPointer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Priority_Queue; 3 | 4 | namespace Engine { 5 | /// 6 | /// Token Pointer class is used for the linear mapping algorithm 7 | /// 8 | public class TokenPointer : StablePriorityQueueNode { 9 | private readonly List _targets; 10 | public readonly Token Token; 11 | private int _index; 12 | public readonly bool EmptyPointer; 13 | 14 | public TokenItem Target => _index < _targets.Count ? _targets[_index] : null; 15 | 16 | /// 17 | /// Creates an empty 18 | /// / 19 | public TokenPointer() { 20 | EmptyPointer = true; 21 | } 22 | /// 23 | /// Creates a new instance of token pointer with a target token and target documents 24 | /// / 25 | public TokenPointer(Token targetToken) { 26 | Token = targetToken; 27 | _targets = targetToken.Documents; 28 | } 29 | 30 | /// 31 | /// Moves the current index forward by 1 32 | /// 33 | public bool MoveForward() { 34 | if (_index + 1 < _targets.Count) { 35 | _index++; 36 | return true; 37 | } 38 | 39 | return false; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Engine/Engine/UML Diagram.cd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | AAAAAAgAAAAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAA= 7 | BaseDocument.cs 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | AAAAAAAAAAAAAAAgAAACAAIAAAAAAAAAAAAAAQIAAAA= 18 | DbDocument.cs 19 | 20 | 21 | 22 | 23 | 24 | CAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAABAAA= 25 | Index.cs 26 | 27 | 28 | 29 | 30 | 31 | AABAAAAAAAAAAAAAAAggAAAAAAEAQgAAAAAAAAEAAAA= 32 | PositionPointer.cs 33 | 34 | 35 | 36 | 37 | 38 | AAAACACAAgGIAAAAAAAAAEgAgAAAIAAAAAAAQAhgAAA= 39 | Querier.cs 40 | 41 | 42 | 43 | 44 | 45 | AAQIBAAAACAACgAAgAAABAIARAAACBEAAAKAABAIAAA= 46 | Stemmer.cs 47 | 48 | 49 | 50 | 51 | 52 | gAAAAAAAAAAAAAAAAAAAAAACgIAAAAAKAABAAAAAAAA= 53 | Token.cs 54 | 55 | 56 | 57 | 58 | 59 | AAAAAAgAAAAAAAAgAQAAAAAAAAAAAAAAEAAAAAAAQAA= 60 | TokenItem.cs 61 | 62 | 63 | 64 | 65 | 66 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= 67 | SearchIndex.cs 68 | 69 | 70 | 71 | 72 | 73 | AAAIAAAgAAAAAAAAAAAAAAAAAAAIACBAAAAACgAAAAA= 74 | Utils.cs 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /Engine/Engine/UML.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/Engine/Engine/UML.pdf -------------------------------------------------------------------------------- /Engine/Engine/Utils.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using StringBuilder = System.Text.StringBuilder; 4 | 5 | namespace Engine { 6 | /// 7 | /// Utils class contains functions used to clean and normalize words before they are saved to database and used for querying. 8 | /// 9 | public static class Utils { 10 | 11 | /// 12 | /// StopWords stores a database of stop words 13 | /// 14 | private const string StopWords = "i me my myself we our ours ourselves you youre youve youll youd your yours yourself yourselves he him his himself she shes her hers herself it its its itself they them their theirs themselves what which who whom this that thatll these those am is are was were be been being have has had having do does did doing a an the and but if or because as until while of at by for with about against between into through during before after above below to from up down in out on off over under again further then once here there when where why how all any both each few more most other some such no nor not only own same so than too very s t can will just don dont should shouldve now d ll m o re ve y ain aren arent couldn couldnt didn didnt doesn doesnt hadn hadnt hasn hasnt haven havent isn isnt ma mightn mightnt mustn mustnt needn neednt shan shant shouldn shouldnt wasn wasnt weren werent wont wouldn wouldnt"; 15 | 16 | /// 17 | /// StopWordsHash is obtained from the conversion of StopWords into an object 18 | /// 19 | private static readonly HashSet StopWordsHash = new HashSet(StopWords.Split(' ')); 20 | 21 | /// 22 | /// NormalizeWhiteSpaceAndRemovePunctuation method is used to remove whitespaces and punctuations 23 | /// 24 | private static string NormalizeWhiteSpaceAndRemovePunctuation(string text) { 25 | var output = new StringBuilder(); 26 | var skipped = false; 27 | 28 | foreach (var c in text) { 29 | if (char.IsWhiteSpace(c)) { 30 | if (!skipped) { 31 | output.Append(' '); 32 | skipped = true; 33 | } 34 | } 35 | else if (char.IsPunctuation(c)) { 36 | if (!skipped) { 37 | output.Append(' '); 38 | skipped = true; 39 | } 40 | } 41 | else { 42 | skipped = false; 43 | output.Append(c); 44 | } 45 | } 46 | 47 | return output.ToString(); 48 | } 49 | 50 | /// 51 | /// IsStopWord returns a boolean if a stop word is found 52 | /// 53 | private static bool IsStopWord(string word) { 54 | return StopWordsHash.Contains(word); 55 | } 56 | 57 | /// 58 | /// SplitAndRemoveStopWords removes stop words in a string 59 | /// 60 | private static string [] SplitAndRemoveStopWords(string text) { 61 | string[] words = text.Split(' '); 62 | 63 | return words.Where(word => !IsStopWord(word)).ToArray(); 64 | } 65 | 66 | /// 67 | /// StemWords method is used to stem the words in a particular array of words 68 | /// 69 | private static void StemWords(string [] validWords) { 70 | Stemmer stemmer = new Stemmer(); 71 | 72 | for (int i = 0; i < validWords.Length; i++) { 73 | validWords[i] = stemmer.StemWord(validWords[i]); 74 | } 75 | } 76 | 77 | 78 | /// 79 | /// CleanAndExtractWords method calls the other methods on a piece of string 80 | /// 81 | public static string[] CleanAndExtractWords(string text) { 82 | string normalizedText = NormalizeWhiteSpaceAndRemovePunctuation(text.ToLower()); 83 | 84 | string[] words = SplitAndRemoveStopWords(normalizedText); 85 | 86 | StemWords(words); 87 | 88 | return words; 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /Engine/Engine/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /GUI/GUI.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.1525 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gui", "Gui\Gui.csproj", "{5C39AF4C-E632-4A37-B5B6-E5E4BFA37841}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {5C39AF4C-E632-4A37-B5B6-E5E4BFA37841}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {5C39AF4C-E632-4A37-B5B6-E5E4BFA37841}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {5C39AF4C-E632-4A37-B5B6-E5E4BFA37841}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {5C39AF4C-E632-4A37-B5B6-E5E4BFA37841}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {DF5249F4-8E7E-4D94-AD3F-A38B49B24F35} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /GUI/GUI.sln.DotSettings.user: -------------------------------------------------------------------------------- 1 |  2 | True 3 | <AssemblyExplorer> 4 | <Assembly Path="C:\Users\deyem\Desktop\Programming\Projects\.net\search-engine\Engine\Engine\bin\Debug\Engine.dll" /> 5 | <Assembly Path="C:\Users\deyem\Desktop\Programming\Projects\.net\search-engine\GUI\packages\CloudinaryDotNet.1.15.2\lib\net452\CloudinaryDotNet.dll" /> 6 | <Assembly Path="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Core.dll" /> 7 | <Assembly Path="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.dll" /> 8 | <Assembly Path="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Web.dll" /> 9 | </AssemblyExplorer> -------------------------------------------------------------------------------- /GUI/Gui/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /GUI/Gui/App.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /GUI/Gui/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace Gui 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /GUI/Gui/Components/MenuButton.xaml: -------------------------------------------------------------------------------- 1 |  3 | 27 | 28 | -------------------------------------------------------------------------------- /GUI/Gui/Components/SearchButton.xaml: -------------------------------------------------------------------------------- 1 |  4 | 10 | -------------------------------------------------------------------------------- /GUI/Gui/Components/SearchInput.xaml: -------------------------------------------------------------------------------- 1 |  3 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /GUI/Gui/Core/ObservableObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Runtime.CompilerServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Gui.Core 10 | { 11 | class ObservableObject : INotifyPropertyChanged 12 | { 13 | public event PropertyChangedEventHandler PropertyChanged; 14 | 15 | protected void OnPropertyChanged([CallerMemberName] string name = null) 16 | { 17 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /GUI/Gui/Core/RelayCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Input; 7 | 8 | namespace Gui.Core 9 | { 10 | class RelayCommand : ICommand 11 | { 12 | 13 | private Action _execute; 14 | private Func _canExecute; 15 | 16 | public event EventHandler CanExecuteChanged 17 | { 18 | add { CommandManager.RequerySuggested += value; } 19 | remove { CommandManager.RequerySuggested -= value; } 20 | } 21 | 22 | public RelayCommand(Action execute, Func canExecute = null) 23 | { 24 | _execute = execute; 25 | _canExecute = canExecute; 26 | } 27 | 28 | public bool CanExecute(object parameter) 29 | { 30 | return _canExecute == null || _canExecute(parameter); 31 | } 32 | 33 | public void Execute(object parameter) 34 | { 35 | _execute(parameter); 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /GUI/Gui/Core/UploadFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using System.Web; 6 | using Amazon.S3; 7 | using Amazon.S3.Model; 8 | 9 | namespace Gui.Core { 10 | public class UploadFile { 11 | static string GetRandomString(int length) 12 | { 13 | const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; 14 | 15 | StringBuilder sb = new StringBuilder(); 16 | using (RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider()) 17 | { 18 | while (sb.Length != length) 19 | { 20 | byte[] oneByte = new byte[1]; 21 | provider.GetBytes(oneByte); 22 | char character = (char)oneByte[0]; 23 | if (valid.Contains(character.ToString())) 24 | { 25 | sb.Append(character); 26 | } 27 | } 28 | } 29 | return sb.ToString(); 30 | } 31 | 32 | private static string bucketName = "404buckengine"; 33 | public static async Task Upload(string filePath) { 34 | var client = new AmazonS3Client(Amazon.RegionEndpoint.USWest2); 35 | 36 | try { 37 | string mimeType = MimeMapping.GetMimeMapping(filePath); 38 | 39 | var key = GetRandomString(10); 40 | PutObjectRequest putRequest = new PutObjectRequest { 41 | BucketName = bucketName, 42 | Key = key, 43 | FilePath = filePath, 44 | ContentType = mimeType 45 | }; 46 | 47 | await client.PutObjectAsync(putRequest); 48 | 49 | return $"https://{bucketName}.s3.us-west-2.amazonaws.com/{key}"; 50 | } 51 | catch (AmazonS3Exception amazonS3Exception) { 52 | if (amazonS3Exception.ErrorCode != null && 53 | (amazonS3Exception.ErrorCode.Equals("InvalidAccessKeyId") 54 | || 55 | amazonS3Exception.ErrorCode.Equals("InvalidSecurity"))) { 56 | throw new Exception("Check the provided AWS Credentials."); 57 | } 58 | else { 59 | throw new Exception("Error occurred: " + amazonS3Exception.Message); 60 | } 61 | } 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /GUI/Gui/Images/Add Button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/GUI/Gui/Images/Add Button.png -------------------------------------------------------------------------------- /GUI/Gui/Images/Home Button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/GUI/Gui/Images/Home Button.png -------------------------------------------------------------------------------- /GUI/Gui/Images/Info Button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/GUI/Gui/Images/Info Button.png -------------------------------------------------------------------------------- /GUI/Gui/Images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/GUI/Gui/Images/bg.jpg -------------------------------------------------------------------------------- /GUI/Gui/Images/excel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/GUI/Gui/Images/excel.png -------------------------------------------------------------------------------- /GUI/Gui/Images/html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/GUI/Gui/Images/html.png -------------------------------------------------------------------------------- /GUI/Gui/Images/json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/GUI/Gui/Images/json.png -------------------------------------------------------------------------------- /GUI/Gui/Images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/GUI/Gui/Images/logo.png -------------------------------------------------------------------------------- /GUI/Gui/Images/pdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/GUI/Gui/Images/pdf.png -------------------------------------------------------------------------------- /GUI/Gui/Images/powerpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/GUI/Gui/Images/powerpoint.png -------------------------------------------------------------------------------- /GUI/Gui/Images/word.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xp3p3x0/csharp_backend/04511eee453547de9793d03764726b496c2e0be6/GUI/Gui/Images/word.png -------------------------------------------------------------------------------- /GUI/Gui/MVVM/View/About.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /GUI/Gui/MVVM/View/About.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace Gui.MVVM.View 17 | { 18 | /// 19 | /// Interaction logic for About.xaml 20 | /// 21 | public partial class About : UserControl 22 | { 23 | public About() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /GUI/Gui/MVVM/View/Homepage.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 41 | 44 | 45 | 53 | 54 | 55 | 56 | 57 | 64 | 65 | 66 | 72 | 73 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /GUI/Gui/MVVM/View/Homepage.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Data; 10 | using System.Windows.Documents; 11 | using System.Windows.Input; 12 | using System.Windows.Media; 13 | using System.Windows.Media.Imaging; 14 | using System.Windows.Navigation; 15 | using System.Windows.Shapes; 16 | using Engine; 17 | 18 | namespace Gui.MVVM.View { 19 | /// 20 | /// Interaction logic for Homepage.xaml 21 | /// 22 | public partial class Homepage : UserControl { 23 | public Homepage() { 24 | //From the App.xaml 25 | InitializeComponent(); 26 | } 27 | 28 | private BaseDocument[] _documents; 29 | 30 | private void HideSuggestionsList() { 31 | Suggestions.Visibility = Visibility.Hidden; 32 | Suggestions.Height = 0; 33 | Suggestions.ItemsSource = new string[0]; 34 | } 35 | 36 | private void ShowSuggestionsList() { 37 | Suggestions.Visibility = Visibility.Visible; 38 | Suggestions.Height = 200; 39 | } 40 | 41 | private void OnKeyDownHandler(object sender, KeyEventArgs e) { 42 | if (e.Key == Key.Return) { 43 | GenerateResults(); 44 | } 45 | } 46 | 47 | private void SearchResults_SelectionChanged(object sender, RoutedEventArgs routedEventArgs) { 48 | try { 49 | var selectedItem = Suggestions.SelectedItem; 50 | if (selectedItem == null) return; 51 | 52 | SearchInput.Text = selectedItem.ToString(); 53 | HideSuggestionsList(); 54 | GenerateResults(); 55 | } 56 | catch (Exception e) { 57 | MessageBox.Show(e.Message); 58 | MessageBox.Show(e.StackTrace); 59 | } 60 | } 61 | 62 | private void OnTextChanged(object sender, RoutedEventArgs routedEventArgs) { 63 | FetchSuggestions(); 64 | } 65 | 66 | private void FetchSuggestions() { 67 | if (!string.IsNullOrWhiteSpace(SearchInput.Text)) { 68 | var pq = Querier.GetPastQueries(SearchInput.Text); 69 | if (pq.Length > 0) { 70 | Suggestions.ItemsSource = Querier.GetPastQueries(SearchInput.Text); 71 | Suggestions.Visibility = Visibility.Visible; 72 | Suggestions.Height = 200; 73 | } 74 | else { 75 | HideSuggestionsList(); 76 | } 77 | } 78 | else { 79 | HideSuggestionsList(); 80 | } 81 | } 82 | 83 | private void Button_OnSearch(object sender, RoutedEventArgs routedEventArgs) { 84 | GenerateResults(); 85 | } 86 | 87 | private async void GenerateResults() { 88 | if (!string.IsNullOrWhiteSpace(SearchInput.Text)) { 89 | HideSuggestionsList(); 90 | var start = DateTime.Now; 91 | var querier = new Querier(); 92 | _documents = await querier.Search(SearchInput.Text); 93 | var seconds = (DateTime.Now - start).TotalMilliseconds; 94 | 95 | SearchResults.Children.Clear(); 96 | 97 | var endText = _documents.Length != 1 ? "s" : ""; 98 | NumberOfResults.Text = $"{_documents.Length} Result{endText}"; 99 | 100 | if (_documents.Length > 0) { 101 | ResponseTime.Text = $"Response time: {seconds}ms"; 102 | 103 | foreach (var document in _documents) { 104 | TextBlock tb = new TextBlock(); 105 | tb.Style = Resources["DownloadLinkWrapper"] as Style; 106 | 107 | Hyperlink hyperlink = new Hyperlink(); 108 | Run run = new Run(); 109 | 110 | run.Text = document.Name; 111 | hyperlink.NavigateUri = new Uri(document.Url); 112 | hyperlink.Style = Resources["DownloadLink"] as Style; 113 | hyperlink.Inlines.Add(run); 114 | 115 | hyperlink.RequestNavigate += (_, e) => { System.Diagnostics.Process.Start(e.Uri.ToString()); }; 116 | 117 | tb.Inlines.Add(hyperlink); 118 | SearchResults.Children.Add(tb); 119 | } 120 | } 121 | else { 122 | TextBlock tb = new TextBlock(); 123 | tb.Style = Resources["DownloadLinkWrapper"] as Style; 124 | tb.Text = $"No Items match your search query: {SearchInput.Text}"; 125 | SearchResults.Children.Add(tb); 126 | } 127 | 128 | Suggestions.ItemsSource = Querier.GetPastQueries(SearchInput.Text); 129 | } 130 | } 131 | } 132 | } -------------------------------------------------------------------------------- /GUI/Gui/MVVM/View/UploadContent.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 58 | 59 | -------------------------------------------------------------------------------- /GUI/Gui/MVVM/View/UploadContent.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.IO; 5 | using System.Threading.Tasks; 6 | using CloudinaryDotNet; 7 | using Gui.Core; 8 | using Microsoft.Win32; 9 | 10 | namespace Gui.MVVM.View { 11 | /// 12 | /// Interaction logic for UploadContent.xaml 13 | /// 14 | public partial class UploadContent : UserControl { 15 | public UploadContent() { 16 | InitializeComponent(); 17 | cloudinary = new Cloudinary(); 18 | } 19 | 20 | private Cloudinary cloudinary; 21 | private string _targetFile; 22 | private bool _uploading = false; 23 | 24 | private void SelectFileBtn_Click(object sender, RoutedEventArgs e) { 25 | OpenFileDialog openFileDialog = new OpenFileDialog(); 26 | 27 | openFileDialog.Filter = 28 | "Word Documents|*.doc|Excel Worksheets|*.xls,*.xlsx|PowerPoint Presentations|*.ppt,*.ppts" + 29 | "|Office Files|*.doc;*.xls;*.ppt;*.pdf;*.docx;" + 30 | "|Web files|*.html,*.xml" + 31 | "|Text files|*.txt"; 32 | 33 | if (openFileDialog.ShowDialog() == true) { 34 | SelectFileButton.Content = "Change File"; 35 | FileName.Text = Path.GetFileName(openFileDialog.FileName); 36 | _targetFile = openFileDialog.FileName; 37 | } 38 | else { 39 | FileName.Text = "No file selected"; 40 | } 41 | } 42 | 43 | private async void UploadFileBtn_Click(object sender, RoutedEventArgs e) { 44 | if (_uploading) return; 45 | 46 | if(String.IsNullOrWhiteSpace(DocumentTitle.Text)) return; 47 | 48 | if (_targetFile == null) return; 49 | 50 | _uploading = true; 51 | UploadFileBtn.Content = "Uploading..."; 52 | UploadFileBtnWrapper.Width = 200; 53 | var url = await UploadFile.Upload(_targetFile); 54 | UploadFileBtnWrapper.Width = 120; 55 | UploadFileBtn.Content = "Upload"; 56 | _uploading = false; 57 | 58 | MessageBox.Show("Your document has been queued for indexing"); 59 | try { 60 | var title = DocumentTitle.Text; 61 | await Task.Run(() => Engine.DbDocument.IndexDocument(title, url)); 62 | } 63 | catch (Exception ss) { 64 | Console.WriteLine(ss.Message); 65 | } 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /GUI/Gui/MVVM/ViewModel/AboutViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Gui.MVVM.ViewModel 8 | { 9 | class AboutViewModel 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /GUI/Gui/MVVM/ViewModel/HomepageViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Gui.Core; 7 | 8 | namespace Gui.MVVM.ViewModel 9 | { 10 | class HomepageViewModel : ObservableObject 11 | { 12 | public HomepageViewModel() 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /GUI/Gui/MVVM/ViewModel/MainViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Engine; 3 | using Gui.Core; 4 | 5 | namespace Gui.MVVM.ViewModel 6 | { 7 | class MainViewModel : ObservableObject 8 | { 9 | 10 | public RelayCommand HomeViewCommand { get; set; } 11 | 12 | public RelayCommand UploadContentCommand { get; set; } 13 | 14 | public RelayCommand AboutCommand { get; set; } 15 | 16 | 17 | public HomepageViewModel HomepageVM { get; set; } 18 | 19 | public UploadContentViewModel UploadContentVM { get; set; } 20 | 21 | public AboutViewModel AboutVM { get; set; } 22 | 23 | 24 | private object _currentView; 25 | 26 | public object CurrentView 27 | { 28 | get { return _currentView; } 29 | set { 30 | _currentView = value; 31 | OnPropertyChanged(); 32 | } 33 | } 34 | 35 | 36 | public MainViewModel() 37 | { 38 | HomepageVM = new HomepageViewModel(); 39 | UploadContentVM = new UploadContentViewModel(); 40 | AboutVM = new AboutViewModel(); 41 | 42 | CurrentView = HomepageVM; 43 | 44 | HomeViewCommand = new RelayCommand(o => 45 | { 46 | CurrentView = HomepageVM; 47 | }); 48 | 49 | UploadContentCommand = new RelayCommand(O => 50 | { 51 | CurrentView = UploadContentVM; 52 | }); 53 | 54 | AboutCommand = new RelayCommand(o => 55 | { 56 | CurrentView = AboutVM; 57 | }); 58 | 59 | Connector.GenerateDb(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /GUI/Gui/MVVM/ViewModel/UploadContentViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Gui.MVVM.ViewModel 8 | { 9 | class UploadContentViewModel 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /GUI/Gui/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /GUI/Gui/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace Gui 17 | { 18 | /// 19 | /// Interaction logic for MainWindow.xaml 20 | /// 21 | public partial class MainWindow : Window 22 | { 23 | public MainWindow() 24 | { 25 | InitializeComponent(); 26 | } 27 | 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /GUI/Gui/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("Gui")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("Gui")] 15 | [assembly: AssemblyCopyright("Copyright © 2021")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /GUI/Gui/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Gui.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Gui.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /GUI/Gui/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Gui.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /GUI/Gui/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /GUI/Gui/SearchResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Gui 8 | { 9 | public class SearchResult 10 | { 11 | public string Title; 12 | public string Description; 13 | public string Link; 14 | 15 | public SearchResult(string title, string desc, string url) 16 | { 17 | Title = title; 18 | Description = desc; 19 | Link = url; 20 | } 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /GUI/Gui/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Search Engine Project 2 | 3 | This repository contains a search engine project with the following parts 4 | 5 | - Engine 6 | 7 | This is the main engine class library that handles indexing the documents and querying the indexes 8 | 9 | - Engine.Test 10 | 11 | This contains the tests which are used to verify that the Engine is performing the actions that it should 12 | 13 | - GUI 14 | 15 | This is a GUI written in [WPF](https://docs.microsoft.com/en-us/visualstudio/designers/getting-started-with-wpf). 16 | It consumes the Engine class library directly and provides a UI for searching documents and uploading new documents to be indexed 17 | 18 | - API 19 | 20 | This is a rest API written with .NET framework that consumes the Engine and provides endpoints for searching and indexing documents 21 | 22 | ## How to Run/Build 23 | - Engine 24 | 25 | You can set either 26 | 27 | 1. Set the mongodb uri via the Connector.SetMongoUri 28 | 2. Install mongodb locally and run it. The connector defaults to using a local instance 29 | 30 | - Engine.Test 31 | 32 | Ensure the Engine project is built then open this project and run it in any IDE of your choice 33 | 34 | - GUI 35 | 36 | Ensure the Engine project is built. 37 | Setup your AWS secrets and credentials using the instructions [here](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/quick-start-s3-1-cross.html). 38 | 39 | - API 40 | 41 | Ensure the engine project is built. 42 | Install the required server for running the project eg [Windows](https://docs.microsoft.com/en-us/iis/extensions/introduction-to-iis-express/iis-express-overview) --------------------------------------------------------------------------------