expression.py 4.0 KB

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