Improve tax profit search relevance
This commit is contained in:
@@ -17,7 +17,7 @@ from search.reviews import ReviewSnapshots, ReviewStore
|
||||
|
||||
|
||||
API_VERSION = "v1"
|
||||
SEARCH_ALGORITHM_VERSION = "search-4"
|
||||
SEARCH_ALGORITHM_VERSION = "search-5"
|
||||
LANGUAGES = {"ru", "ky"}
|
||||
CODE = re.compile(r"^[0-9]+$")
|
||||
MAX_PAGE_SIZE = 100
|
||||
|
||||
@@ -17,7 +17,7 @@ from typing import Iterator
|
||||
|
||||
from search.catalog import authority_codes, legal_force_code, source_code
|
||||
|
||||
APP_VERSION = "0.10.2"
|
||||
APP_VERSION = "0.10.3"
|
||||
LANGUAGES = {"ru", "ky"}
|
||||
DEFAULT_MAPPING = Path(__file__).with_name("minjust-fragments-index.json")
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ LEGAL_FORM_ALIASES = {
|
||||
},
|
||||
}
|
||||
COMPANY_REGISTRATION_DOCUMENTS = (("230044970", 3000), ("667", 2000), ("4", 1000))
|
||||
TAX_PROFIT_DOCUMENTS = (("112340", 3000),)
|
||||
|
||||
|
||||
def query_variants(language: str, query: str) -> list[str]:
|
||||
@@ -34,6 +35,26 @@ def query_variants(language: str, query: str) -> list[str]:
|
||||
return list(dict.fromkeys(variants))
|
||||
|
||||
|
||||
def document_priority_clauses(language: str, documents: tuple[tuple[str, int], ...]) -> list[dict]:
|
||||
status = {"ru": "Действует", "ky": "Күчүндө"}[language]
|
||||
return [
|
||||
{
|
||||
"constant_score": {
|
||||
"filter": {
|
||||
"bool": {
|
||||
"filter": [
|
||||
{"term": {"document_code": code}},
|
||||
{"term": {f"status_{language}": status}},
|
||||
]
|
||||
}
|
||||
},
|
||||
"boost": boost,
|
||||
}
|
||||
}
|
||||
for code, boost in documents
|
||||
]
|
||||
|
||||
|
||||
def company_registration_clauses(language: str, query: str) -> list[dict]:
|
||||
non_registration_context = {
|
||||
"ru": (r"\bсч[её]т\w*", r"\bбанк\w*", r"\bфилиал\w*", r"\bпредставительств\w*"),
|
||||
@@ -66,38 +87,28 @@ def company_registration_clauses(language: str, query: str) -> list[dict]:
|
||||
if not any(re.search(pattern.format(form=forms), normalized_query) for pattern in patterns):
|
||||
return []
|
||||
|
||||
status = {"ru": "Действует", "ky": "Күчүндө"}[language]
|
||||
# ponytail: curated legal mapping; replace with a reviewed intent catalog when coverage expands.
|
||||
def clause(document_code: str, boost: int) -> dict:
|
||||
return {
|
||||
"constant_score": {
|
||||
"filter": {
|
||||
"bool": {
|
||||
"filter": [
|
||||
{"term": {"document_code": document_code}},
|
||||
{"term": {f"status_{language}": status}},
|
||||
]
|
||||
}
|
||||
},
|
||||
"boost": boost,
|
||||
}
|
||||
}
|
||||
return document_priority_clauses(language, COMPANY_REGISTRATION_DOCUMENTS)
|
||||
|
||||
return [clause(code, boost) for code, boost in COMPANY_REGISTRATION_DOCUMENTS]
|
||||
|
||||
def tax_profit_clauses(language: str, query: str) -> list[dict]:
|
||||
if language != "ru" or not re.search(r"\bналог\w*\s+(?:на\s+)?прибыл\w*\b", query.casefold()):
|
||||
return []
|
||||
# ponytail: exact topic anchor; expand only with verified tax-code sources.
|
||||
return document_priority_clauses(language, TAX_PROFIT_DOCUMENTS)
|
||||
|
||||
|
||||
def build_search_query(language: str, query: str) -> dict:
|
||||
intent_clauses = company_registration_clauses(language, query)
|
||||
registration_clauses = company_registration_clauses(language, query)
|
||||
intent_clauses = [*registration_clauses, *tax_profit_clauses(language, query)]
|
||||
operator = "OR" if registration_clauses else "AND"
|
||||
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",
|
||||
}
|
||||
}
|
||||
clause
|
||||
for variant in query_variants(language, query)
|
||||
for clause in (
|
||||
{"match": {f"text_{language}": {"query": variant, "operator": operator}}},
|
||||
{"match": {f"document_name_{language}": {"query": variant, "operator": operator, "boost": 4}}},
|
||||
{"match_phrase": {f"text_{language}": {"query": variant, "boost": 3}}},
|
||||
)
|
||||
]
|
||||
planned = {"should": [*full_text, *intent_clauses], "minimum_should_match": 1}
|
||||
return {"bool": planned}
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
<div class="review__actions"><div class="review__actions-inner"><label>Ваше имя или псевдоним<input id="reviewer" maxlength="120" autocomplete="name" placeholder="Например, Айжан К."></label> <label>Комментарий к выдаче <input id="overall-comment" maxlength="4000" placeholder="Что важно учесть? (необязательно)"></label> <button class="review__button--primary" id="save" type="button">Сохранить разметку</button></div></div>
|
||||
<dialog id="document-dialog"><button class="review__dialog-close" id="dialog-close" type="button">Закрыть</button><h2 id="dialog-heading">Документ</h2><div class="review__document-body" id="dialog-body"></div></dialog>
|
||||
<div class="review__tour-spotlight" id="tutorial-spotlight" hidden aria-hidden="true"></div><dialog class="review__tour" id="tutorial-dialog" aria-labelledby="tutorial-heading" aria-describedby="tutorial-text"><div class="review__tour-content"><p class="review__tour-kicker">Короткое знакомство</p><h2 id="tutorial-heading"></h2><p id="tutorial-text"></p><div class="review__tour-footer"><span class="review__tour-progress" id="tutorial-progress"></span><div class="review__tour-actions"><button id="tutorial-skip" type="button">Пропустить</button><button class="review__button--primary" id="tutorial-next" type="button">Далее</button></div></div></div></dialog>
|
||||
<footer class="review__header">Акылдаш · Backend v0.10.2 · Внутренняя лаборатория релевантности</footer>
|
||||
<footer class="review__header">Акылдаш · Backend v0.10.3 · Внутренняя лаборатория релевантности</footer>
|
||||
<script>
|
||||
const DRAFT_KEY = 'akyldash-search-review-draft';
|
||||
const TOUR_KEY = 'akyldash-search-review-tour-seen';
|
||||
|
||||
Reference in New Issue
Block a user