a simple solution would be to iterate over releases and check if their project is published
(caution: i did not test/verify this pseudo code)
from Acquisition import aq_parent
from plone import api
result = []
releases = api.content.find(portal_type='release', review_state='final', sort...)
for brain in releases:
release = brain.getObject() # this might be a performance hit if iterating over hundreds or thousands of releases
if api.content.get_state(aq_parent(release)) == 'published':
result.append(release)
# result now contains all releases located in a published project
this should be straight forward and not a big performance deal. if you have thousands of releases to iterate over (and can't use pagination and yield) you can alternatively search for published projects first and then limit the search for releases to these projects (works if projects can't be nested)
published_projects = api.content.find(portal_type='project', review_state='published')
# filter by path (multiple paths should work, did not test)
published_paths = [brain.getPath() for brain in published_projects]
release_brains = api.content.find(portal_type='release', path=published_paths, review_state='final')
#filter by uid
release_uids = []
for brain in published_projects:
project = brain.getObject()
release_uids += [brain.UID for brain in api.content.find(context=project, portal_type='release')]
releases = api.content.find(UID=release_uids, review_state='final', sort...)
maybe also https://pypi.org/project/Products.AdvancedQuery/ can help you to do more sophisticated queries