In Spring'16 release Salesforce introduced Files uploaded to the Attachments related list on records are uploaded as Salesforce Files, not as attachments in Salesforce Files Settings:
Files that you upload to the Notes & Attachments related list on records in Salesforce Classic are now Salesforce Files objects, rather than the old attachment objects. A new org pref in Salesforce Files Settings controls this behavior, and is enabled by default for new orgs.
The following SOQL query would allow you to get Ids of Files (Attachments) and New Notes related to the record
SELECT ContentDocumentId,Id,LinkedEntityId,ShareType
FROM ContentDocumentLink WHERE LinkedEntityId = 'YourRecordId'
Salesforce has two sorts of notes and attachments. There's the Classic type, represented by the Note
and Attachment
sObjects, and the Lightning type, represented by the Content suite of objects - ContentDocument
, ContentVersion
, ContentDocumentLink
, and ContentNote
. These two types are accessed in quite different ways.
The classic Note
and Attachment
records have a single polymorphic lookup to the record to which they're attached. You can query them simply:
List<Note> notes = [SELECT Id FROM Note WHERE ParentId = :myRecord];
List<Attachment> attachments = [SELECT Id FROM Attachment WHERE ParentId = :myRecord];
In the Lightning world of Content, the object model is quite a bit more complex. Both Notes and Attachments are represented as ContentDocument
, a parent object over potentially multiple ContentVersion
records. The ContentDocument
is then linked to all of the locations in which it's been shared, which may be many, via ContentDocumentLink
.
To make things even more confusing, there's a facade object, ContentNote
, that acts like a ContentDocument
with a single ContentVersion
inherent in its Content
field. You'll mostly have to worry about ContentNote
if you're adding Notes, however.
Anyway - not to try to summarize the substantial complexity involved in the Content system - to query both Notes and Attachments in the Lightning style for some object whose Id is myRecord
, you'd do
List<ContentDocumentLink> cdls = [SELECT ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId = :myRecord];
Set<Id> documentIds = new Set<Id>();
for (ContentDocumentLink cdl : cdls) {
documentIds.add(cdl.ContentDocumentId);
}
// ContentDocumentLink doesn't support semi-joins, which is frustrating.
List<ContentDocument> documents = [SELECT Id FROM ContentDocument WHERE Id IN :documentIds];
This will return both ContentNote
and regular ContentDocument
Attachments. If you want just Notes, query on ContentNote
rather than ContentDocument.
If you want just Attachments and not Notes, filter on FileType != 'SNOTE'
.
To get the actual content, for ContentDocument
, you can execute a ContentVersion
query against ContentDocumentId
and IsLatest = true
. For ContentNote
, you can simply ask for the Content
field in your ContentNote
query, which is a Blob field.
To delete records, you only need to delete
the top-level ContentDocument
record. That will take the ContentDocumentLink
records with it. Deleting ContentDocumentLink
records may make an Attachment inaccessible, but it won't be deleted entirely.
Best Answer
You have to use the correct Relationship Name (Notes). Also you don't need to check for parentId condition again in the inner query..