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