An organisation has local campaign groups. Some of these campaign groups belong to a set we call the "core activist network".

I want the core activist network group to contain:

  • all these groups that are manually added
  • all individuals that are related to a local campaign group which is part of the group.

I've set this up as follows:

  1. Search for "name contains 'a'" - anything that will return at least one result.
  2. Save the search as a Smart Group.
  3. Add at least one local campaign group to this group manually (Add to Group).
  4. Edit the smart group criteria, remove the silly 'a' criteria, then go to Relationships, choose "Target Contact(s) in group" and enter the name of this smart group!
  5. Run the search and from there click Update Smart Group.

Amazingly, this appears to have worked! But is there a reason why this is likely to end up in knots, e.g. some infinite loop? Since calculating the smart group requires...calculating the smart group.

I realise that this is likely to fall foul of SE's "answers likely to be opinion based" but I think this is a valid case. I encourage anyone to upvote any answers that point out potential problems with this situation. Of course, if someone says "I've done this and it works beautifully" then I'll accept that answer as an expression of joy :-). I'm also interested in any answers that are along the lines of "that's daft, but you can do it like this instead:..."

Thanks in advance wonderful Civi community!

1 Answer 1


I've been reading the code, and I think this is pretty safe, at least on server with MySQL 5.7.5+ or MariaDB 10.0.2+ (if your database is older than this, it's pretty old).

The code that updates smart groups has various checks in place to make sure it only does each group once in a request.

All the contacts in a smart group are stored in a "cache" table. (A cache is a temporary store of data that can be recreated as needs be, e.g. when updating the smart group). The cache is entirely separate from the manually added/removed contacts; the cache contains and only contains all the contacts in the group, whether they were included manually or because they match the criteria.

The process is:

  1. Delete all the cache records for this group. This is important in the example of this question, since it means the search for "contacts whose related contact is in the group" will only be looking at the related contacts that are manually included in the group 👍

  2. Creates a temporary table to store new cache entries.

  3. Add to the temporary store by running the search, excluding any that are manually removed. In this question: this means we'll be adding the individuals that are related to the local campaign groups, unless they have been manually removed from the group for whatever reason.

  4. Add to the temporary store a copy of all those manually added. Of course these contacts may already be in there (from step 2), but that's fine, it ensures every contact is only included once. By now, the temporary store has a copy of everyone that should be in the group. In our example, it means the temporary store now includes:

    • All the local campaign groups (and possibly other contacts) that were manually added to the smart group.
    • All the activists related to the local campaign groups, unless they had been manually removed.

But it goes on if this group has any child (sub) groups. This is not relevant to this question but I thought I'd include it for completeness' sake...

  1. For each child group, it looks up the contacts in that, exclude any manually excluded from our group, and adds these to our temporary store (again, ignoring any duplicates).

    It will also refresh the cache of any child smart groups, so if there was a smart group within the child groups that referenced our main smart group (!) it would not result in an infinite loop because it knows we've just updated [are updating] that, but I'm unclear what contacts it would see because:

    • if it goes on the cache table, that's empty at this point.
    • if it goes on (manually added + cache table) then it will see the manually added contacts only.
  2. Finally, it empties our group's cache (again? I wonder why...) and copies all the entries from our temporary store into the cache table, and we're done!

So: I think, miraculously, this should actually work and be safe for the purpose stated in my original question!

Please comment, or post other answers though if you have any experience to share.

  • Link to related dev chat thread Commented Jul 10, 2020 at 9:05
  • It feels relatively safe in that the groups are added statically and those pulled in via relationship are related to those groups. But I'd rely much more on your code analysis!
    – Aidan
    Commented Jul 10, 2020 at 11:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.