У меня странное поведение с запросом SOQL в классе Apex.

Set<String> contentDocumentIds = new Set<String>();
Set<String> contentVersionIds = new Set<String>();

contentDocumentIds.add('0694L00000575o8QAA');
contentDocumentIds.add('0694L0000057iUjQAI');

ContentVersion[] cvs = [SELECT Id, IsLatest 
                       From    ContentVersion 
                       WHERE   (ContentDocumentId IN :contentDocumentIds AND IsLatest = true)
                       OR      (Id IN :contentVersionIds)];
system.debug(cvs);

ContentVersion[] cvs2 = [SELECT Id, IsLatest 
                         From   ContentVersion 
                         WHERE  ContentDocumentId IN :contentDocumentIds AND IsLatest = true];
system.debug(cvs2);

Два ContentDocumentId являются допустимыми и имеют ContentVersions с IsLatest=True. Первый запрос возвращает одну строку, второй запрос (с удаленным предложением ИЛИ) возвращает две строки. Вот результат: -

> [11]|DEBUG|(ContentVersion:{Id=0684L0000057TM9QAM, IsLatest=true})
> [16]|DEBUG|(ContentVersion:{Id=0684L00000587HUQAY, IsLatest=true},
> ContentVersion:{Id=0684L0000057TM9QAM, IsLatest=true})

Это похоже на проблему с парсером запросов SF. У кого-нибудь есть идеи?

Заранее спасибо.

5
Sander de Jong 2 Дек 2021 в 15:18
Добро пожаловать! Пожалуйста, добавьте isLatest в оператор SELECT и повторите запуск. Пожалуйста, обновите журнал отладки. Я думаю, вы обнаружите, что ваши предположения неверны. Если они оба IsLatest = true, нам потребуется больше информации для решения.
 – 
dbwood3
28 Ноя 2021 в 16:44
3
Общий комментарий: похоже, что вы ищете зебр в табуне лошадей... мы все так делаем... 99,9999% времени ответ не в том, что Salesforce разорился, а в том, что вы сделали плохой предположение... Когда я ловлю себя на мысли, что Salesforce разорился, я отхожу от клавиатуры, совершаю короткую прогулку (увеличивая приток кислорода к мозгу) и снова смотрю на нее.
 – 
dbwood3
28 Ноя 2021 в 16:55
1
Более правдиво было бы сказать, что я немного ругаюсь с продавцами, говоря себе, что это не может быть моей проблемой, трачу неопределенное количество времени, а затем затем отхожу от клавиатуры... .
 – 
dbwood3
28 Ноя 2021 в 16:57
4
Вы добавили фильтр, где Id IN :emptyCollection, что привело к сканированию таблицы. Вам следует просмотреть свой план запроса и, возможно, отредактировать свой вопрос, включив в него размер таблица и избирательность меняется в запросах.
 – 
Adrian Larson
28 Ноя 2021 в 19:28
5
В вашей организации много ContentDocuments? Я предполагаю, что Адриан прав в том, что сканирование пустого набора/таблицы вызывает у вас проблемы, поскольку, вероятно, применяется ограничение, поэтому «другая» запись не возвращается в усеченных возвращаемых значениях. Служба поддержки может включить пермь, чтобы снять это ограничение, с которым я столкнулся при обработке запроса к ContentDocumentLink в этом вопрос, который, как я подозреваю, связан с этим.
 – 
Kris Goncalves
1 Дек 2021 в 19:56

1 ответ

Это поведение связано с тем, как ContentVersion, Document и ContentDocumentLink взаимодействуют друг с другом и как к ним осуществляется доступ. Ваш первый запрос "открыт" (OR), ваш второй запрос "закрыт".

Поэтому первый запрос должен возвращать только «доступные» версии контента, второй запрос должен включать все, что явно отфильтровано.

TL; DR

Вот еще немного подробностей о том, как SOQL ведет себя при запросе версий контента:

  1. В Apex версии контента видны только тому пользователю, который их создал или имеет явный доступ к совместному использованию. На это не влияют функции «Просмотреть все данные» и without sharing.
  2. Вы по-прежнему можете получать версии контента, если вы знаете их идентификаторы напрямую (даже если вы не можете получить к ним доступ в обычном запросе).
  3. Вам всегда нужен прокси-сервер (например, родительская запись, где версия контента является общей) для надежного извлечения версий контента.

Доступность версий контента

Если вы запрашиваете версии контента, пользователь видит только свои версии контента. Сюда входят версии, в которых пользователь является владельцем версии или у него есть явный доступ к совместному использованию (через совместное использование вручную) к определенному документу.

Это кажется совершенно недетерминированным, поскольку пользователь может получить доступ к версии содержимого через пользовательский интерфейс.

Существует разрешение, которое можно включить, чтобы обойти это. Однако это разрешение требует просмотра всех данных и настроек, поэтому его не следует использовать для обычных пользователей (и, как следствие, оно не может исправить проблемы, возникающие в производственном коде, работающем в пользовательской области).

View all data permission for content versions

Чтобы убедиться, что на вас влияет такое поведение (а не что-то еще более странное), проверьте, является ли исполняющий пользователь владельцем обоих документов, которые вы пытаетесь получить. Если нет, поэкспериментируйте с явным совместным использованием и попытайтесь воспроизвести такое поведение.

Как надежно запрашивать версии контента

Версия содержимого всегда доступна по ее идентификатору, даже если вы не можете получить к ней доступ в «открытом» запросе.

SELECT Id FROM ContentVersion

Итак, как вы уже выяснили, этот запрос извлекает только те версии контента, к которым у вас есть явный доступ.

Чтобы усложнить нам задачу, вы не можете использовать ContentDocumentLink в подзапросе. Вам нужно написать код вершины для извлечения соответствующих ContentVersionId, создать список идентификаторов и запросить эти идентификаторы, в частности. И вот что интересно: Вам нужен прокси-сервер, например LinkedEntity, для поиска версий контента и их документов. Вы не можете просто получить к ним доступ напрямую, не зная, где они находятся.

SELECT Id FROM ContentVersion WHERE Id IN :contentVersionIds

Этот запрос извлечет все версии контента в списке идентификаторов. Это будет включать версии контента, которых нет в первом запросе.

Это очень раздражает, если вам нужно идентифицировать Document на основе версии контента. Если у вас нет версии, полный документ будет невидим для вашего запроса.

Вот код, который я написал для получения всех версий содержимого контракта службы. Он принимает список и возвращает родительский документ содержимого. Вы можете легко использовать это для получения всех версий контента.

public static Map<Id, ContentDocument> getLatestContractSummaryDocuments(List<Id> serviceContractIds) {
    Map<Id, ServiceContract> crs = new Map<Id, ServiceContract>(
        [
            SELECT
                Id,
                (
                    SELECT Id, ContentDocumentId, ContentDocument.LatestPublishedVersionId, ContentDocument.Title, LinkedEntityId
                    FROM ContentDocumentLinks
                    ORDER BY Id ASC
                )
            FROM ServiceContract
            WHERE Id IN :serviceContractIds
        ]
    );
    List<Id> contentVersionIds = new List<Id>();
    for (ServiceContract sc : crs.values()) {
        for (ContentDocumentLink cdl : sc.ContentDocumentLinks) {
            contentVersionIds.add(cdl.ContentDocument.LatestPublishedVersionId);
        }
    }
    Map<Id, ContentVersion> contentVersions = new Map<Id, ContentVersion>(
        [
            SELECT Id, Title, DocumentTemplateName__c, ContentDocumentId
            FROM ContentVersion
            WHERE Id IN :contentVersionIds AND DocumentTemplateName__c = 'CONTRACT_SUMMARY'
        ]
    );
    Map<Id, ContentDocument> documentsByContractIds = new Map<Id, ContentDocument>();
    for (Id contractId : serviceContractIds) {
        documentsByContractIds.put(contractId, null);
        ServiceContract sc = crs.get(contractId);
        if (sc == null) {
            continue;
        }
        for (ContentDocumentLink cdl : sc.ContentDocumentLinks) {
            if (contentVersions.containsKey(cdl.ContentDocument.LatestPublishedVersionId)) {
                documentsByContractIds.put(contractId, cdl.ContentDocument);
            }
        }
    }
    return documentsByContractIds;
}

В конкретном случае меня интересуют только версии контента определенного типа, это фильтр на DocumentTemplateName__c

7
J. Schreiber 4 Дек 2021 в 18:03