We have a support Community where customers can create Cases and attach files to said cases for our support people to follow up on.

I am trying to write an Apex batch job that purges the file attachments on cases after a certain of time when the case has been closed. It should be simple enough. Just have a SOQL query find all the file objects with ParentIds referencing Cases that have closed outside the time frame where we want to keep the files and delete them. But what sObject(s) do I want to target in my query?

I created a test case and attached a file to it. When I look at the case in the GUI, my file shows up under both the "Attachments" section and the "Files" section. Intuitively, then, I would expect my file to show up in a SOQL query against Attachment, but it doesn't. Nor against File (which is not actually an sObject type). I can get it to show up if I query the ContentDocument object, but it tells me ParentId is not a valid field there (is that a permission issue?).

So which is it? Are Case attachments Attachments, or ContentDocuments? Or does it depend? Do I need to query both to make sure I get everything?

Or am I going about this completely the wrong way by writing an Apex job to do it?

As requested, here is the class I have written so far. I'll note this isn't tested yet. It was when I was writing the unit test that I realized I wasn't sure what objects I needed to mock up, so I'm not sure I'm on the right track. The query is the heart of my question. Am I targeting the right sObject(s)?

global class CaseAttachmentCleanup implements Database.Batchable<sObject>, Schedulable {

global final String query;

global CaseAttachmentCleanup(String objectType) {
    query = 'SELECT Id FROM ' + objectType +
            ' WHERE ParentId IN (SELECT Id FROM Case ' +
                                'WHERE ClosedDate <= N_DAYS_AGO:14)';

global void execute(SchedulableContext sc) {
    Database.executebatch(new CaseAttachmentCleanup('Attachment'));
    Database.executebatch(new CaseAttachmentCleanup('ContentDocument'));

global Database.QueryLocator start(Database.BatchableContext br) {
    return Database.getQueryLocator(query);

global void execute(Database.BatchableContext bc, List<sObject> scope) {
    delete scope;

global void finish(Database.BatchableContext bc) {}


In addition I grabbed the ID of my file attachment and ran a couple SOQL queries in the dev console.

SELECT Id FROM ContentDocument WHERE Id = 'my attachment ID'

gave me a result. (though the attempt to include Name and ParentId told me they were invalid fields. It may be a permission issue which I will investigate.)

SELECT Id, Name, ParentId FROM Attachment WHERE Id = 'my attachment ID'

did not. This was confounding because I really expected it to show up as an Attachment.

Will my Case attachments ever be Attachment objects, or will they only ever be ContentDocuments? Is there an object type I'm missing?

More information to confuse matters. It appears ParentId may not be a field on the ContentDocument object, as it does not appear in the Field Reference Guide. It does, however, appear in the API Developer Guide which has served me well in the past. Going off the Reference Guide, I'm not sure what field links the ContentDocument object to my Case. - Answered by Oleksiy in comments below.

  • Can we see some of the code you wrote? – Sebastian Kessel Feb 23 '17 at 21:01
  • Should be visible under Attachments not any other object. – Raul Feb 23 '17 at 21:02
  • Class code and some clarification added. @Rahul, that's the confusing thing, my attachment is not showing up as an Attachment object, it's a ContentDocument. Not what I was expecting. – Seth R Feb 23 '17 at 22:08
  • 1
    You have to query ContentDocumentLink records to get ContentDocument Ids shared with old Cases. – o-lexi Feb 23 '17 at 22:30
  • 1
    @SethR It's a subquery you are using to identify your cases closed over 14 days ago SELECT Id FROM Case WHERE ClosedDate <= LAST_N_DAYS:14. Keep in mind, your relative date criteria LAST_N_DAYS:14 used incorrectly - it will include also cases closed 1 days ago, as you are trying to use it as inclusive <=, but it returns EACH day ago from 14th to 1 (yesterday). You have to use N_DAYS_AGO:14 for your use case. – o-lexi Feb 23 '17 at 22:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.