feat: improve legal search intent and aliases

This commit is contained in:
2026-09-16 09:48:46 +03:00
parent 94ba04ff56
commit af6b0b8098
11 changed files with 111 additions and 37 deletions

View File

@@ -10,12 +10,52 @@ import urllib.parse
from search.minjust_opensearch import APP_VERSION, request_json
LEGAL_FORM_ALIASES = {
"ru": {
"осоо": ("ооо", "общество с ограниченной ответственностью"),
"ооо": ("осоо", "общество с ограниченной ответственностью"),
"общество с ограниченной ответственностью": ("осоо", "ооо"),
"общества с ограниченной ответственностью": ("осоо", "ооо"),
},
"ky": {
"жчк": ("жоопкерчилиги чектелген коом",),
"жоопкерчилиги чектелген коом": ("жчк",),
},
}
COMPANY_REGISTRATION_DOCUMENTS = ("667", "4")
def query_variants(language: str, query: str) -> list[str]:
variants = [query]
for alias, expansions in LEGAL_FORM_ALIASES[language].items():
pattern = re.compile(rf"(?<!\w){re.escape(alias)}(?!\w)", re.IGNORECASE)
if pattern.search(query):
variants.extend(pattern.sub(expansion, query) for expansion in expansions)
return list(dict.fromkeys(variants))
def company_registration_clauses(language: str, query: str) -> list[dict]:
patterns = {
"ru": (r"\ак\s+откры\w*\s+осоо\b", r"\b(порядок|процедура)\s+откры\w*\s+осоо\b", r"\ак\s+зарегистр\w*\s+осоо\b"),
"ky": (r"\bжчк\s+ач\w*\s+тартиби\b", r"\bжчк\s+кантип\s+ач\w*\b"),
forms, patterns = {
"ru": (
r"(?:осоо|ооо|обществ\w*\s+с\s+ограниченной\s+ответственностью)",
(
r"\ак\s+(?:откры\w*|созда\w*|зарегистр\w*)\s+{form}\b",
r"\b(?:порядок|процедура)\s+(?:открыти\w*|создани\w*|регистраци\w*)\s+{form}\b",
r"\b(?:государственн\w+\s+)?регистраци\w*\s+{form}\b",
r"\bсоздани\w*\s+{form}\b",
),
),
"ky": (
r"(?:жчк|жоопкерчилиги\s+чектелген\s+коом)",
(
r"\bжчк\s+ач\w*\s+тартиби\b",
r"\bжчк\s+кантип\s+ач\w*\b",
r"\b{form}\s+(?:ачуу|ачуунун|каттоо|каттоонун)\b",
r"\b(?:ачуу|каттоо)\s+{form}\b",
),
),
}[language]
if not any(re.search(pattern, query.casefold()) for pattern in patterns):
if not any(re.search(pattern.format(form=forms), query.casefold()) for pattern in patterns):
return []
status = {"ru": "Действует", "ky": "Күчүндө"}[language]
@@ -35,28 +75,34 @@ def company_registration_clauses(language: str, query: str) -> list[dict]:
}
}
return [clause("230044970", 2000), clause("667", 1000)]
return [clause(COMPANY_REGISTRATION_DOCUMENTS[0], 2000), clause(COMPANY_REGISTRATION_DOCUMENTS[1], 1000)]
def build_search_query(language: str, query: str) -> dict:
intent_clauses = company_registration_clauses(language, query)
full_text = [
{
"multi_match": {
"query": variant,
"fields": [f"document_name_{language}^4", f"text_{language}"],
"type": "cross_fields",
"operator": "OR" if intent_clauses else "AND",
}
}
for variant in query_variants(language, query)
]
planned = {"should": [*full_text, *intent_clauses], "minimum_should_match": 1}
if intent_clauses:
planned["filter"] = [{"terms": {"document_code": list(COMPANY_REGISTRATION_DOCUMENTS)}}]
return {"bool": planned}
def build_search_body(language: str, query: str, top_k: int) -> bytes:
full_text = {
"multi_match": {
"query": query,
"fields": [f"document_name_{language}", f"text_{language}"],
"type": "cross_fields",
}
}
clauses = company_registration_clauses(language, query)
bool_query = {"filter": {"term": {"language": language}}}
if clauses:
bool_query.update({"should": [full_text, *clauses], "minimum_should_match": 1})
else:
bool_query["must"] = full_text
return json.dumps({
"size": top_k,
"track_total_hits": False,
"_source": ["document_code"],
"query": {"bool": bool_query},
"query": {"bool": {"filter": [{"term": {"language": language}}, {"term": {"is_current_edition": True}}], "must": build_search_query(language, query)}},
"collapse": {"field": "document_code"},
}, ensure_ascii=False).encode()