|
| 1 | +import t from 'tap' |
| 2 | +import { create, insert, search } from '@orama/orama' |
| 3 | +import { createTokenizer } from '../src/korean.js' |
| 4 | + |
| 5 | +const db = create({ |
| 6 | + schema: { |
| 7 | + name: 'string' |
| 8 | + }, |
| 9 | + components: { |
| 10 | + tokenizer: createTokenizer() |
| 11 | + } |
| 12 | +}) |
| 13 | + |
| 14 | +function getHitsNames(hits) { |
| 15 | + return hits.map((hit) => hit.document.name) |
| 16 | +} |
| 17 | + |
| 18 | +t.test('Korean tokenizer', async (t) => { |
| 19 | + await insert(db, { name: '서울' }) // Seoul |
| 20 | + await insert(db, { name: '부산' }) // Busan |
| 21 | + await insert(db, { name: '대구' }) // Daegu |
| 22 | + await insert(db, { name: '인천' }) // Incheon |
| 23 | + await insert(db, { name: '광주' }) // Gwangju |
| 24 | + await insert(db, { name: '수원' }) // Suwon |
| 25 | + await insert(db, { name: '포항' }) // Pohang |
| 26 | + await insert(db, { name: '서울대학교' }) // Seoul National University |
| 27 | + await insert(db, { name: '부산대학교' }) // Pusan National University |
| 28 | + await insert(db, { name: '포항공과대학교' }) // Pohang University of Science and Technology |
| 29 | + await insert(db, { name: '대전 울산 세종' }) // space-separated multi-word string (Daejeon Ulsan Sejong) |
| 30 | + |
| 31 | + // Intl.Segmenter('ko', { granularity: 'word' }) segments Korean on whitespace (UAX#29), so |
| 32 | + // space-less compounds such as '서울대학교' stay a single token and are NOT split into '서울' + '대학교' |
| 33 | + // (unlike the Japanese/Mandarin tokenizers, where dictionary segmentation splits e.g. '東京大学' into |
| 34 | + // '東京' + '大学'). This is an experimental limitation of the Korean tokenizer. The '서울'/'부산'/'포항' |
| 35 | + // searches below still return the compound because Orama matches the radix index by prefix |
| 36 | + // (default exact: false): '서울' is a prefix of the '서울대학교' token. |
| 37 | + const resultsSeoul = await search(db, { term: '서울', threshold: 0 }) |
| 38 | + |
| 39 | + t.equal(resultsSeoul.count, 2) |
| 40 | + t.equal(getHitsNames(resultsSeoul.hits).join(', '), '서울, 서울대학교') |
| 41 | + |
| 42 | + const resultsBusan = await search(db, { term: '부산', threshold: 0 }) |
| 43 | + |
| 44 | + t.equal(resultsBusan.count, 2) |
| 45 | + t.equal(getHitsNames(resultsBusan.hits).join(', '), '부산, 부산대학교') |
| 46 | + |
| 47 | + const resultsGwangju = await search(db, { term: '광주', threshold: 0 }) |
| 48 | + |
| 49 | + t.equal(resultsGwangju.count, 1) |
| 50 | + t.equal(getHitsNames(resultsGwangju.hits).join(', '), '광주') |
| 51 | + |
| 52 | + const resultsIncheon = await search(db, { term: '인천', threshold: 0 }) |
| 53 | + |
| 54 | + t.equal(resultsIncheon.count, 1) |
| 55 | + t.equal(getHitsNames(resultsIncheon.hits).join(', '), '인천') |
| 56 | + |
| 57 | + const resultsPohang = await search(db, { term: '포항', threshold: 0 }) |
| 58 | + |
| 59 | + t.equal(resultsPohang.count, 2) |
| 60 | + t.equal(getHitsNames(resultsPohang.hits).join(', '), '포항, 포항공과대학교') |
| 61 | + |
| 62 | + // Searching the shared suffix '대학교' (university) returns nothing: the compounds are indexed as |
| 63 | + // whole tokens ('서울대학교', '부산대학교', '포항공과대학교'), so the suffix is not a prefix of any token. |
| 64 | + // Contrast japanese.test.ts, where '大学' matches all three universities because the Japanese |
| 65 | + // tokenizer dictionary-splits the compounds. This assertion locks in the no-segmentation behavior. |
| 66 | + const resultsUniversity = await search(db, { term: '대학교', threshold: 0 }) |
| 67 | + |
| 68 | + t.equal(resultsUniversity.count, 0) |
| 69 | + |
| 70 | + // Whitespace word segmentation works: the space-separated '대전 울산 세종' is tokenized into |
| 71 | + // '대전' / '울산' / '세종', so searching the middle word finds that document. |
| 72 | + const resultsUlsan = await search(db, { term: '울산', threshold: 0 }) |
| 73 | + |
| 74 | + t.equal(resultsUlsan.count, 1) |
| 75 | + t.equal(getHitsNames(resultsUlsan.hits).join(', '), '대전 울산 세종') |
| 76 | +}) |
0 commit comments