Skip to content

Fix existing followed assets not getting listed after search Reindexing #22621

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 30, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ protected EntityRepository(

fieldSupportMap.put(FIELD_TAGS, Pair.of(supportsTags, this::fetchAndSetTags));
fieldSupportMap.put(FIELD_OWNERS, Pair.of(supportsOwners, this::fetchAndSetOwners));
fieldSupportMap.put(FIELD_FOLLOWERS, Pair.of(supportsFollower, this::fetchAndSetFollowers));
fieldSupportMap.put(FIELD_DOMAINS, Pair.of(supportsDomains, this::fetchAndSetDomains));
fieldSupportMap.put(FIELD_REVIEWERS, Pair.of(supportsReviewers, this::fetchAndSetReviewers));
fieldSupportMap.put(FIELD_EXTENSION, Pair.of(supportsExtension, this::fetchAndSetExtension));
Expand Down Expand Up @@ -5018,6 +5019,16 @@ private void fetchAndSetOwners(List<T> entities, Fields fields) {
}
}

private void fetchAndSetFollowers(List<T> entities, Fields fields) {
if (!fields.contains(FIELD_FOLLOWERS) || !supportsFollower) {
return;
}
Map<UUID, List<EntityReference>> followersMap = batchFetchFollowers(entities);
for (T entity : entities) {
entity.setFollowers(followersMap.getOrDefault(entity.getId(), Collections.emptyList()));
}
}

private void fetchAndSetTags(List<T> entities, Fields fields) {
if (!fields.contains(FIELD_TAGS) || !supportsTags) {
return;
Expand Down Expand Up @@ -5143,6 +5154,31 @@ private Map<UUID, List<EntityReference>> batchFetchOwners(List<T> entities) {
return ownersMap;
}

private Map<UUID, List<EntityReference>> batchFetchFollowers(List<T> entities) {
if (entities.isEmpty()) {
return Collections.emptyMap();
}

List<CollectionDAO.EntityRelationshipObject> followerRecords =
daoCollection
.relationshipDAO()
.findFromBatch(
entityListToStrings(entities), Relationship.FOLLOWS.ordinal(), Include.ALL);

Map<UUID, List<EntityReference>> followersMap = new HashMap<>();
for (CollectionDAO.EntityRelationshipObject record : followerRecords) {
UUID entityId = UUID.fromString(record.getToId());
followersMap
.computeIfAbsent(entityId, k -> new ArrayList<>())
.add(
new EntityReference()
.withId(UUID.fromString(record.getFromId()))
.withType(record.getFromEntity()));
}

return followersMap;
}

/**
* Creates a unique key for a TagLabel combining TagFQN and Source for fast Set-based lookups.
* This replaces O(n) stream().anyMatch() operations with O(1) Set.contains() operations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -899,14 +899,36 @@ void test_fieldFetchersEfficiency(TestInfo test) throws HttpResponseException {
}

if (fields.contains("followers") && supportsFollowers) {
// Note: followers might be null if no followers exist, which is expected
List<?> bulkFollowers = (List<?>) getField(entity, "followers");
List<?> individualFollowers = (List<?>) getField(individualEntity, "followers");
// Get the actual followers from both bulk and individual loading
List<EntityReference> bulkFollowers = entity.getFollowers();
List<EntityReference> individualFollowers = individualEntity.getFollowers();

// The test should verify that followers are actually fetched and populated
// Previously this might have passed because both were null/empty due to missing field
// support
assertEquals(
listOrEmpty(bulkFollowers).size(),
listOrEmpty(individualFollowers).size(),
"Followers field should be consistently loaded in bulk operations for fields: "
+ fields);

// Add verification that if followers exist, they have proper entity references
if (!listOrEmpty(bulkFollowers).isEmpty()) {
for (EntityReference follower : bulkFollowers) {
assertNotNull(follower.getId(), "Follower should have valid ID");
assertNotNull(follower.getName(), "Follower should have valid name");
assertNotNull(follower.getType(), "Follower should have valid type");
}
}

// Verify that the followers data matches between bulk and individual fetch
if (!listOrEmpty(bulkFollowers).isEmpty()
&& !listOrEmpty(individualFollowers).isEmpty()) {
assertEquals(
bulkFollowers,
individualFollowers,
"Followers data should be identical between bulk and individual loading");
}
}
}

Expand Down
Loading