Database Reference
In-Depth Information
def
def
choose_ads
(
site_id
,
zone_id
,
user_id
,
keywords
):
site
=
db
.
ad
.
zone
.
find_one
({
'site_id'
:
site_id
,
'zone_id'
:
zone_id
})
iif
site
iis
None
:
return
return
[]
ads
=
ad_iterator
(
site
[
'ads'
],
keywords
)
user
=
db
.
ad
.
user
.
find_one
({
'user_id'
:
user_id
})
iif
user
iis
None
:
return
return
ads
for
for
ad
iin
ads
:
advertiser_id
=
ad
[
'campaign_id'
]
.
split
(
':'
,
1
)[
0
]
iif
ad_is_acceptable
(
ad
,
user
[
advertiser_id
]):
yield
yield
ad
return
return
None
Our
ad_iterator
method has been modified to allow us to score ads based on both their
eCPM as well as their relevance:
def
def
ad_iterator
(
ads
,
keywords
):
'''Find available ads, sorted by score, with random sort for ties'''
keywords
=
set
(
keywords
)
scored_ads
=
[
(
ad_score
(
ad
,
keywords
),
ad
)
for
for
ad
iin
ads
]
score_groups
=
groupby
(
sorted
(
scored_ads
),
key
=
lambda
lambda
score
,
ad
:
score
)
for
for
score
,
ad_group
iin
score_groups
:
ad_group
=
list
(
ad_group
)
shuffle
(
ad_group
)
for
for
ad
iin
ad_group
:
yield
yield
ad
def
def
ad_score
(
ad
,
keywords
):
'''Compute a desirability score based on the ad eCPM and keywords'''
matching
=
set
(
ad
[
'keywords'
])
.
intersection
(
keywords
)
return
return
ad
[
'ecpm'
]
*
math
.
log
(
1.1
+
len
(
matching
))
def
def
ad_is_acceptible
(
ad
,
profile
):
# same as above
The main thing to note in the preceding code is that ads must now be sorted according to some
score
, which in this case is computed based on a combination of the
ecpm
of the ad as well
as the number of keywords matched. More advanced use cases may boost the importance of
various keywords, but this goes beyond the scope of this use case. One thing to keep in mind
is that because the ads are now being sorted at display time, there may be performance issues
if a large number of ads are competing for the same display slot.