Browse Source

added new search params

Luke Pulverenti 11 years ago
parent
commit
650dc0ccac

+ 44 - 30
MediaBrowser.Api/SearchService.cs

@@ -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);
-            }
-
-            if (request.Limit.HasValue)
-            {
-                returnResults = returnResults.Take(request.Limit.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
+
+            }).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();
 

+ 4 - 5
MediaBrowser.Controller/Library/ISearchEngine.cs

@@ -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);
     }
 }

+ 3 - 0
MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj

@@ -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>

+ 3 - 0
MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj

@@ -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>

+ 4 - 7
MediaBrowser.Model/ApiClient/IApiClient.cs

@@ -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>

+ 1 - 0
MediaBrowser.Model/MediaBrowser.Model.csproj

@@ -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 - 0
MediaBrowser.Model/Search/SearchQuery.cs

@@ -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;
+        }
+    }
+}

+ 0 - 292
MediaBrowser.Server.Implementations/Library/LuceneSearchEngine.cs

@@ -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 - 0
MediaBrowser.Server.Implementations/Library/SearchEngine.cs

@@ -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();
+        }
+    }
+}

+ 1 - 1
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -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" />

+ 1 - 1
MediaBrowser.ServerApplication/ApplicationHost.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 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -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" />

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -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 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -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>