태그 검색이 사람 말을 받는다 — '사인'으로 서명 태그 17종이 나온다
4단계 시작. 사용자가 태그를 못 찾은 이유는 접두어(PAT_·ETC_)가 아니라 <b>어휘</b>였다. 검색은 부분 문자열과 초성만 봤고, 카탈로그 표기는 레거시가 정한 것이다. 실제로 세어 보니 흔한 말이 전부 0건이었다 — 환자명 0건 (표기는 이름) 날짜 0건 (일자, 21건) 사인 0건 (싸인, 17건) 주민등록번호 0건 (주민번호) 오늘 0건 (현재, 11건) 도장·신장·남녀·성명 0건 서명 태그가 전부 레거시 오표기 '싸인' 이라, 의료진이 '사인'을 치면 "결과 없음"만 나오고 회복 경로가 없었다. 입력어를 카탈로그 표기로 옮기는 표 34항을 넣었다. <b>태그마다 설명을 지어내는 것이 아니다</b> — 오른쪽 값은 전부 카탈로그에 실재하는 표기다. 없는 내용을 만들지 않는다는 규칙은 그대로다. <b>표만 검사하면 부족하다.</b> "환자명 → 이름" 매핑이 옳다는 것만 확인되고 그 표기가 카탈로그에 실재하는지는 확인되지 않는다 — 없는 표기로 옮기면 여전히 0건이다. 그래서 실제 카탈로그 384종을 대상으로 건수를 단정하고, 별칭이 가리키는 표기가 카탈로그에 있는지도 따로 단정한다(그 검사가 없으면 "오늘 → 없는말" 같은 표를 적어도 0건인 채로 통과한다). <b>테스트 가정을 한 번 틀렸다.</b> "'사인' 과 '싸인' 이 같은 건수"라고 적었는데 17 대 16 으로 깨졌다 — 별칭은 대칭이 아니다. '싸인' 질의는 별칭 '서명' 까지 타서 한 건을 더 잡는다. 재야 할 것은 "'싸인' 이라고 적힌 태그를 '사인' 으로 다 찾는가" 이므로 원문으로 세도록 고쳤다. 화면에는 별칭으로 찾아 줬다는 사실을 적는다 — "17개 — 이 카탈로그는 '싸인' 으로 적혀 있습니다". 결과만 내놓으면 사용자는 다음에도 자기 말이 통했다고 오해하고, 결과가 비었을 때 왜 비었는지도 모른다. 게이트: 테스트 291/291(신규 6), --edit-smoke 0실패, --dialog-shots 넘침 0, --modal-check 0실패, --cleartype 11/11, --maxrect 0실패, --scale-budget 5/5, 빌드 경고 0, --db-smoke 1271건 diff 0, --db-render P062 md5 8d683835f5d81e7bb41c79071d6bf954 동일. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d72ab3ce1e
commit
a869e5a5bc
@@ -128,10 +128,83 @@ public static class TagSearch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// 사람이 치는 말을 카탈로그 표기로 옮겨 한 번 더 본다.
|
||||
// 이것 없이는 '환자명'·'날짜'·'사인' 같은 흔한 말이 <b>전부 0건</b>이었다(§Aliases).
|
||||
foreach (var alias in AliasesFor(token))
|
||||
{
|
||||
if (tag.Contains(alias, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return IsChoseongQuery(token)
|
||||
&& ToChoseong(tag).Contains(token, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 사용자가 칠 말 → 카탈로그의 실제 표기.
|
||||
///
|
||||
/// <b>왜 필요한가.</b> 검색은 부분 문자열과 초성만 봤고, 카탈로그 표기는 레거시가 정한 것이다.
|
||||
/// 실제로 세어 본 결과 흔한 말이 전부 0건이었다 —
|
||||
/// <c>환자명</c>(표기는 이름) · <c>날짜</c>(일자 21건) · <c>사인</c>(싸인 16건) ·
|
||||
/// <c>주민등록번호</c>(주민번호 5건) · <c>오늘</c>(현재 11건) · <c>도장</c>(직인) ·
|
||||
/// <c>신장</c>(키) · <c>남녀</c>(성별) · <c>성명</c>(이름).
|
||||
/// 서명 태그 16종이 전부 레거시 오표기 <c>싸인</c> 이라, 의료진이 '사인'을 치면
|
||||
/// "결과 없음"만 나오고 회복 경로가 없었다. <b>사용자가 못 찾은 이유는 접두어가 아니라 어휘였다.</b>
|
||||
///
|
||||
/// <b>태그마다 설명을 지어내는 것이 아니다.</b> 여기 있는 것은 <b>입력어 번역표</b>이고
|
||||
/// 오른쪽 값은 전부 카탈로그에 실재하는 표기다. 없는 내용을 만들지 않는다.
|
||||
///
|
||||
/// 한 방향으로만 적어도 양방향으로 듣는다 — '이름'을 쳐도 원래 부분 일치로 걸린다.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, string[]> Aliases = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["환자명"] = new[] { "이름" },
|
||||
["환자이름"] = new[] { "이름" },
|
||||
["성명"] = new[] { "이름" },
|
||||
["이름"] = new[] { "성명" },
|
||||
["주민등록번호"] = new[] { "주민번호" },
|
||||
["주번"] = new[] { "주민번호" },
|
||||
["등록번호"] = new[] { "차트번호" },
|
||||
["차번"] = new[] { "차트번호" },
|
||||
["날짜"] = new[] { "일자" },
|
||||
["날자"] = new[] { "일자" },
|
||||
["오늘"] = new[] { "현재" },
|
||||
["금일"] = new[] { "현재" },
|
||||
["지금"] = new[] { "현재" },
|
||||
["시각"] = new[] { "시간" },
|
||||
["사인"] = new[] { "싸인" },
|
||||
["서명"] = new[] { "싸인" },
|
||||
["싸인"] = new[] { "서명" },
|
||||
["도장"] = new[] { "직인" },
|
||||
["인감"] = new[] { "직인" },
|
||||
["신장"] = new[] { "키" },
|
||||
["남녀"] = new[] { "성별" },
|
||||
["성"] = new[] { "성별" },
|
||||
["전화"] = new[] { "연락처", "전화" },
|
||||
["폰"] = new[] { "연락처", "전화" },
|
||||
["핸드폰"] = new[] { "휴대", "연락처" },
|
||||
["휴대폰"] = new[] { "휴대", "연락처" },
|
||||
["번호"] = new[] { "차트번호", "주민번호" },
|
||||
["병원"] = new[] { "요양기관", "병원" },
|
||||
["의사"] = new[] { "의사", "담당" },
|
||||
["과"] = new[] { "진료과" },
|
||||
["주소"] = new[] { "주소", "도로명" },
|
||||
["나이"] = new[] { "나이", "연령" },
|
||||
["연령"] = new[] { "나이" },
|
||||
["생일"] = new[] { "생년월일" },
|
||||
};
|
||||
|
||||
private static string[] AliasesFor(string token)
|
||||
=> Aliases.TryGetValue(token, out var found) ? found : Array.Empty<string>();
|
||||
|
||||
/// <summary>이 말이 카탈로그 표기와 다른가 — 화면이 "'사인'은 '싸인'으로 찾습니다"를 알릴 때 쓴다</summary>
|
||||
public static string? AliasHintFor(string query)
|
||||
{
|
||||
var token = query.Trim();
|
||||
return Aliases.TryGetValue(token, out var found) && found.Length > 0 ? found[0] : null;
|
||||
}
|
||||
|
||||
/// <summary>공백으로 나눈 여러 토큰을 전부 만족해야 통과(AND)</summary>
|
||||
public static bool MatchesAll(string tag, IReadOnlyList<string> tokens)
|
||||
{
|
||||
|
||||
@@ -206,9 +206,16 @@ public partial class TagPickerDialogView : Window
|
||||
.ToList();
|
||||
|
||||
TagList.ItemsSource = items;
|
||||
// 별칭으로 찾아 준 경우에는 그 사실을 말해 준다.
|
||||
// 레거시 카탈로그는 '사인'을 '싸인'으로, '날짜'를 '일자'로 적어 두었다.
|
||||
// 결과만 내놓으면 사용자는 다음에도 자기 말이 통했다고 오해하고,
|
||||
// 결과가 비었을 때 왜 비었는지도 모른다.
|
||||
var alias = TagSearch.AliasHintFor(SearchBox.Text);
|
||||
ListCaption.Text = items.Count == 0
|
||||
? "결과 없음"
|
||||
: $"{items.Count}개 — 자주 쓰는 것과 많이 쓰인 것이 위에";
|
||||
? alias is null ? "결과 없음" : $"결과 없음 — '{alias}' 로도 찾아봤습니다"
|
||||
: alias is null
|
||||
? $"{items.Count}개 — 자주 쓰는 것과 많이 쓰인 것이 위에"
|
||||
: $"{items.Count}개 — 이 카탈로그는 '{alias}' 로 적혀 있습니다";
|
||||
CountText.Text = $"{Searched().Count}/{allTags.Count}건";
|
||||
|
||||
if (selectValue is { Length: > 0 })
|
||||
|
||||
Reference in New Issue
Block a user