---
title: Improving custom macro performance in scoring and contact groups
related:
  - https://docs.kentico.com/k9/on-line-marketing-features/contact-management/contact-segmentation.md
  - https://docs.kentico.com/k9/on-line-marketing-features/contact-management/scoring-contacts.md
  - https://docs.kentico.com/k9/macro-expressions.md
  - https://docs.kentico.com/k9/macro-expressions/writing-macro-conditions/creating-macro-rules.md
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

This topic describes two ways in which you can improve [macro](https://docs.kentico.com/k9/macro-expressions.md) performance in scoring and contact groups:

1. [Create a custom macro rule translator](#creating-a-custom-macro-rule-translator) - by default, [custom macro](https://docs.kentico.com/k9/macro-expressions/extending-the-macro-engine/registering-custom-macro-methods.md) rules that you use in scoring and contact group conditions evaluate individually for each contact. You can improve the recalculation performance by implementing and registering translators for these macro rules. Once a macro rule has a translator registered, a database query is constructed that then selects only the contacts that fit the given macro condition.
2. Limit unnecessary recalculations - you can further improve your site performance by making sure your [custom](https://docs.kentico.com/k9/macro-expressions/extending-the-macro-engine/registering-custom-macro-methods.md) contact group and scoring macro rules are recalculated only when users perform activity types that are relevant to the macro rule.

> **Info:** **Default macro rules**
>
> Macro rules available in Kentico by default have both these optimizations implemented out of the box.

## Creating a custom macro rule translator

1. Create a class that implements the _IMacroRuleInstanceTranslator_ interface.
2. Implement the logic of the macro rule in a method that returns a query of the _ObjectQuery_ type. The returned object needs to contain the contacts that fulfill the condition. Note that you do not have to handle contextual information, such as site context, in the query.

   ```csharp title="Example of an implemented ContactIsFemaleMacro rule translator"

   using System;

   using CMS.DataEngine;
   using CMS.Membership;
   using CMS.OnlineMarketing;

   internal class ContactIsFemaleInstanceTranslator: IMacroRuleInstanceTranslator
   {
       /// <summary>
       /// Translates ContactIsFemaleMacro rule.
       /// </summary>
       public ObjectQuery<ContactInfo> Translate(MacroRuleInstance macroRuleInstance)
       {
           if (macroRuleInstance == null)
           {
               throw new ArgumentNullException("macroRuleInstance");
           }
           if (macroRuleInstance.MacroRuleName != "ContactIsFemaleMacro")
           {
               throw new ArgumentException("[ContactIsFemaleTranslator.Translate]: Only macro rule instances of type ContactIsFemaleMacro can be translated");
           }

           var ruleParameters = macroRuleInstance.Parameters;
           // ruleParameters contains the data user enters in macro fields
           string paramIs = ruleParameters["_is"].Value;
           QueryOperator queryOperator = paramIs == "!" ? QueryOperator.NotEquals : QueryOperator.Equals;

           return ContactInfoProvider.GetContacts().Where("ContactGender", queryOperator, (int)UserGenderEnum.Female);
       }
   }

   ```
3. Register an instance of the _MacroRuleMetadat&#x61;_&#x63;lass through the MacroRuleMetadataContainer.RegisterMetadata method. There are two ways in which you can register the class at the beginning of the application's lifecycle:

   - During the initialization process of the application itself — use the **CMSModuleLoader** partial class in the **App\_Code** folder.
   - When initializing [custom modules](https://docs.kentico.com/k9/custom-development/creating-custom-modules/initializing-modules-to-run-custom-code.md) — override the **OnInit** method of the module class.

     ```csharp

     protected override void OnInit()
     {
         ...
         MacroRuleMetadata metadata = new MacroRuleMetadata(
             "ContactIsFemaleMacro", 
             new ContactIsFemaleInstanceTranslator(), 
             affectingActivities: new List<string>(0),
             affectingAttributes: new List<string>(0));
         MacroRuleMetadataContainer.RegisterMetadata(metadata);
         ...
     }

     ```
4. Save the files.\
   **Important**: note that this way, the recalculation is not performed for any contact activity or attribute change. You can [make the rule recalculate](#recalculating-contact-group-and-scoring-rules-only-for-specific-activities-and-attributes) for a specific set of activities and attribute changes.

Now, when you use the translated macro rule in a scoring or contact group condition, the query is only performed once when you run recalculation.

> **Note:** **Compound conditions**
>
> All macro rules used in a contact group or scoring macro conditions need to be translated. Otherwise, the evaluation will be performed in the non-optimized way.

## Recalculating contact group and scoring rules only for specific activities and attributes

Another way of improving your site performance is by making sure that contact groups and scores are recalculated only when necessary. You can make sure your custom contact group and scoring macro rules are only recalculated for specified activities or when specific contact attributes change.

**Examples:**

- The "ContactIsFemale" macro doesn't need to be rebuilt by any contact activity that the visitor performs on the site. Instead, it only needs to be rebuilt by a change in the contact's "contactgender" attribute.
- A rule that checks whether a contact is registered for an event ("CMSContactIsRegisteredForSpecifiedEvent" macro by default) only needs to be rebuilt when a visitor performs an event booking activity on the site ("EVENT\_BOOKING" activity by default).
- A rule that checks whether a contact has visited a page ("CMSContactHasVisitedSpecifiedPageInLastXDays" macro by default) only needs to be rebuilt when a visitor performs an event booking activity on the site ("PAGE\_VISIT" activity by default).

**To specify the activities and attribute changes that rebuild your custom macro rule**

1. Create a [custom macro rule translator](#creating-a-custom-macro-rule-translator).
2. Extend the metadata instantiation by the following parameters:

   1. **IList affectingActivities** - a list of activities for which you want the macro to rebuild.
   2. **IList** **affectingAttributes** - a list of attributes for which you want the macro to rebuild.

```csharp title="Rebuild custom macro rule for specific activities or attribute changes"

protected override void OnInit()
{
    ...
    MacroRuleMetadata metadata = new MacroRuleMetadata(
        "CustomMacroRule", 
        new CustomMacroRuleTranslator(), 
        affectingActivities: new List<string>
        {
            // Recalculate for a predefined activity
            PredefinedActivityType.PAGE_VISIT,
            // Recalculate for a custom activity
            "custom_activity_codename"
        }
        affectingAttributes: new List<string>
        {
            // Recalculate when specific attributes change
            "contactgender",
            "contactcountryid"
        }); 
    MacroRuleMetadataContainer.RegisterMetadata(metadata);
    ...
}

```

```csharp title="Do not rebuild custom macro rule for any activities or attribute changes"

protected override void OnInit()
{
    ...
    var noNeedToRecalculate = new List<string>(0);

    MacroRuleMetadata metadata = new MacroRuleMetadata(
        "CustomMacroRule",
        new CustomMacroRuleTranslator(),
        affectingActivities : noNeedToRecalculate,
        affectingAttributes : noNeedToRecalculate
        );
    MacroRuleMetadataContainer.RegisterMetadata(metadata);
    ...
}

```

```csharp title="Rebuild custom macro rule for all activities and attribute changes"

protected override void OnInit()
{
    ...
    MacroRuleMetadata metadata = new MacroRuleMetadata(
        "CustomMacroRule",
        new CustomMacroRuleTranslator(),
        affectingActivities: new List<string>
        {
            MacroRuleMetadata.ALL_ACTIVITIES
        },
        affectingAttributes: new List<string>
        {
            MacroRuleMetadata.ALL_ATTRIBUTES
        });
    MacroRuleMetadataContainer.RegisterMetadata(metadata);
    ...
}

```

**Note**: You can also combine the parameters as you see fit.
3\. Save the files.

Now, only the specified contact activities or changes in contact attributes will trigger a recalculation of the custom macro rule.
