'Performs a full text search over events with given keys.
Args:
room_ids (list): List of room ids to search in
search_term (str): Search term to search for
keys (list): List of keys to search in, currently supports
"content.body", "content.name", "content.topic"
Returns:
list of dicts'

@defer.inlineCallbacks
def search_msgs(self, room_ids, search_term, keys):

    clauses = []
    search_query = search_query = _parse_query(self.database_engine, search_term)
    args = []
    if (len(room_ids) < 500):
        clauses.append(('room_id    IN    (%s)' % (','.join((['?'] * len(room_ids))),)))
        args.extend(room_ids)
    local_clauses = []
    for key in keys:
        local_clauses.append('key    =    ?')
        args.append(key)
    clauses.append(('(%s)' % ('    OR    '.join(local_clauses),)))
    count_args = args
    count_clauses = clauses
    if isinstance(self.database_engine, PostgresEngine):
        sql = "SELECT    ts_rank_cd(vector,    to_tsquery('english',    ?))    AS    rank,    room_id,    event_id    FROM    event_search    WHERE    vector    @@    to_tsquery('english',    ?)"
        args = ([search_query, search_query] + args)
        count_sql = "SELECT    room_id,    count(*)    as    count    FROM    event_search    WHERE    vector    @@    to_tsquery('english',    ?)"
        count_args = ([search_query] + count_args)
    elif isinstance(self.database_engine, Sqlite3Engine):
        sql = 'SELECT    rank(matchinfo(event_search))    as    rank,    room_id,    event_id    FROM    event_search    WHERE    value    MATCH    ?'
        args = ([search_query] + args)
        count_sql = 'SELECT    room_id,    count(*)    as    count    FROM    event_search    WHERE    value    MATCH    ?'
        count_args = ([search_term] + count_args)
    else:
        raise Exception('Unrecognized    database    engine')
    for clause in clauses:
        sql += ('    AND    ' + clause)
    for clause in count_clauses:
        count_sql += ('    AND    ' + clause)
    sql += '    ORDER    BY    rank    DESC    LIMIT    500'
    results = (yield self._execute('search_msgs', self.cursor_to_dict, sql, *args))
    results = filter((lambda row: (row['room_id'] in room_ids)), results)
    events = (yield self._get_events([r['event_id'] for r in results]))
    event_map = {ev.event_id: ev for ev in events}
    highlights = None
    if isinstance(self.database_engine, PostgresEngine):
        highlights = (yield self._find_highlights_in_postgres(search_query, events))
    count_sql += '    GROUP    BY    room_id'
    count_results = (yield self._execute('search_rooms_count', self.cursor_to_dict, count_sql, *count_args))
    count = sum((row['count'] for row in count_results if (row['room_id'] in room_ids)))
    defer.returnValue({'results': [{'event': event_map[r['event_id']], 'rank': r['rank']} for r in results if (r['event_id'] in event_map)], 'highlights': highlights, 'count': count})
