RTB4FREE Rules Processing

Last updated: March 24, 2017

Overview

Selection of a campaign, and the creatives within a campaign is a function of a list of "rules" that are applied to attributes of the incoming bid request. Each rule is applied in turn to the bid request. Each rule returns a value of True or False. A value of True means as far as that individual rule is concerned, the Campaign/creative is a candidate for making a bid. If at any time, a rule evaluates to false the Campaign/creative is not a candidate for making a bid and rule processing for that Campaign/creative is terminated.

What is a Rule

A rule is simply a question applied to the values on the bid request against a constraint defined in the Campaign or Creative. The question is framed such that the response will be True or False. An informal rule might be "Is user.id equal to "XXX", or "Is user.id contained in the set "my_cookies".

A series of rules are used to "filter" the Bid Request against the definition of the campaign.

Defining Rules

The Rule is a JSON object within an array object of the Campaign definition called "attributes". So, from here on out, we will use the term "constraint" to mean a rule defined as a JSON object in the list of "attributes". A creative, which is a sub-object of the campaign can also have its own array of attributes. So the rules defined at the campaign level apply to all creatives, while the constraints within a creative apply only to that creative.

The form of the constraint is basically the following:

{
	"bidRequestValues" : [ array of strings defining the bid request JSON object ]
	"op" : the operation to apply,
	"value" : the-value you are looking for,
	"notPresentOk" : is-the-bidrequest-value-presence-mandatory
}

An example constraint that implements a blacklist against the site.domain value in a bid request, to exclude google.com and weather.com would look like the following:

{
	"bidRequestValues" : [ "site", "domain" ]
	"op" : "NOT_MEMBER"
	"value" : ["google.com", "weather.com"],
	"notPresentOk" : false
}

So in this constraint a site.domain of the value of "abraxus.com" will return True.

In this example the bidRequestValues resolves to a String. But, the value could resolve to a number. You have to know what the type of the object is the rule will use and apply the correct rule. Here is a rule that tests a number for equality:

{
	"bidRequestValues" : [ "at" ]
	"op" : "EQUALS""
	"value" : 2,
	"notPresentOk" : false
}

In this example, we only will bid on requests where the auction is second price.

Sometimes, the bidRequestValues are an array. So here, a set operation like INTERSECTS is important. For example, suppose you decided you want to make sure you don't bid on categories IAB-25 and IAB7-19. So in this example we will use an intersection against the bid request's "bcat" object:

{
	"bidRequestValues" : [ "bcat" ]
	"op" : "NOT_INTERSECTS"
	"value" : ["IAB-25", "IAB7-19"],
	"notPresentOk" : false
}

In this example we make sure there is no intersection with restricted categories.

Dealing with objects in impressions. requires a wildcard character. For example, you want to bid on a banner ad whose width is less than 728px. The rule looks like:

{
	"bidRequestValues" : [ "imp", "*", "banner", "w" ]
	"op" : "LESS_THAN""
	"value" : 728
	"notPresentOk" : false
}

This means any impression in the array with a banner and width < 728 is eligable. Then all the other rules are applied as well to this impression. If multiple impressions will resolve, they are sorted into an array after processing, and one of the candidates is chosen at random.

Operators

A rich set of operators is provided for the RTB to work with. This list includes all the actions we have encountered over the past several years.

  1. EQUALS - Tests bid request scalar being equal to another scalar
  2. NOT_EQUALS - Tests bid request scalar != to another scalar
  3. LESS_THAN - Tests bid request scalar < than another scalar
  4. LESS_THAN_EQUALS - Tests bid request scalar <= another scalar
  5. GREATER_THAN - Tests bid request scalar > another scalar
  6. GREATER_THAN_EQUALS - Tests bid request scalar >= another scalar
  7. MEMBER - Tests bid request scalar is a member of a set
  8. NOT_MEMBER - Tests bid request scalar not a member of a set
  9. INTERSECTS - Tests bid request array intersects another set and is not empty set
  10. NOT_INTERSECTS Tests bid request array intersected with another set is an empty set
  11. DOMAIN - Tests bid request scalar is between 2 numbers (defined in an array)
  12. NOT_DOMAIN - Tests bid request scalar is not between 2 numbers (defined in an array)r
  13. STRINGIN - Tests a string is a substring of the string in the bid request object.
  14. NOT_STRINGIN - Tests a string is not a substring of the string in the bid request object.
  15. REGEX - Tests for a match of a regular expression against a string in the bid request object.
  16. NOT_REGEX - Tests for the non-match of a regular expression against a string in the bid request object.
  17. EXISTS - Tests that an object is in existance within the bid request.
  18. NOT_EXISTS - Tests that an object does not exist within the bid request object.
  19. INRANGE - Tests that the lat/lon of a geo object in the bid request is within a certain number of Km from a specified point
  20. NOT_INRANGE Tests that the lat/lon of a geo object in the bid request is at least a certain number of km from a specified point.

Efficient Processing

When the bidder receives the bid request it is a text string. It is then converted into a JACKSON JSON object. The issue now is what is the best way to traverse the JACKSON object for each rule. One way is to simply traverse JSON tree on every rule. With a lot of campaigns, creatives and rules, this is an expensive operation. There is a better way...

When a new campaign is introduced into the system, the bidder "compiles" all of the bidRequest objects it finds into a 2 lists. The first list is simply a JAVA set of strings that represents the bid request. So, ["device", "geo", "lat"] becomes "device.geo.lat" - a key. A set is used, because multiple campaigns/creatives could be querying the same object. So we can reduce duplicate traversals straight away. The second set is actually the array of objects that needs to be traversed for that key.

Now when a bid request comes in, it converted to JSON, and then the list of keys is iterated against. For each key, query the Bid Request for the object specified by the key, and store the result in a HashMap using the bidRequest key.

Now when then constraint is applied, the value of bidRequest object in the constraint is obtained from the HashMap, not from the JSON object. So, in essence, we flatten the JSON tree into only those items that we need.