added new search params
This commit is contained in:
parent
5392ff4da4
commit
650dc0ccac
|
@ -7,9 +7,6 @@ using MediaBrowser.Controller.Library;
|
|||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Search;
|
||||
using ServiceStack;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -41,7 +38,7 @@ namespace MediaBrowser.Api
|
|||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "Optional. Supply a user id to search within a user's library or omit to search all.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public Guid? UserId { get; set; }
|
||||
public string UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Search characters used to find items
|
||||
|
@ -49,6 +46,31 @@ namespace MediaBrowser.Api
|
|||
/// <value>The index by.</value>
|
||||
[ApiMember(Name = "SearchTerm", Description = "The search term to filter on", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string SearchTerm { get; set; }
|
||||
|
||||
|
||||
[ApiMember(Name = "IncludePeople", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool IncludePeople { get; set; }
|
||||
|
||||
[ApiMember(Name = "IncludeMedia", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool IncludeMedia { get; set; }
|
||||
|
||||
[ApiMember(Name = "IncludeGenres", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool IncludeGenres { get; set; }
|
||||
|
||||
[ApiMember(Name = "IncludeStudios", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool IncludeStudios { get; set; }
|
||||
|
||||
[ApiMember(Name = "IncludeArtists", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool IncludeArtists { get; set; }
|
||||
|
||||
public GetSearchHints()
|
||||
{
|
||||
IncludeArtists = true;
|
||||
IncludeGenres = true;
|
||||
IncludeMedia = true;
|
||||
IncludePeople = true;
|
||||
IncludeStudios = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -56,10 +78,6 @@ namespace MediaBrowser.Api
|
|||
/// </summary>
|
||||
public class SearchService : BaseApiService
|
||||
{
|
||||
/// <summary>
|
||||
/// The _user manager
|
||||
/// </summary>
|
||||
private readonly IUserManager _userManager;
|
||||
/// <summary>
|
||||
/// The _search engine
|
||||
/// </summary>
|
||||
|
@ -71,12 +89,12 @@ namespace MediaBrowser.Api
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SearchService" /> class.
|
||||
/// </summary>
|
||||
/// <param name="userManager">The user manager.</param>
|
||||
/// <param name="searchEngine">The search engine.</param>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
public SearchService(IUserManager userManager, ISearchEngine searchEngine, ILibraryManager libraryManager, IDtoService dtoService, IImageProcessor imageProcessor)
|
||||
/// <param name="dtoService">The dto service.</param>
|
||||
/// <param name="imageProcessor">The image processor.</param>
|
||||
public SearchService(ISearchEngine searchEngine, ILibraryManager libraryManager, IDtoService dtoService, IImageProcessor imageProcessor)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_searchEngine = searchEngine;
|
||||
_libraryManager = libraryManager;
|
||||
_dtoService = dtoService;
|
||||
|
@ -102,29 +120,25 @@ namespace MediaBrowser.Api
|
|||
/// <returns>Task{IEnumerable{SearchHintResult}}.</returns>
|
||||
private async Task<SearchHintResult> GetSearchHintsAsync(GetSearchHints request)
|
||||
{
|
||||
var inputItems = GetAllLibraryItems(request.UserId, _userManager, _libraryManager);
|
||||
|
||||
var results = await _searchEngine.GetSearchHints(inputItems, request.SearchTerm).ConfigureAwait(false);
|
||||
|
||||
var searchResultArray = results.ToList();
|
||||
|
||||
IEnumerable<SearchHintInfo> returnResults = searchResultArray;
|
||||
|
||||
if (request.StartIndex.HasValue)
|
||||
var result = await _searchEngine.GetSearchHints(new SearchQuery
|
||||
{
|
||||
returnResults = returnResults.Skip(request.StartIndex.Value);
|
||||
}
|
||||
Limit = request.Limit,
|
||||
SearchTerm = request.SearchTerm,
|
||||
IncludeArtists = request.IncludeArtists,
|
||||
IncludeGenres = request.IncludeGenres,
|
||||
IncludeMedia = request.IncludeMedia,
|
||||
IncludePeople = request.IncludePeople,
|
||||
IncludeStudios = request.IncludeStudios,
|
||||
StartIndex = request.StartIndex,
|
||||
UserId = request.UserId
|
||||
|
||||
if (request.Limit.HasValue)
|
||||
{
|
||||
returnResults = returnResults.Take(request.Limit.Value);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
return new SearchHintResult
|
||||
{
|
||||
TotalRecordCount = searchResultArray.Count,
|
||||
TotalRecordCount = result.TotalRecordCount,
|
||||
|
||||
SearchHints = returnResults.Select(GetSearchHintResult).ToArray()
|
||||
SearchHints = result.Items.Select(GetSearchHintResult).ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -189,7 +203,7 @@ namespace MediaBrowser.Api
|
|||
var songs = album.GetRecursiveChildren().OfType<Audio>().ToList();
|
||||
|
||||
result.SongCount = songs.Count;
|
||||
|
||||
|
||||
result.Artists = _libraryManager.GetAllArtists(songs)
|
||||
.ToArray();
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Search;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Controller.Library
|
||||
|
@ -12,9 +12,8 @@ namespace MediaBrowser.Controller.Library
|
|||
/// <summary>
|
||||
/// Gets the search hints.
|
||||
/// </summary>
|
||||
/// <param name="inputItems">The input items.</param>
|
||||
/// <param name="searchTerm">The search term.</param>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>Task{IEnumerable{SearchHintInfo}}.</returns>
|
||||
Task<IEnumerable<SearchHintInfo>> GetSearchHints(IEnumerable<BaseItem> inputItems, string searchTerm);
|
||||
Task<QueryResult<SearchHintInfo>> GetSearchHints(SearchQuery query);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -374,6 +374,9 @@
|
|||
<Compile Include="..\MediaBrowser.Model\Search\SearchHintResult.cs">
|
||||
<Link>Search\SearchHintResult.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Search\SearchQuery.cs">
|
||||
<Link>Search\SearchQuery.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Serialization\IJsonSerializer.cs">
|
||||
<Link>Serialization\IJsonSerializer.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
@ -361,6 +361,9 @@
|
|||
<Compile Include="..\MediaBrowser.Model\Search\SearchHintResult.cs">
|
||||
<Link>Search\SearchHintResult.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Search\SearchQuery.cs">
|
||||
<Link>Search\SearchQuery.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Serialization\IJsonSerializer.cs">
|
||||
<Link>Serialization\IJsonSerializer.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
@ -107,13 +107,9 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// <summary>
|
||||
/// Gets the search hints async.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user id.</param>
|
||||
/// <param name="searchTerm">The search term.</param>
|
||||
/// <param name="startIndex">The start index.</param>
|
||||
/// <param name="limit">The limit.</param>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>Task{SearchHintResult}.</returns>
|
||||
Task<SearchHintResult> GetSearchHintsAsync(string userId, string searchTerm, int? startIndex = null,
|
||||
int? limit = null);
|
||||
Task<SearchHintResult> GetSearchHintsAsync(SearchQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the theme videos async.
|
||||
|
@ -682,8 +678,9 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="url">The URL.</param>
|
||||
/// <param name="args">The args.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{``0}.</returns>
|
||||
Task<T> PostAsync<T>(string url, Dictionary<string, string> args)
|
||||
Task<T> PostAsync<T>(string url, Dictionary<string, string> args, CancellationToken cancellationToken)
|
||||
where T : class;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -115,6 +115,7 @@
|
|||
<Compile Include="Querying\SessionQuery.cs" />
|
||||
<Compile Include="Querying\SimilarItemsQuery.cs" />
|
||||
<Compile Include="Querying\UserQuery.cs" />
|
||||
<Compile Include="Search\SearchQuery.cs" />
|
||||
<Compile Include="Session\BrowseRequest.cs" />
|
||||
<Compile Include="Session\MessageCommand.cs" />
|
||||
<Compile Include="Session\PlayRequest.cs" />
|
||||
|
|
45
MediaBrowser.Model/Search/SearchQuery.cs
Normal file
45
MediaBrowser.Model/Search/SearchQuery.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
|
||||
namespace MediaBrowser.Model.Search
|
||||
{
|
||||
public class SearchQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// The user to localize search results for
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public string UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the search term.
|
||||
/// </summary>
|
||||
/// <value>The search term.</value>
|
||||
public string SearchTerm { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Skips over a given number of items within the results. Use for paging.
|
||||
/// </summary>
|
||||
/// <value>The start index.</value>
|
||||
public int? StartIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of items to return
|
||||
/// </summary>
|
||||
/// <value>The limit.</value>
|
||||
public int? Limit { get; set; }
|
||||
|
||||
public bool IncludePeople { get; set; }
|
||||
public bool IncludeMedia { get; set; }
|
||||
public bool IncludeGenres { get; set; }
|
||||
public bool IncludeStudios { get; set; }
|
||||
public bool IncludeArtists { get; set; }
|
||||
|
||||
public SearchQuery()
|
||||
{
|
||||
IncludeArtists = true;
|
||||
IncludeGenres = true;
|
||||
IncludeMedia = true;
|
||||
IncludePeople = true;
|
||||
IncludeStudios = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,292 +0,0 @@
|
|||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// Class LuceneSearchEngine
|
||||
/// http://www.codeproject.com/Articles/320219/Lucene-Net-ultra-fast-search-for-MVC-or-WebForms
|
||||
/// </summary>
|
||||
public class LuceneSearchEngine : ISearchEngine, IDisposable
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public LuceneSearchEngine(IServerApplicationPaths serverPaths, ILogManager logManager, ILibraryManager libraryManager)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
|
||||
_logger = logManager.GetLogger("Lucene");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the search hints.
|
||||
/// </summary>
|
||||
/// <param name="inputItems">The input items.</param>
|
||||
/// <param name="searchTerm">The search term.</param>
|
||||
/// <returns>IEnumerable{SearchHintResult}.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">searchTerm</exception>
|
||||
public Task<IEnumerable<SearchHintInfo>> GetSearchHints(IEnumerable<BaseItem> inputItems, string searchTerm)
|
||||
{
|
||||
if (string.IsNullOrEmpty(searchTerm))
|
||||
{
|
||||
throw new ArgumentNullException("searchTerm");
|
||||
}
|
||||
|
||||
var terms = GetWords(searchTerm);
|
||||
|
||||
var hints = new List<Tuple<BaseItem, string, int>>();
|
||||
|
||||
var items = inputItems.Where(i => !(i is MusicArtist)).ToList();
|
||||
|
||||
// Add search hints based on item name
|
||||
hints.AddRange(items.Where(i => !string.IsNullOrEmpty(i.Name)).Select(item =>
|
||||
{
|
||||
var index = GetIndex(item.Name, searchTerm, terms);
|
||||
|
||||
return new Tuple<BaseItem, string, int>(item, index.Item1, index.Item2);
|
||||
}));
|
||||
|
||||
// Find artists
|
||||
var artists = _libraryManager.GetAllArtists(items)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in artists)
|
||||
{
|
||||
var index = GetIndex(item, searchTerm, terms);
|
||||
|
||||
if (index.Item2 != -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var artist = _libraryManager.GetArtist(item);
|
||||
|
||||
hints.Add(new Tuple<BaseItem, string, int>(artist, index.Item1, index.Item2));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting {0}", ex, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find genres, from non-audio items
|
||||
var genres = items.Where(i => !(i is IHasMusicGenres) && !(i is Game))
|
||||
.SelectMany(i => i.Genres)
|
||||
.Where(i => !string.IsNullOrEmpty(i))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in genres)
|
||||
{
|
||||
var index = GetIndex(item, searchTerm, terms);
|
||||
|
||||
if (index.Item2 != -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var genre = _libraryManager.GetGenre(item);
|
||||
|
||||
hints.Add(new Tuple<BaseItem, string, int>(genre, index.Item1, index.Item2));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting {0}", ex, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find music genres
|
||||
var musicGenres = items.Where(i => i is IHasMusicGenres)
|
||||
.SelectMany(i => i.Genres)
|
||||
.Where(i => !string.IsNullOrEmpty(i))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in musicGenres)
|
||||
{
|
||||
var index = GetIndex(item, searchTerm, terms);
|
||||
|
||||
if (index.Item2 != -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var genre = _libraryManager.GetMusicGenre(item);
|
||||
|
||||
hints.Add(new Tuple<BaseItem, string, int>(genre, index.Item1, index.Item2));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting {0}", ex, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find music genres
|
||||
var gameGenres = items.OfType<Game>()
|
||||
.SelectMany(i => i.Genres)
|
||||
.Where(i => !string.IsNullOrEmpty(i))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in gameGenres)
|
||||
{
|
||||
var index = GetIndex(item, searchTerm, terms);
|
||||
|
||||
if (index.Item2 != -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var genre = _libraryManager.GetGameGenre(item);
|
||||
|
||||
hints.Add(new Tuple<BaseItem, string, int>(genre, index.Item1, index.Item2));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting {0}", ex, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find studios
|
||||
var studios = items.SelectMany(i => i.Studios)
|
||||
.Where(i => !string.IsNullOrEmpty(i))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in studios)
|
||||
{
|
||||
var index = GetIndex(item, searchTerm, terms);
|
||||
|
||||
if (index.Item2 != -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var studio = _libraryManager.GetStudio(item);
|
||||
|
||||
hints.Add(new Tuple<BaseItem, string, int>(studio, index.Item1, index.Item2));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting {0}", ex, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find persons
|
||||
var persons = items.SelectMany(i => i.People)
|
||||
.Select(i => i.Name)
|
||||
.Where(i => !string.IsNullOrEmpty(i))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in persons)
|
||||
{
|
||||
var index = GetIndex(item, searchTerm, terms);
|
||||
|
||||
if (index.Item2 != -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var person = _libraryManager.GetPerson(item);
|
||||
|
||||
hints.Add(new Tuple<BaseItem, string, int>(person, index.Item1, index.Item2));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting {0}", ex, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var returnValue = hints.Where(i => i.Item3 >= 0).OrderBy(i => i.Item3).Select(i => new SearchHintInfo
|
||||
{
|
||||
Item = i.Item1,
|
||||
MatchedTerm = i.Item2
|
||||
});
|
||||
|
||||
return Task.FromResult(returnValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <param name="searchInput">The search input.</param>
|
||||
/// <param name="searchWords">The search input.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
private Tuple<string, int> GetIndex(string input, string searchInput, List<string> searchWords)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
throw new ArgumentNullException("input");
|
||||
}
|
||||
|
||||
if (string.Equals(input, searchInput, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new Tuple<string, int>(searchInput, 0);
|
||||
}
|
||||
|
||||
var index = input.IndexOf(searchInput, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
return new Tuple<string, int>(searchInput, 1);
|
||||
}
|
||||
if (index > 0)
|
||||
{
|
||||
return new Tuple<string, int>(searchInput, 2);
|
||||
}
|
||||
|
||||
var items = GetWords(input);
|
||||
|
||||
for (var i = 0; i < searchWords.Count; i++)
|
||||
{
|
||||
var searchTerm = searchWords[i];
|
||||
|
||||
for (var j = 0; j < items.Count; j++)
|
||||
{
|
||||
var item = items[j];
|
||||
|
||||
if (string.Equals(item, searchTerm, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new Tuple<string, int>(searchTerm, 3 + (i + 1) * (j + 1));
|
||||
}
|
||||
|
||||
index = item.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
return new Tuple<string, int>(searchTerm, 4 + (i + 1) * (j + 1));
|
||||
}
|
||||
if (index > 0)
|
||||
{
|
||||
return new Tuple<string, int>(searchTerm, 5 + (i + 1) * (j + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Tuple<string, int>(null, -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the words.
|
||||
/// </summary>
|
||||
/// <param name="term">The term.</param>
|
||||
/// <returns>System.String[][].</returns>
|
||||
private List<string> GetWords(string term)
|
||||
{
|
||||
return term.Split().Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
|
||||
}
|
||||
}
|
||||
}
|
339
MediaBrowser.Server.Implementations/Library/SearchEngine.cs
Normal file
339
MediaBrowser.Server.Implementations/Library/SearchEngine.cs
Normal file
|
@ -0,0 +1,339 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Search;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// Class LuceneSearchEngine
|
||||
/// http://www.codeproject.com/Articles/320219/Lucene-Net-ultra-fast-search-for-MVC-or-WebForms
|
||||
/// </summary>
|
||||
public class SearchEngine : ISearchEngine
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public SearchEngine(ILogManager logManager, ILibraryManager libraryManager, IUserManager userManager)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_userManager = userManager;
|
||||
|
||||
_logger = logManager.GetLogger("Lucene");
|
||||
}
|
||||
|
||||
public async Task<QueryResult<SearchHintInfo>> GetSearchHints(SearchQuery query)
|
||||
{
|
||||
var user = _userManager.GetUserById(new Guid(query.UserId));
|
||||
|
||||
var inputItems = user.RootFolder.GetRecursiveChildren(user, null);
|
||||
|
||||
var results = await GetSearchHints(inputItems, query).ConfigureAwait(false);
|
||||
|
||||
var searchResultArray = results.ToArray();
|
||||
results = searchResultArray;
|
||||
|
||||
var count = searchResultArray.Length;
|
||||
|
||||
if (query.StartIndex.HasValue)
|
||||
{
|
||||
results = results.Skip(query.StartIndex.Value);
|
||||
}
|
||||
|
||||
if (query.Limit.HasValue)
|
||||
{
|
||||
results = results.Take(query.Limit.Value);
|
||||
}
|
||||
|
||||
return new QueryResult<SearchHintInfo>
|
||||
{
|
||||
TotalRecordCount = count,
|
||||
|
||||
Items = results.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the search hints.
|
||||
/// </summary>
|
||||
/// <param name="inputItems">The input items.</param>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>IEnumerable{SearchHintResult}.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">searchTerm</exception>
|
||||
private Task<IEnumerable<SearchHintInfo>> GetSearchHints(IEnumerable<BaseItem> inputItems, SearchQuery query)
|
||||
{
|
||||
var searchTerm = query.SearchTerm;
|
||||
|
||||
if (string.IsNullOrEmpty(searchTerm))
|
||||
{
|
||||
throw new ArgumentNullException("searchTerm");
|
||||
}
|
||||
|
||||
var terms = GetWords(searchTerm);
|
||||
|
||||
var hints = new List<Tuple<BaseItem, string, int>>();
|
||||
|
||||
var items = inputItems.Where(i => !(i is MusicArtist)).ToList();
|
||||
|
||||
if (query.IncludeMedia)
|
||||
{
|
||||
// Add search hints based on item name
|
||||
hints.AddRange(items.Where(i => !string.IsNullOrEmpty(i.Name)).Select(item =>
|
||||
{
|
||||
var index = GetIndex(item.Name, searchTerm, terms);
|
||||
|
||||
return new Tuple<BaseItem, string, int>(item, index.Item1, index.Item2);
|
||||
}));
|
||||
}
|
||||
|
||||
if (query.IncludeArtists)
|
||||
{
|
||||
// Find artists
|
||||
var artists = _libraryManager.GetAllArtists(items)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in artists)
|
||||
{
|
||||
var index = GetIndex(item, searchTerm, terms);
|
||||
|
||||
if (index.Item2 != -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var artist = _libraryManager.GetArtist(item);
|
||||
|
||||
hints.Add(new Tuple<BaseItem, string, int>(artist, index.Item1, index.Item2));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting {0}", ex, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (query.IncludeGenres)
|
||||
{
|
||||
// Find genres, from non-audio items
|
||||
var genres = items.Where(i => !(i is IHasMusicGenres) && !(i is Game))
|
||||
.SelectMany(i => i.Genres)
|
||||
.Where(i => !string.IsNullOrEmpty(i))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in genres)
|
||||
{
|
||||
var index = GetIndex(item, searchTerm, terms);
|
||||
|
||||
if (index.Item2 != -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var genre = _libraryManager.GetGenre(item);
|
||||
|
||||
hints.Add(new Tuple<BaseItem, string, int>(genre, index.Item1, index.Item2));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting {0}", ex, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find music genres
|
||||
var musicGenres = items.Where(i => i is IHasMusicGenres)
|
||||
.SelectMany(i => i.Genres)
|
||||
.Where(i => !string.IsNullOrEmpty(i))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in musicGenres)
|
||||
{
|
||||
var index = GetIndex(item, searchTerm, terms);
|
||||
|
||||
if (index.Item2 != -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var genre = _libraryManager.GetMusicGenre(item);
|
||||
|
||||
hints.Add(new Tuple<BaseItem, string, int>(genre, index.Item1, index.Item2));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting {0}", ex, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find music genres
|
||||
var gameGenres = items.OfType<Game>()
|
||||
.SelectMany(i => i.Genres)
|
||||
.Where(i => !string.IsNullOrEmpty(i))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in gameGenres)
|
||||
{
|
||||
var index = GetIndex(item, searchTerm, terms);
|
||||
|
||||
if (index.Item2 != -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var genre = _libraryManager.GetGameGenre(item);
|
||||
|
||||
hints.Add(new Tuple<BaseItem, string, int>(genre, index.Item1, index.Item2));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting {0}", ex, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (query.IncludeStudios)
|
||||
{
|
||||
// Find studios
|
||||
var studios = items.SelectMany(i => i.Studios)
|
||||
.Where(i => !string.IsNullOrEmpty(i))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in studios)
|
||||
{
|
||||
var index = GetIndex(item, searchTerm, terms);
|
||||
|
||||
if (index.Item2 != -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var studio = _libraryManager.GetStudio(item);
|
||||
|
||||
hints.Add(new Tuple<BaseItem, string, int>(studio, index.Item1, index.Item2));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting {0}", ex, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (query.IncludePeople)
|
||||
{
|
||||
// Find persons
|
||||
var persons = items.SelectMany(i => i.People)
|
||||
.Select(i => i.Name)
|
||||
.Where(i => !string.IsNullOrEmpty(i))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in persons)
|
||||
{
|
||||
var index = GetIndex(item, searchTerm, terms);
|
||||
|
||||
if (index.Item2 != -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var person = _libraryManager.GetPerson(item);
|
||||
|
||||
hints.Add(new Tuple<BaseItem, string, int>(person, index.Item1, index.Item2));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting {0}", ex, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var returnValue = hints.Where(i => i.Item3 >= 0).OrderBy(i => i.Item3).Select(i => new SearchHintInfo
|
||||
{
|
||||
Item = i.Item1,
|
||||
MatchedTerm = i.Item2
|
||||
});
|
||||
|
||||
return Task.FromResult(returnValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <param name="searchInput">The search input.</param>
|
||||
/// <param name="searchWords">The search input.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
private Tuple<string, int> GetIndex(string input, string searchInput, List<string> searchWords)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
throw new ArgumentNullException("input");
|
||||
}
|
||||
|
||||
if (string.Equals(input, searchInput, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new Tuple<string, int>(searchInput, 0);
|
||||
}
|
||||
|
||||
var index = input.IndexOf(searchInput, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
return new Tuple<string, int>(searchInput, 1);
|
||||
}
|
||||
if (index > 0)
|
||||
{
|
||||
return new Tuple<string, int>(searchInput, 2);
|
||||
}
|
||||
|
||||
var items = GetWords(input);
|
||||
|
||||
for (var i = 0; i < searchWords.Count; i++)
|
||||
{
|
||||
var searchTerm = searchWords[i];
|
||||
|
||||
for (var j = 0; j < items.Count; j++)
|
||||
{
|
||||
var item = items[j];
|
||||
|
||||
if (string.Equals(item, searchTerm, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new Tuple<string, int>(searchTerm, 3 + (i + 1) * (j + 1));
|
||||
}
|
||||
|
||||
index = item.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
return new Tuple<string, int>(searchTerm, 4 + (i + 1) * (j + 1));
|
||||
}
|
||||
if (index > 0)
|
||||
{
|
||||
return new Tuple<string, int>(searchTerm, 5 + (i + 1) * (j + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Tuple<string, int>(null, -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the words.
|
||||
/// </summary>
|
||||
/// <param name="term">The term.</param>
|
||||
/// <returns>System.String[][].</returns>
|
||||
private List<string> GetWords(string term)
|
||||
{
|
||||
return term.Split().Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -127,7 +127,7 @@
|
|||
<Compile Include="IO\DirectoryWatchers.cs" />
|
||||
<Compile Include="Library\CoreResolutionIgnoreRule.cs" />
|
||||
<Compile Include="Library\LibraryManager.cs" />
|
||||
<Compile Include="Library\LuceneSearchEngine.cs" />
|
||||
<Compile Include="Library\SearchEngine.cs" />
|
||||
<Compile Include="Library\ResolverHelper.cs" />
|
||||
<Compile Include="Library\Resolvers\Audio\AudioResolver.cs" />
|
||||
<Compile Include="Library\Resolvers\Audio\MusicAlbumResolver.cs" />
|
||||
|
|
|
@ -257,7 +257,7 @@ namespace MediaBrowser.ServerApplication
|
|||
ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager, FileSystemManager, ItemRepository);
|
||||
RegisterSingleInstance(ProviderManager);
|
||||
|
||||
RegisterSingleInstance<ISearchEngine>(() => new LuceneSearchEngine(ApplicationPaths, LogManager, LibraryManager));
|
||||
RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LogManager, LibraryManager, UserManager));
|
||||
|
||||
SessionManager = new SessionManager(UserDataManager, ServerConfigurationManager, Logger, UserRepository, LibraryManager, UserManager);
|
||||
RegisterSingleInstance(SessionManager);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>MediaBrowser.Common.Internal</id>
|
||||
<version>3.0.297</version>
|
||||
<version>3.0.298</version>
|
||||
<title>MediaBrowser.Common.Internal</title>
|
||||
<authors>Luke</authors>
|
||||
<owners>ebr,Luke,scottisafool</owners>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
|
||||
<copyright>Copyright © Media Browser 2013</copyright>
|
||||
<dependencies>
|
||||
<dependency id="MediaBrowser.Common" version="3.0.297" />
|
||||
<dependency id="MediaBrowser.Common" version="3.0.298" />
|
||||
<dependency id="NLog" version="2.1.0" />
|
||||
<dependency id="SimpleInjector" version="2.4.0" />
|
||||
<dependency id="sharpcompress" version="0.10.2" />
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>MediaBrowser.Common</id>
|
||||
<version>3.0.297</version>
|
||||
<version>3.0.298</version>
|
||||
<title>MediaBrowser.Common</title>
|
||||
<authors>Media Browser Team</authors>
|
||||
<owners>ebr,Luke,scottisafool</owners>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>MediaBrowser.Server.Core</id>
|
||||
<version>3.0.297</version>
|
||||
<version>3.0.298</version>
|
||||
<title>Media Browser.Server.Core</title>
|
||||
<authors>Media Browser Team</authors>
|
||||
<owners>ebr,Luke,scottisafool</owners>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<description>Contains core components required to build plugins for Media Browser Server.</description>
|
||||
<copyright>Copyright © Media Browser 2013</copyright>
|
||||
<dependencies>
|
||||
<dependency id="MediaBrowser.Common" version="3.0.297" />
|
||||
<dependency id="MediaBrowser.Common" version="3.0.298" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
|
|
Loading…
Reference in New Issue
Block a user