def search(self, event, limit, search_type, pattern, start): limit = limit and min(int(limit), self.limit) or self.default start = start and max(int(start) - 1, 0) or 0 search_type = search_type and search_type.lower() or u"" origpattern = pattern m = self.regex_re.match(pattern) is_regex = False if m: pattern = m.group(1) is_regex = bool(m.group(2)) # Hack: We replace $arg with _%, but this won't match a partial # "$arg" string if is_regex: filter_op = get_regexp_op(event.session) name_pattern = pattern.replace(r'\$arg', '_%') else: filter_op = lambda x, y: x.like(y, escape='#') pattern = '%%%s%%' % escape_like_re.sub(r'#\1', pattern) name_pattern = pattern.replace('$arg', '#_#%') query = event.session.query(Factoid)\ .join(Factoid.names).add_entity(FactoidName)\ .join(Factoid.values) if search_type.startswith('fact'): query = query.filter(filter_op(FactoidName.name, name_pattern)) elif search_type.startswith('value'): query = query.filter(filter_op(FactoidValue.value, pattern)) else: query = query.filter( or_(filter_op(FactoidName.name, name_pattern), filter_op(FactoidValue.value, pattern))) # Pre-evalute the iterable or the if statement will be True in SQLAlchemy 0.4. LP: #383286 matches = [match for match in query.all()] bounded_matches = matches[start:start + limit] if bounded_matches: event.addresponse(u'; '.join( u'%s [%s]' % (fname.name, len(factoid.values)) for factoid, fname in bounded_matches)) elif len(matches): event.addresponse( u"I could only find %(number)d things that matched '%(pattern)s'", { u'number': len(matches), u'pattern': origpattern, }) else: event.addresponse(u"I couldn't find anything that matched '%s'" % origpattern)
def search(self, event, limit, search_type, pattern, start): limit = limit and min(int(limit), self.limit) or self.default start = start and max(int(start) - 1, 0) or 0 search_type = search_type and search_type.lower() or u"" origpattern = pattern m = self.regex_re.match(pattern) is_regex = False if m: pattern = m.group(1) is_regex = bool(m.group(2)) # Hack: We replace $arg with _%, but this won't match a partial # "$arg" string if is_regex: filter_op = get_regexp_op(event.session) name_pattern = pattern.replace(r'\$arg', '_%') else: filter_op = lambda x, y: x.like(y, escape='#') pattern = '%%%s%%' % escape_like_re.sub(r'#\1', pattern) name_pattern = pattern.replace('$arg', '#_#%') query = event.session.query(Factoid)\ .join(Factoid.names).add_entity(FactoidName)\ .join(Factoid.values) if search_type.startswith('fact'): query = query.filter(filter_op(FactoidName.name, name_pattern)) elif search_type.startswith('value'): query = query.filter(filter_op(FactoidValue.value, pattern)) else: query = query.filter(or_(filter_op(FactoidName.name, name_pattern), filter_op(FactoidValue.value, pattern))) # Pre-evalute the iterable or the if statement will be True in SQLAlchemy 0.4. LP: #383286 matches = [match for match in query.all()] bounded_matches = matches[start:start+limit] if bounded_matches: event.addresponse(u'; '.join( u'%s [%s]' % (fname.name, len(factoid.values)) for factoid, fname in bounded_matches)) elif len(matches): event.addresponse(u"I could only find %(number)d things that matched '%(pattern)s'", { u'number': len(matches), u'pattern': origpattern, }) else: event.addresponse(u"I couldn't find anything that matched '%s'" % origpattern)
def get_factoid(session, name, number, pattern, is_regex, all=False, literal=False): """session: SQLAlchemy session name: Factoid name (can contain arguments unless literal query) number: If not None, restrict to factoid[number] (or factoid[number:] for literal queries) pattern: If not None, restrict to factoids matching this pattern (cannot be used in conjuction with number) is_regex: Pattern is a real regex all: Return a random factoid from the set if False literal: Match factoid name literally (implies all) if all or literal, a list is returned; otherwise a single factoid is returned if nothing is found, either an empty list or None is returned """ assert not (number and pattern), u'number and pattern cannot be used together' if literal: all = True # as mentioned in the docstring # First pass for exact matches, if necessary again for wildcard matches if literal: passes = (False,) else: passes = (False, True) for wild in passes: factoid = None query = session.query(Factoid)\ .add_entity(FactoidName).join(Factoid.names)\ .add_entity(FactoidValue).join(Factoid.values) if wild: # Reversed LIKE because factoid name contains SQL wildcards if # factoid supports arguments query = query.filter( 'lower(:fact) LIKE lower(name) ESCAPE :escape' ).params(fact=name, escape='\\') else: query = query.filter(FactoidName.name == escape_name(name)) # For normal matches, restrict to the subset applicable if not literal: query = query.filter(FactoidName.wild == wild) if pattern: if is_regex: op = get_regexp_op(session) query = query.filter(op(FactoidValue.value, pattern)) else: pattern = '%%%s%%' % escape_like_re.sub(r'#\1', pattern) query = query.filter(func.lower(FactoidValue.value) .like(pattern, escape='#')) if number is not None: number = max(0, int(number) - 1) try: # The .all() is to ensure the result is a list. # It gets len()ed later if literal: return query.order_by(FactoidValue.id).all()[number:] else: factoid = query.order_by(FactoidValue.id).all()[number] except IndexError: continue if all: if factoid is not None: return [factoid] else: factoid = query.all() else: factoid = factoid or query.order_by(func.random()).first() if factoid: return factoid if all: return [] return None
def get_factoid(session, name, number, pattern, is_regex, all=False, literal=False): """session: SQLAlchemy session name: Factoid name (can contain arguments unless literal query) number: If not None, restrict to factoid[number] (or factoid[number:] for literal queries) pattern: If not None, restrict to factoids matching this pattern (cannot be used in conjuction with number) is_regex: Pattern is a real regex all: Return a random factoid from the set if False literal: Match factoid name literally (implies all) if all or literal, a list is returned; otherwise a single factoid is returned if nothing is found, either an empty list or None is returned """ assert not (number and pattern), u'number and pattern cannot be used together' if literal: all = True # as mentioned in the docstring # First pass for exact matches, if necessary again for wildcard matches if literal: passes = (False, ) else: passes = (False, True) for wild in passes: factoid = None query = session.query(Factoid)\ .add_entity(FactoidName).join(Factoid.names)\ .add_entity(FactoidValue).join(Factoid.values) if wild: # Reversed LIKE because factoid name contains SQL wildcards if # factoid supports arguments query = query.filter( 'lower(:fact) LIKE lower(name) ESCAPE :escape').params( fact=name, escape='\\') else: query = query.filter(FactoidName.name == escape_name(name)) # For normal matches, restrict to the subset applicable if not literal: query = query.filter(FactoidName.wild == wild) if pattern: if is_regex: op = get_regexp_op(session) query = query.filter(op(FactoidValue.value, pattern)) else: pattern = '%%%s%%' % escape_like_re.sub(r'#\1', pattern) query = query.filter( func.lower(FactoidValue.value).like(pattern, escape='#')) if number is not None: number = max(0, int(number) - 1) try: # The .all() is to ensure the result is a list. # It gets len()ed later if literal: return query.order_by(FactoidValue.id).all()[number:] else: factoid = query.order_by(FactoidValue.id).all()[number] except IndexError: continue if all: if factoid is not None: return [factoid] else: factoid = query.all() else: factoid = factoid or query.order_by(func.random()).first() if factoid: return factoid if all: return [] return None