Ejemplo n.º 1
0
    def _by_search_tsearch(self, query: Query, operand: str,
                           maybe_negate: ConditionTransform) -> Query:
        tsquery = func.plainto_tsquery(literal("zulip.english_us_search"), literal(operand))
        query = query.column(ts_locs_array(literal("zulip.english_us_search"),
                                           column("rendered_content"),
                                           tsquery).label("content_matches"))
        # We HTML-escape the topic in Postgres to avoid doing a server round-trip
        query = query.column(ts_locs_array(literal("zulip.english_us_search"),
                                           func.escape_html(topic_column_sa()),
                                           tsquery).label("topic_matches"))

        # Do quoted string matching.  We really want phrase
        # search here so we can ignore punctuation and do
        # stemming, but there isn't a standard phrase search
        # mechanism in Postgres
        for term in re.findall(r'"[^"]+"|\S+', operand):
            if term[0] == '"' and term[-1] == '"':
                term = term[1:-1]
                term = '%' + connection.ops.prep_for_like_query(term) + '%'
                cond = or_(column("content").ilike(term),
                           topic_column_sa().ilike(term))
                query = query.where(maybe_negate(cond))

        cond = column("search_tsvector").op("@@")(tsquery)
        return query.where(maybe_negate(cond))
Ejemplo n.º 2
0
def add_narrow_conditions(user_profile: UserProfile,
                          inner_msg_id_col: ColumnElement,
                          query: Query,
                          narrow: OptionalNarrowListT) -> Tuple[Query, bool]:
    is_search = False  # for now

    if narrow is None:
        return (query, is_search)

    # Build the query for the narrow
    builder = NarrowBuilder(user_profile, inner_msg_id_col)
    search_operands = []

    # As we loop through terms, builder does most of the work to extend
    # our query, but we need to collect the search operands and handle
    # them after the loop.
    for term in narrow:
        if term['operator'] == 'search':
            search_operands.append(term['operand'])
        else:
            query = builder.add_term(query, term)

    if search_operands:
        is_search = True
        query = query.column(topic_column_sa()).column(column("rendered_content"))
        search_term = dict(
            operator='search',
            operand=' '.join(search_operands),
        )
        query = builder.add_term(query, search_term)

    return (query, is_search)
Ejemplo n.º 3
0
 def _by_search_pgroonga(self, query: Query, operand: str,
                         maybe_negate: ConditionTransform) -> Query:
     match_positions_character = func.pgroonga_match_positions_character
     query_extract_keywords = func.pgroonga_query_extract_keywords
     operand_escaped = func.escape_html(operand)
     keywords = query_extract_keywords(operand_escaped)
     query = query.column(match_positions_character(column("rendered_content"),
                                                    keywords).label("content_matches"))
     query = query.column(match_positions_character(func.escape_html(topic_column_sa()),
                                                    keywords).label("topic_matches"))
     condition = column("search_pgroonga").op("&@~")(operand_escaped)
     return query.where(maybe_negate(condition))
Ejemplo n.º 4
0
def messages_in_narrow_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    msg_ids: List[int] = REQ(validator=check_list(check_int)),
    narrow: OptionalNarrowListT = REQ(converter=narrow_parameter),
) -> HttpResponse:

    first_visible_message_id = get_first_visible_message_id(user_profile.realm)
    msg_ids = [
        message_id for message_id in msg_ids
        if message_id >= first_visible_message_id
    ]
    # This query is limited to messages the user has access to because they
    # actually received them, as reflected in `zerver_usermessage`.
    query = select(
        [column("message_id"),
         topic_column_sa(),
         column("rendered_content")],
        and_(
            column("user_profile_id") == literal(user_profile.id),
            column("message_id").in_(msg_ids)),
        join(
            table("zerver_usermessage"), table("zerver_message"),
            literal_column("zerver_usermessage.message_id") == literal_column(
                "zerver_message.id")))

    builder = NarrowBuilder(user_profile, column("message_id"))
    if narrow is not None:
        for term in narrow:
            query = builder.add_term(query, term)

    sa_conn = get_sqlalchemy_connection()
    query_result = list(sa_conn.execute(query).fetchall())

    search_fields = dict()
    for row in query_result:
        message_id = row['message_id']
        topic_name = row[DB_TOPIC_NAME]
        rendered_content = row['rendered_content']
        if 'content_matches' in row:
            content_matches = row['content_matches']
            topic_matches = row['topic_matches']
        else:
            content_matches = topic_matches = []
        search_fields[message_id] = get_search_fields(rendered_content,
                                                      topic_name,
                                                      content_matches,
                                                      topic_matches)

    return json_success({"messages": search_fields})