expression.py 3.8 KB

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