public Task DeleteAsync(UserSignalsInsertDeleteOptions deleteOptions)
 {
     string rowKey = BuildRowKey(deleteOptions.UserId, deleteOptions.Signal);
     BoundStatement boundStatement = _deleteStatement.Value.Bind(rowKey, deleteOptions.ItemId, deleteOptions.DateTime);
     return _session.Get().ExecuteAsync(boundStatement);
 }
        public Task AddViewAsync(string space, string itemId, string userId)
        {
            if (string.IsNullOrEmpty(itemId))
            {
                throw new ArgumentNullException("itemId");
            }

            // Writing to all required key families
            const SignalType signal = SignalType.View;
            DateTime dateTime = DateTime.UtcNow;
            var tasks = new List<Task>();


            // 1. Item and user relations

            // Building composite row key to isolated different spaces of statistics
            string itemRowKey = BuildGlobalId(space, itemId);

            // Item cumulative counts
            tasks.Add(_itemCountsRepository.IncViewsAsync(itemRowKey));

            if (!string.IsNullOrEmpty(userId))
            {
                // Building composite row key to isolated different spaces of statistics
                string userRowKey = BuildGlobalId(space, userId);

                // User cumulative counts
                tasks.Add(_userCountsRepository.IncViewsAsync(userRowKey));

                // Users who viewed an item
                tasks.Add(_itemSignalsRepository.AddAsync(new ItemSignalsInsertDeleteOptions { ItemId = itemRowKey, UserId = userId, DateTime = dateTime, Signal = signal }));

                // User viewed items
                tasks.Add(Task.Run(() =>
                {
                    // add new view signal
                    var addTasks = new List<Task>();

                    var userSignal = new UserSignalsInsertDeleteOptions { ItemId = itemId, UserId = userRowKey, DateTime = dateTime, Signal = signal };
                    addTasks.Add(_userSignalsRepository.AddAsync(userSignal));
                    addTasks.Add(_userSignalsUnorderedRepository.AddAsync(userSignal));

                    return Task.WhenAll(addTasks);
                }));
            }


            // 2. Affinity groups
            IEnumerable<string> affinityGroups = BuildAffinityGroups(dateTime);
            foreach (string affinityGroup in affinityGroups)
            {
                // Building composite row keys to isolated different spaces of statistics
                string groupId = BuildGlobalId(space, affinityGroup);

                // Affinity group most viewed
                tasks.Add(Task.Run(async () =>
                {
                    // getting current version of most viewed items
                    AffinityGroupMostSignaledVersionEntity currentVersion = await _affinityGroupMostSignaledVersionRepository.GetAsync(groupId, signal);

                    // getting item counts
                    IEnumerable<AffinityGroupItemCountsEntity> itemCounts = await _affinityGroupItemCountsRepository.GetSequenceAsync(groupId, signal, 1000);

                    // building new version of most viewed items
                    Dictionary<string, AffinityGroupItemCountsEntity> itemCountsDict = itemCounts.ToDictionary(i => i.ItemId);
                    var insertOptions = new List<AffinityGroupMostSignaledInsertOptions>();
                    foreach (var i in itemCountsDict)
                    {
                        var insertItem = new AffinityGroupMostSignaledInsertOptions { ItemId = i.Key, Count = i.Value.Count };
                        if (insertItem.ItemId == itemId)
                        {
                            insertItem.Count = insertItem.Count + 1;
                        }

                        insertOptions.Add(insertItem);
                    }

                    // ensuring current item
                    if (!itemCountsDict.ContainsKey(itemId))
                    {
                        insertOptions.Add(new AffinityGroupMostSignaledInsertOptions { ItemId = itemId, Count = 1 });
                    }

                    await _affinityGroupMostSignaledRepository.AddAsync(groupId, signal, currentVersion.Version + 1, insertOptions);

                    // incrementing counter of itemId
                    await _affinityGroupItemCountsRepository.IncAsync(groupId, signal, itemId);

                    // incrementing version
                    await _affinityGroupMostSignaledVersionRepository.UpdateAsync(groupId, signal, currentVersion.Version + 1);
                }));

                // incrementing affinity group cumulative counts
                tasks.Add(_affinityGroupCountsRepository.IncAsync(groupId, signal));
            }


            // 3. Time series
            string eventId = BuildTimeSeriesRowKey(itemRowKey, signal);

            // Rollups
            var rollupOptions = new TimeSeriesRollupsInsertOptions
            {
                EventId = eventId,
                DateTime = dateTime
            };

            // Rollups for days
            tasks.Add(_timeSeriesRollupsDayRepository.IncAsync(rollupOptions));

            // Rollups for hour
            tasks.Add(_timeSeriesRollupsHourRepository.IncAsync(rollupOptions));

            // Rollups for minute
            tasks.Add(_timeSeriesRollupsMinuteRepository.IncAsync(rollupOptions));


            return Task.WhenAll(tasks);
        }
 public Task AddAsync(UserSignalsInsertDeleteOptions insertOptions)
 {
     string rowKey = BuildRowKey(insertOptions.UserId, insertOptions.Signal);
     BoundStatement boundStatement = _insertStatement.Value.Bind(rowKey, insertOptions.DateTime, insertOptions.ItemId);
     return _session.Get().ExecuteAsync(boundStatement);
 }
        public async Task DeleteDislikeAsync(string space, string itemId, string userId)
        {
            if (string.IsNullOrEmpty(itemId))
            {
                throw new ArgumentNullException("itemId");
            }

            if (string.IsNullOrEmpty(userId))
            {
                throw new ArgumentNullException("userId");
            }

            // Writing to all required key families
            const SignalType signal = SignalType.Dislike;
            DateTime dateTime = DateTime.UtcNow;
            var tasks = new List<Task>();


            // 1. Item and user relations

            // Building composite row key to isolated different spaces of statistics
            string itemRowKey = BuildGlobalId(space, itemId);
            string userRowKey = BuildGlobalId(space, userId);

            // Item cumulative counts
            tasks.Add(_itemCountsRepository.DecDislikesAsync(itemRowKey));

            // User cumulative counts
            tasks.Add(_userCountsRepository.DecDislikesAsync(userRowKey));

            // User dislikes
            tasks.Add(Task.Run(() =>
            {
                // cancel dislike
                var deleteTasks = new List<Task>();
                var deleteOptions = new UserSignalsInsertDeleteOptions { ItemId = itemId, Signal = signal, UserId = userRowKey, DateTime = dateTime };

                deleteTasks.Add(_userSignalsUnorderedRepository.DeleteAsync(deleteOptions));
                deleteTasks.Add(_userSignalsRepository.DeleteAsync(deleteOptions));

                return Task.WhenAll(deleteTasks);
            }));

            // Users who disliked an item
            tasks.Add(_itemSignalsRepository.DeleteAsync(new ItemSignalsInsertDeleteOptions { ItemId = itemRowKey, UserId = userId, Signal = signal, DateTime = dateTime }));


            // 2. Affinity groups
            IEnumerable<string> affinityGroups = BuildAffinityGroups(dateTime);
            foreach (string affinityGroup in affinityGroups)
            {
                // Building composite row keys to isolated different spaces of statistics
                string groupId = BuildGlobalId(space, affinityGroup);

                // Affinity group most liked
                tasks.Add(Task.Run(async () =>
                {
                    // getting current version of most signaled items
                    AffinityGroupMostSignaledVersionEntity currentVersion = await _affinityGroupMostSignaledVersionRepository.GetAsync(groupId, signal);

                    // getting item counts
                    IEnumerable<AffinityGroupItemCountsEntity> itemCounts = await _affinityGroupItemCountsRepository.GetSequenceAsync(groupId, signal, 1000);

                    // building new version of most liked
                    Dictionary<string, AffinityGroupItemCountsEntity> itemCountsDict = itemCounts.ToDictionary(i => i.ItemId);
                    var insertOptions = new List<AffinityGroupMostSignaledInsertOptions>();
                    foreach (var i in itemCountsDict)
                    {
                        var insertItem = new AffinityGroupMostSignaledInsertOptions { ItemId = i.Key, Count = i.Value.Count };
                        if (insertItem.ItemId == itemId)
                        {
                            insertItem.Count = insertItem.Count - 1;
                        }

                        insertOptions.Add(insertItem);
                    }

                    await _affinityGroupMostSignaledRepository.AddAsync(groupId, signal, currentVersion.Version + 1, insertOptions);

                    // decrementing counter of itemId
                    await _affinityGroupItemCountsRepository.DecAsync(groupId, signal, itemId);

                    // incrementing version
                    await _affinityGroupMostSignaledVersionRepository.UpdateAsync(groupId, signal, currentVersion.Version + 1);
                }));

                // decrementing affinity group cumulative counts
                tasks.Add(_affinityGroupCountsRepository.DecAsync(groupId, signal));
            }

            await Task.WhenAll(tasks);
        }