float ReqOptMatcher_Score_IMP(RequiredOptionalMatcher *self) { RequiredOptionalMatcherIVARS *const ivars = ReqOptMatcher_IVARS(self); int32_t const current_doc = Matcher_Get_Doc_ID(ivars->req_matcher); if (ivars->opt_matcher_first_time) { ivars->opt_matcher_first_time = false; if (ivars->opt_matcher != NULL && !Matcher_Advance(ivars->opt_matcher, current_doc)) { DECREF(ivars->opt_matcher); ivars->opt_matcher = NULL; } } if (ivars->opt_matcher == NULL) { return Matcher_Score(ivars->req_matcher) * ivars->coord_factors[1]; } else { int32_t opt_matcher_doc = Matcher_Get_Doc_ID(ivars->opt_matcher); if (opt_matcher_doc < current_doc) { opt_matcher_doc = Matcher_Advance(ivars->opt_matcher, current_doc); if (!opt_matcher_doc) { DECREF(ivars->opt_matcher); ivars->opt_matcher = NULL; float req_score = Matcher_Score(ivars->req_matcher); return req_score * ivars->coord_factors[1]; } } if (opt_matcher_doc == current_doc) { float score = Matcher_Score(ivars->req_matcher) + Matcher_Score(ivars->opt_matcher); score *= ivars->coord_factors[2]; return score; } else { return Matcher_Score(ivars->req_matcher) * ivars->coord_factors[1]; } } }
static int32_t S_advance_after_current(ORScorer *self, ORScorerIVARS *ivars) { float *const scores = ivars->scores; Matcher *child; // Get the top Matcher, or bail because there are no Matchers left. if (!ivars->size) { return 0; } else { child = ivars->top_hmd->matcher; } // The top matcher will already be at the correct doc, so start there. ivars->doc_id = ivars->top_hmd->doc; scores[0] = Matcher_Score(child); ivars->matching_kids = 1; do { // Attempt to advance past current doc. int32_t top_doc_id = SI_top_next((ORMatcher*)self, (ORMatcherIVARS*)ivars); if (!top_doc_id) { if (!ivars->size) { break; // bail, no more to advance } } if (top_doc_id != ivars->doc_id) { // Bail, least doc in queue is now past the one we're scoring. break; } else { // Accumulate score. child = ivars->top_hmd->matcher; scores[ivars->matching_kids] = Matcher_Score(child); ivars->matching_kids++; } } while (true); return ivars->doc_id; }
static INLINE bool SI_competitive(SortCollectorIVARS *ivars, int32_t doc_id) { /* Ordinarily, we would cache local copies of more member variables in * const automatic variables in order to improve code clarity and provide * more hints to the compiler about what variables are actually invariant * for the duration of this routine: * * uint8_t *const actions = ivars->actions; * const uint32_t num_rules = ivars->num_rules; * const int32_t bubble_doc = ivars->bubble_doc; * * However, our major goal is to return as quickly as possible, and the * common case is that we'll have our answer before the first loop iter * finishes -- so we don't worry about the cost of performing extra * dereferencing on subsequent loop iters. * * The goal of returning quickly also drives the choice of a "do-while" * loop instead of a "for" loop, and the switch statement optimized for * compilation to a jump table. */ uint8_t *const actions = ivars->actions; uint32_t i = 0; // Iterate through our array of actions, returning as quickly as possible. do { switch (actions[i] & ACTIONS_MASK) { case AUTO_ACCEPT: return true; case AUTO_REJECT: return false; case AUTO_TIE: break; case COMPARE_BY_SCORE: { float score = Matcher_Score(ivars->matcher); if (*(int32_t*)&score == *(int32_t*)&ivars->bubble_score) { break; } if (score > ivars->bubble_score) { MatchDoc_IVARS(ivars->bumped)->score = score; return true; } else if (score < ivars->bubble_score) { return false; } } break; case COMPARE_BY_SCORE_REV: { float score = Matcher_Score(ivars->matcher); if (*(int32_t*)&score == *(int32_t*)&ivars->bubble_score) { break; } if (score < ivars->bubble_score) { MatchDoc_IVARS(ivars->bumped)->score = score; return true; } else if (score > ivars->bubble_score) { return false; } } break; case COMPARE_BY_DOC_ID: if (doc_id > ivars->bubble_doc) { return false; } else if (doc_id < ivars->bubble_doc) { return true; } break; case COMPARE_BY_DOC_ID_REV: if (doc_id > ivars->bubble_doc) { return true; } else if (doc_id < ivars->bubble_doc) { return false; } break; case COMPARE_BY_ORD1: { int32_t comparison = SI_compare_by_ord1( ivars, i, SI_validate_doc_id(ivars, doc_id), ivars->bubble_doc); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_ORD1_REV: { int32_t comparison = SI_compare_by_ord1( ivars, i, ivars->bubble_doc, SI_validate_doc_id(ivars, doc_id)); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_ORD2: { int32_t comparison = SI_compare_by_ord2( ivars, i, SI_validate_doc_id(ivars, doc_id), ivars->bubble_doc); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_ORD2_REV: { int32_t comparison = SI_compare_by_ord2( ivars, i, ivars->bubble_doc, SI_validate_doc_id(ivars, doc_id)); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_ORD4: { int32_t comparison = SI_compare_by_ord4( ivars, i, SI_validate_doc_id(ivars, doc_id), ivars->bubble_doc); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_ORD4_REV: { int32_t comparison = SI_compare_by_ord4( ivars, i, ivars->bubble_doc, SI_validate_doc_id(ivars, doc_id)); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_ORD8: { int32_t comparison = SI_compare_by_ord8( ivars, i, SI_validate_doc_id(ivars, doc_id), ivars->bubble_doc); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_ORD8_REV: { int32_t comparison = SI_compare_by_ord8( ivars, i, ivars->bubble_doc, SI_validate_doc_id(ivars, doc_id)); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_ORD16: { int32_t comparison = SI_compare_by_ord16( ivars, i, SI_validate_doc_id(ivars, doc_id), ivars->bubble_doc); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_ORD16_REV: { int32_t comparison = SI_compare_by_ord16( ivars, i, ivars->bubble_doc, SI_validate_doc_id(ivars, doc_id)); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_ORD32: { int32_t comparison = SI_compare_by_ord32( ivars, i, SI_validate_doc_id(ivars, doc_id), ivars->bubble_doc); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_ORD32_REV: { int32_t comparison = SI_compare_by_ord32( ivars, i, ivars->bubble_doc, SI_validate_doc_id(ivars, doc_id)); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_NATIVE_ORD16: { int32_t comparison = SI_compare_by_native_ord16( ivars, i, SI_validate_doc_id(ivars, doc_id), ivars->bubble_doc); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_NATIVE_ORD16_REV: { int32_t comparison = SI_compare_by_native_ord16( ivars, i, ivars->bubble_doc, SI_validate_doc_id(ivars, doc_id)); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_NATIVE_ORD32: { int32_t comparison = SI_compare_by_native_ord32( ivars, i, SI_validate_doc_id(ivars, doc_id), ivars->bubble_doc); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; case COMPARE_BY_NATIVE_ORD32_REV: { int32_t comparison = SI_compare_by_native_ord32( ivars, i, ivars->bubble_doc, SI_validate_doc_id(ivars, doc_id)); if (comparison < 0) { return true; } else if (comparison > 0) { return false; } } break; default: THROW(ERR, "UNEXPECTED action %u8", actions[i]); } } while (++i < ivars->num_actions); // If we've made it this far and we're still tied, reject the doc so that // we prefer items already in the queue. This has the effect of // implicitly breaking ties by doc num, since docs are collected in order. return false; }
void SortColl_collect(SortCollector *self, int32_t doc_id) { SortCollectorIVARS *const ivars = SortColl_IVARS(self); // Add to the total number of hits. ivars->total_hits++; // Collect this hit if it's competitive. if (SI_competitive(ivars, doc_id)) { MatchDoc *const match_doc = ivars->bumped; MatchDocIVARS *const match_doc_ivars = MatchDoc_IVARS(match_doc); match_doc_ivars->doc_id = doc_id + ivars->base; if (ivars->need_score && match_doc_ivars->score == F32_NEGINF) { match_doc_ivars->score = Matcher_Score(ivars->matcher); } // Fetch values so that cross-segment sorting can work. if (ivars->need_values) { VArray *values = match_doc_ivars->values; for (uint32_t i = 0, max = ivars->num_rules; i < max; i++) { SortCache *cache = ivars->sort_caches[i]; Obj *old_val = (Obj*)VA_Delete(values, i); if (cache) { int32_t ord = SortCache_Ordinal(cache, doc_id); Obj *blank = old_val ? old_val : SortCache_Make_Blank(cache); Obj *val = SortCache_Value(cache, ord, blank); if (val) { VA_Store(values, i, (Obj*)val); } else { DECREF(blank); } } } } // Insert the new MatchDoc. ivars->bumped = (MatchDoc*)HitQ_Jostle(ivars->hit_q, (Obj*)match_doc); if (ivars->bumped) { if (ivars->bumped == match_doc) { /* The queue is full, and we have established a threshold for * this segment as to what sort of document is definitely not * acceptable. Turn off AUTO_ACCEPT and start actually * testing whether hits are competitive. */ ivars->bubble_score = match_doc_ivars->score; ivars->bubble_doc = doc_id; ivars->actions = ivars->derived_actions; } // Recycle. MatchDoc_IVARS(ivars->bumped)->score = ivars->need_score ? F32_NEGINF : F32_NAN; } else { // The queue isn't full yet, so create a fresh MatchDoc. VArray *values = ivars->need_values ? VA_new(ivars->num_rules) : NULL; float fake_score = ivars->need_score ? F32_NEGINF : F32_NAN; ivars->bumped = MatchDoc_new(INT32_MAX, fake_score, values); DECREF(values); } } }