|
@@ -1,9 +1,4 @@
|
|
-using System;
|
|
|
|
-using System.Collections.Generic;
|
|
|
|
-using System.IO;
|
|
|
|
-using System.Linq;
|
|
|
|
-using System.Threading.Tasks;
|
|
|
|
-using Lucene.Net.Analysis.Standard;
|
|
|
|
|
|
+using Lucene.Net.Analysis.Standard;
|
|
using Lucene.Net.Documents;
|
|
using Lucene.Net.Documents;
|
|
using Lucene.Net.Index;
|
|
using Lucene.Net.Index;
|
|
using Lucene.Net.QueryParsers;
|
|
using Lucene.Net.QueryParsers;
|
|
@@ -11,8 +6,13 @@ using Lucene.Net.Search;
|
|
using Lucene.Net.Store;
|
|
using Lucene.Net.Store;
|
|
using MediaBrowser.Controller;
|
|
using MediaBrowser.Controller;
|
|
using MediaBrowser.Controller.Entities;
|
|
using MediaBrowser.Controller.Entities;
|
|
|
|
+using MediaBrowser.Controller.Entities.Audio;
|
|
using MediaBrowser.Controller.Library;
|
|
using MediaBrowser.Controller.Library;
|
|
using MediaBrowser.Model.Logging;
|
|
using MediaBrowser.Model.Logging;
|
|
|
|
+using System;
|
|
|
|
+using System.Collections.Generic;
|
|
|
|
+using System.Linq;
|
|
|
|
+using System.Threading.Tasks;
|
|
|
|
|
|
namespace MediaBrowser.Server.Implementations.Library
|
|
namespace MediaBrowser.Server.Implementations.Library
|
|
{
|
|
{
|
|
@@ -22,15 +22,22 @@ namespace MediaBrowser.Server.Implementations.Library
|
|
/// </summary>
|
|
/// </summary>
|
|
public class LuceneSearchEngine : ILibrarySearchEngine, IDisposable
|
|
public class LuceneSearchEngine : ILibrarySearchEngine, IDisposable
|
|
{
|
|
{
|
|
- public LuceneSearchEngine(IServerApplicationPaths serverPaths, ILogManager logManager)
|
|
|
|
|
|
+ private readonly ILibraryManager _libraryManager;
|
|
|
|
+ private readonly ILogger _logger;
|
|
|
|
+
|
|
|
|
+ public LuceneSearchEngine(IServerApplicationPaths serverPaths, ILogManager logManager, ILibraryManager libraryManager)
|
|
{
|
|
{
|
|
|
|
+ _libraryManager = libraryManager;
|
|
|
|
+
|
|
|
|
+ _logger = logManager.GetLogger("Lucene");
|
|
|
|
+
|
|
//string luceneDbPath = serverPaths.DataPath + "\\SearchIndexDB";
|
|
//string luceneDbPath = serverPaths.DataPath + "\\SearchIndexDB";
|
|
//if (!System.IO.Directory.Exists(luceneDbPath))
|
|
//if (!System.IO.Directory.Exists(luceneDbPath))
|
|
// System.IO.Directory.CreateDirectory(luceneDbPath);
|
|
// System.IO.Directory.CreateDirectory(luceneDbPath);
|
|
//else if(File.Exists(luceneDbPath + "\\write.lock"))
|
|
//else if(File.Exists(luceneDbPath + "\\write.lock"))
|
|
// File.Delete(luceneDbPath + "\\write.lock");
|
|
// File.Delete(luceneDbPath + "\\write.lock");
|
|
|
|
|
|
- //LuceneSearch.Init(luceneDbPath, logManager.GetLogger("Lucene"));
|
|
|
|
|
|
+ //LuceneSearch.Init(luceneDbPath, _logger);
|
|
|
|
|
|
//BaseItem.LibraryManager.LibraryChanged += LibraryChanged;
|
|
//BaseItem.LibraryManager.LibraryChanged += LibraryChanged;
|
|
}
|
|
}
|
|
@@ -82,6 +89,164 @@ namespace MediaBrowser.Server.Implementations.Library
|
|
|
|
|
|
//LuceneSearch.CloseAll();
|
|
//LuceneSearch.CloseAll();
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /// <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 async Task<IEnumerable<BaseItem>> GetSearchHints(IEnumerable<BaseItem> inputItems, string searchTerm)
|
|
|
|
+ {
|
|
|
|
+ if (string.IsNullOrEmpty(searchTerm))
|
|
|
|
+ {
|
|
|
|
+ throw new ArgumentNullException("searchTerm");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var hints = new List<Tuple<BaseItem, int>>();
|
|
|
|
+
|
|
|
|
+ var items = inputItems.Where(i => !(i is MusicArtist)).ToList();
|
|
|
|
+
|
|
|
|
+ foreach (var item in items)
|
|
|
|
+ {
|
|
|
|
+ var index = IndexOf(item.Name, searchTerm);
|
|
|
|
+
|
|
|
|
+ if (index != -1)
|
|
|
|
+ {
|
|
|
|
+ hints.Add(new Tuple<BaseItem, int>(item, index));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var artists = items.OfType<Audio>()
|
|
|
|
+ .SelectMany(i => new[] { i.Artist, i.AlbumArtist })
|
|
|
|
+ .Where(i => !string.IsNullOrEmpty(i))
|
|
|
|
+ .ToList();
|
|
|
|
+
|
|
|
|
+ foreach (var item in artists)
|
|
|
|
+ {
|
|
|
|
+ var index = IndexOf(item, searchTerm);
|
|
|
|
+
|
|
|
|
+ if (index != -1)
|
|
|
|
+ {
|
|
|
|
+ var artist = await _libraryManager.GetArtist(item).ConfigureAwait(false);
|
|
|
|
+
|
|
|
|
+ hints.Add(new Tuple<BaseItem, int>(artist, index));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Find genres
|
|
|
|
+ var genres = items.SelectMany(i => i.Genres)
|
|
|
|
+ .Where(i => !string.IsNullOrEmpty(i))
|
|
|
|
+ .Distinct(StringComparer.OrdinalIgnoreCase)
|
|
|
|
+ .ToList();
|
|
|
|
+
|
|
|
|
+ foreach (var item in genres)
|
|
|
|
+ {
|
|
|
|
+ var index = IndexOf(item, searchTerm);
|
|
|
|
+
|
|
|
|
+ if (index != -1)
|
|
|
|
+ {
|
|
|
|
+ var genre = await _libraryManager.GetGenre(item).ConfigureAwait(false);
|
|
|
|
+
|
|
|
|
+ hints.Add(new Tuple<BaseItem, int>(genre, index));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 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 = IndexOf(item, searchTerm);
|
|
|
|
+
|
|
|
|
+ if (index != -1)
|
|
|
|
+ {
|
|
|
|
+ var studio = await _libraryManager.GetStudio(item).ConfigureAwait(false);
|
|
|
|
+
|
|
|
|
+ hints.Add(new Tuple<BaseItem, int>(studio, index));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 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 = IndexOf(item, searchTerm);
|
|
|
|
+
|
|
|
|
+ if (index != -1)
|
|
|
|
+ {
|
|
|
|
+ var person = await _libraryManager.GetPerson(item).ConfigureAwait(false);
|
|
|
|
+
|
|
|
|
+ hints.Add(new Tuple<BaseItem, int>(person, index));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return hints.OrderBy(i => i.Item2).Select(i => i.Item1);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Gets the hints.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="item">The item.</param>
|
|
|
|
+ /// <param name="searchTerm">The search term.</param>
|
|
|
|
+ /// <returns>IEnumerable{Tuple{SearchHintResultSystem.Int32}}.</returns>
|
|
|
|
+ private async Task<IEnumerable<Tuple<BaseItem, int>>> GetHints(BaseItem item, string searchTerm)
|
|
|
|
+ {
|
|
|
|
+ var hints = new List<Tuple<BaseItem, int>>();
|
|
|
|
+
|
|
|
|
+ var index = IndexOf(item.Name, searchTerm);
|
|
|
|
+
|
|
|
|
+ if (index != -1)
|
|
|
|
+ {
|
|
|
|
+ hints.Add(new Tuple<BaseItem, int>(item, index));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return hints;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Gets the words.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="term">The term.</param>
|
|
|
|
+ /// <returns>System.String[][].</returns>
|
|
|
|
+ private string[] GetWords(string term)
|
|
|
|
+ {
|
|
|
|
+ // TODO: Improve this to be more accurate and respect culture
|
|
|
|
+ var words = term.Split(' ');
|
|
|
|
+
|
|
|
|
+ return words;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Indexes the of.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="input">The input.</param>
|
|
|
|
+ /// <param name="term">The term.</param>
|
|
|
|
+ /// <returns>System.Int32.</returns>
|
|
|
|
+ private int IndexOf(string input, string term)
|
|
|
|
+ {
|
|
|
|
+ var index = 0;
|
|
|
|
+
|
|
|
|
+ foreach (var word in GetWords(input))
|
|
|
|
+ {
|
|
|
|
+ if (word.IndexOf(term, StringComparison.OrdinalIgnoreCase) != -1)
|
|
|
|
+ {
|
|
|
|
+ return index;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ index++;
|
|
|
|
+ }
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
public static class LuceneSearch
|
|
public static class LuceneSearch
|