Query for differentials with no reviewers

We have a fairly large phabricator installation with over 20,000 differentials. I would like to make a query to search for open differentials that do not have a reviewer assigned. However, I cannot seem to find a way to query for differential reviews with no reviewers, only search for specific people by name (in the API it is done by PHID)

How can I query for all differential reviews that have no reviewer?

There’s currently no UI/API support for this – it hasn’t really come up before, maybe because revisions always still have a responsible author so it’s harder to lose track of them. In Maniphest, you can search for tasks assigned to “No Owner” (or none() in the API), but there is no corresponding token to perform this search in Differential today.

As a possible starting point, you can use this query directly to identify revisions with no reviewers:

USE phabricator_differential;
SELECT revision.id RevisionID, count(reviewer.reviewerPHID) reviewerCount
  FROM differential_revision revision
  LEFT JOIN differential_reviewer reviewer ON revision.phid = reviewer.revisionPHID
  GROUP BY revision.id
  HAVING reviewerCount = 0;

Thank you.

It turns out this problem is especially hard to search for, as if you use the string “no reviewers specified” you just find every phabricator instance on the internet, and as you said, people do not seem to run into this very often. However, the FreeBSD project is a rather large installation, with contributions from outsiders who don’t know who to tag as a reviewer, so we want to construct a query to find those and triage them.

Since the use case is fairly clear and this feature is generally reasonable, improves consistency, and isn’t difficult to implement, I’ve added support in https://secure.phabricator.com/D20537.

After that lands, you’ll be able to search for “No Reviewers” in the UI or “none()” in the API to find revisions with no active reviewers. (Note that “Resigned” reviewers are ignored by this constraint, but are not ignored by the query workaround above.)

1 Like

Also if for some reason you need to access it not from DB via SQL, you can use Conduit interfaces. For example in Python with Phabricator bindings:

from phabricator import Phabricator
ph = Phabricator()
ph.update_interfaces()
some = ph.differential.revision.search(attachments={'reviewers':True}).data
x = [r for r in some if not r['attachments']['reviewers']['reviewers']]

You should keep in mind that Conduit won’t provide you with more than 100 results though, so you’ll also need something like

def search_all(endpoint, **kwargs):
    while True:
        r = endpoint(**kwargs)
        for x in r.data:
            yield x
        after = r.cursor['after']
        if after is None:
            return
        kwargs['after'] = after

all = search_all(ph.differential.revision.search, attachments={'reviewers':True})
y = [r for r in all if not r['attachments']['reviewers']['reviewers']]
print(len(x)) # e.g 12
print(len(y)) # e.g. 96