package com.twitter.home_mixer.functional_component.feature_hydrator import com.twitter.home_mixer.model.HomeFeatures._ import com.twitter.product_mixer.component_library.candidate_source.timeline_ranker.TimelineRankerInNetworkSourceTweetsByTweetIdMapFeature import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate import com.twitter.product_mixer.core.feature.Feature import com.twitter.product_mixer.core.feature.featuremap.FeatureMap import com.twitter.product_mixer.core.feature.featuremap.FeatureMapBuilder import com.twitter.product_mixer.core.functional_component.feature_hydrator.BulkCandidateFeatureHydrator import com.twitter.product_mixer.core.model.common.CandidateWithFeatures import com.twitter.product_mixer.core.model.common.identifier.FeatureHydratorIdentifier import com.twitter.product_mixer.core.pipeline.PipelineQuery import com.twitter.search.common.features.thriftscala.ThriftTweetFeatures import com.twitter.stitch.Stitch import com.twitter.timelineranker.thriftscala.CandidateTweet object SourceTweetEarlybirdFeature extends Feature[TweetCandidate, Option[ThriftTweetFeatures]] /** * Feature Hydrator that bulk hydrates source tweets' features to retweet candidates */ object RetweetSourceTweetFeatureHydrator extends BulkCandidateFeatureHydrator[PipelineQuery, TweetCandidate] { override val identifier: FeatureHydratorIdentifier = FeatureHydratorIdentifier( "RetweetSourceTweet") override val features: Set[Feature[_, _]] = Set( SourceTweetEarlybirdFeature, ) private val DefaultFeatureMap = FeatureMapBuilder() .add(SourceTweetEarlybirdFeature, None) .build() override def apply( query: PipelineQuery, candidates: Seq[CandidateWithFeatures[TweetCandidate]] ): Stitch[Seq[FeatureMap]] = { val sourceTweetsByTweetId: Option[Map[Long, CandidateTweet]] = { query.features.map( _.getOrElse( TimelineRankerInNetworkSourceTweetsByTweetIdMapFeature, Map.empty[Long, CandidateTweet])) } /** * Return DefaultFeatureMap (no-op to candidate) when it is unfeasible to hydrate the * source tweet's feature to the current candidate: early bird does not return source * tweets info / candidate is not a retweet / sourceTweetId is not found */ Stitch.value { if (sourceTweetsByTweetId.exists(_.nonEmpty)) { candidates.map { candidate => val candidateIsRetweet = candidate.features.getOrElse(IsRetweetFeature, false) val sourceTweetId = candidate.features.getOrElse(SourceTweetIdFeature, None) if (!candidateIsRetweet || sourceTweetId.isEmpty) { DefaultFeatureMap } else { val sourceTweet = sourceTweetsByTweetId.flatMap(_.get(sourceTweetId.get)) if (sourceTweet.nonEmpty) { val source = sourceTweet.get FeatureMapBuilder() .add(SourceTweetEarlybirdFeature, source.features) .build() } else { DefaultFeatureMap } } } } else { candidates.map(_ => DefaultFeatureMap) } } } }