github.com/prebid/prebid-server/v2@v2.18.0/exchange/targeting.go (about)

     1  package exchange
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  
     7  	"github.com/prebid/openrtb/v20/openrtb2"
     8  	"github.com/prebid/prebid-server/v2/openrtb_ext"
     9  )
    10  
    11  const MaxKeyLength = 20
    12  
    13  // targetData tracks information about the winning Bid in each Imp.
    14  //
    15  // All functions on this struct are nil-safe. If the targetData struct is nil, then they behave
    16  // like they would if no targeting information is needed.
    17  //
    18  // All functions on this struct are all nil-safe.
    19  // If the value is nil, then no targeting data will be tracked.
    20  type targetData struct {
    21  	priceGranularity          openrtb_ext.PriceGranularity
    22  	mediaTypePriceGranularity openrtb_ext.MediaTypePriceGranularity
    23  	includeWinners            bool
    24  	includeBidderKeys         bool
    25  	includeCacheBids          bool
    26  	includeCacheVast          bool
    27  	includeFormat             bool
    28  	preferDeals               bool
    29  	alwaysIncludeDeals        bool
    30  	// cacheHost and cachePath exist to supply cache host and path as targeting parameters
    31  	cacheHost string
    32  	cachePath string
    33  }
    34  
    35  // setTargeting writes all the targeting params into the bids.
    36  // If any errors occur when setting the targeting params for a particular bid, then that bid will be ejected from the auction.
    37  //
    38  // The one exception is the `hb_cache_id` key. Since our APIs explicitly document cache keys to be on a "best effort" basis,
    39  // it's ok if those stay in the auction. For now, this method implements a very naive cache strategy.
    40  // In the future, we should implement a more clever retry & backoff strategy to balance the success rate & performance.
    41  func (targData *targetData) setTargeting(auc *auction, isApp bool, categoryMapping map[string]string, truncateTargetAttr *int, multiBidMap map[string]openrtb_ext.ExtMultiBid) {
    42  	for impId, topBidsPerImp := range auc.allBidsByBidder {
    43  		overallWinner := auc.winningBids[impId]
    44  		for originalBidderName, topBidsPerBidder := range topBidsPerImp {
    45  			targetingBidderCode := originalBidderName
    46  			bidderCodePrefix, maxBids := getMultiBidMeta(multiBidMap, originalBidderName.String())
    47  
    48  			for i, topBid := range topBidsPerBidder {
    49  				// Limit targeting keys to maxBids (default 1 bid).
    50  				// And, do not apply targeting for more than 1 bid if bidderCodePrefix is not defined.
    51  				if i == maxBids || (i == 1 && bidderCodePrefix == "") {
    52  					break
    53  				}
    54  
    55  				if i > 0 { // bidderCode is used for first bid, generated bidderCodePrefix for following bids
    56  					targetingBidderCode = openrtb_ext.BidderName(fmt.Sprintf("%s%d", bidderCodePrefix, i+1))
    57  				}
    58  
    59  				if maxBids > openrtb_ext.DefaultBidLimit { // add targetingbiddercode only if multibid is set for this bidder
    60  					topBid.TargetBidderCode = targetingBidderCode.String()
    61  				}
    62  
    63  				isOverallWinner := overallWinner == topBid
    64  
    65  				bidHasDeal := len(topBid.Bid.DealID) > 0
    66  
    67  				targets := make(map[string]string, 10)
    68  				if cpm, ok := auc.roundedPrices[topBid]; ok {
    69  					targData.addKeys(targets, openrtb_ext.HbpbConstantKey, cpm, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
    70  				}
    71  				targData.addKeys(targets, openrtb_ext.HbBidderConstantKey, string(targetingBidderCode), targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
    72  				if hbSize := makeHbSize(topBid.Bid); hbSize != "" {
    73  					targData.addKeys(targets, openrtb_ext.HbSizeConstantKey, hbSize, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
    74  				}
    75  				if cacheID, ok := auc.cacheIds[topBid.Bid]; ok {
    76  					targData.addKeys(targets, openrtb_ext.HbCacheKey, cacheID, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
    77  				}
    78  				if vastID, ok := auc.vastCacheIds[topBid.Bid]; ok {
    79  					targData.addKeys(targets, openrtb_ext.HbVastCacheKey, vastID, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
    80  				}
    81  				if targData.includeFormat {
    82  					targData.addKeys(targets, openrtb_ext.HbFormatKey, string(topBid.BidType), targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
    83  				}
    84  
    85  				if targData.cacheHost != "" {
    86  					targData.addKeys(targets, openrtb_ext.HbConstantCacheHostKey, targData.cacheHost, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
    87  				}
    88  				if targData.cachePath != "" {
    89  					targData.addKeys(targets, openrtb_ext.HbConstantCachePathKey, targData.cachePath, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
    90  				}
    91  
    92  				if bidHasDeal {
    93  					targData.addKeys(targets, openrtb_ext.HbDealIDConstantKey, topBid.Bid.DealID, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
    94  				}
    95  
    96  				if isApp {
    97  					targData.addKeys(targets, openrtb_ext.HbEnvKey, openrtb_ext.HbEnvKeyApp, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
    98  				}
    99  				if len(categoryMapping) > 0 {
   100  					targData.addKeys(targets, openrtb_ext.HbCategoryDurationKey, categoryMapping[topBid.Bid.ID], targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal)
   101  				}
   102  				topBid.BidTargets = targets
   103  			}
   104  		}
   105  	}
   106  }
   107  
   108  func (targData *targetData) addKeys(keys map[string]string, key openrtb_ext.TargetingKey, value string, bidderName openrtb_ext.BidderName, overallWinner bool, truncateTargetAttr *int, bidHasDeal bool) {
   109  	var maxLength int
   110  	if truncateTargetAttr != nil {
   111  		maxLength = *truncateTargetAttr
   112  		if maxLength < 0 {
   113  			maxLength = MaxKeyLength
   114  		}
   115  	} else {
   116  		maxLength = MaxKeyLength
   117  	}
   118  	if targData.includeBidderKeys || (targData.alwaysIncludeDeals && bidHasDeal) {
   119  		keys[key.BidderKey(bidderName, maxLength)] = value
   120  	}
   121  	if targData.includeWinners && overallWinner {
   122  		keys[key.TruncateKey(maxLength)] = value
   123  	}
   124  }
   125  
   126  func makeHbSize(bid *openrtb2.Bid) string {
   127  	if bid.W != 0 && bid.H != 0 {
   128  		return strconv.FormatInt(bid.W, 10) + "x" + strconv.FormatInt(bid.H, 10)
   129  	}
   130  	return ""
   131  }
   132  
   133  func getMultiBidMeta(multiBidMap map[string]openrtb_ext.ExtMultiBid, bidder string) (string, int) {
   134  	if multiBidMap != nil {
   135  		if multiBid, ok := multiBidMap[bidder]; ok {
   136  			return multiBid.TargetBidderCodePrefix, *multiBid.MaxBids
   137  		}
   138  	}
   139  
   140  	return "", openrtb_ext.DefaultBidLimit
   141  }