static void ApplyPredicate(ref HashSet <CrawledIssue> result, CrawledIndex index, Func <CrawledIssue, bool> predicate)
 {
     if (result is null)
     {
         result = new HashSet <CrawledIssue>(index.Repos.SelectMany(r => r.Issues.Values).Where(predicate));
     }
     else
     {
         result.RemoveWhere(i => !predicate(i));
     }
 }
            static void ApplyNegatedTerm(ref HashSet <CrawledIssue> result, CrawledIndex index, string term)
            {
                if (result is null)
                {
                    result = new HashSet <CrawledIssue>(index.Repos.SelectMany(r => r.Issues.Values));
                }

                var issues = index.Trie.Lookup(term);

                result.ExceptWith(issues);
            }
        public static CrawledIssueResults Execute(this IssueQuery query, CrawledIndex index)
        {
            var result = (HashSet <CrawledIssue>)null;

            foreach (var filter in query.Filters)
            {
                var next = Execute(index, filter);
                if (result is null)
                {
                    result = next;
                }
                else
                {
                    result.UnionWith(next);
                }
            }

            if (result is null)
            {
                return(CrawledIssueResults.Empty);
            }

            var sorts = query.Filters.SelectMany(f => f.Sort)
                        .Distinct()
                        .ToArray();

            if (!sorts.Any())
            {
                sorts = _defaultSort;
            }

            var groups = query.Filters.SelectMany(f => f.Groups)
                         .Distinct()
                         .Select(CrawledIssueGroupKey.Get)
                         .ToArray();

            if (!groups.Any())
            {
                return(CrawledIssueResults.Create(result, sorts));
            }

            var groupSorts = query.Filters.SelectMany(f => f.GroupSort)
                             .Distinct()
                             .ToArray();

            if (!groupSorts.Any())
            {
                groupSorts = _defaultGroupSort;
            }

            return(CrawledIssueResults.Create(result, sorts, groups, groupSorts));
        }
        public CrawledIndexCompletionProvider(CrawledIndex index)
        {
            _orgs = new SortedSet <string>(
                index.Repos.Select(r => r.Org),
                StringComparer.OrdinalIgnoreCase
                ).ToArray();

            _repos = new SortedSet <string>(
                index.Repos.SelectMany(r => new[] { r.Name, r.FullName }),
                StringComparer.OrdinalIgnoreCase
                ).ToArray();

            _users = new SortedSet <string>(
                index.Repos.SelectMany(r => r.Issues.Values)
                .SelectMany(i => new[] { i.CreatedBy }.Concat(i.Assignees)),
                StringComparer.OrdinalIgnoreCase
                ).ToArray();

            _labels = new SortedSet <string>(
                index.Repos.SelectMany(r => r.Labels)
                .Select(l => l.Name),
                StringComparer.OrdinalIgnoreCase
                ).ToArray();

            _milestones = new SortedSet <string>(
                index.Repos.SelectMany(r => r.Milestones)
                .Select(m => m.Title),
                StringComparer.OrdinalIgnoreCase
                ).ToArray();

            _areaPaths = new SortedSet <string>(
                index.Repos.SelectMany(r => r.Labels)
                .SelectMany(l => TextTokenizer.GetAreaPaths(l.Name)),
                StringComparer.OrdinalIgnoreCase
                ).ToArray();

            _areaNodes = new SortedSet <string>(
                index.Repos.SelectMany(r => r.Labels)
                .SelectMany(l => TextTokenizer.GetAreaPaths(l.Name, segmentsOnly: true)),
                StringComparer.OrdinalIgnoreCase
                ).ToArray();
        }
        private static HashSet <CrawledIssue> Execute(CrawledIndex index, IssueFilter filter)
        {
            var result = (HashSet <CrawledIssue>)null;

            foreach (var term in filter.IncludedTerms)
            {
                ApplyTerm(ref result, index.Trie, term);
            }

            foreach (var org in filter.IncludedOrgs)
            {
                ApplyTerm(ref result, index.Trie, $"org:{org}");
            }

            foreach (var repo in filter.IncludedRepos)
            {
                ApplyTerm(ref result, index.Trie, $"repo:{repo}");
            }

            foreach (var assignee in filter.IncludedAssignees)
            {
                ApplyTerm(ref result, index.Trie, $"assignee:{assignee}");
            }

            foreach (var label in filter.IncludedLabels)
            {
                ApplyTerm(ref result, index.Trie, $"label:{label}");
            }

            foreach (var area in filter.IncludedAreas)
            {
                ApplyTerm(ref result, index.Trie, $"area-under:{area}");
            }

            foreach (var areaNode in filter.IncludedAreaNodes)
            {
                ApplyTerm(ref result, index.Trie, $"area-node:{areaNode}");
            }

            foreach (var areaLead in filter.IncludedAreaLeads)
            {
                ApplyTerm(ref result, index.Trie, $"area-lead:{areaLead}");
            }

            foreach (var areaOwner in filter.IncludedAreaOwners)
            {
                ApplyTerm(ref result, index.Trie, $"area-owner:{areaOwner}");
            }

            if (filter.Author != null)
            {
                ApplyTerm(ref result, index.Trie, $"author:{filter.Author}");
            }

            if (filter.Milestone != null)
            {
                ApplyTerm(ref result, index.Trie, $"milestone:{filter.Milestone}");
            }

            if (filter.IsOpen == true)
            {
                ApplyPredicate(ref result, index, i => i.IsOpen);
            }
            else if (filter.IsOpen == false)
            {
                ApplyPredicate(ref result, index, i => !i.IsOpen);
            }

            if (filter.IsLocked == true)
            {
                ApplyPredicate(ref result, index, i => i.IsLocked);
            }
            else if (filter.IsLocked == false)
            {
                ApplyPredicate(ref result, index, i => !i.IsLocked);
            }

            if (filter.IsPullRequest == true)
            {
                ApplyPredicate(ref result, index, i => i.IsPullRequest);
            }
            else if (filter.IsPullRequest == false)
            {
                ApplyPredicate(ref result, index, i => !i.IsPullRequest);
            }

            if (filter.IsMerged == true)
            {
                ApplyPredicate(ref result, index, i => i.IsMerged);
            }
            else if (filter.IsMerged == false)
            {
                ApplyPredicate(ref result, index, i => i.IsPullRequest && !i.IsMerged);
            }

            if (filter.IsDraft == true)
            {
                ApplyPredicate(ref result, index, i => i.IsDraft);
            }
            else if (filter.IsDraft == false)
            {
                ApplyPredicate(ref result, index, i => i.IsPullRequest && !i.IsDraft);
            }

            if (filter.IsArchived == true)
            {
                ApplyPredicate(ref result, index, i => i.Repo.IsArchived);
            }
            else if (filter.IsArchived == false)
            {
                ApplyPredicate(ref result, index, i => !i.Repo.IsArchived);
            }

            if (filter.NoAssignees == true)
            {
                ApplyPredicate(ref result, index, i => i.Assignees.Length == 0);
            }
            else if (filter.NoAssignees == false)
            {
                ApplyPredicate(ref result, index, i => i.Assignees.Length > 0);
            }

            if (filter.NoLabels == true)
            {
                ApplyPredicate(ref result, index, i => i.Labels.Length == 0);
            }
            else if (filter.NoLabels == false)
            {
                ApplyPredicate(ref result, index, i => i.Labels.Length > 0);
            }

            if (filter.NoArea == true)
            {
                ApplyPredicate(ref result, index, i => !i.Areas.Any());
            }
            else if (filter.NoArea == false)
            {
                ApplyPredicate(ref result, index, i => i.Areas.Any());
            }

            if (filter.NoAreaLead == true)
            {
                ApplyPredicate(ref result, index, i => !i.AreaLeads.Any());
            }
            else if (filter.NoAreaLead == false)
            {
                ApplyPredicate(ref result, index, i => i.AreaLeads.Any());
            }

            if (filter.NoAreaOwner == true)
            {
                ApplyPredicate(ref result, index, i => !i.AreaOwners.Any());
            }
            else if (filter.NoAreaOwner == false)
            {
                ApplyPredicate(ref result, index, i => i.AreaOwners.Any());
            }

            if (filter.NoMilestone == true)
            {
                ApplyPredicate(ref result, index, i => i.Milestone is null);
            }
            else if (filter.NoMilestone == false)
            {
                ApplyPredicate(ref result, index, i => i.Milestone is not null);
            }

            if (filter.Created is not null)
            {
                ApplyPredicate(ref result, index, i => filter.Created.Contains(i.CreatedAt));
            }
            if (filter.Updated is not null)
            {
                ApplyPredicate(ref result, index, i => i.UpdatedAt != null && filter.Updated.Contains(i.UpdatedAt.Value));
            }
            if (filter.Closed is not null)
            {
                ApplyPredicate(ref result, index, i => i.ClosedAt != null && filter.Closed.Contains(i.ClosedAt.Value));
            }

            if (filter.Comments is not null)
            {
                ApplyPredicate(ref result, index, i => filter.Comments.Contains(i.Comments));
            }
            if (filter.Reactions is not null)
            {
                ApplyPredicate(ref result, index, i => filter.Reactions.Contains(i.Reactions));
            }
            if (filter.Interactions is not null)
            {
                ApplyPredicate(ref result, index, i => filter.Interactions.Contains(i.Interactions));
            }

            foreach (var org in filter.ExcludedOrgs)
            {
                ApplyNegatedTerm(ref result, index, $"org:{org}");
            }

            foreach (var repo in filter.ExcludedRepos)
            {
                ApplyNegatedTerm(ref result, index, $"repo:{repo}");
            }

            foreach (var term in filter.ExcludedTerms)
            {
                ApplyNegatedTerm(ref result, index, term);
            }

            foreach (var assignee in filter.ExcludedAssignees)
            {
                ApplyNegatedTerm(ref result, index, $"assignee:{assignee}");
            }

            foreach (var author in filter.ExcludedAuthors)
            {
                ApplyNegatedTerm(ref result, index, $"author:{author}");
            }

            foreach (var label in filter.ExcludedLabels)
            {
                ApplyNegatedTerm(ref result, index, $"label:{label}");
            }

            foreach (var area in filter.ExcludedAreas)
            {
                ApplyNegatedTerm(ref result, index, $"area-under:{area}");
            }

            foreach (var areaNode in filter.ExcludedAreaNodes)
            {
                ApplyNegatedTerm(ref result, index, $"area-node:{areaNode}");
            }

            foreach (var areaLead in filter.ExcludedAreaLeads)
            {
                ApplyNegatedTerm(ref result, index, $"area-lead:{areaLead}");
            }

            foreach (var areaOwner in filter.ExcludedAreaOwners)
            {
                ApplyNegatedTerm(ref result, index, $"area-owner:{areaOwner}");
            }

            foreach (var milestone in filter.ExcludedMilestones)
            {
                ApplyNegatedTerm(ref result, index, $"milestone:{milestone}");
            }

            return(result ?? new HashSet <CrawledIssue>());