expression.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. # dialects/mysql/expression.py
  2. # Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: https://www.opensource.org/licenses/mit-license.php
  7. from __future__ import annotations
  8. from typing import Any
  9. from ... import exc
  10. from ... import util
  11. from ...sql import coercions
  12. from ...sql import elements
  13. from ...sql import operators
  14. from ...sql import roles
  15. from ...sql.base import _generative
  16. from ...sql.base import Generative
  17. from ...util.typing import Self
  18. class match(Generative, elements.BinaryExpression[Any]):
  19. """Produce a ``MATCH (X, Y) AGAINST ('TEXT')`` clause.
  20. E.g.::
  21. from sqlalchemy import desc
  22. from sqlalchemy.dialects.mysql import match
  23. match_expr = match(
  24. users_table.c.firstname,
  25. users_table.c.lastname,
  26. against="Firstname Lastname",
  27. )
  28. stmt = (
  29. select(users_table)
  30. .where(match_expr.in_boolean_mode())
  31. .order_by(desc(match_expr))
  32. )
  33. Would produce SQL resembling:
  34. .. sourcecode:: sql
  35. SELECT id, firstname, lastname
  36. FROM user
  37. WHERE MATCH(firstname, lastname) AGAINST (:param_1 IN BOOLEAN MODE)
  38. ORDER BY MATCH(firstname, lastname) AGAINST (:param_2) DESC
  39. The :func:`_mysql.match` function is a standalone version of the
  40. :meth:`_sql.ColumnElement.match` method available on all
  41. SQL expressions, as when :meth:`_expression.ColumnElement.match` is
  42. used, but allows to pass multiple columns
  43. :param cols: column expressions to match against
  44. :param against: expression to be compared towards
  45. :param in_boolean_mode: boolean, set "boolean mode" to true
  46. :param in_natural_language_mode: boolean , set "natural language" to true
  47. :param with_query_expansion: boolean, set "query expansion" to true
  48. .. versionadded:: 1.4.19
  49. .. seealso::
  50. :meth:`_expression.ColumnElement.match`
  51. """
  52. __visit_name__ = "mysql_match"
  53. inherit_cache = True
  54. modifiers: util.immutabledict[str, Any]
  55. def __init__(self, *cols: elements.ColumnElement[Any], **kw: Any):
  56. if not cols:
  57. raise exc.ArgumentError("columns are required")
  58. against = kw.pop("against", None)
  59. if against is None:
  60. raise exc.ArgumentError("against is required")
  61. against = coercions.expect(
  62. roles.ExpressionElementRole,
  63. against,
  64. )
  65. left = elements.BooleanClauseList._construct_raw(
  66. operators.comma_op,
  67. clauses=cols,
  68. )
  69. left.group = False
  70. flags = util.immutabledict(
  71. {
  72. "mysql_boolean_mode": kw.pop("in_boolean_mode", False),
  73. "mysql_natural_language": kw.pop(
  74. "in_natural_language_mode", False
  75. ),
  76. "mysql_query_expansion": kw.pop("with_query_expansion", False),
  77. }
  78. )
  79. if kw:
  80. raise exc.ArgumentError("unknown arguments: %s" % (", ".join(kw)))
  81. super().__init__(left, against, operators.match_op, modifiers=flags)
  82. @_generative
  83. def in_boolean_mode(self) -> Self:
  84. """Apply the "IN BOOLEAN MODE" modifier to the MATCH expression.
  85. :return: a new :class:`_mysql.match` instance with modifications
  86. applied.
  87. """
  88. self.modifiers = self.modifiers.union({"mysql_boolean_mode": True})
  89. return self
  90. @_generative
  91. def in_natural_language_mode(self) -> Self:
  92. """Apply the "IN NATURAL LANGUAGE MODE" modifier to the MATCH
  93. expression.
  94. :return: a new :class:`_mysql.match` instance with modifications
  95. applied.
  96. """
  97. self.modifiers = self.modifiers.union({"mysql_natural_language": True})
  98. return self
  99. @_generative
  100. def with_query_expansion(self) -> Self:
  101. """Apply the "WITH QUERY EXPANSION" modifier to the MATCH expression.
  102. :return: a new :class:`_mysql.match` instance with modifications
  103. applied.
  104. """
  105. self.modifiers = self.modifiers.union({"mysql_query_expansion": True})
  106. return self