# dialects/mysql/expression.py # Copyright (C) 2005-2025 the SQLAlchemy authors and contributors # # # This module is part of SQLAlchemy and is released under # the MIT License: https://www.opensource.org/licenses/mit-license.php from __future__ import annotations from typing import Any from ... import exc from ... import util from ...sql import coercions from ...sql import elements from ...sql import operators from ...sql import roles from ...sql.base import _generative from ...sql.base import Generative from ...util.typing import Self class match(Generative, elements.BinaryExpression[Any]): """Produce a ``MATCH (X, Y) AGAINST ('TEXT')`` clause. E.g.:: from sqlalchemy import desc from sqlalchemy.dialects.mysql import match match_expr = match( users_table.c.firstname, users_table.c.lastname, against="Firstname Lastname", ) stmt = ( select(users_table) .where(match_expr.in_boolean_mode()) .order_by(desc(match_expr)) ) Would produce SQL resembling: .. sourcecode:: sql SELECT id, firstname, lastname FROM user WHERE MATCH(firstname, lastname) AGAINST (:param_1 IN BOOLEAN MODE) ORDER BY MATCH(firstname, lastname) AGAINST (:param_2) DESC The :func:`_mysql.match` function is a standalone version of the :meth:`_sql.ColumnElement.match` method available on all SQL expressions, as when :meth:`_expression.ColumnElement.match` is used, but allows to pass multiple columns :param cols: column expressions to match against :param against: expression to be compared towards :param in_boolean_mode: boolean, set "boolean mode" to true :param in_natural_language_mode: boolean , set "natural language" to true :param with_query_expansion: boolean, set "query expansion" to true .. versionadded:: 1.4.19 .. seealso:: :meth:`_expression.ColumnElement.match` """ __visit_name__ = "mysql_match" inherit_cache = True modifiers: util.immutabledict[str, Any] def __init__(self, *cols: elements.ColumnElement[Any], **kw: Any): if not cols: raise exc.ArgumentError("columns are required") against = kw.pop("against", None) if against is None: raise exc.ArgumentError("against is required") against = coercions.expect( roles.ExpressionElementRole, against, ) left = elements.BooleanClauseList._construct_raw( operators.comma_op, clauses=cols, ) left.group = False flags = util.immutabledict( { "mysql_boolean_mode": kw.pop("in_boolean_mode", False), "mysql_natural_language": kw.pop( "in_natural_language_mode", False ), "mysql_query_expansion": kw.pop("with_query_expansion", False), } ) if kw: raise exc.ArgumentError("unknown arguments: %s" % (", ".join(kw))) super().__init__(left, against, operators.match_op, modifiers=flags) @_generative def in_boolean_mode(self) -> Self: """Apply the "IN BOOLEAN MODE" modifier to the MATCH expression. :return: a new :class:`_mysql.match` instance with modifications applied. """ self.modifiers = self.modifiers.union({"mysql_boolean_mode": True}) return self @_generative def in_natural_language_mode(self) -> Self: """Apply the "IN NATURAL LANGUAGE MODE" modifier to the MATCH expression. :return: a new :class:`_mysql.match` instance with modifications applied. """ self.modifiers = self.modifiers.union({"mysql_natural_language": True}) return self @_generative def with_query_expansion(self) -> Self: """Apply the "WITH QUERY EXPANSION" modifier to the MATCH expression. :return: a new :class:`_mysql.match` instance with modifications applied. """ self.modifiers = self.modifiers.union({"mysql_query_expansion": True}) return self