LogoLogo
Support
Product Discovery Developer Guide
Product Discovery Developer Guide
  • Product Discovery Developer Guide
  • 🛒Item catalog management
    • What is the Items API?
    • How to work with Items
      • Item Schema
        • Attributes
        • Nested Item Schemas
        • Using the Item Schema API
        • DefaultLocale API
        • Onboarding on/migrating to Fredhopper
        • List of Reserved Attributes
      • Category Tree
        • Using the Category Tree API
        • Onboarding on XO
      • Item Catalog
        • Using the Catalog API
      • Items
        • Using the streaming Items API
        • Using the batch Items API
    • Step by Step guide
      • Step by step guide for Fredhopper customers
    • Feedback
      • Using the Feedback API
    • Authorization to APIs
    • Troubleshooting API errors
  • 🎯XO Recommendations
    • Introduction
    • Using the Recommendations API
    • Setting up the Chrome extension
    • Micro-segmentation
    • XO Legacy APIs
  • 🔎XO Search
    • Introduction
    • Getting started
    • API Reference
      • Search API
      • Autocomplete API (Beta)
      • Product Suggest API
    • API Parameters
      • Search
      • Pagination
      • Faceting
      • Sorting
      • Grouping
      • Filtering
      • Disable features
      • Response mask
      • Context
    • Configuration limitation
  • 🧪A/B testing
    • Fredhopper A/B testing
      • Integration steps for a non-caching solution
      • Integration steps for a caching solution
        • Java SDK Integration
          • Setup
          • Retrieve running A/B tests - Java SDK
          • Filter and request variant - Java SDK
          • Extending the SDK
        • Manual A/B tests integration
          • Retrieve running A/B tests
          • Filter out irrelevant A/B tests
          • Assign variants to user
          • Request variant for page
        • Limitations and Best Practices
  • 📚Resources
    • Glossary
    • Best Practices
      • Tracker Best Practices
      • Items API Best Practices
      • Fredhopper Data Configuration Best Practices
      • Fredhopper Query Response Best Practices
      • Fredhopper Merchandising Studio Best Practices
    • Privacy Notice
  • Archived Pages
    • FHR Tracking plan
    • XO Tracking plan
    • The Tracking API and JS Library
      • What to Track
        • Generic Actions
          • View
          • Click
          • Add to Cart
          • Remove from Cart
          • Purchase
        • Custom Actions
      • Initializing the JavaScript Library
      • REST API Technical Documentation
Powered by GitBook

Copyright @ 2024 Crownpeak Technology, Inc. All rights reserved.

On this page
  1. A/B testing
  2. Fredhopper A/B testing
  3. Integration steps for a caching solution
  4. Manual A/B tests integration

Assign variants to user

Assuming you have already filtered out any irrelevant A/B tests for a given page, it's now time to assign variants of the relevant A/B tests for the page to the user at hand.

Assume you have the following running A/B tests (the filters object has been omitted for brevity, and ids are shown as human readable):

[
    {
      "id": "ab-test-1",
      "variations":[
         {
            "id": "variant-a",
            "weight":50
         },
         {
            "id": "variant-b",
            "weight":50
         },
      ]
    },
    {
      "id": "ab-test-2",
      "variations":[
         {
            "id": "variant-a",
            "weight":20
         },
         {
            "id": "variant-b",
            "weight":30
         },
         {
            "id": "variant-c",
            "weight":50
         }
      ]
    },
]

For the above example, the following things need to be considered:

  • Every user should have a variant assigned for each of the two A/B tests. This means there's a total of six combinations which can occur. Adding more tests exponentially increases the number of combinations available, thus the filtering we did in the previous step is extremely important.

  • The user should be assigned a variant from ab-test-1 with an equal chance of getting variant-a or variant-b as their weight distribution is the same.

  • The user should be assigned a variant from ab-test-2 with a higher chance of getting variant-c as it has the biggest weight.

  • If the total among the weights does not equal 100, there's a chance equal to the difference between 100 and total that no variant will be applied, and the user will be shown the default or standard version of the page. In this case they are equal to 100.

  • The same user should always be assigned the same variants in order to ensure consistency.

Given the above considerations we need to ensure that the variant selection is fair and adheres to the weights provided, and that the same user always gets the same variant. To do this, we can use hashing. By hashing the session id of the user which should be stored and thus always the same, and the id of the A/B test we're picking variants for, we will always get the same value. This ensures consistency for the user. Since both ids are completely randomized, the hash value will also be random, thus by taking the remainder when dividing with 100, we get a percentile value which we can then use to pick the variant based on their weights. With a large number of users, and the randomization mentioned, the test will achieve the appropriate traffic split configured through the A/B test variant weights. Consider the following Java snippet below:

import com.google.common.hash.Hashing; // The hashing library in question

List<String> getAbTestsAndVariantsForSessionId(List<RunningAbTest> runningABTests, String sessionId) {
    ArrayList<String> selections = new ArrayList<>();
    for (RunningAbTest abTest : runningABTests) { // for every running AB test
        long percentile = Hashing.murmur3_32().hashUnencodedChars(sessionId + "#" + abTest.getId()).padToLong() % 100; //this is a JAVA implementation of the hashing algorithm to calculate the percentile. The session id and AB test id should be joined by "#"
        List<RunningAbTestVariant> variations = abTest.getVariations(); // the list of available variations for the given AB test
        int acc = 0; // start by setting the limit to 0
        for (RunningAbTestVariant variant : variations) {
            acc += variant.getWeight(); // add the weight of the current variation to the limit and check whether the percentile is below that target number. If yes, select the variant and stop. If not, repeat until it's carried out.
            if (acc > percentile) {
                selections.add(abTest.getId() + ":" + variant.getId()); // join the test id with the variant id to conform to the format defined in the "Calling FAS with A/B variant information" section i.e. "id-of-test:id-of-variant-in-test"
                break;
            }
        }
    return selections; // return a list of all variants selected for the given user
}
const MurmurHash3 = require('imurmurhash'); // The hashing library in question

const getAbTestsAndVariantsForSessionId = (runningAbTests, sessionId) => {
    const selections = [];
    runningAbTests.forEach(abTest => { // for every running AB test
        const percentile = MurmurHash3(sessionId + "#" + abTest.id).result() % 100; //this is the hashing algorithm to calculate the percentile. The session id and AB test id should be joined by "#"
        const variations = abTest.variations; // the list of available variations for the given AB test
        let acc = 0; // start by setting the limit to 0
        for(let variation of variations) {
            acc += variation.weight; // add the weight of the current variation to the limit and check whether the percentile is below that target number. If yes, select the variant and stop. If not, repeat until it's carried out.
            if(acc > percentile) {
                selections.push(abTest.id + ":" + variation.id); // join the test id with the variant id to conform to the format defined in the "Calling FAS with A/B variant information" section i.e. "id-of-test:id-of-variant-in-test"
                break;
            }
        }
    });
    return selections; // return a list of all variants selected for the given user
}
// you will need php 8.1

function getAbTestsAndVariantsForSessionId($runningAbTests, $sessionId) {
    $selections = [];
    foreach($runningAbTests as $abTest) { // for every running AB test
        $percentile = hash('murmur3a', $sessionId . "#" . $abTest['id']) % 100; //this is the hashing algorithm to calculate the percentile. The session id and AB test id should be joined by "#"
        $variations = $abTest['variations']; // the list of available variations for the given AB test
        $acc = 0; // start by setting the limit to 0
        foreach($variations as $variation) {
            $acc += $variation['weight']; // add the weight of the current variation to the limit and check whether the percentile is below that target number. If yes, select the variant and stop. If not, repeat until it's carried out.
            if($acc > $percentile) {
                array_push($selections,$abTest['id'] . ":" . $variation['id']); // join the test id with the variant id to conform to the format defined in the "Calling FAS with A/B variant information" section i.e. "id-of-test:id-of-variant-in-test"
                break;
            }
        }
    });
    return $selections; // return a list of all variants selected for the given user
}

As you can see from the code the important things to note are:

  • the selection for a given test needs to follow the ab-test-id:variant-id format, or for example: ab-test-1:variant-b .

  • The string to be hashed should be equal to {session-id}#{ab-test-id} or a # joined string of both ids.

  • The hashing algorithm used should be MurmurHash3 . You should be able to find as suitable implementation in any language.

Everything mentioned in this section is also covered via the SDK.

Once you have all of the necessary selections for the user in the format ab-test-id:variant-id you can join them together in a single string with the semicolon ; symbol, in the format selection-1;selection-2 or for a complete example using the running A/B tests in our example response: ab-test-1:variant-b;ab-test-2:variantc. This value can then be used in the fh_abtests parameter to request the exact version of the page we want. More on that below.

PreviousFilter out irrelevant A/B testsNextRequest variant for page

Last updated 3 years ago

Hint: If you need more information about the session id, check out the identities section .

🧪
here
Request variant for page