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