Skip to content

Commit fc77cdf

Browse files
committed
feat: add select_one_record & FORCE INDEX
1 parent a1c4326 commit fc77cdf

File tree

7 files changed

+70
-6
lines changed

7 files changed

+70
-6
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,14 @@ await AccountMgr.select_custom_fields(
5050
# wheres=Q(Q(id__in=aids), Q(gender=1), join_type="AND"),
5151
# wheres={"id__in": aids, "gender": 1},
5252
# wheres=[Q(id__in=aids), Q(gender=1)],
53+
index="PRIMARY",
5354
)
5455
```
5556
Generate sql and execute
5657
```sql
5758
SELECT
5859
id, extend ->> '$.last_login.ipv4' ipv4, extend ->> '$.last_login.start_datetime' start_datetime, CAST(extend ->> '$.last_login.online_sec' AS SIGNED) online_sec
59-
FROM account
60+
FROM account FORCE INDEX (`PRIMARY`)
6061
WHERE id IN (1,2,3) AND gender=1
6162
```
6263

examples/service/routers/account.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ async def update_view(
4545
async def query_by_id_view(
4646
aid: int = Query(...),
4747
):
48-
account = await AccountMgr.get_by_pk(aid)
48+
# account = await AccountMgr.get_by_pk(aid)
49+
account = await AccountMgr.select_one_record(
50+
fields=["*"],
51+
wheres=Q(id=aid),
52+
)
4953
return {"account": account}
5054

5155

fastapi_esql/orm/base_manager.py

+29-1
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,19 @@ async def select_custom_fields(
6363
cls,
6464
fields: List[str],
6565
wheres: Union[str, Q, Dict[str, Any], List[Q]],
66+
index: Optional[str] = None,
6667
groups: Optional[List[str]] = None,
6768
having: Optional[str] = None,
6869
orders: Optional[List[str]] = None,
6970
offset: Optional[int] = None,
70-
limit: int = 0,
71+
limit: Optional[int] = None,
7172
conn: Optional[BaseDBAsyncClient] = None,
7273
):
7374
sql = SQLizer.select_custom_fields(
7475
cls.table,
7576
fields,
7677
wheres,
78+
index,
7779
groups,
7880
having,
7981
orders,
@@ -84,6 +86,32 @@ async def select_custom_fields(
8486
conn = conn or cls.ro_conn
8587
return await CursorHandler.fetch_dicts(sql, conn, logger)
8688

89+
@classmethod
90+
async def select_one_record(
91+
cls,
92+
fields: List[str],
93+
wheres: Union[str, Q, Dict[str, Any], List[Q]],
94+
index: Optional[str] = None,
95+
groups: Optional[List[str]] = None,
96+
having: Optional[str] = None,
97+
orders: Optional[List[str]] = None,
98+
conn: Optional[BaseDBAsyncClient] = None,
99+
):
100+
sql = SQLizer.select_custom_fields(
101+
cls.table,
102+
fields,
103+
wheres,
104+
index,
105+
groups,
106+
having,
107+
orders,
108+
0,
109+
1,
110+
cls.model,
111+
)
112+
conn = conn or cls.ro_conn
113+
return await CursorHandler.fetch_one(sql, conn, logger)
114+
87115
@classmethod
88116
async def update_json_field(
89117
cls,

fastapi_esql/utils/cursor_handler.py

+16
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@ async def fetch_dicts(
1919
logger.exception(f"{e} SQL=>{sql}")
2020
return None
2121

22+
@classmethod
23+
async def fetch_one(
24+
cls,
25+
sql: str,
26+
conn: BaseDBAsyncClient,
27+
logger: Logger,
28+
) -> Optional[Dict[str, Any]]:
29+
try:
30+
dicts = await conn.execute_query_dict(sql)
31+
if dicts:
32+
return dicts[0]
33+
return {}
34+
except Exception as e:
35+
logger.exception(f"{e} SQL=>{sql}")
36+
return None
37+
2238
@classmethod
2339
async def sum_row_cnt(
2440
cls,

fastapi_esql/utils/sqlizer.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,12 @@ def select_custom_fields(
114114
table: str,
115115
fields: List[str],
116116
wheres: Union[str, Q, Dict[str, Any], List[Q]],
117+
index: Optional[str] = None,
117118
groups: Optional[List[str]] = None,
118119
having: Optional[str] = None,
119120
orders: Optional[List[str]] = None,
120121
offset: Optional[int] = None,
121-
limit: int = 0,
122+
limit: Optional[int] = None,
122123
model: Optional[Model] = None,
123124
) -> Optional[str]:
124125
if not all([table, fields, wheres]):
@@ -136,11 +137,12 @@ def select_custom_fields(
136137
sql = """
137138
SELECT
138139
{}
139-
FROM {}
140+
FROM {}{}
140141
WHERE {}
141142
{}""".format(
142143
", ".join(fields),
143144
table,
145+
f" FORCE INDEX (`{index}`)" if index else "",
144146
cls.resolve_wheres(wheres, model),
145147
"\n".join(i for i in extras if i),
146148
)

tests/test_cursor_handler.py

+12
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,18 @@ async def test_fetch_dicts(self):
2121
print(f"result => {result}")
2222
assert result
2323

24+
async def test_fetch_one(self):
25+
one = await CursorHandler.fetch_one("SELECT 1 idx", self.conn, logger)
26+
assert one == {"idx": 1}
27+
28+
with patch(
29+
"tortoise.backends.mysql.client_class.execute_query_dict",
30+
) as mock_exec:
31+
mock_exec.return_value = []
32+
empty = await CursorHandler.fetch_one("SELECT 1 idx", self.conn, logger)
33+
print(f"empty => {empty}")
34+
assert empty == {}
35+
2436
@patch("tortoise.backends.mysql.client_class.execute_query")
2537
async def test_sum_row_cnt(self, mock_exec):
2638
mock_exec.return_value = 10, object()

tests/test_sqlizer.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,13 @@ def test_select_custom_fields(self):
114114
"CAST(extend ->> '$.last_login.online_sec' AS SIGNED) online_sec"
115115
],
116116
wheres=f"id IN ({','.join(map(str, aids))}) AND gender=1",
117+
index="PRIMARY",
117118
model=self.model,
118119
)
119120
assert basic_sql == """
120121
SELECT
121122
id, extend ->> '$.last_login.ipv4' ipv4, extend ->> '$.last_login.start_datetime' start_datetime, CAST(extend ->> '$.last_login.online_sec' AS SIGNED) online_sec
122-
FROM account
123+
FROM account FORCE INDEX (`PRIMARY`)
123124
WHERE id IN (1,2,3) AND gender=1
124125
"""
125126

0 commit comments

Comments
 (0)