0

I'm working on a script to remove and re-add about 100 contacts. I have 12 different search criteria for ContactsApp.getContactsByEmailAddress, which the initiated know takes 30+ seconds to run. Is there some way I can only run it once and search all of my criteria? I've looked for others trying to do this same thing and was unsuccessful.

Below is one of the searches from my function (repeats 12 times with various search terms being passed to ContactsApp.getContactsByEmailAddress). I added the try-catch block because the script kept throwing errors out for seemingly no reason during various delete loops.

Would appreciate any and all advice.

var apa = ContactsApp.getContactsByEmailAddress('apa.')
  try{
for (var i in apa) {
apa[i].deleteContact()
}
  } catch(e){
      Logger.log(e)
}
hello_world
  • 778
  • 1
  • 10
  • 26
ReD_Cl0uD
  • 3
  • 3
  • Related: https://stackoverflow.com/questions/21292573/how-do-you-use-query-parameters-to-limit-retrieved-contacts-in-the-google-contac – tehhowch Jul 18 '18 at 19:05

1 Answers1

1

With the Contacts Service, you are limited to a single search criteria. Thus, the only way to search multiple patterns is to call the method once with each search parameter. You can, thankfully, use standard programming practices to minimize the amount of repeated code:

function getContactsWithEmails(emailSearchCriteria) {
  if (!emailSearchCriteria || !emailSearchCriteria.length)
    throw new Error("No search inputs given");

  // Collect the results of each search into a single Array.
  const matches = [];
  emailSearchCriteria.forEach(function (email) {
    var results = ContactsApp.getContactsByEmailAddress(email);
    if (results.length)
      Array.prototype.push.apply(matches, results);
    else
      console.log({message: "No results for search query '" + email + "'", query: email, resultsSoFar: matches});
  });

  return matches;
}

function deleteContacts(arrayOfContacts) {
  if (!arrayOfContacts || !arrayOfContacts.length)
    throw new Error("No contacts to delete");

  arrayOfContacts.forEach(function (contact) {
    ContactsApp.deleteContact(contact);
  });
}

// Our function that uses the above helper methods to do what we want.
function doSomething() {
  // Define all email searches to be performed.
  const emailFragmentsToSearchWith = [
    "email1",
    ...
    "emailN"
  ];
  const matchingContacts = getContactsWithEmails(emailFragmentsToSearchWith);
  if (matchingContacts.length) {
    /** do something with the contacts that matched the search.
     * someMethodThatSavesContacts(matchingContacts);
     * someMethodThatModifiesContacts(matchingContacts);
     * deleteContacts(matchingContacts);
     * ...
     */
  }
  /** do other stuff that doesn't need those contacts. */
}

The Google Calendar v3 GData API, as mentioned in this SO question, does support multiple query parameters. However, there is no simple integration with this API - you will need to write the appropriate URL requests and execute them with UrlFetchApp.

In addition to the Google Contacts API, you could use the Google People REST API, specifically the people.connections#list endpoint.

Both of these APIs require you to associate your Apps Script project with a Google Cloud Project that has the respective API enabled, and will likely require you to manually set the scopes your Apps Script project requires in its manifest file, in addition to providing OAuth2 authorizations of the associated HTTP requests you make to the API endpoints.

References

tehhowch
  • 9,645
  • 4
  • 24
  • 42
  • Thank you for this thorough response. I don't think I will be able to deal with the Calendar API or people REST API, but I will certainly try your suggestion to improve my delete function. Apologies for my ignorance, but I'm confused by a few things and if you would be so kind, a quick explanation would be so helpful to me. On line 12, why isn't the variable being passed to the loop" emailsearches"? I think I don't understand the function (email) part. Also, on line 19, I would have expected the input variable to be matches ... I don't understand how arrayOfContacts or "contact" are working. – ReD_Cl0uD Jul 19 '18 at 13:26
  • @ReD_Cl0uD the link in my answer to documentation on the `forEach` method should be very useful. In the examples I included in my answer, there is not a need to bind a reference to the 2nd, 3rd, or 4th arguments passed to the unnamed callback functions. You should also review examples and tutorials on variable scope (though it is important to remember that Apps Script uses an older version of JavaScript than many interactive online resources you may find). – tehhowch Jul 19 '18 at 13:38
  • I actually started looking at that link right after I wrote my previous comment. Thanks very much for that. Are there any resources you would recommend for further reading on the serialization technique you mentioned? – ReD_Cl0uD Jul 19 '18 at 14:18
  • @ReD_Cl0uD it's just an abstract idea. If the collection operation is "expensive", saving the results for a "cheap" recollection can be sensible. Perhaps you store the contacts on a spreadsheet, in the properties service, in cache, etc. – tehhowch Jul 19 '18 at 15:19
  • Gotcha. Would you expect the code you provided to work? When I run these functions in order, the deleteContacts function runs in 0 seconds and doesn't actually delete any contacts. – ReD_Cl0uD Jul 19 '18 at 18:08
  • @ReD_Cl0uD as shown in the example function, you have to pass an `Array` of `Contact`s as the parameter to `deleteContacts`. The `getMatchingContacts` function will return the expected format, provided your searches have results. If there are no results, the array would be empty. – tehhowch Jul 19 '18 at 19:02
  • This is what I was getting at with my original question ... so would I need to add deleteContacts(matches) at the end of GetMatchingContacts in order to pass that array? My understanding is the matches array will be gone after getMatchingContacts finishes running. – ReD_Cl0uD Jul 19 '18 at 20:10
  • @ReD_Cl0uD you may benefit from completing examples and tutorials on the `return` keyword. Re: your suggestion, no, one would not want to call `deleteContacts` within the function `getMatchingContacts` - by name alone one would not expect `getMatchingContacts` to do anything except return an object with the matching contacts. – tehhowch Jul 19 '18 at 20:22
  • I'll do that. Thanks for your patience and kind replies. – ReD_Cl0uD Jul 19 '18 at 20:24