Tagging Framework
The Tagging Framework allows you to track information about objects in Elastic Path Commerce. You can then build conditions to evaluate that information and make decisions about application behavior.
A tag is a key that can have a request-specific value associated to it. You can populate tags programmatically based on customer profile details or through the Cortex traits header. You can use tags to store information about shoppers. The shopper’s tag set is evaluated against Price List Assignment conditions to determine which Price Lists to apply. You can also use tags to identify valid promotions on each API request.
For example, when a shopper arrives at the store’s page, the referrer URL is stored in a tag in the shopper’s tag set. Before displaying a page, the shopper’s tag set is evaluated against all Dynamic Content Delivery conditions to determine if there is Dynamic Content to display. One piece of Dynamic Content has a Delivery condition that requires the referrer URL to be a page on an affiliate site. The shopper’s referrer URL tag contains the expected page’s URL, so the Dynamic Content, a special promotion for affiliate customers, is displayed.
Workflow
If you want to create custom tags:
You need to set them up in the database.
Next, you need to create conditions.
If your application users will be building conditions, you can use the condition builder widget.
Finally, you will need to evaluate those conditions.
The Tagging Framework includes APIs for working with tags and evaluating conditional expressions.
Domain Model
The following diagram represents the domain model of the Tagging Framework.
Tagging classes and files
Most of the classes and interfaces of the Tagging Framework are located in the core library (com.elasticpath.core
) in the com.elasticpath.tags
package. Classes related to the generic condition builder UI widget are located in the com.elasticpath.cmclient.conditionbuilder
CM plugin project.
Tag
: a piece of information about an objectTagSet
: a set of tags associated with an objectTagDefinition
: defines a piece of information that can be stored and tracked by the applicationTagDictionary
: a set of tag definitionsCondition
: a rule that can be used to evaluate tag valuesConditionalExpression
: a group ofCondition
, joined by a logical operator (AND
,OR
, orNOT
) and evaluated as a single unitConditionDSLBuilder
: a service used to convert conditional expressions to strings and vice versa. The strings contain the expression in Elastic Path’s DSL (Domain Specific Language) for tag evaluationConditionEvaluatorService
: a service that evaluates a set of tags against a conditional expression and returns true or falseConditionBuilderFactoryImpl
a factory class used to create RCP (Rich Client Platform)Composite
objects containing the condition builder UI
Tag-related service beans are defined in WEB-INF/conf/spring/service/service.xml
.
Tagging database schema
The following database tables are used by the Tagging Framework:
TTAGALLOWEDVALUE
: specifies the Allowed tag values for each tag value typeTTAGCONDITION
: contains conditional expressionsTTAGDEFINITION
: contains the Tag definitions supported by the applicationTTAGDICTIONARY
: contains the available tag dictionariesTTAGDICTIONARYTAGDEFINITION
: associates tags with tag dictionariesTTAGGROUP
: contains tag groups used to organize tags in the condition builder UITTAGOPERATOR
: associates tag operators to tag value typesTTAGVALUETYPE
: contains the tag value types (the "business types") used to configure validation for the different tagsTTAGVALUETAGOPERATOR
: associates tag operators to tag value typesTVALIDATIONCONSTRAINTS
: specifies the Tag validations for tag value types (associated withTTAGVALUETYPE
throughobject_uid
column, with type set toTagValueType
)
In addition, the TSELLINGCONTEXTCONDITION
table is used to associate conditional expressions stored in TTAGCONDITION
with Dynamic Content Delivery.
Setting up tags in the database
The Tagging Framework touches several parts of the Elastic Path Commerce Tagging database schema. Depending on your needs, you may need to make changes to different ones.
Tag definitions
A tag definition contains information about a tag (name, description, tag value type, etc.). Before you create the tag definition, you need to decide what Tag value types the tag is going to be storing. Also, if you need to display the tag in a Using the Condition Builder UI widget in the CM, you also need to add the tag to a Tag groups.
The following SQL (Structured Query Language) inserts a tag definition row into the TTAGDEFINITION
table.
insert into TTAGDEFINITION(uidpk, guid, name, description, tagvaluetype_guid, taggroup_uid)
values(22, 'CUSTOM_TAG_1', 'Custom 1', 'A custom tag', 'text', 5);
The tagvaluetype_guid
must contain the GUID of an existing Tag value types. The taggroup_uid
can be null or may contain the UIDPK
of an existing Tag groups.
The tag’s display name can be localized by adding localized strings to the TLOCALIZEDPROPERTIES
table. The following SQL inserts English and French display names for a tag definition into the TLOCALIZEDPROPERTIES
table.
insert into TLOCALIZEDPROPERTIES (UIDPK,LOCALIZED_PROPERTY_KEY,VALUE,TYPE,OBJECT_UID)
values (50062,'tagDefinitionDisplayName_en','Custom tag 1','TagDefinition',22);
insert into TLOCALIZEDPROPERTIES (UIDPK,LOCALIZED_PROPERTY_KEY,VALUE,TYPE,OBJECT_UID)
values (50063,'tagDefinitionDisplayName_fr','Mot-clé 1','TagDefinition',22);
LOCALIZED_PROPERTY_KEY
must be set totagDefinitionDisplayName_
followed by the locale codeTYPE
must be set toTagDefinition
OBJECT_UID
must be set to theUIDPK
of the tag definition row
After a tag is defined, you need to add it to at least one tag dictionaries. The following SQL adds a tag to the tag dictionary whose GUID
is CUSTOM
:
insert into TTAGDICTIONARYTAGDEFINITION(tagdictionary_guid, tagdefinition_guid)
values('CUSTOM', 'CUSTOM_TAG_1');
Tag dictionaries
A tag dictionary is a logical set of tag definitions that share common characteristics. All tag definitions must be associated with at least one tag dictionary.
Tag dictionaries are defined in the TTAGDICTIONARY
table. Out of the box, Elastic Path Commerce includes the following tag dictionaries:
SHOPPER
: contains tags that relate to shoppersTIME
: contains tags that relate to timeSTORES
: contains tags that relate to storesPLA_SHOPPER
: contains tags that relate to shoppers used in the Commerce Manager "Price List Assignment wizard"PROMOTIONS_SHOPPER
: contains tags that relate to shoppers used in the Commerce Manager "Promotions wizard"
You can use existing dictionaries for your custom tags or you can create your own dictionaries.
insert into TTAGDICTIONARY(uidpk, guid, name, purpose)
values(4, 'CUSTOM', 'Custom', 'Used to store custom tags and conditions');
Tag groups
A tag group is a logical set of Tag definitions used to organize tags displayed in the Using the Condition Builder UI widget. A tag group can contain tags from multiple dictionaries.
Tag groups are defined in the TTAGGROUP
table. The following tag groups are provided out of the box:
BROWSE_BEHAVIOR
: Contains tags relating to the user’s browsing behavior (referring URL, search engine keywords, etc.)GEO_LOCATION
: Contains tags that store information retrieved from a GeoIP service (domain, country, city, etc.)CUSTOMER_PROFILE
: Contains tags that store information from the user’s profile (gender, age)SHOPPING_CART
: Contains tags relating to the shopping cart (cart subtotal)
The following SQL creates a tag group.
insert into TTAGGROUP(uidpk, guid) values(5, 'CUSTOM_GROUP');
The following SQL sets the localized display name for the tag group.
insert into TLOCALIZEDPROPERTIES (UIDPK,LOCALIZED_PROPERTY_KEY,VALUE,TYPE,OBJECT_UID)
values (50066,'tagGroupDisplayName_en','Custom group','TagGroup',5);
A tag can be assigned to a tag group by setting the taggroup_uid
.
insert into TTAGDEFINITION(uidpk, guid, name, description, tagvaluetype_guid, taggroup_uid)
values(22, 'CUSTOM_TAG_1', 'Custom 1', 'A custom tag', 'text', 5);
Tag value types
A tag value type is a high-level data type for tags. It is used to configure supported:
Every tag must be associated with one and only one tag value type.
Tag value types are defined in the TTAGVALUETYPE
table. The following types are provided out-of-the-box:
Tab Value Type | Description | Data Type | Validation Constraint |
---|---|---|---|
time | The date/time in 13-digit Java format | java.lang.Long | None |
age | A person’s age | java.lang.Integer | Must be between 0 and 120 |
gender | A person’s gender | java.lang.String | Must be either M or F (case-sensitive) |
text | Free-form text | java.lang.String | None |
money | A currency value | java.lang.BigDecimal | Must be a non-negative decimal |
time_zone | A time zone | java.lang.Float | Must be a float |
category | A category | java.lang.String | None |
country_code | A country code | java.lang.String | None |
ip_routing_type | An IP Routing type | java.lang.String | None |
ip_connection_type | An IP Connection type | java.lang.String | None |
continent | A continent | java.lang.String | None |
state | A state | java.lang.String | None |
city | A city | java.lang.String | None |
domain_name | A domain name | java.lang.String | None |
zip_code | A zip code | java.lang.String | Must be less than or equal to 10 characters |
search_phrase | A search phrase | java.lang.String | None |
Creating custom tag values
You can specify what values are allowed for your custom Tag value types. Allowed tag values are defined in the TTAGALLOWEDVALUE
table.
For example, you want to create a marital status tag to store each customer’s marital status. The possible values would be single, married, divorced, widowed, or common law. You could use the out-of-the-box text
tag value type, but that would allow CM users to enter invalid values when creating conditions with this tag. A better way would be to define a marital status tag value type that would only allow users to enter specific values. The following SQL creates the tag value type.
insert into TTAGVALUETYPE(uidpk, guid, java_type) values(17, 'maritalStatus', 'java.lang.String');
The following SQL defines the operators that are available to users when creating condition statements with tags of this type.
insert into TTAGVALUETYPEOPERATOR(tagvaluetype_guid, tagoperator_guid)
values('maritalStatus', 'equalTo'), ('maritalStatus', 'notEqualTo');
The following SQL sets the values that are allowed for the tag value type.
insert into TTAGALLOWEDVALUE(uidpk, value, tagvaluetype_guid, description, ordering)
values(42, 'single', 'maritalStatus', 'Single', 1),
(43, 'married', 'maritalStatus', 'Married', 2),
(44, 'divorced', 'maritalStatus', 'Divorced', 3),
(45, 'widowed', 'maritalStatus', 'Widowed', 4),
(46, 'common law', 'maritalStatus', 'Common law', 5);
In the CM’s serviceCM.xml
file, find the selectableTagValueServiceLocator
bean definition and add the following value provider:
<entry>
<key><value>maritalStatus</value></key>
<bean class="com.elasticpath.tags.service.impl.InternalSelectableStringTagValueProviderImpl"></bean>
</entry>
When a user creates a condition statement with this tag, the condition builder UI displays a drop-down list containing only the allowed values.
Tag operators
You can specify what operators are supported by Tag value types. The following tag operators are provided out of the box:
equalTo
Supported data type: String
, Integer
Checks if the tag value is equal to the value. For strings, this is a case-sensitive comparison.
notEqualTo
Supported data type: String
, Integer
Checks if the tag value is equal to the value. For strings, this is a case-sensitive comparison.
includes
Supported data type: String
Checks if the tag value contains the string. This is a case-sensitive comparison.
notIncludes
Supported data type: String
Checks if the tag value does not contain the string. This is a case-sensitive comparison.
equalsIgnoreCase
Supported data type: String
Checks if the tag value is equal to the string. This is a case-insensitive comparison.
includesIgnoreCase
Supported data type: String
Checks if the tag value contains the string. This is a case-insensitive comparison.
lessThan
Supported data type: Integer
Checks if the tag value is less than the number.
greaterThan
Supported data type: Integer
Checks if the tag value is greater than the number.
lessThanOrEqualTo
Supported data type: Integer
Checks if the tag value is less than or equal to the number.
greaterThanOrEqualTo
Supported data type: Integer
Checks if the tag value is greater than or equal to the number.
notEqualsIgnoreCase
Supported data type: String
, Integer
Checks if the tag value is not equal to the value. This is a case-insensitive comparison.
notIncludesIgnoreCase
Supported data type: String
Checks if the tag value contains the string. This is a case-insensitive comparison.
The following SQL associates a tag operator with a tag value type.
insert into TTAGVALUETYPEOPERATOR(tagvaluetype_guid, tagoperator_guid)
values('phone_number', 'includes'),
('phone_number', 'greaterThan'),
('phone_number', 'lessThan'),
('phone_number', 'notIncludes');
Tag validations
You can specify validation constraints on Tag value types to prevent users from setting invalid tag values. Validation constraints are stored as strings in the TVALIDATIONCONSTRAINTS
table. They are expressed in the Spring Expression Language format (also called SpEL), which provides a rich yet easy to learn syntax for defining validation rules.
For example, you want to create a tag to store North American telephone numbers (XXX-XXX-XXXX
). The following SQL creates the tag value type and its supported operators.
insert into TTAGVALUETYPE(uidpk, guid, java_type)
values(17, 'phone_number', 'java.lang.String');
insert into TTAGVALUETYPEOPERATOR(tagvaluetype_guid, tagoperator_guid)
values('phone_number', 'includes'),
('phone_number', 'greaterThan'),
('phone_number', 'lessThan'),
('phone_number', 'notIncludes');
The following SQL creats the validation constraint that will be used to determine whether the user input is valid.
insert into TVALIDATIONCONSTRAINTS(uidpk, object_uid, error_message_key, validation_constraint, type)
values(17, 17, 'validationTagPhoneNumberError',
'(#isValidConditionType((#this))) == true AND tagValue.matches(\'[2-9]\\\\d{2}-\\\\d{3}-\\\\d{4}\')',
'TagValueType');
The validation_constraint
column contains the SpEL code that is used to evaluate the user input. In this case, it tests the user input against a regular expression.
Conditional Expressions
Creating conditional expressions
A condition is a rule consisting of a tag name, an operator, and a value. A conditional expression consists of one or more conditions joined by a concatenation operator (AND
, OR
, and NOT
).
Usually, conditional expressions are built by application users through the Using the Condition Builder UI widget in the Commerce Manager UI. Conditional expressions can also be built programmatically. The following code uses the ConditionHandler
service to create two conditions.
import com.elasticpath.tags.domain.Condition;
import com.elasticpath.tags.domain.LogicalOperator;
import com.elasticpath.cmclient.conditionbuilder.wizard.conditions.handlers.ConditionBuilder;
import com.elasticpath.tags.service.ConditionDSLBuilder;
...
ConditionHandler conditionHandler = new ConditionHandler();
// Create the female shopper condition
String tagName1 = "CUSTOMER_GENDER";
String op1 = "equalTo";
String value1 = "F";
Condition condition1 = conditionHandler.buildCondition(tagName1, op1, value1);
// Create the age over 65 condition
String tagName2 = "CUSTOMER_AGE_YEARS";
String op2 = "greaterThan";
Integer value2 = new Integer(65);
Condition condition2 = conditionHandler.buildCondition(tagName2, op2, value2);
The buildCondition
method creates a Condition
object. The first parameter must be a valid Tag definitions name. An InvalidArgumentException
is thrown if the name does not match a tag definition in the TTAGDEFINITION
table. The second parameter must be an operator that is valid for the specified tag’s Tag value types. For example, the greaterThan
operator is not valid for the TARGET_URL
tag.
note
The ConditionalOperatorLookupService
can be used to retrieve the list of operators that are valid for a given tag.
The third parameter is the value you want to compare to the value in the tag set. The type of the value must match the data type for that tag’s tag value type as specified in the TTAGVALUETYPE
table.
After the condition or conditions have been created, they need to be joined and added to a conditional expression. This is required even when there is only one condition. The following code builds a conditional expression by joining the two conditions created in the previous example using the AND
operator.
// Create an AND operator to join the two conditions
LogicalOperator logicalOp = new LogicalOperator(LogicalOperatorType.AND);
logicalOp.addCondition(condition1);
logicalOp.addCondition(condition2);
// Create a string containing the AND-ed conditions
ConditionDSLBuilder dslBuilder = (ConditionDSLBuilder)
getBeanByName(ContextIdNames.TAG_CONDITION_DSL_BUILDER);
String exprString = dslBuilder.getConditionalDSLString(logicalOp);
// Create the conditional expression from the expression string
ConditionalExpression condExpr = new ConditionalExpression();
condExpr.setConditionString(exprStr);
condExpr.setName("Female shoppers over 65");
The conditional expression can then be used by the ConditionEvaluatorService
to Evaluating conditional expressions.
Evaluating conditional expressions
The ConditionEvaluatorService
is used to evaluate a tag set against Creating conditional expressions. The evaluateConditionOnTags
method of the ConditionEvaluatorService
takes a tag set and a conditional expression as arguments and returns true
if all the values in the tag set meet the criteria of the conditional statement.
import com.elasticpath.tags.service.ConditionEvaluatorService;
...
ConditionEvaluatorService evaluatorService =
(ConditionEvaluatorService) getBeanByName(ContextIdNames.TAG_CONDITION_EVALUATOR_SERVICE);
boolean result = evaluatorService.evaluateConditionOnTags(tagSet, condExpr);
Using the Condition Builder UI widget
The Condition Builder UI widget provides Commerce Manager users with an easy way to build conditional expressions to evaluate tags.
note
You can also create conditional expressions programmatically using the ConditionBuilder
service.
The "widget" is an RCP Composite
created using the com.elasticpath.cmclient.conditionbuilder.impl.tag.ConditionBuilderFactoryImpl
class. The following code sample shows how it works.
private Composite initializeModel() {
this.tagOperatorService = ServiceLocator.getService(ContextIdNames.TAG_OPERATOR_SERVICE);
TagDictionaryService tagDictionaryService = ServiceLocator.getService(ContextIdNames.TAG_DICTIONARY_SERVICE);
TagGroupService tagGroupService = ServiceLocator.getService(ContextIdNames.TAG_GROUP_SERVICE);
TagDictionary tagDictionary = tagDictionaryService.findByGuid(dictionary);
if (tagDefinitionsList == null) {
tagDefinitionsList = tagDictionary.getTagDefinitions();
}
Set<TagGroup> tagSet = new LinkedHashSet<>();
for (TagDefinition tagDefinition : tagDefinitionsList) {
if (tagDefinition.getGroup() == null) {
continue;
}
TagGroup tagGroup = tagGroupService.findByGuid(tagDefinition.getGroup().getGuid());
tagGroup.getTagDefinitions().retainAll(tagDefinitionsList);
tagSet.add(tagGroup);
}
tagGroupsList = tagSet;
}
Then call com.elasticpath.cmclient.conditionbuilder.impl.tag.ConditionBuilderFactoryImpl
from e,g com.elasticpath.cmclient.store.targetedselling.conditionalexpression.editors.ShopperConditionBuilderSection
public ShopperConditionBuilderSection(final FormPage formPage, final AbstractCmClientFormEditor editor) {
super(formPage, editor);
conditionBuilderFactory = new ConditionBuilderFactoryImpl();
conditionBuilderFactory.setLocale(CorePlugin.getDefault().getDefaultLocale());
conditionBuilderFactory.setDataBindingContext(getEditor().getDataBindingContext());
conditionBuilderFactory.setAddButtonText("ConditionBuilder_AddConditionButton"); //$NON-NLS-1$
conditionBuilderFactory.setConditionBuilderTitle("ConditionBuilder_Title"); //$NON-NLS-1$
conditionBuilderFactory.setTagDictionary(SHOPPER_DICTIONARY);
conditionBuilderFactory.getResourceAdapterFactory().setResourceAdapterForLogicalOperator(
object -> {
return TargetedSellingMessages.get().getMessage(object.getMessageKey());
});
conditionBuilderFactory.setListenerForRefreshParentComposite(
object -> {
getSection().getParent().setRedraw(false);
getSection().pack();
getSection().getParent().layout(true);
getSection().getParent().setRedraw(true);
getManagedForm().reflow(false);
});
conditionBuilderFactory.setListenerForMarkEditorState(
object -> {
if (!initConditionBuilder) {
getEditor().pageModified();
ShopperConditionBuilderSection.this.markDirty();
}
});
}
Making the Condition Builder State Policy Aware
The plugin’s State Policy is not automatically applied to the constituent controls of the Condition Builder composite. You must add a StateChangeTarget
to the state policy container and override StateChangeTarget
's setState
to call EpControlFactory.changeEpStateForComposite
. The first parameter to the changeEpStateForComposite
is the Condition Builder composite. The second parameter is the new state.
The following example shows how to apply the state policy to those controls.
// apply the state policy to the condition builder widget
policyContainer.addTarget(new StateChangeTarget() {
public void setState(final EpState state) {
EpControlFactory.changeEpStateForComposite(composite, state);
}
});