Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • coscine/backend/apis/search
1 result
Show changes
Commits on Source (4)
using Coscine.Api.Search.Models;
using Coscine.Api.Search.Helpers;
using Coscine.Api.Search.Models;
using Coscine.ApiCommons;
using Coscine.Configuration;
using Coscine.SemanticSearch;
using Coscine.SemanticSearch.Clients;
using Coscine.SemanticSearch.Core;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Coscine.Api.Search.Controllers
......@@ -15,7 +18,7 @@ namespace Coscine.Api.Search.Controllers
/// Controller for the search.
/// </summary>
[Authorize]
public class SemanticSearchController : Controller
public class SearchController : Controller
{
private readonly IConfiguration _configuration;
private readonly Authenticator _authenticator;
......@@ -29,7 +32,7 @@ namespace Coscine.Api.Search.Controllers
/// <summary>
/// Default constructor.
/// </summary>
public SemanticSearchController()
public SearchController()
{
_configuration = Program.Configuration;
_authenticator = new Authenticator(this, Program.Configuration);
......@@ -42,7 +45,7 @@ namespace Coscine.Api.Search.Controllers
/// <summary>
/// Search with SemanticSearch;
/// Search
/// </summary>
/// <param name="query">Elasticsearch query</param>
/// <param name="user">Specify user or only public metadata records could be found</param>
......@@ -50,31 +53,35 @@ namespace Coscine.Api.Search.Controllers
/// <param name="languages">Set the used languages</param>
/// <returns>Search results</returns>
[HttpGet("[controller]/{query?}/{user?}/{adv?}/{languages?}")]
public async Task<ActionResult<IEnumerable<SearchResult>>> SearchAsync(string query = "*", bool user = true, bool adv = false, [System.Web.Http.FromUri] string[] languages = null)
public async Task<ActionResult<SearchResult>> SearchAsync(string query = "*", bool user = true, bool adv = false, [System.Web.Http.FromUri] string[] languages = null)
{
try
var currentUser = _authenticator.GetUser();
if (languages is null || languages.Length == 0)
{
if (languages == null || languages.Length == 0)
{
languages = _defaultLanguages;
}
languages = _defaultLanguages;
}
string userId = null;
try
{
var files = await Searchers.SearchForFilesAsync(currentUser.Id, query, adv, includePrivateRecords: user, _connector, _searchClient, languages.ToList());
var projectsToAppend = files.Where(f => f.ProjectId is not null).Select(f => f.ProjectId).Distinct().ToList();
var resourcesToAppend = files.Where(f => f.ProjectId is not null).Select(f => f.ResourceId).Distinct().ToList();
if (user)
// Advanced query looks like so: (vvm.txt) + (belongsToProject: "https://purl.org/coscine/projects/1a6cf186-12cb-4710-81c6-edb981f77761")
var formattedQueryMatch = Regex.Match(query, @"(?<=\().+?(?=\))");
var formattedQuery = formattedQueryMatch.Success ? formattedQueryMatch.Value : query;
var res = new SearchResult()
{
userId = _authenticator.GetUser().Id.ToString();
}
var mapper = new RdfSearchMapper(_connector, _searchClient, languages.ToList());
var results = await mapper.SearchAsync(query, userId, adv);
return Ok(SearchResult.ParseResultsToList(results));
Projects = Searchers.SearchForProjects(currentUser.Id, formattedQuery, projectsToAppend: projectsToAppend),
Resources = Searchers.SearchForResources(currentUser.Id, formattedQuery, resourcesToAppend: resourcesToAppend),
Files = files,
};
return res;
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
}
}
using Coscine.Api.Search.Models;
using Coscine.Database.DataModel;
using Coscine.Database.Models;
using Coscine.Database.ReturnObjects;
using Coscine.Database.Util;
using Coscine.SemanticSearch;
using Coscine.SemanticSearch.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Coscine.Api.Search.Helpers;
/// <summary>
/// Class implementing static searcher methods
/// </summary>
public class Searchers
{
/// <summary>
///
/// </summary>
/// <param name="userId"></param>
/// <param name="searchQuery"></param>
/// <param name="advancedSearch"></param>
/// <param name="includePrivateRecords"></param>
/// <param name="connector"></param>
/// <param name="searchClient"></param>
/// <param name="languages"></param>
/// <returns>Found files</returns>
public static async Task<IEnumerable<FileSearchResult>> SearchForFilesAsync(Guid userId, string searchQuery, bool advancedSearch, bool includePrivateRecords, IRdfConnector connector, ISearchClient searchClient, List<string> languages)
{
string userIdentifier = includePrivateRecords ? userId.ToString() : null;
var mapper = new RdfSearchMapper(connector, searchClient, languages, GetPublicVisibilityId());
var results = await mapper.SearchAsync(searchQuery, userIdentifier, advancedSearch);
return FileSearchResult.ParseResultsToList(results);
}
/// <summary>
/// This method searches through projects
/// </summary>
/// <param name="userId">ID of the user</param>
/// <param name="searchQuery">Search query</param>
/// <param name="showSubProjects">Shows subprojects</param>
/// <param name="projectsToAppend">List of project IDs to include in the result</param>
/// <returns>Found projects</returns>
public static IEnumerable<ProjectObject> SearchForProjects(Guid userId, string searchQuery, bool showSubProjects = false, List<string> projectsToAppend = null)
{
if (string.IsNullOrEmpty(searchQuery) || searchQuery == "*")
{
searchQuery = "";
}
if (projectsToAppend is null)
{
projectsToAppend = new List<string>();
}
var projectModel = new ProjectModel();
return DatabaseConnection.ConnectToDatabase((db) =>
{
var allSubProjects = (from sp in db.SubProjects select sp.SubProjectId).ToList();
var allSubProjectsList = new List<Guid>();
allSubProjectsList.AddRange(allSubProjects);
return (from p in db.Projects
join pr in db.ProjectRoles on p.Id equals pr.ProjectId into joinedPr
from jpr in joinedPr.DefaultIfEmpty()
join v in db.Visibilities on p.VisibilityId equals v.Id into joinedV
from jv in joinedV.DefaultIfEmpty()
join pd in db.ProjectDisciplines on p.Id equals pd.ProjectId into joinedPd
from jpd in joinedPd.DefaultIfEmpty()
join d in db.Disciplines on jpd.DisciplineId equals d.Id into joinedD
from jd in joinedD.DefaultIfEmpty()
join pi in db.ProjectInstitutes on p.Id equals pi.ProjectId into joinedPi
from jpi in joinedPi.DefaultIfEmpty()
where p.Deleted == false &&
((!showSubProjects && !allSubProjectsList.Contains(p.Id)) ||
(showSubProjects && allSubProjectsList.Contains(p.Id))) &&
(jpr.UserId == userId || jv.DisplayName == "Public") &&
(searchQuery == "" ||
p.ProjectName.Contains(searchQuery) ||
p.Description.Contains(searchQuery) ||
p.Keywords.Contains(searchQuery) ||
p.DisplayName.Contains(searchQuery) ||
p.PrincipleInvestigators.Contains(searchQuery) ||
p.GrantId.Contains(searchQuery) ||
jv.DisplayName.Contains(searchQuery) ||
jd.Url.Contains(searchQuery) ||
jd.DisplayNameDe.Contains(searchQuery) ||
jd.DisplayNameEn.Contains(searchQuery)) ||
projectsToAppend.Contains(p.Id.ToString())
select projectModel.CreateReturnObjectFromDatabaseObject(p)).ToList().OrderBy(element => element.DisplayName).DistinctBy(p => p.Id).ToList();
});
}
/// <summary>
/// This method searches through resources
/// </summary>
/// <param name="userId">ID of the user</param>
/// <param name="searchQuery">Search Query</param>
/// <param name="resourcesToAppend">List of resource IDs to include in the result</param>
/// <returns>Found resources</returns>
public static IEnumerable<ResourceSearchResult> SearchForResources(Guid userId, string searchQuery, List<string> resourcesToAppend = null)
{
if (string.IsNullOrEmpty(searchQuery) || searchQuery == "*")
{
searchQuery = "";
}
if (resourcesToAppend is null)
{
resourcesToAppend = new List<string>();
}
var resourceModel = new ResourceModel();
var resourceObjects = DatabaseConnection.ConnectToDatabase((db) =>
{
var resourceObjects = (from r in db.Resources
join pres in db.ProjectResources on r.Id equals pres.ResourceId into joinedPres
from jpres in joinedPres.DefaultIfEmpty()
join p in db.Projects on jpres.ProjectId equals p.Id into joinedP
from jp in joinedP.DefaultIfEmpty()
join pr in db.ProjectRoles on jp.Id equals pr.ProjectId into joinedPr
from jpr in joinedPr.DefaultIfEmpty()
join v in db.Visibilities on r.VisibilityId equals v.Id into joinedV
from jv in joinedV.DefaultIfEmpty()
join rd in db.ResourceDisciplines on r.Id equals rd.ResourceId into joinedRd
from jrd in joinedRd.DefaultIfEmpty()
join d in db.Disciplines on jrd.DisciplineId equals d.Id into joinedD
from jd in joinedD.DefaultIfEmpty()
join l in db.Licenses on r.LicenseId equals l.Id into joinedL
from jl in joinedL.DefaultIfEmpty()
join rt in db.ResourceTypes on r.TypeId equals rt.Id into joinedRt
from jrt in joinedRt.DefaultIfEmpty()
where jp.Deleted == false &&
(jpr.UserId == userId || jv.DisplayName == "Public") &&
(searchQuery == "" ||
r.ResourceName.Contains(searchQuery) ||
r.DisplayName.Contains(searchQuery) ||
r.ResourceName.Contains(searchQuery) ||
r.Keywords.Contains(searchQuery) ||
r.UsageRights.Contains(searchQuery) ||
r.Description.Contains(searchQuery) ||
r.ApplicationProfile.Contains(searchQuery) ||
jrt.DisplayName.Contains(searchQuery) ||
jl.DisplayName.Contains(searchQuery) ||
jd.DisplayNameDe.Contains(searchQuery) ||
jd.DisplayNameEn.Contains(searchQuery)) ||
resourcesToAppend.Contains(r.Id.ToString())
select resourceModel.CreateReturnObjectFromDatabaseObject(r)).ToList().OrderBy(element => element.DisplayName).DistinctBy(r => r.Id).ToList();
// Remove fixed values
foreach (var resource in resourceObjects)
{
resource.FixedValues = null;
}
return resourceObjects;
});
var projectResourceModel = new ProjectResourceModel();
var parentProjects = projectResourceModel.GetAll();
static Guid? getParentId(ProjectResource projectObject = null)
{
if (projectObject is not null)
{
return projectObject.ProjectId;
}
return null;
}
var result = new List<ResourceSearchResult>(resourceObjects.Count);
result.AddRange(resourceObjects.Select(i => new ResourceSearchResult(i, getParentId(parentProjects.FirstOrDefault(p => p.ResourceId.Equals(i.Id))))));
return result;
}
private static Guid GetPublicVisibilityId()
{
var visibilityModel = new VisibilityModel();
return visibilityModel.GetWhere(v => v.DisplayName.ToLower().Contains("public")).Id; // Handle null.Id case?
}
}
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Coscine.Api.Search.Models;
/// <summary>
/// File Search Result Object
/// </summary>
public class FileSearchResult
{
/// <summary>
/// Graph Name
/// </summary>
[JsonProperty("graphName")]
public Uri GraphName { get; set; }
/// <summary>
/// ID of Project containing the file
/// </summary>
[JsonProperty("projectId")]
public string ProjectId { get; set; }
/// <summary>
/// ID of Resource containing the file
/// </summary>
[JsonProperty("resourceId")]
public string ResourceId { get; set; }
/// <summary>
/// Search result fields
/// </summary>
[JsonProperty("source")]
public JObject Source { get; set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="key">Search result entry key containing the resource id</param>
/// <param name="projectValue">URI containing the project id</param>
/// <param name="value">Search result</param>
public FileSearchResult(string key, string projectValue, JObject value)
{
GraphName = new Uri(key);
ProjectId = GetGuid(projectValue);
ResourceId = GetGuid(key);
Source = value;
}
/// <summary>
/// Method to convert the Semantic Search dictionary to a list of File Search Result Objects
/// </summary>
/// <param name="results"></param>
/// <returns></returns>
public static IEnumerable<FileSearchResult> ParseResultsToList(IDictionary<string, JObject> results)
{
var output = new List<FileSearchResult>();
foreach (var entry in results)
{
var searchResult = new FileSearchResult(entry.Key, (string)entry.Value["belongsToProject"], entry.Value);
output.Add(searchResult);
}
return output;
}
private static string GetGuid(string value)
{
var regex = new Regex(@"[({]?[a-fA-F0-9]{8}[-]?([a-fA-F0-9]{4}[-]?){3}[a-fA-F0-9]{12}[})]?", RegexOptions.IgnoreCase);
var match = regex.Match(value);
return match.Success ? match.Value : null;
}
}
using Coscine.Database.ReturnObjects;
using System;
namespace Coscine.Api.Search.Models;
public class ResourceSearchResult : ResourceObject
{
public ResourceSearchResult(ResourceObject resourceObject, Guid? parentProjectId) : base(resourceObject.Id, resourceObject.DisplayName, resourceObject.ResourceName, resourceObject.Description, resourceObject.Keywords, resourceObject.UsageRights, resourceObject.Type, resourceObject.Disciplines, resourceObject.Visibility, resourceObject.License, resourceObject.ResourceTypeOption, resourceObject.ApplicationProfile, null, resourceObject.Creator, resourceObject.Archived)
{
ProjectId = parentProjectId;
}
public Guid? ProjectId { get; set; }
}
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using Coscine.Database.ReturnObjects;
using System.Collections.Generic;
namespace Coscine.Api.Search.Models
{
/// <summary>
/// Search result return object
/// </summary>
public class SearchResult
{
[JsonProperty("graphName")]
public Uri GraphName { get; set; }
[JsonProperty("project")]
public Uri Project { get; set; }
[JsonProperty("source")]
public JObject Source { get; set; }
public SearchResult(string key, string projectValue, JObject value)
{
GraphName = new Uri(key);
Project = new Uri(projectValue);
Source = value;
}
public static IEnumerable<SearchResult> ParseResultsToList(IDictionary<string, JObject> results)
{
var output = new List<SearchResult>();
foreach (var entry in results)
{
var searchResult = new SearchResult(entry.Key, (string)entry.Value["belongsToProject"], entry.Value);
output.Add(searchResult);
}
return output;
}
/// <summary>
/// Search result projects
/// </summary>
public IEnumerable<ProjectObject> Projects { get; set; }
/// <summary>
/// Search result resources
/// </summary>
public IEnumerable<ResourceSearchResult> Resources { get; set; }
/// <summary>
/// Search result files
/// </summary>
public IEnumerable<FileSearchResult> Files { get; set; }
}
}
......@@ -5,12 +5,12 @@
<AssemblyName>Coscine.Api.Search</AssemblyName>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TargetFramework>net6.0</TargetFramework>
<Version>1.3.2</Version>
<Version>1.4.0</Version>
</PropertyGroup>
<PropertyGroup>
<Authors>RWTH Aachen University</Authors>
<Company>IT Center, RWTH Aachen University</Company>
<Copyright>2021 IT Center, RWTH Aachen University</Copyright>
<Copyright>2022 IT Center, RWTH Aachen University</Copyright>
<Description>Search is a part of the Coscine group.</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://git.rwth-aachen.de/coscine/backend/apis/Search</PackageProjectUrl>
......@@ -20,6 +20,6 @@
<PackageReference Include="Coscine.ApiCommons" Version="2.*-*" />
<PackageReference Include="Coscine.Database" Version="2.*-*" />
<PackageReference Include="Coscine.SemanticSearch" Version="1.*-*" />
<PackageReference Include="Microsoft.AspNet.WebApi.Core" Version="5.2.7" />
<PackageReference Include="Microsoft.AspNet.WebApi.Core" Version="5.2.9" />
</ItemGroup>
</Project>