def general_search(query): """ Queries of full equations should not call this, but we won't catch pieces of an equation without '=', '+', lt, or gt, so we have to account for those. An icontains filter ought to cover it. """ predicates = [Q(term__icontains=s) for s in predicate_string_set(query)] term_results = SearchTerm.objects.filter(reduce(operator.or_, predicates))\ .prefetch_related(*chain(__VAR_PREFETCH, __EQ_PREFETCH, __UNIT_PREFETCH)) if not term_results.count(): return None dataQ = SmartPQ() for t in term_results: for obj in chain(t.variable_set.all(), t.equation_set.all(), t.unit_set.all()): investigate_object(obj, query, dataQ) return dataQ.ordered_list()
def equation_exclusive_search(query): """ Since there are math characters in the query, we don't have to waste time querying other models. We can just check equations. """ # our equations should not have spaces or stars. Underscores and parens may # occur, but there should be a SearchTerm in the database with parens and # underscores stripped. For edge cases, we include those queries anyway init_predicate_strings = predicate_string_set(query) eq_operator_split = lambda q: re.split(r'[=+\-*/^<>]+', q) predicate_strings = copy(init_predicate_strings) for q_string in init_predicate_strings: for component in eq_operator_split(q_string): predicate_strings.add(component) predicates = [Q(term__icontains=s) for s in predicate_strings] term_results = SearchTerm.objects.filter(reduce(operator.or_, predicates))\ .prefetch_related(*__EQ_PREFETCH) if not term_results.count(): # using .count() so that we don't evaluate the queryset if not needed return None dataQ = SmartPQ() # custom priority queue for O(1) lookup! any_in_set = lambda fcn, set_: any(fcn(q) for q in set_) for t in term_results: for eq in t.equation_set.all(): to_add = eq if eq.is_definition(): to_add = eq.defined_var if dataQ.has_value(to_add): continue eqname = eq.quick_name l_eqname = eqname.lower() # don't bother checking full_names since we know we have math text. # full_names do not contain math. # PRIORITY 0: quick name exact match for any in basic string set if any_in_set(lambda q: q == eqname, init_predicate_strings): dataQ.put((0, to_add)) # PRIORITY 1: same as 0, but case insensitive elif any_in_set(lambda q: q.lower() == l_eqname, init_predicate_strings): dataQ.put((1, to_add)) # PRIORITY 2: alternative name case insensitive match elif any(any_in_set(lambda q: q.lower() == alt.term.lower(), init_predicate_strings) for alt in eq.search_terms.all() if alt.term != eqname): dataQ.put((2, to_add)) # expand modified query set to components of operator splits # PRIORITY 3: substrings of quick_names elif any_in_set(lambda q: q.lower() in l_eqname, predicate_strings): dataQ.put((3, to_add)) # PRIORITY 4: substrings of altnames elif any(any_in_set(lambda q: q.lower() in alt.term.lower(), predicate_strings) for alt in eq.search_terms.all() if alt.term != eqname): dataQ.put((4, to_add)) # PRIORITY 5: anything else caught in our database query else: dataQ.put((5, to_add)) return dataQ.ordered_list()