using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sony.Filtr.PersistentCaching; namespace Sony.Filtr.PlaylistGeneration.Autocomplete { public class RedisAutocompleteFactory : IAutocompleteFactory { private const string autocompleteRedisKey = "Artist-Autocomplete"; private const char fullWordMarker = '*'; private readonly string _index; public RedisAutocompleteFactory(string index = null) { _index = index ?? string.Empty; } private string GetArtistAutocompleteKey() { return _index + autocompleteRedisKey; } private string GetArtistIndexedKey() { return _index + "IndexedArtists"; } public IEnumerable SearchArtists(string search, int count) { search = search.ToLower(); var startTask = PersistentCachingManager.Instance.Connection.SortedSets.Rank(0, GetArtistAutocompleteKey(), search); try { if (startTask.Result == 0) return new string[] { }; } catch (Exception ex) { //We need to have this try-catch because of a bug in booksleeve that doesn't handle non-existing keys in SortedSets. //MusicMatch.ErrorLogging.ErrorLoggingManager.Instance.LogError(ex); return new string[] { }; } var start = startTask.Result; if (!start.HasValue) return new string[] { }; List results = new List(); var rangeLength = 50; while (results.Count < count) { long endIndex = start.Value + rangeLength - 1; var range = PersistentCachingManager.Instance.Connection.SortedSets.Range(0, GetArtistAutocompleteKey(), start.Value, endIndex); start += rangeLength; if (range.Result == null || !range.Result.Any()) break; foreach (var item in range.Result) { var itemKey = Encoding.UTF8.GetString(item.Key); var minLength = Math.Min(itemKey.Length, search.Length); if (itemKey.Substring(0, minLength) != search.Substring(0, minLength)) count = results.Count; if (itemKey.EndsWith(fullWordMarker.ToString()) && results.Count != count) results.Add(itemKey.Substring(0, itemKey.Length - 1)); } } return results.OrderBy(r => r); } public void IndexArtistAsync(string artistName) { if (HasIndexed(artistName)) return; artistName = artistName.ToLower(); IndexAsync(artistName); var withoutPrefix = StripCommonPrefix(artistName); if (withoutPrefix != artistName) IndexAsync(withoutPrefix); SetAsIndexed(artistName); } private string[] GetIndexedArtists() { return PersistentCachingManager.Instance.Connection.Sets.GetAllString(0, GetArtistIndexedKey()).Result; } private void SetAsIndexed(string artistName) { PersistentCachingManager.Instance.Connection.Sets.Add(0, GetArtistIndexedKey(), artistName); } public bool HasIndexed(string artistName) { return PersistentCachingManager.Instance.Connection.Sets.Contains(0, GetArtistIndexedKey(), artistName.ToLower()).Result; } private void IndexAsync(string value) { value = value.ToLower(); for (int i = 0; i < value.Length; i++) { PersistentCachingManager.Instance.Connection.SortedSets.Add(0, GetArtistAutocompleteKey(), value.Substring(0, value.Length - i), 0); } PersistentCachingManager.Instance.Connection.SortedSets.Add(0, GetArtistAutocompleteKey(), value + fullWordMarker, 0); } private string StripCommonPrefix(string value) { if (value.StartsWith("the ", StringComparison.InvariantCultureIgnoreCase)) value = value.Substring(4); else if (value.StartsWith("a ", StringComparison.InvariantCultureIgnoreCase)) value = value.Substring(2); return value; } } }