Commit da5f8e50 authored by Chunchi Che's avatar Chunchi Che

update sqlite

parent 0d3c3239
Pipeline #23026 failed with stages
in 12 minutes and 38 seconds
import sqliteMiddleWare, { sqliteCmd } from "@/middleware/sqlite"; import sqliteMiddleWare, { sqliteCmd } from "@/middleware/sqlite";
import { FtsParams } from "@/middleware/sqlite/fts";
export interface CardMeta { export interface CardMeta {
id: number; id: number;
...@@ -62,14 +63,10 @@ export async function fetchCard(id: number): Promise<CardMeta> { ...@@ -62,14 +63,10 @@ export async function fetchCard(id: number): Promise<CardMeta> {
* @returns 卡片数据 * @returns 卡片数据
* *
* */ * */
export async function searchCards( export async function searchCards(params: FtsParams): Promise<CardMeta[]> {
query: string,
type?: number
): Promise<CardMeta[]> {
// TODO: 让type支持多种filter类型
const res = await sqliteMiddleWare({ const res = await sqliteMiddleWare({
cmd: sqliteCmd.FTS, cmd: sqliteCmd.FTS,
payload: { query, type }, payload: { ftsParams: params },
}); });
return res.ftsResult ?? []; return res.ftsResult ?? [];
} }
......
import { Database } from "sql.js";
import { CardData, CardMeta, CardText } from "@/api";
interface FtsConditions {
// 过滤条件
types?: number[]; // 卡片类型
levels?: number[]; // 星阶/刻度/link值
atk?: [number, number]; // 攻击力区间
def?: [number, number]; // 防御力区间
race?: number; // 种族
attribute?: number; // 属性
}
export interface FtsParams {
query: string; // 用于全文检索的query
conditions: FtsConditions; // 过滤条件
}
export function invokeFts(db: Database, params: FtsParams): CardMeta[] {
const { query, conditions } = params;
const ftsTexts: CardText[] = [];
const ftsMetas: CardMeta[] = [];
const textStmt = db.prepare("SELECT * FROM texts WHERE name LIKE $query");
textStmt.bind({ $query: `%${query}%` });
while (textStmt.step()) {
const row = textStmt.getAsObject();
ftsTexts.push(row);
}
for (const text of ftsTexts) {
const id = text.id;
if (id) {
const sql = `SELECT * FROM datas ${getFtsCondtions(conditions)}`;
const dataStmt = db.prepare(sql);
const data: CardData = dataStmt.getAsObject({ $id: id });
ftsMetas.push({ id, data, text });
}
}
return ftsMetas;
}
function getFtsCondtions(conditions: FtsConditions): string {
const { types, levels, atk, def, race, attribute } = conditions;
const typesCondition = types
?.map((type) => `(types & ${type}) > 0`)
.join(" OR ");
const levelsCondition = levels
?.map((level) => `level = ${level}`)
.join(" OR ");
const atkCondition = atk ? `atk BETWEEN ${atk[0]} AND ${atk[1]}` : undefined;
const defCondition = def ? `def BETWEEN ${def[0]} AND ${def[1]}` : undefined;
const raceCondition = race !== undefined ? `race = ${race}` : undefined;
const attributeCondition =
attribute !== undefined ? `attribute = ${attribute}` : undefined;
const merged = [
typesCondition,
levelsCondition,
atkCondition,
defCondition,
raceCondition,
attributeCondition,
]
.filter((condition) => condition !== undefined)
.map((condition) => `(${condition})`)
.join(" AND ");
return merged !== "" ? `WHERE ${merged}` : "";
}
...@@ -11,6 +11,8 @@ import { CardData, CardMeta, CardText } from "@/api/cards"; ...@@ -11,6 +11,8 @@ import { CardData, CardMeta, CardText } from "@/api/cards";
import { useConfig } from "@/config"; import { useConfig } from "@/config";
import { pfetch } from "@/infra"; import { pfetch } from "@/infra";
import { FtsParams, invokeFts } from "./fts";
const NeosConfig = useConfig(); const NeosConfig = useConfig();
export enum sqliteCmd { export enum sqliteCmd {
...@@ -29,11 +31,9 @@ export interface sqliteAction { ...@@ -29,11 +31,9 @@ export interface sqliteAction {
dbUrl: string; dbUrl: string;
progressCallback?: (progress: number) => void; // 用于获取读取进度 progressCallback?: (progress: number) => void; // 用于获取读取进度
}; };
// 需要读取卡牌数据的ID
payload?: { payload?: {
id?: number; // 卡牌ID id?: number; // 卡牌ID
query?: string; // 用于全文检索的query ftsParams?: FtsParams; // 用于全文检索的参数
type?: number; // 通过`type`过滤
}; };
} }
...@@ -89,44 +89,10 @@ export default async function (action: sqliteAction): Promise<sqliteResult> { ...@@ -89,44 +89,10 @@ export default async function (action: sqliteAction): Promise<sqliteResult> {
return {}; return {};
} }
case sqliteCmd.FTS: { case sqliteCmd.FTS: {
if (YGODB && action.payload && action.payload.query) { if (YGODB && action.payload && action.payload.ftsParams) {
// TODO: 这里应该可以优化为联表查询 const metas = invokeFts(YGODB, action.payload.ftsParams);
const query = action.payload.query;
const type = action.payload.type;
const ftsTexts: CardText[] = [];
const ftsMetas: CardMeta[] = [];
const textStmt = YGODB.prepare(
"SELECT * FROM texts WHERE name LIKE $query"
);
textStmt.bind({ $query: `%${query}%` });
while (textStmt.step()) {
const row = textStmt.getAsObject();
ftsTexts.push(row);
}
for (const text of ftsTexts) {
const id = text.id;
if (id && type !== undefined) {
const sql = "SELECT * FROM datas WHERE ID = $id AND type = $type";
const dataStmt = YGODB.prepare(sql);
const data: CardData = dataStmt.getAsObject({
$id: id,
$type: type,
});
ftsMetas.push({ id, data, text });
} else if (id) {
const sql = "SELECT * FROM datas WHERE ID = $id";
const dataStmt = YGODB.prepare(sql);
const data: CardData = dataStmt.getAsObject({ $id: id });
ftsMetas.push({ id, data, text });
}
}
return { ftsResult: ftsMetas }; return { ftsResult: metas };
} else { } else {
console.warn("ygo db not init or query not provied!"); console.warn("ygo db not init or query not provied!");
} }
......
import { import {
DeleteOutlined, DeleteOutlined,
DownloadOutlined, DownloadOutlined,
PlusOutlined,
FileAddOutlined, FileAddOutlined,
PlusOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Button, Dropdown, MenuProps } from "antd"; import { Button, Dropdown, MenuProps } from "antd";
......
...@@ -13,10 +13,10 @@ import { ...@@ -13,10 +13,10 @@ import {
Button, Button,
ConfigProvider, ConfigProvider,
Input, Input,
Dropdown,
Space, Space,
type ThemeConfig, type ThemeConfig,
} from "antd"; } from "antd";
import classNames from "classnames";
import { memo, useEffect, useRef, useState } from "react"; import { memo, useEffect, useRef, useState } from "react";
import { DndProvider, useDrag, useDrop } from "react-dnd"; import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend"; import { HTML5Backend } from "react-dnd-html5-backend";
...@@ -48,7 +48,6 @@ import { ...@@ -48,7 +48,6 @@ import {
iDeckToEditingDeck, iDeckToEditingDeck,
type Type, type Type,
} from "./utils"; } from "./utils";
import classNames from "classnames";
const theme: ThemeConfig = { const theme: ThemeConfig = {
components: { components: {
...@@ -209,9 +208,9 @@ const Search: React.FC = () => { ...@@ -209,9 +208,9 @@ const Search: React.FC = () => {
const [searchWord, setSearchWord] = useState(""); const [searchWord, setSearchWord] = useState("");
const [searchResult, setSearchResult] = useState<CardMeta[]>([]); const [searchResult, setSearchResult] = useState<CardMeta[]>([]);
const handleSearch = async () => { const handleSearch = async () => {
const result = (await searchCards(searchWord)).filter( const result = (
(card) => !isToken(card.data.type ?? 0) await searchCards({ query: searchWord, conditions: {} })
); // 衍生物不显示 ).filter((card) => !isToken(card.data.type ?? 0)); // 衍生物不显示
setSearchResult(result); setSearchResult(result);
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment