diff --git a/src/Core/Services/Implementations/CipherService.cs b/src/Core/Services/Implementations/CipherService.cs index 037813d08a..7d5e140b5e 100644 --- a/src/Core/Services/Implementations/CipherService.cs +++ b/src/Core/Services/Implementations/CipherService.cs @@ -192,7 +192,7 @@ namespace Bit.Core.Services if(cipher.UserId.HasValue && cipher.Favorite) { - cipher.Favorites = $"[{{\"u\":\"{cipher.UserId.ToString().ToUpperInvariant()}\"}}]"; + cipher.Favorites = $"{{\"{cipher.UserId.ToString().ToUpperInvariant()}\":\"true\"}}"; } } @@ -213,8 +213,8 @@ namespace Bit.Core.Services continue; } - cipher.Folders = $"[{{\"u\":\"{cipher.UserId.ToString().ToUpperInvariant()}\"," + - $"\"f\":\"{folder.Id.ToString().ToUpperInvariant()}\"}}]"; + cipher.Folders = $"{{\"{cipher.UserId.ToString().ToUpperInvariant()}\":" + + $"\"{folder.Id.ToString().ToUpperInvariant()}\"}}"; } // Create it all diff --git a/src/Sql/dbo/Functions/CipherDetails.sql b/src/Sql/dbo/Functions/CipherDetails.sql index 333efaf9fe..d1c3d3b180 100644 --- a/src/Sql/dbo/Functions/CipherDetails.sql +++ b/src/Sql/dbo/Functions/CipherDetails.sql @@ -11,35 +11,14 @@ SELECT C.[RevisionDate], CASE WHEN C.[Favorites] IS NULL - OR ( - SELECT TOP 1 - 1 - FROM - OPENJSON(C.[Favorites]) - WITH ( - [Favorites_UserId] UNIQUEIDENTIFIER '$.u' - ) - WHERE - [Favorites_UserId] = @UserId - ) IS NULL + OR JSON_VALUE(C.[Favorites], CONCAT('$."', @UserId, '"')) IS NULL THEN 0 ELSE 1 END [Favorite], CASE WHEN C.[Folders] IS NULL THEN NULL - ELSE ( - SELECT TOP 1 - [Folders_FolderId] - FROM - OPENJSON(C.[Folders]) - WITH ( - [Folders_UserId] UNIQUEIDENTIFIER '$.u', - [Folders_FolderId] UNIQUEIDENTIFIER '$.f' - ) - WHERE - [Folders_UserId] = @UserId - ) + ELSE TRY_CONVERT(UNIQUEIDENTIFIER, JSON_VALUE(C.[Folders], CONCAT('$."', @UserId, '"'))) END [FolderId] FROM [dbo].[Cipher] C diff --git a/src/Sql/dbo/Stored Procedures/Cipher_UpdatePartial.sql b/src/Sql/dbo/Stored Procedures/Cipher_UpdatePartial.sql index c414e84f4b..64e73b82e8 100644 --- a/src/Sql/dbo/Stored Procedures/Cipher_UpdatePartial.sql +++ b/src/Sql/dbo/Stored Procedures/Cipher_UpdatePartial.sql @@ -7,153 +7,30 @@ AS BEGIN SET NOCOUNT ON - DECLARE @FavoritesJson VARCHAR(MAX) = NULL - DECLARE @FoldersJson VARCHAR(MAX) = NULL + DECLARE @UserIdKey VARCHAR(50) = CONCAT('"', @UserId, '"') + DECLARE @UserIdPath VARCHAR(50) = CONCAT('$.', @UserIdKey) - SELECT - @FavoritesJson = [Favorites], - @FoldersJson = [Folders] - FROM + UPDATE [dbo].[Cipher] + SET + [Folders] = + CASE + WHEN @FolderId IS NOT NULL AND [Folders] IS NULL THEN + CONCAT('{', @UserIdKey, ':"', @FolderId, '"', '}') + WHEN @FolderId IS NOT NULL THEN + JSON_MODIFY([Folders], @UserIdPath, CAST(@FolderId AS VARCHAR(50))) + ELSE + JSON_MODIFY([Folders], @UserIdPath, NULL) + END, + [Favorites] = + CASE + WHEN @Favorite = 1 AND [Favorites] IS NULL THEN + CONCAT('{', @UserIdKey, ':true}') + WHEN @Favorite = 1 THEN + JSON_MODIFY([Favorites], @UserIdPath, CAST(1 AS BIT)) + ELSE + JSON_MODIFY([Favorites], @UserIdPath, NULL) + END WHERE [Id] = @Id - - DECLARE @ExistingFolderId UNIQUEIDENTIFIER = NULL - - -- NOTE - -- JSON_MODIFY operations involving @Existing__Index may be subject to race conditions involving that index/key. - -- Look for a better approach, like removing objects by conditions. - DECLARE @ExistingFolderIndex INT = NULL - DECLARE @ExistingFavoriteIndex INT = NULL - - IF @FoldersJson IS NOT NULL - BEGIN - SELECT TOP 1 - @ExistingFolderId = JSON_VALUE([Value], '$.f'), - @ExistingFolderIndex = [Key] - FROM ( - SELECT - [Key], - [Value] - FROM - OPENJSON(@FoldersJson) - ) [Results] - WHERE JSON_VALUE([Value], '$.u') = @UserId - END - - IF @FavoritesJson IS NOT NULL - BEGIN - SELECT TOP 1 - @ExistingFavoriteIndex = [Key] - FROM ( - SELECT - [Key], - [Value] - FROM - OPENJSON(@FavoritesJson) - ) [Results] - WHERE JSON_VALUE([Value], '$.u') = @UserId - END - - -- ---------------------------- - -- Update [Folders] - -- ---------------------------- - - IF @ExistingFolderId IS NOT NULL AND @FolderId IS NULL - BEGIN - -- User had an existing folder, but now they have removed the folder assignment. - -- Remove the index of the existing folder object from the [Folders] JSON array. - - UPDATE - [dbo].[Cipher] - SET - -- TODO: How to remove index? - [Folders] = JSON_MODIFY([Folders], CONCAT('$[', @ExistingFolderIndex, ']'), NULL) - WHERE - [Id] = @Id - END - ELSE IF @FolderId IS NOT NULL - BEGIN - IF @FoldersJson IS NULL - BEGIN - -- [Folders] has no existing JSON data. - -- Set @FolderId (with @UserId) as a new JSON object for index 0 of an array. - - UPDATE - [dbo].[Cipher] - SET - [Folders] = JSON_QUERY((SELECT @UserId u, @FolderId f FOR JSON PATH)) - WHERE - [Id] = @Id - END - ELSE IF @ExistingFolderId IS NULL - BEGIN - -- [Folders] has some existing JSON data, but the user had no existing folder. - -- Append @FolderId (with @UserId) as a new JSON object. - - UPDATE - [dbo].[Cipher] - SET - [Folders] = JSON_MODIFY([Folders], 'append $', - JSON_QUERY((SELECT @UserId u, @FolderId f FOR JSON PATH, WITHOUT_ARRAY_WRAPPER))) - WHERE - [Id] = @Id - END - ELSE IF @FolderId != @ExistingFolderId - BEGIN - -- User had an existing folder assignemnt, but have changed the assignment to another folder. - -- Update the index of the existing folder object from the [Folders] JSON array to to include the new @FolderId - - UPDATE - [dbo].[Cipher] - SET - [Folders] = JSON_MODIFY([Folders], CONCAT('$[', @ExistingFolderIndex, ']'), - JSON_QUERY((SELECT @UserId u, @FolderId f FOR JSON PATH, WITHOUT_ARRAY_WRAPPER))) - WHERE - [Id] = @Id - END - END - - -- ---------------------------- - -- Update [Favorites] - -- ---------------------------- - - IF @Favorite = 0 AND @ExistingFavoriteIndex IS NOT NULL - BEGIN - -- User had the cipher marked as a favorite, but now it is not. - -- Remove the index of the existing user object from the [Favorites] JSON array. - - UPDATE - [dbo].[Cipher] - SET - -- TODO: How to remove index? - [Favorites] = JSON_MODIFY([Favorites], CONCAT('$[', @ExistingFavoriteIndex, ']'), NULL) - WHERE - [Id] = @Id - END - ELSE IF @Favorite = 1 AND @FavoritesJson IS NULL - BEGIN - -- User is marking the cipher as a favorite and there is no existing JSON data - -- Set @UserId as a new JSON object for index 0 of an array. - - UPDATE - [dbo].[Cipher] - SET - [Favorites] = JSON_QUERY((SELECT @UserId u FOR JSON PATH)) - WHERE - [Id] = @Id - END - ELSE IF @Favorite = 1 AND @ExistingFavoriteIndex IS NULL - BEGIN - -- User is marking the cipher as a favorite whenever it previously was not. - -- Append @UserId as a new JSON object. - - UPDATE - [dbo].[Cipher] - SET - [Favorites] = JSON_MODIFY([Favorites], 'append $', - JSON_QUERY((SELECT @UserId u FOR JSON PATH, WITHOUT_ARRAY_WRAPPER))) - WHERE - [Id] = @Id - END END \ No newline at end of file