@ -22,6 +22,7 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Channels ;
using MediaBrowser.Controller.Configuration ;
using MediaBrowser.Controller.Playlists ;
using MediaBrowser.Model.Dto ;
using MediaBrowser.Model.LiveTv ;
namespace MediaBrowser.Server.Implementations.Persistence
@ -94,7 +95,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
private IDbCommand _updateInheritedRatingCommand ;
private IDbCommand _updateInheritedTagsCommand ;
public const int LatestSchemaVersion = 9 2 ;
public const int LatestSchemaVersion = 9 5 ;
/// <summary>
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
@ -157,7 +158,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create table if not exists UserDataKeys (ItemId GUID, UserDataKey TEXT, PRIMARY KEY (ItemId, UserDataKey))" ,
"create index if not exists idx_UserDataKeys1 on UserDataKeys(ItemId)" ,
"create table if not exists ItemValues (ItemId GUID, Type INT, Value TEXT )",
"create table if not exists ItemValues (ItemId GUID, Type INT, Value TEXT , CleanValue TEXT )",
"create index if not exists idx_ItemValues on ItemValues(ItemId)" ,
"create index if not exists idx_ItemValues2 on ItemValues(ItemId,Type)" ,
@ -263,6 +264,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
_connection . AddColumn ( Logger , "TypedBaseItems" , "SeriesName" , "Text" ) ;
_connection . AddColumn ( Logger , "UserDataKeys" , "Priority" , "INT" ) ;
_connection . AddColumn ( Logger , "ItemValues" , "CleanValue" , "Text" ) ;
string [ ] postQueries =
{
@ -568,10 +570,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
_deleteItemValuesCommand . Parameters . Add ( _deleteItemValuesCommand , "@Id" ) ;
_saveItemValuesCommand = _connection . CreateCommand ( ) ;
_saveItemValuesCommand . CommandText = "insert into ItemValues (ItemId, Type, Value ) values (@ItemId, @Type, @Value)";
_saveItemValuesCommand . CommandText = "insert into ItemValues (ItemId, Type, Value , CleanValue ) values (@ItemId, @Type, @Value, @Clean Value)";
_saveItemValuesCommand . Parameters . Add ( _saveItemValuesCommand , "@ItemId" ) ;
_saveItemValuesCommand . Parameters . Add ( _saveItemValuesCommand , "@Type" ) ;
_saveItemValuesCommand . Parameters . Add ( _saveItemValuesCommand , "@Value" ) ;
_saveItemValuesCommand . Parameters . Add ( _saveItemValuesCommand , "@CleanValue" ) ;
// provider ids
_deleteProviderIdsCommand = _connection . CreateCommand ( ) ;
@ -905,7 +908,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
UpdateUserDataKeys ( item . Id , item . GetUserDataKeys ( ) . Distinct ( StringComparer . OrdinalIgnoreCase ) . ToList ( ) , transaction ) ;
UpdateImages ( item . Id , item . ImageInfos , transaction ) ;
UpdateProviderIds ( item . Id , item . ProviderIds , transaction ) ;
UpdateItemValues ( item . Id , GetItemValues ( item ) , transaction ) ;
UpdateItemValues ( item . Id , GetItemValues ToSave ( item ) , transaction ) ;
}
transaction . Commit ( ) ;
@ -2019,7 +2022,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
if ( string . Equals ( name , ItemSortBy . SeriesDatePlayed , StringComparison . OrdinalIgnoreCase ) )
{
return new Tuple < string , bool > ( "(Select MAX(LastPlayedDate) from TypedBaseItems B" + GetJoinUserDataText ( query ) + " where B.Guid in (Select ItemId from AncestorIds where AncestorId in (select guid from typedbaseitems c where C.Type = 'MediaBrowser.Controller.Entities.TV.Series' And C.Guid in (Select AncestorId from AncestorIds where ItemId=A.Guid))))" , false ) ;
return new Tuple < string , bool > ( "(Select MAX(LastPlayedDate) from TypedBaseItems B" + GetJoinUserDataText ( query ) + " where B.Guid in (Select ItemId from AncestorIds where AncestorId in (select guid from typedbaseitems c where C.Type = 'MediaBrowser.Controller.Entities.TV.Series' And C.Guid in (Select AncestorId from AncestorIds where ItemId=A.Guid))))" , false ) ;
}
return new Tuple < string , bool > ( name , false ) ;
@ -2245,7 +2248,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
}
private List < string > GetWhereClauses ( InternalItemsQuery query , IDbCommand cmd )
private List < string > GetWhereClauses ( InternalItemsQuery query , IDbCommand cmd , string paramSuffix = "" )
{
var whereClauses = new List < string > ( ) ;
@ -2321,8 +2324,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
var includeTypes = query . IncludeItemTypes . SelectMany ( MapIncludeItemTypes ) . ToArray ( ) ;
if ( includeTypes . Length = = 1 )
{
whereClauses . Add ( "type=@type" ) ;
cmd . Parameters . Add ( cmd , "@type" , DbType . String ) . Value = includeTypes [ 0 ] ;
whereClauses . Add ( "type=@type" + paramSuffix ) ;
cmd . Parameters . Add ( cmd , "@type" + paramSuffix , DbType . String ) . Value = includeTypes [ 0 ] ;
}
else if ( includeTypes . Length > 1 )
{
@ -2626,8 +2629,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
var index = 0 ;
foreach ( var artist in query . ArtistNames )
{
clauses . Add ( "@ArtistName" + index + " in (select v alue from itemvalues where ItemId=Guid and Type <= 1)") ;
cmd . Parameters . Add ( cmd , "@ArtistName" + index , DbType . String ) . Value = artist ;
clauses . Add ( "@ArtistName" + index + " in (select CleanV alue from itemvalues where ItemId=Guid and Type <= 1)") ;
cmd . Parameters . Add ( cmd , "@ArtistName" + index , DbType . String ) . Value = artist .RemoveDiacritics ( ) ;
index + + ;
}
var clause = "(" + string . Join ( " OR " , clauses . ToArray ( ) ) + ")" ;
@ -2640,8 +2643,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
var index = 0 ;
foreach ( var item in query . Genres )
{
clauses . Add ( "@Genre" + index + " in (select v alue from itemvalues where ItemId=Guid and Type=2)") ;
cmd . Parameters . Add ( cmd , "@Genre" + index , DbType . String ) . Value = item ;
clauses . Add ( "@Genre" + index + " in (select CleanV alue from itemvalues where ItemId=Guid and Type=2)") ;
cmd . Parameters . Add ( cmd , "@Genre" + index , DbType . String ) . Value = item .RemoveDiacritics ( ) ;
index + + ;
}
var clause = "(" + string . Join ( " OR " , clauses . ToArray ( ) ) + ")" ;
@ -2654,8 +2657,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
var index = 0 ;
foreach ( var item in query . Tags )
{
clauses . Add ( "@Tag" + index + " in (select v alue from itemvalues where ItemId=Guid and Type=4)") ;
cmd . Parameters . Add ( cmd , "@Tag" + index , DbType . String ) . Value = item ;
clauses . Add ( "@Tag" + index + " in (select CleanV alue from itemvalues where ItemId=Guid and Type=4)") ;
cmd . Parameters . Add ( cmd , "@Tag" + index , DbType . String ) . Value = item .RemoveDiacritics ( ) ;
index + + ;
}
var clause = "(" + string . Join ( " OR " , clauses . ToArray ( ) ) + ")" ;
@ -2668,8 +2671,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
var index = 0 ;
foreach ( var item in query . Studios )
{
clauses . Add ( "@Studio" + index + " in (select v alue from itemvalues where ItemId=Guid and Type=3)") ;
cmd . Parameters . Add ( cmd , "@Studio" + index , DbType . String ) . Value = item ;
clauses . Add ( "@Studio" + index + " in (select CleanV alue from itemvalues where ItemId=Guid and Type=3)") ;
cmd . Parameters . Add ( cmd , "@Studio" + index , DbType . String ) . Value = item .RemoveDiacritics ( ) ;
index + + ;
}
var clause = "(" + string . Join ( " OR " , clauses . ToArray ( ) ) + ")" ;
@ -2682,8 +2685,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
var index = 0 ;
foreach ( var item in query . Keywords )
{
clauses . Add ( "@Keyword" + index + " in (select v alue from itemvalues where ItemId=Guid and Type=5)") ;
cmd . Parameters . Add ( cmd , "@Keyword" + index , DbType . String ) . Value = item ;
clauses . Add ( "@Keyword" + index + " in (select CleanV alue from itemvalues where ItemId=Guid and Type=5)") ;
cmd . Parameters . Add ( cmd , "@Keyword" + index , DbType . String ) . Value = item .RemoveDiacritics ( ) ;
index + + ;
}
var clause = "(" + string . Join ( " OR " , clauses . ToArray ( ) ) + ")" ;
@ -2812,6 +2815,20 @@ namespace MediaBrowser.Server.Implementations.Persistence
whereClauses . Add ( "MediaType in (" + val + ")" ) ;
}
if ( query . ItemIds . Length > 0 )
{
var excludeIds = new List < string > ( ) ;
var index = 0 ;
foreach ( var id in query . ItemIds )
{
excludeIds . Add ( "Guid = @IncludeId" + index ) ;
cmd . Parameters . Add ( cmd , "@IncludeId" + index , DbType . Guid ) . Value = new Guid ( id ) ;
index + + ;
}
whereClauses . Add ( string . Join ( " OR " , excludeIds . ToArray ( ) ) ) ;
}
if ( query . ExcludeItemIds . Length > 0 )
{
var excludeIds = new List < string > ( ) ;
@ -3478,7 +3495,292 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
}
private List < Tuple < int , string > > GetItemValues ( BaseItem item )
public QueryResult < Tuple < BaseItem , ItemCounts > > GetArtists ( InternalItemsQuery query )
{
return GetItemValues ( query , 0 , typeof ( MusicArtist ) . FullName ) ;
}
public QueryResult < Tuple < BaseItem , ItemCounts > > GetAlbumArtists ( InternalItemsQuery query )
{
return GetItemValues ( query , 1 , typeof ( MusicArtist ) . FullName ) ;
}
public QueryResult < Tuple < BaseItem , ItemCounts > > GetStudios ( InternalItemsQuery query )
{
return GetItemValues ( query , 3 , typeof ( Studio ) . FullName ) ;
}
public QueryResult < Tuple < BaseItem , ItemCounts > > GetGenres ( InternalItemsQuery query )
{
return GetItemValues ( query , 2 , typeof ( Genre ) . FullName ) ;
}
public QueryResult < Tuple < BaseItem , ItemCounts > > GetGameGenres ( InternalItemsQuery query )
{
return GetItemValues ( query , 2 , typeof ( GameGenre ) . FullName ) ;
}
public QueryResult < Tuple < BaseItem , ItemCounts > > GetMusicGenres ( InternalItemsQuery query )
{
return GetItemValues ( query , 2 , typeof ( MusicGenre ) . FullName ) ;
}
private QueryResult < Tuple < BaseItem , ItemCounts > > GetItemValues ( InternalItemsQuery query , int itemValueType , string returnType )
{
if ( query = = null )
{
throw new ArgumentNullException ( "query" ) ;
}
if ( ! query . Limit . HasValue )
{
query . EnableTotalRecordCount = false ;
}
CheckDisposed ( ) ;
var now = DateTime . UtcNow ;
using ( var cmd = _connection . CreateCommand ( ) )
{
var itemCountColumns = new List < Tuple < string , string > > ( ) ;
var typesToCount = query . IncludeItemTypes . ToList ( ) ;
if ( typesToCount . Count = = 0 )
{
//typesToCount.Add("Item");
}
foreach ( var type in typesToCount )
{
var itemCountColumnQuery = "Select Count(Value) from ItemValues where ItemValues.CleanValue=CleanName AND Type=@ItemValueType AND ItemId in (" ;
itemCountColumnQuery + = "select guid" + GetFromText ( ) ;
var typeSubQuery = new InternalItemsQuery ( query . User )
{
ExcludeItemTypes = query . ExcludeItemTypes ,
MediaTypes = query . MediaTypes ,
AncestorIds = query . AncestorIds ,
ExcludeItemIds = query . ExcludeItemIds ,
ItemIds = query . ItemIds ,
TopParentIds = query . TopParentIds ,
ParentId = query . ParentId ,
IsPlayed = query . IsPlayed
} ;
if ( string . Equals ( type , "Item" , StringComparison . OrdinalIgnoreCase ) )
{
typeSubQuery . IncludeItemTypes = query . IncludeItemTypes ;
}
else
{
typeSubQuery . IncludeItemTypes = new [ ] { type } ;
}
var whereClauses = GetWhereClauses ( typeSubQuery , cmd , type ) ;
var typeWhereText = whereClauses . Count = = 0 ?
string . Empty :
" where " + string . Join ( " AND " , whereClauses . ToArray ( ) ) ;
itemCountColumnQuery + = typeWhereText ;
itemCountColumnQuery + = ")" ;
var columnName = type + "Count" ;
itemCountColumns . Add ( new Tuple < string , string > ( columnName , "(" + itemCountColumnQuery + ") as " + columnName ) ) ;
}
var columns = _retriveItemColumns . ToList ( ) ;
columns . AddRange ( itemCountColumns . Select ( i = > i . Item2 ) . ToArray ( ) ) ;
cmd . CommandText = "select " + string . Join ( "," , GetFinalColumnsToSelect ( query , columns . ToArray ( ) , cmd ) ) + GetFromText ( ) ;
cmd . CommandText + = GetJoinUserDataText ( query ) ;
var innerQuery = new InternalItemsQuery ( query . User )
{
ExcludeItemTypes = query . ExcludeItemTypes ,
IncludeItemTypes = query . IncludeItemTypes ,
MediaTypes = query . MediaTypes ,
AncestorIds = query . AncestorIds ,
ExcludeItemIds = query . ExcludeItemIds ,
ItemIds = query . ItemIds ,
TopParentIds = query . TopParentIds ,
ParentId = query . ParentId ,
IsPlayed = query . IsPlayed
} ;
var innerWhereClauses = GetWhereClauses ( innerQuery , cmd ) ;
var innerWhereText = innerWhereClauses . Count = = 0 ?
string . Empty :
" where " + string . Join ( " AND " , innerWhereClauses . ToArray ( ) ) ;
var whereText = " where Type=@SelectType" ;
whereText + = " And CleanName In (Select CleanValue from ItemValues where Type=@ItemValueType AND ItemId in (select guid from TypedBaseItems" + innerWhereText + "))" ;
cmd . CommandText + = whereText ;
var outerQuery = new InternalItemsQuery ( query . User )
{
IsFavorite = query . IsFavorite ,
IsFavoriteOrLiked = query . IsFavoriteOrLiked ,
IsLiked = query . IsLiked ,
IsLocked = query . IsLocked ,
NameLessThan = query . NameLessThan ,
NameStartsWith = query . NameStartsWith ,
NameStartsWithOrGreater = query . NameStartsWithOrGreater ,
AlbumArtistStartsWithOrGreater = query . AlbumArtistStartsWithOrGreater ,
Tags = query . Tags ,
OfficialRatings = query . OfficialRatings ,
Genres = query . GenreIds ,
Years = query . Years
} ;
var outerWhereClauses = GetWhereClauses ( outerQuery , cmd ) ;
var outerWhereText = outerWhereClauses . Count = = 0 ?
string . Empty :
" AND " + string . Join ( " AND " , outerWhereClauses . ToArray ( ) ) ;
cmd . CommandText + = outerWhereText ;
cmd . Parameters . Add ( cmd , "@SelectType" , DbType . String ) . Value = returnType ;
cmd . Parameters . Add ( cmd , "@ItemValueType" , DbType . Int32 ) . Value = itemValueType ;
if ( EnableJoinUserData ( query ) )
{
cmd . Parameters . Add ( cmd , "@UserId" , DbType . Guid ) . Value = query . User . Id ;
}
//cmd.CommandText += GetGroupBy(query);
cmd . CommandText + = " group by PresentationUniqueKey" ;
cmd . CommandText + = " order by SortName" ;
if ( query . Limit . HasValue | | query . StartIndex . HasValue )
{
var limit = query . Limit ? ? int . MaxValue ;
cmd . CommandText + = " LIMIT " + limit . ToString ( CultureInfo . InvariantCulture ) ;
if ( query . StartIndex . HasValue )
{
cmd . CommandText + = " OFFSET " + query . StartIndex . Value . ToString ( CultureInfo . InvariantCulture ) ;
}
}
cmd . CommandText + = ";" ;
var isReturningZeroItems = query . Limit . HasValue & & query . Limit < = 0 ;
if ( isReturningZeroItems )
{
cmd . CommandText = "" ;
}
if ( query . EnableTotalRecordCount )
{
cmd . CommandText + = "select count (guid)" + GetFromText ( ) ;
cmd . CommandText + = GetJoinUserDataText ( query ) ;
cmd . CommandText + = whereText ;
}
else
{
cmd . CommandText = cmd . CommandText . TrimEnd ( ';' ) ;
}
var list = new List < Tuple < BaseItem , ItemCounts > > ( ) ;
var count = 0 ;
var commandBehavior = isReturningZeroItems | | ! query . EnableTotalRecordCount
? ( CommandBehavior . SequentialAccess | CommandBehavior . SingleResult )
: CommandBehavior . SequentialAccess ;
using ( var reader = cmd . ExecuteReader ( commandBehavior ) )
{
LogQueryTime ( "GetItemValues" , cmd , now ) ;
if ( isReturningZeroItems )
{
if ( reader . Read ( ) )
{
count = reader . GetInt32 ( 0 ) ;
}
}
else
{
while ( reader . Read ( ) )
{
var item = GetItem ( reader ) ;
if ( item ! = null )
{
var countStartColumn = columns . Count - typesToCount . Count ;
list . Add ( new Tuple < BaseItem , ItemCounts > ( item , GetItemCounts ( reader , countStartColumn , typesToCount ) ) ) ;
}
}
if ( reader . NextResult ( ) & & reader . Read ( ) )
{
count = reader . GetInt32 ( 0 ) ;
}
}
}
if ( count = = 0 )
{
count = list . Count ;
}
return new QueryResult < Tuple < BaseItem , ItemCounts > >
{
Items = list . ToArray ( ) ,
TotalRecordCount = count
} ;
}
}
private ItemCounts GetItemCounts ( IDataReader reader , int countStartColumn , List < string > typesToCount )
{
var counts = new ItemCounts ( ) ;
for ( var i = 0 ; i < typesToCount . Count ; i + + )
{
var value = reader . GetInt32 ( countStartColumn + i ) ;
var type = typesToCount [ i ] ;
if ( string . Equals ( type , "Series" , StringComparison . OrdinalIgnoreCase ) )
{
counts . SeriesCount = value ;
}
else if ( string . Equals ( type , "Episode" , StringComparison . OrdinalIgnoreCase ) )
{
counts . EpisodeCount = value ;
}
else if ( string . Equals ( type , "Movie" , StringComparison . OrdinalIgnoreCase ) )
{
counts . MovieCount = value ;
}
else if ( string . Equals ( type , "MusicAlbum" , StringComparison . OrdinalIgnoreCase ) )
{
counts . AlbumCount = value ;
}
else if ( string . Equals ( type , "Audio" , StringComparison . OrdinalIgnoreCase ) )
{
counts . SongCount = value ;
}
else if ( string . Equals ( type , "Game" , StringComparison . OrdinalIgnoreCase ) )
{
counts . GameCount = value ;
}
counts . ItemCount + = value ;
}
return counts ;
}
private List < Tuple < int , string > > GetItemValuesToSave ( BaseItem item )
{
var list = new List < Tuple < int , string > > ( ) ;
@ -3604,6 +3906,14 @@ namespace MediaBrowser.Server.Implementations.Persistence
_saveItemValuesCommand . GetParameter ( 0 ) . Value = itemId ;
_saveItemValuesCommand . GetParameter ( 1 ) . Value = pair . Item1 ;
_saveItemValuesCommand . GetParameter ( 2 ) . Value = pair . Item2 ;
if ( pair . Item2 = = null )
{
_saveItemValuesCommand . GetParameter ( 3 ) . Value = null ;
}
else
{
_saveItemValuesCommand . GetParameter ( 3 ) . Value = pair . Item2 . RemoveDiacritics ( ) ;
}
_saveItemValuesCommand . Transaction = transaction ;
_saveItemValuesCommand . ExecuteNonQuery ( ) ;