示例#1
0
        public SortedDictionary <string, string[]> CompareOwners(
            SortedDictionary <string, SortedSet <string> > oldData,
            SortedDictionary <string, SortedSet <string> > newData)
        {
            if (oldData.Comparer != StringComparer.OrdinalIgnoreCase)
            {
                throw new ArgumentException("The old data should have a case-insensitive comparer.", nameof(oldData));
            }

            if (newData.Comparer != StringComparer.OrdinalIgnoreCase)
            {
                throw new ArgumentException("The new data should have a case-insensitive comparer.", nameof(newData));
            }

            // Use ordinal comparison to allow username case changes to flow through.
            var stopwatch = Stopwatch.StartNew();
            var result    = CompareData(
                oldData,
                newData,
                "package ID",
                "owners",
                StringComparer.Ordinal);

            stopwatch.Stop();
            _telemetryService.TrackOwnerSetComparison(oldData.Count, newData.Count, result.Count, stopwatch.Elapsed);

            return(result);
        }
        public SortedDictionary <string, string[]> Compare(
            SortedDictionary <string, SortedSet <string> > oldData,
            SortedDictionary <string, SortedSet <string> > newData)
        {
            if (oldData.Comparer != StringComparer.OrdinalIgnoreCase)
            {
                throw new ArgumentException("The old data should have a case-insensitive comparer.", nameof(oldData));
            }

            if (newData.Comparer != StringComparer.OrdinalIgnoreCase)
            {
                throw new ArgumentException("The new data should have a case-insensitive comparer.", nameof(newData));
            }

            var stopwatch = Stopwatch.StartNew();

            // We use a very simplistic algorithm here. Perform one pass on the new data to find the added or changed
            // package IDs. Then perform a second pass on the old data to find removed package IDs. We can optimize
            // this later if necessary.
            //
            // We emit all of the usernames when a package ID's owners have changed instead of the delta. This is
            // because Azure Search does not have a way to append or remove a specific item from a field that is an
            // array. The entire new array needs to be provided.
            var result = new SortedDictionary <string, string[]>(StringComparer.OrdinalIgnoreCase);

            // First pass: find added or changed sets.
            foreach (var pair in newData)
            {
                var id        = pair.Key;
                var newOwners = pair.Value;
                if (!oldData.TryGetValue(id, out var oldOwners))
                {
                    // ADDED: The package ID does not exist in the old data, which means the package ID was added.
                    result.Add(id, newOwners.ToArray());
                    _logger.LogInformation(
                        "The package ID {ID} has been added, with {AddedCount} owners.",
                        id,
                        newOwners.Count);
                }
                else
                {
                    // The package ID exists in the old data. We need to check if the owner set has changed. Perform
                    // an ordinal comparison to allow username case changes to flow through.
                    var removedUsernames = oldOwners.Except(newOwners, StringComparer.Ordinal).ToList();
                    var addedUsernames   = newOwners.Except(oldOwners, StringComparer.Ordinal).ToList();

                    if (removedUsernames.Any() || addedUsernames.Any())
                    {
                        // CHANGED: The username set has changed.
                        result.Add(id, newOwners.ToArray());
                        _logger.LogInformation(
                            "The package ID {ID} has an ownership change, with {RemovedCount} owners removed and " +
                            "{AddedCount} owners added.",
                            id,
                            removedUsernames.Count,
                            addedUsernames.Count);
                    }
                }
            }

            // Second pass: find removed sets.
            foreach (var pair in oldData)
            {
                var id        = pair.Key;
                var oldOwners = pair.Value;

                if (!newData.TryGetValue(id, out var newOwners))
                {
                    // REMOVED: The package ID does not exist in the new data, which means the package ID was removed.
                    result.Add(id, EmptyStringArray);
                    _logger.LogInformation(
                        "The package ID {ID} has been removed, with {RemovedCount} owners",
                        id,
                        oldOwners.Count);
                }
            }

            stopwatch.Stop();
            _telemetryService.TrackOwnerSetComparison(oldData.Count, newData.Count, result.Count, stopwatch.Elapsed);

            return(result);
        }