Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(search): 支持拼音全拼搜索以及多音字搜索 #129

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions src/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,30 @@ const Search: React.FC = () => {

if (!advanced) {
result = OIerDb.oiers.filter(
(oier) => oier.lowered_name === input || oier.initials === input
(oier) =>
oier.lowered_name === input ||
oier.initials === input ||
(() => {
//match pinyin
const pinyinBatch = Array.from(oier.lowered_name).map((char) => [
...OIerDb.pinyins.getFull(char),
...OIerDb.pinyins.getInitial(char),
]);
if (pinyinBatch.some((p) => p.length === 0)) return false;
let matched = [-1];
for (const pinyins of pinyinBatch) {
const newMached = [];
for (const i of matched) {
for (const pinyin of pinyins) {
if (input.slice(i + 1).startsWith(pinyin)) {
newMached.push(i + pinyin.length);
}
}
}
matched = newMached;
}
return matched.some((i) => i == input.length - 1);
})()
);
} else if (!input && !grade && !school) {
result = [];
Expand Down Expand Up @@ -147,7 +170,7 @@ const Search: React.FC = () => {
{!advanced ? (
<Input
fluid
placeholder="键入学生姓名或其拼音首字母..."
placeholder="键入学生姓名,拼音或其拼音首字母..."
loading={isPending}
onChange={(_, { value }) => setInput(value.toLowerCase())}
spellCheck="false"
Expand All @@ -158,7 +181,7 @@ const Search: React.FC = () => {
<Form.Group widths="equal">
<Form.Input
label="姓名"
placeholder="姓名或姓名拼音首字母"
placeholder="姓名,姓名拼音或姓名拼音首字母"
spellCheck="false"
onChange={(_, { value }) => setInput(value.toLowerCase())}
defaultValue={input}
Expand Down
150 changes: 114 additions & 36 deletions src/libs/OIerDb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,25 @@ export class School {
award_counts: { [key: string]: { [key: number]: Counter<string> } };
}

export class PinyinQueryer extends Map<string, string[]> {
//也许需要些什么...?
constructor(entries?: any) {
super(entries);
}
getFull(char: string) {
return this.get(char) || [];
}
getInitial(char: string) {
//首字母 不去重!!(可提升效率)
return (this.get(char) || []).map((p) => p[0]);
}
}

export interface OIerDbData {
oiers: OIer[];
schools: School[];
contests: Contest[];
pinyins: PinyinQueryer;
enroll_middle_years: number[];
}

Expand All @@ -107,21 +122,33 @@ const urls = [

let __DATA__: OIerDbData = null;

const checkSha512 = (staticSha512: string, resultSha512: string) => {
const checkSha512 = (
staticSha512: string,
resultSha512: string,
pinyinSha512: string
) => {
try {
const { staticSha512: localStaticSha152, resultSha512: localResultSha512 } =
localStorage;
const {
staticSha512: localStaticSha152,
resultSha512: localResultSha512,
pinyinSha512: localPinyinSha512,
} = localStorage;

return (
staticSha512 === localStaticSha152 && resultSha512 === localResultSha512
staticSha512 === localStaticSha152 &&
resultSha512 === localResultSha512 &&
pinyinSha512 === localPinyinSha512
);
} catch (e) {
console.error(e);
return false;
}
};

const saveDataToIndexedDb = async (name: 'static' | 'oiers', data: any) => {
const saveDataToIndexedDb = async (
name: 'static' | 'oiers' | 'pinyins',
data: any
) => {
const db = await openDB('OIerDb', 2, {
upgrade(db) {
if (!db.objectStoreNames.contains('main')) {
Expand All @@ -135,7 +162,7 @@ const saveDataToIndexedDb = async (name: 'static' | 'oiers', data: any) => {
await os.put(data, name);
};

const getDataFromIndexedDb = async (name: 'static' | 'oiers') => {
const getDataFromIndexedDb = async (name: 'static' | 'oiers' | 'pinyins') => {
const db = await openDB('OIerDb');

if (!db.objectStoreNames.contains('main')) {
Expand Down Expand Up @@ -222,6 +249,14 @@ const textToRaw = (text: string) => {

return data;
};
const textToPinyinMap = (text: string) => {
const textToPinyinMap = new Map<string, string[]>();
text.split('\n').forEach((line) => {
const list = line.split(',');
textToPinyinMap.set(list[0], list.slice(1));
});
return textToPinyinMap;
};

const processData = (data: any) => {
const add_contestant = function (contest: Contest, record: Record) {
Expand Down Expand Up @@ -297,15 +332,15 @@ const processData = (data: any) => {
...new Set(result.oiers.map((oier) => oier.enroll_middle)),
];

result.pinyins = new PinyinQueryer(data.pinyins);

return result;
};

const getData = async (
urls: string | string[],
size: number,
setProgressPercent?: (p: number) => void,
start = 0,
end = 100,
setProgress?: (p: number) => void,
trackLabel = ''
) => {
const startTime = performance.now();
Expand Down Expand Up @@ -335,10 +370,8 @@ const getData = async (
chunks.push(value);
receivedSize += value.length;

if (setProgressPercent) {
setProgressPercent(
Math.ceil(start + Math.min((receivedSize / size) * (end - start), end))
);
if (setProgress) {
setProgress(receivedSize / size);
}
}

Expand Down Expand Up @@ -367,6 +400,31 @@ const getData = async (

return data;
};
const progressManager = (
begin: number,
end: number,
setProgressPercent: (p: number) => void
) => {
let total = 0;
const childCount: number[] = [];
const update = () => {
setProgressPercent(
begin +
Math.ceil(
(end - begin) * (childCount.reduce((s, v) => s + v, 0) / total)
)
);
};
return (size: number) => {
total += size;
childCount.push(0);
const i = childCount.length - 1;
return (p: number) => {
childCount[i] = p * size;
update();
};
};
};

export const initDb = async (setProgressPercent?: (p: number) => void) => {
if (__DATA__) return __DATA__;
Expand All @@ -392,40 +450,55 @@ export const initDb = async (setProgressPercent?: (p: number) => void) => {

setProgressPercent(8);

if (checkSha512(staticSha512, resultSha512)) {
const {
sha512: pinyinSha512,
size: pinyinSize,
}: { sha512: string; size: number } = await promiseAny(
infoUrls.map((url) => fetch(`${url}/pinyin.info.json?_=${+new Date()}`))
).then((res) => res.json());

if (checkSha512(staticSha512, resultSha512, pinyinSha512)) {
setProgressPercent(91);

const [staticData, oiers] = await Promise.all([
const [staticData, oiers, pinyins] = await Promise.all([
await getDataFromIndexedDb('static'),
await getDataFromIndexedDb('oiers'),
await getDataFromIndexedDb('pinyins'),
]);

setProgressPercent(96);

if (staticData && oiers) {
return (__DATA__ = processData({ static: staticData, oiers }));
if (staticData && oiers && pinyins) {
return (__DATA__ = processData({
static: staticData,
oiers,
pinyins: pinyins,
}));
}
}

setProgressPercent(10);

const staticData = await getData(
urls.map((url) => `${url}/static.${staticSha512.substring(0, 7)}.json`),
staticSize,
setProgressPercent,
10,
40,
'static.json'
).then((res) => JSON.parse(res));

const oiers = await getData(
urls.map((url) => `${url}/result.${resultSha512.substring(0, 7)}.txt`),
resultSize,
setProgressPercent,
40,
90,
'result.txt'
).then(textToRaw);
const setDownlodProgressFunc = progressManager(10, 90, setProgressPercent);
const [staticData, oiers, pinyins] = await Promise.all([
getData(
urls.map((url) => `${url}/static.${staticSha512.substring(0, 7)}.json`),
staticSize,
setDownlodProgressFunc(staticSize),
'static.json'
).then((res) => JSON.parse(res)),
getData(
urls.map((url) => `${url}/result.${resultSha512.substring(0, 7)}.txt`),
resultSize,
setDownlodProgressFunc(resultSize),
'result.txt'
).then(textToRaw),
getData(
urls.map((url) => `${url}/pinyin.${pinyinSha512.substring(0, 7)}.txt`),
pinyinSize,
setDownlodProgressFunc(pinyinSize),
'pinyin.txt'
).then(textToPinyinMap),
]);

setProgressPercent(91);

Expand All @@ -435,14 +508,19 @@ export const initDb = async (setProgressPercent?: (p: number) => void) => {

await saveDataToIndexedDb('oiers', oiers);

setProgressPercent(95);

await saveDataToIndexedDb('pinyins', pinyins);

setProgressPercent(96);

localStorage.setItem('staticSha512', staticSha512);
localStorage.setItem('resultSha512', resultSha512);
localStorage.setItem('pinyinSha512', pinyinSha512);

setProgressPercent(97);

__DATA__ = processData({ static: staticData, oiers });
__DATA__ = processData({ static: staticData, oiers, pinyins: pinyins });

setProgressPercent(100);

Expand Down
Loading