This commit is contained in:
init_mahdi 2025-11-29 15:48:25 +03:30
commit 5a984da74c
66 changed files with 17369 additions and 0 deletions

2
app/__init__.py Normal file
View File

@ -0,0 +1,2 @@

Binary file not shown.

2
app/config/__init__.py Normal file
View File

@ -0,0 +1,2 @@

Binary file not shown.

Binary file not shown.

71
app/config/settings.py Normal file
View File

@ -0,0 +1,71 @@
import os
from functools import lru_cache
from pydantic_settings import BaseSettings, SettingsConfigDict
from pathlib import Path
from typing import Optional
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
jwtKey: Optional[str] = ""
is_mode_develop: int = 0
# Elasticsearch connection
elasticsearch_url: str = "http://localhost:9200"
elasticsearch_username: Optional[str] = None
elasticsearch_password: Optional[str] = None
elasticsearch_verify_certs: Optional[bool] = False
elasticsearch_ca_certs_path: Optional[str] = None
ai_rag_host: Optional[str] = None
ai_rag_host_gpu: Optional[str] = None
backup_file: Optional[str] = None
# Config base path where per-type configs live
config_base_path: str = "app/configs"
enable_back_permition: bool = False
# Networking
request_timeout_seconds: int = 30
disable_es_startup: bool = False
base_project:Path = Path(__file__).resolve().parent.parent.parent
current_user_id:Optional[int] = 0
permit_type:Optional[str] = "DB"
permit_db_type:Optional[str] = "mysql"
permit_database_user:Optional[str] = None
permit_database_password:Optional[str] = None
permit_database_host:Optional[str] = None
permit_database_port:Optional[int] = 8090
permit_database_name:Optional[str] = None
MYSQL_host:Optional[str] = None
MYSQL_username:Optional[str] = None
MYSQL_port:Optional[str] = None
MYSQL_password:Optional[str] = None
@lru_cache
def get_settings() -> Settings:
settings = Settings(env_file="config.env",env_file_encoding="utf-8",extra="ignore")
settings.elasticsearch_username = os.environ.get("ELASTIC_username")
settings.elasticsearch_password = os.environ.get("ELASTIC_password")
settings.elasticsearch_url = os.environ.get("ELASTIC_URL")
settings.ai_rag_host = os.environ.get("ai_rag_host")
settings.ai_rag_host_gpu = os.environ.get("ai_rag_host_gpu")
settings.jwtKey = os.environ.get("TAVASI_jwtKey")
settings.enable_back_permition = os.environ.get("CONFIG_enable_back_permition")
settings.is_mode_develop = os.environ.get("CONFIG_develop")
settings.MYSQL_host = os.environ.get("MYSQL_host")
settings.MYSQL_username = os.environ.get("MYSQL_username")
settings.MYSQL_port = os.environ.get("MYSQL_port")
settings.MYSQL_password = os.environ.get("MYSQL_password")
return settings

2
app/core/__init__.py Normal file
View File

@ -0,0 +1,2 @@

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

1071
app/core/elastic_helper.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,925 @@
from __future__ import annotations
import re
from typing import Any, Dict, List, Optional, Union
from app.core.map_index_reader import MapIndexReader
from app.routes.v1.models import (
SearchRequest,
InsertRequest,
UpdateByQueryRequest,
DeleteByQueryRequest,
)
class ElasticQueryBuilder:
"""Builds Elasticsearch queries based on configs and request models.
This class consolidates functionality from both elastic_query_builder.py and
elastic_query_builder_1.py to provide a comprehensive query building solution.
"""
def __init__(self, map_index_reader: Optional[MapIndexReader] = None):
"""Initialize the query builder with an optional MapIndexReader instance.
Args:
map_index_reader: Optional MapIndexReader instance for backward compatibility
"""
self.map_index_reader = map_index_reader
self.stopwords_fr = [
"یا",
"را",
"این",
"با",
"آن",
"و",
"در",
"به",
"که",
"از",
"طی",
"پس",
"چه",
"اگر",
"نه",
"آنها",
"هر",
"او",
"ما",
"من",
"تا",
"نیز",
"اما",
"یک",
"بر",
"هم",
"برای",
"کن",
"کرد",
"کردن",
"باش",
"بود",
"بودن",
"شو",
"شد",
"شدن",
"دار",
"داشت",
"داشتن",
"خواه",
"خواست",
"خواستن",
"گوی",
"گفت",
"گفتن",
"گیر",
"گرفت",
"گرفتن",
"آی",
"آمد",
"آمدن",
"توان",
"توانستن",
"یاب",
"یافتن",
"آور",
"آورد",
"آوردن",
"1",
"2",
"3",
"ص",
"4",
"و",
"5",
"ج",
"6",
"a",
"top",
"href",
"pageno",
"است",
"یعنی",
"یابی",
"بعد",
"اینکه",
"وقتی",
"دارد",
"ولی",
""
]
# ============================================================================
# CORE ...
# ============================================================================
def get_source_fileds(self, reader, request:Dict):
# Source filtering
if request and request.include_fields:
includes = request.include_fields or reader.get_include_fields()
else:
includes = reader.get_include_fields()
if request and request.exclude_fields:
excludes = request.exclude_fields or reader.get_exclude_fields()
else:
excludes = reader.get_exclude_fields()
_source = {}
if len (includes) and len (excludes) :
_source = {"includes": includes, "excludes": excludes}
elif len (includes) :
_source["includes"] = includes
elif len (excludes) :
_source["excludes"] = excludes
else :
return None
return _source
# ============================================================================
# CORE SEARCH QUERY BUILDING METHODS
# ============================================================================
def build_search_query(self, reader: MapIndexReader, request: SearchRequest) -> Dict[str, Any]:
query: Dict[str, Any] = {"bool": {"must": [], "filter": [], "should": [], "must_not": []}}
# Basic full-text search
normal = []
if request.query:
normal = reader.get_query_normal(request.query, request.search_type)
query["bool"]["should"] = normal
# Filters
if request.filters:
for field_key, value in request.filters.items():
field = reader.get_filter_key(field_key)
print("request.filters", field_key, field, value )
if not field :
continue
if isinstance(value, dict) and any(k in value for k in ("gte", "lte", "gt", "lt")):
query["bool"]["filter"].append({"range": {field: value}})
elif isinstance(value, list):
query["bool"]["filter"].append({"terms": {field: value}})
else:
query["bool"]["filter"].append({"term": {field: value}})
body: Dict[str, Any] = {
"query": query,
"from": request.from_,
"size": request.size,
"track_total_hits": request.track_total_hits,
}
# Source filtering
_source = self.get_source_fileds(reader, request)
if _source :
body["_source"] = _source
# Sorting
sort: List[Dict[str, Any]] = []
if request.sort:
for item in request.sort:
if isinstance(item, dict):
sort.append(item)
else:
# Handle string forms: "field:asc", "field:desc, lasttitle, title"
direction = "asc"
field_name = item
if ":" in item:
field_name, direction = item.split(":", 1)
if reader.is_sortable(field_name) or field_name == "_score":
sort.append({field_name: {"order": direction}})
else:
# eg : "lasttitle, title"
_sort = reader.get_sort_key("item")
if _sort :
sort.append(_sort)
else:
sort = reader.default_sort()
if sort:
body["sort"] = sort
# Highlight
highlight = {}
if normal : # اگر کوئری وجود داشته باشد، هایلایت لازم هست
highlight = request.highlight if request.highlight is not None else reader.default_highlight()
if highlight:
highlight_tmp = highlight
if not 'require_field_match' in highlight_tmp :
highlight_tmp['require_field_match'] = False
if not 'fragment_size' in highlight_tmp :
highlight_tmp['fragment_size'] = 200
if not 'number_of_fragments' in highlight_tmp :
highlight_tmp['number_of_fragments'] = 3
if not 'boundary_scanner' in highlight_tmp :
highlight_tmp['boundary_scanner'] = 'sentence'
body["highlight"] = highlight_tmp
# suggest
# ......
# Add aggregations
aggregation_fields = request.aggregation_fields if request.aggregation_fields is not None else reader.default_aggregation_fields()
aggs = {}
aggregation_fields_for_itreate = aggregation_fields.items() if isinstance(aggregation_fields,dict) else aggregation_fields
for field, value in aggregation_fields_for_itreate:
if field in reader.list_fields():
nead_keyword, field_type, field_meta = reader.needs_keyword_suffix(field)
name = field
size = 10
if isinstance(value, int) :
size = value
elif isinstance(value, dict) :
if 'size' in value :
size = value['size']
if 'name' in value :
name = value['name']
if nead_keyword:
aggs[f"{name}"] = {
"terms": {
"field": field+'.keyword',
"size": size
}
}
elif field_type in ["integer", "long", "float", "double", "date"]:
aggs[f"{name}"] = {
"terms": {
"field": field,
"size": size
}
}
# elif field_type in ["integer", "long", "float", "double"]:
# aggs[f"{field}_stats"] = {
# "stats": {"field": field},
# "size": value
# }
# elif field_type == "date":
# aggs[f"{field}_histogram"] = {
# "date_histogram": {
# "field": field,
# "size": value,
# "calendar_interval": "1y"
# }
# }
if aggs:
body["aggs"] = aggs
# Collapse
collapse_field = request.collapse_field
collapse_field_info = reader.get_collapse_field(collapse_field)
if collapse_field_info:
body["collapse"] = {
"field": collapse_field,
"inner_hits" :{
"name": "by_collapse",
"size" : 5
}
}
if highlight :
body["collapse"]["inner_hits"]["highlight"] = highlight
if collapse_field_info and collapse_field_info.sort :
body["collapse"]["inner_hits"]["sort"] = collapse_field_info.sort
if collapse_field_info and collapse_field_info.size :
body["collapse"]["inner_hits"]["size"] = collapse_field_info.size
if collapse_field_info and collapse_field_info.name :
body["collapse"]["inner_hits"]["name"] = collapse_field_info.name
if not "aggs" in body :
body["aggs"] = {}
# Add collapse-specific aggregations
# body["aggs"]['total'] = {
# 'cardinality': {
# 'field': collapse_field
# }
# }
body["aggs"]['total_collapse'] = {
'cardinality': {
'field': collapse_field,
'precision_threshold': 200000 # For accuracy in calculations
}
}
# Search after
if request.search_after:
body["search_after"] = request.search_after
return body
# Enhanced query building methods utilizing new field properties
def build_enhanced_search_query(self, reader: MapIndexReader, request: SearchRequest) -> Dict[str, Any]:
"""Build an enhanced search query with advanced field handling.
Args:
reader: MapIndexReader instance for index configuration
request: SearchRequest containing search parameters
Returns:
Enhanced Elasticsearch query body
"""
# Build basic query
body = self.build_search_query(reader, request)
# Source filtering
_source = self.get_source_fileds(reader, request)
if _source :
body["_source"] = _source
# Enhanced sorting with field validation
if body.get("sort"):
validated_sort = []
for sort_item in body["sort"]:
if isinstance(sort_item, dict):
for field, config in sort_item.items():
if field == "_score" or reader.is_sortable(field):
validated_sort.append(sort_item)
break
else:
validated_sort.append(sort_item)
body["sort"] = validated_sort
return body
def build_aggregation_query(self, reader: MapIndexReader, request: SearchRequest,
aggregation_fields: List[str]) -> Dict[str, Any]:
"""Build a query with aggregations for specified fields.
Args:
reader: MapIndexReader instance for index configuration
request: SearchRequest containing search parameters
aggregation_fields: List of fields to aggregate on
Returns:
Query body with aggregations
"""
# Build base query
body = self.build_field_aware_query(reader, request)
# Add aggregations
aggs = {}
for field in aggregation_fields:
if field in reader.list_fields():
field_type = reader.get_field_type(field)
field_meta = reader.get_field_meta(field)
if field_type == "keyword" or field_meta.get("needs_keyword", False):
aggs[f"{field}_counts"] = {
"terms": {
"field": field,
"size": 100
}
}
elif field_type in ["integer", "long", "float", "double"]:
aggs[f"{field}_stats"] = {
"stats": {"field": field}
}
elif field_type == "date":
aggs[f"{field}_histogram"] = {
"date_histogram": {
"field": field,
"calendar_interval": "1y"
}
}
if aggs:
body["aggs"] = aggs
return body
def build_export_query(self, reader: MapIndexReader, request: SearchRequest) -> Dict[str, Any]:
"""Build a query optimized for data export.
Args:
reader: MapIndexReader instance for index configuration
request: SearchRequest containing search parameters
Returns:
Export-optimized query body
"""
# Build base query
body = self.build_field_aware_query(reader, request)
# Source filtering
_source = self.get_source_fileds(reader, request)
if _source :
body["_source"] = _source
# Remove highlighting for export
if "highlight" in body:
del body["highlight"]
# Remove aggregations for export
if "aggs" in body:
del body["aggs"]
return body
def build_update_by_query(self, reader: MapIndexReader, request: UpdateByQueryRequest) -> Dict[str, Any]:
"""Build update by query body.
Args:
reader: MapIndexReader instance for index configuration
request: UpdateByQueryRequest containing update parameters
Returns:
Update by query body
"""
# Build query portion reused from search builder
search_body = self.build_search_query(
reader,
SearchRequest(
query=None,
phrase=False,
filters=request.filters,
sort=None,
include_fields=None,
exclude_fields=None,
from_=0,
size=0,
collapse_field=None,
highlight=None,
track_total_hits=False,
search_after=None,
search_fields=None,
default_search_field="_all",
),
)
# Remove fields not applicable to update_by_query
update_query = {"query": search_body["query"]}
script = request.script
if not script and request.set_fields:
# Build painless script to set fields
assigns = []
params: Dict[str, Any] = {}
for k, v in request.set_fields.items():
assigns.append(f"ctx._source.{k} = params.{k};")
params[k] = v
script = {"source": " ".join(assigns), "params": params}
body: Dict[str, Any] = {"query": update_query["query"]}
if script:
body["script"] = script
return body
def build_delete_by_query(self, reader: MapIndexReader, request: DeleteByQueryRequest) -> Dict[str, Any]:
"""Build delete by query body.
Args:
reader: MapIndexReader instance for index configuration
request: DeleteByQueryRequest containing delete parameters
Returns:
Delete by query body
"""
# Build query portion reused from search builder
search_body = self.build_search_query(
reader,
SearchRequest(
query=None,
phrase=False,
filters=request.filters,
sort=None,
include_fields=None,
exclude_fields=None,
from_=0,
size=0,
collapse_field=None,
highlight=None,
track_total_hits=False,
search_after=None,
search_fields=None,
default_search_field="_all",
),
)
return {"query": search_body["query"]}
# ============================================================================
# ADVANCED SEARCH AND FILTERING METHODS
# ============================================================================
def create_filter_term(self, value: str, field: str) -> Dict[str, Any]:
"""Create filter term for Elasticsearch query.
Args:
value: Value to filter by (supports $ separated multiple values)
field: Field name to filter on
Returns:
Filter term dictionary
"""
if not value:
return {'term': {field: ''}}
value_items = value.split('$') if value else []
if len(value_items) < 2:
return {'term': {field: value}}
else:
# Multiple values - create bool should query
should_clauses = []
for item in value_items:
should_clauses.append({'term': {field: item}})
return {'bool': {'should': should_clauses}}
def parse_advance_search(self, query_normal: str) -> tuple[str, List[Dict[str, str]]]:
"""Parse advanced search queries with hashtags.
Args:
query_normal: The input query string
Returns:
Tuple of (cleaned_query, parsed_tags)
"""
pattern = r'#(?P<tag>[^\b]+?) '
matches = list(re.finditer(pattern, query_normal))
q_normal = ''
index = 0
res = []
key = ''
key_prev = ''
for match in matches:
key_prev = key
key = match.group('tag')
m_index = match.start()
if index < m_index:
sub = query_normal[index:m_index - 1]
if index == 0:
q_normal += sub
else:
res.append({'key': key_prev, 'value': sub})
index += len(sub)
index = m_index + len(key) + 1 # +1 for the #
if index < len(query_normal):
sub = query_normal[index:]
if index == 0:
q_normal += sub
else:
res.append({'key': key, 'value': sub})
return q_normal, res
def get_suggest(self, field: str, query: str) -> List[Dict[str, Any]]:
"""Generate a suggestion query for Elasticsearch.
Args:
field: The field to search in
query: The search query text
Returns:
Suggestion query structure or empty list if query is empty
"""
if not query:
return []
return [{
'result': {
'text': query,
'term': {
'field': field
}
}
}]
def get_query_advance(self, index_key: str, query_normal: str,
iscollapse: int, filter_list: List[Dict[str, Any]],
query_type: str = 'match', normal: List[Dict[str, Any]] = None) -> Dict[str, Any]:
"""Build advanced search query with hashtag parsing.
Args:
index_key: Index key identifier
query_normal: Query string with hashtags
iscollapse: Collapse parameter
filter_list: List of filters
query_type: Type of query (match, match_phrase, and)
normal: Normal query clauses
Returns:
Dictionary with normal and parent query clauses
"""
if normal is None:
normal = []
parent_tags = []
filter_list = []
cleaned_query, items = self.parse_advance_search(query_normal)
res = []
# Get tags from reader if available
reader = getattr(self, 'reader', None) or self.map_index_reader
if reader and hasattr(reader, 'get_search_advance_tags'):
tags = reader.get_search_advance_tags(index_key, True)
else:
tags = {"tags": {}}
if not tags.get('tags'):
return {"normal": normal, "parent": parent_tags}
tags = tags['tags']
iscollapse = -1
for item in items:
key = item['key']
if key not in tags:
continue
value = item['value'].strip()
tag_info = tags[key]
if iscollapse == -1:
iscollapse = 1 # first tag
iscollapse = iscollapse * tag_info.get('iscollapse', 1)
boost = 30
if 'boost_offset' in tag_info:
boost += int(tag_info['boost_offset'])
tag_keys = tag_info['key'].split(',')
for tag_key in tag_keys:
# Check for range search like date range
term = self.parse_range_advance(value, tag_key)
if term: # Range term found
# For proper date range search, it needs to be added to filter
filter_list.append(term)
term = {}
continue
elif not term and value != '': # Normal search
if query_type == 'and':
words = value.split(' ')
term = self.get_and_temp(tag_key, words, boost)
elif query_type in ['match', 'match_phrase']:
term = {
query_type: {
tag_key: {
'query': value,
'boost': boost
}
}
}
if term: # Valid term found
# This filter should first search in another index and use results in current index
if 'index_parent' in tag_info:
parent_tags.append({"query": term, "tag": tag_info})
else:
res.append(term)
if res:
res = res + normal
else:
res = normal
return {"normal": res, "parent": parent_tags}
def get_query_bookmarks_flag(self, ref_ids: List[str], user_id: str,
bookmark_index_name: str = "gn_entity") -> Dict[str, Any]:
"""Build query for bookmarks with user filtering.
Args:
ref_ids: List of reference IDs
user_id: User ID for filtering
bookmark_index_name: Index name for entity data
Returns:
Query parameters for bookmarks
"""
filter_list = []
filter_list.append({"terms": {'ref_id': ref_ids}})
filter_list.append(self.create_filter_term('bookmark', 'data_type'))
filter_list.append(self.create_filter_term(user_id, 'user_id'))
params = {
'index': bookmark_index_name,
'body': {
'_source': ['ref_id', 'time_edit'],
'query': self.get_base_query([], filter_list),
'size': len(ref_ids) + 100
}
}
return params
# ============================================================================
# QUERY BUILDING UTILITY METHODS
# ============================================================================
@staticmethod
def get_synonym_normal_query(synonyms: Dict[str, Union[str, List[str]]],
fields: List[str]) -> List[Dict[str, Any]]:
"""Convert synonyms dictionary to Elasticsearch query clauses.
Args:
synonyms: Dictionary mapping words to their synonyms
Format: {"w1": ["syn1"], "w2": ["syn1", "syn2", "syn3"], "w3": []}
fields: List of fields to search in Elasticsearch
Returns:
List of Elasticsearch query clauses
"""
normal_queries = []
for key, value in synonyms.items():
# Convert value to list if it's a string
values = []
if isinstance(value, str):
values = value.split(',') if value else []
elif isinstance(value, list):
values = value
# Build the query string
q = key
if values:
# Escape quotes in synonyms and join with OR clauses
escaped_synonyms = [f'\\"{syn}\\"' for syn in values]
q += ' OR (\"' + ") OR (\"".join(escaped_synonyms) + '\")'
# Create the query clause
normal_queries.append({
'query_string': {
'fields': fields,
'query': q,
'default_operator': 'AND'
}
})
return normal_queries
def get_base_query(self, normal: List[Dict[str, Any]],
filter_list: Optional[List[Dict[str, Any]]] = None,
not_list: Optional[List[Dict[str, Any]]] = None,
permit_filter: Optional[List[Dict[str, Any]]] = None) -> Dict[str, Any]:
"""Build base bool query structure.
Args:
normal: Normal query clauses (should)
filter_list: Filter clauses (must)
not_list: Must not clauses
permit_filter: Permitted filter clauses (should)
Returns:
Bool query structure
"""
# Initialize empty lists if parameters are None
if filter_list is None:
filter_list = []
if not_list is None:
not_list = []
if permit_filter is None:
permit_filter = []
query = {
'bool': {
'must': [
{
'bool': {
'should': normal,
'must_not': not_list
}
},
{
'bool': {
'filter': {
'bool': {
'must': filter_list,
'should': permit_filter
}
}
}
}
]
}
}
# Clean up empty query parts
if not normal:
query['bool']['must'][0]['bool'].pop('should', None)
elif not not_list:
query['bool']['must'][0]['bool'].pop('must_not', None)
if not filter_list:
query['bool']['must'][1]['bool']['filter']['bool'].pop('must', None)
elif not permit_filter:
query['bool']['must'][1]['bool']['filter']['bool'].pop('should', None)
return query
# ============================================================================
# HELPER METHODS (to be implemented based on specific needs)
# ============================================================================
def parse_range_advance(self, value: str, field: str, type_: str = "time") -> Dict[str, Any]:
value = value.replace(" تا ", "--")
if "--" not in value:
return {}
values = (value or "").split("--")
if len(values) != 2:
return {}
value1 = values[0].strip()
value2 = values[1].strip()
term: Dict[str, Any] = {}
if field == "sort_date_timestamp" or type_ == "time":
value1 = value1.replace("/", "-").replace("\\", "-")
value2 = value2.replace("/", "-").replace("\\", "-")
time1 = self.jalali_to_tsmp(value1, "-") if value1 else ""
time2 = self.jalali_to_tsmp(value2, "-") if value2 else ""
self.debug_data.append({"value1": value1})
self.debug_data.append({"value2": value2})
self.debug_data.append({"time1": time1})
self.debug_data.append({"time2": time2})
term = {
"range": {
field: {}
}
}
if time1:
term["range"][field]["gte"] = f"{time1}000" # ثانیه به میلی‌ثانیه
if time2:
term["range"][field]["lte"] = f"{time2}000"
elif type_ == "date":
value1 = value1.replace("/", "-").replace("\\", "-")
value2 = value2.replace("/", "-").replace("\\", "-")
term = {
"range": {
field: {}
}
}
if value1:
term["range"][field]["gte"] = value1
if value2:
term["range"][field]["lte"] = value2
return term
def get_and_temp(self, field: str, words: List[str], boost: float, del_stop_words: bool = True) -> Dict[str, Any]:
must = []
count = len(words)
if count == 0:
return {"bool": {"must": []}}
w_boost = boost / count
for i, w1 in enumerate(words):
if not w1: # skip empty strings
continue
if del_stop_words and w1 in self.stopwords_fr:
continue
myboost = w_boost + count - i
temp1 = {
"match": {
field: {
"query": w1,
"boost": myboost
}
}
}
must.append(temp1)
res = {
"bool": {
"must": must
}
}
return res

View File

@ -0,0 +1,55 @@
import inspect
import asyncio
class AsyncSafeWrapper:
def __init__(self, obj):
object.__setattr__(self, '_obj', obj)
def __getattribute__(self, name):
_obj = object.__getattribute__(self, '_obj')
try:
attr = getattr(_obj, name)
except AttributeError:
raise AttributeError(f"'{type(_obj).__name__}' object has no attribute '{name}'")
if callable(attr) and not name.startswith('_'):
is_async = inspect.iscoroutinefunction(attr)
try:
sig = inspect.signature(attr)
params = list(sig.parameters.items())
for item in params:
if 'index' in item:
index = 'index'
elif 'index_name' in item:
index = 'index_name'
print(f"📋 Method '{name}' parameters: {params}")
except (ValueError, TypeError):
print(f"📋 Method '{name}' - cannot inspect signature")
if is_async:
async def async_wrapper(*args, **kwargs):
kwargs[index]='use_for_test'
print(f"🔄 Calling async method: {name}")
result = await attr(*args, **kwargs)
print(f"✅ Async method '{name}' completed")
return result
return async_wrapper
else:
def sync_wrapper(*args, **kwargs):
kwargs[index]='use_for_test'
print(f"⚡ Calling sync method: {name}")
result = attr(*args, **kwargs)
print(f"✅ Sync method '{name}' completed")
return result
return sync_wrapper
return attr
def __setattr__(self, name, value):
if name == '_obj':
object.__setattr__(self, name, value)
else:
setattr(object.__getattribute__(self, '_obj'), name, value)

418
app/core/field_processor.py Normal file
View File

@ -0,0 +1,418 @@
from __future__ import annotations
from datetime import datetime
from typing import Any, Dict, List, Optional
from app.core.map_index_reader import MapIndexReader
class FieldProcessor:
"""
Handles enhanced field processing, validation, and transformation based on backend.json configuration.
This class provides utilities for:
- Field validation and type checking
- Data transformation based on join processes
- Field metadata extraction and processing
- Export and import field handling
"""
def __init__(self, map_index_reader: MapIndexReader):
self.reader = map_index_reader
# def set_valid_is_array_field(self, reader, document, field, prefix_field = ''):
# if reader.is_array( prefix_field + field ) and field in document and not isinstance(document[field], list):
# document[field] = [document[field]]
# return document
# def set_valid_document(self, reader, document, field = '', prefix_field = ''):
# if field and not field in document :
# return document
# document_temp = document
# if field :
# document_temp = document[field]
# for doc_field in document_temp.items():
# if reader.is_object(prefix_field + doc_field) :
# prefix_field_tmp = doc_field + '.'
# document_temp[doc_field] = self.set_valid_document(reader, document_temp[doc_field], doc_field, prefix_field_tmp)
# else:
# document_temp[doc_field] = self.set_valid_is_array_field(reader, document_temp, doc_field, prefix_field)
# if field :
# document[field] = document_temp
# document[field] = self.set_valid_is_array_field(reader, document, field, prefix_field)
# else :
# document = document_temp
# return document
def validate_field_value(self, field_name: str, value: Any, prefix_field: Optional[str] = '',
properties: Optional[Dict] = None) -> Dict[str, Any]:
"""
Validate a field value against its configuration (recursive for arrays/objects).
Args:
field_name: Name of the field to validate
value: Value to validate
Returns:
Validation result with status and any errors/warnings
"""
try:
field_meta = self.reader.get_field_prefix_meta(field_name, prefix_field, properties)
validation = {
"valid": True,
"value": value,
"errors": [],
"warnings": [],
"field_name": field_name,
"field_type": field_meta.get("type")
}
# # Required check
# if self.reader.is_required(prefix_field+field_name) and value is None:
# validation["valid"] = False
# validation["errors"].append("Field is required but value is None")
# return validation
field_type = field_meta.get("type")
# Type-specific validation
if field_type == "date":
if not self._is_valid_date(value):
validation["valid"] = False
validation["errors"].append("Invalid date format")
elif field_type in ["integer", "long"]:
if not self._is_valid_integer(value):
validation["valid"] = False
validation["errors"].append("Value must be an integer")
elif field_type in ["float", "double"]:
if not self._is_valid_float(value):
validation["valid"] = False
validation["errors"].append("Value must be a number")
elif field_type == "boolean":
if not isinstance(value, bool):
validation["warnings"].append("Value should be boolean")
# Array validation (recursive on elements)
if self.reader.is_array(prefix_field + field_name):
if not isinstance(value, list):
validation["warnings"].append("Field is configured as array but value is not a list")
value = [value]
validated_items = []
prefix_field = prefix_field + field_name + '.'
for i, item in enumerate(value):
item_validation = self.validate_field_value(field_name, item, prefix_field, field_meta)
validated_items.append(item_validation["value"])
if not item_validation["valid"]:
validation["valid"] = False
validation["errors"].append(
f"Array item {i}: {', '.join(item_validation['errors'])}"
)
validation["warnings"].extend(item_validation["warnings"])
validation["value"] = validated_items
# Object validation (recursive on fields)
if field_meta.get("object", False):
if not isinstance(value, dict):
validation["valid"] = False
validation["errors"].append("Field is configured as object but value is not a dict")
else:
validated_obj = {}
prefix_field = prefix_field + field_name + '.'
for sub_field, sub_value in value.items():
sub_validation = self.validate_field_value(sub_field, sub_value, prefix_field, field_meta)
validated_obj[sub_field] = sub_validation["value"]
if not sub_validation["valid"]:
validation["valid"] = False
validation["errors"].append(
f"Object field '{sub_field}': {', '.join(sub_validation['errors'])}"
)
validation["warnings"].extend(sub_validation["warnings"])
validation["value"] = validated_obj
return validation
except Exception as e:
return {
"valid": False,
"errors": [f"Validation error: {str(e)}"],
"warnings": [],
"field_name": field_name,
"field_type": "unknown"
}
def process_field_value(self, field_name: str, value: Any) -> Any:
"""
Process a field value based on its configuration and join processes.
Args:
field_name: Name of the field to process
value: Value to process
Returns:
Processed value
"""
try:
field_meta = self.reader.get_field_meta(field_name)
# Handle array fields
if field_meta.get("array", False) and not isinstance(value, list):
value = [value]
# Apply join processes
join_processes = self.reader.get_join_processes(field_name)
if join_processes:
return self._apply_join_processes(field_name, value, join_processes)
return value
except Exception:
return value
def _apply_join_processes(self, field_name: str, value: Any, processes: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
Apply join processes to a field value.
Args:
field_name: Name of the field
value: Original value
processes: List of join processes to apply
Returns:
Dictionary with original value and processed properties
"""
result = {field_name: value}
for process in processes:
process_name = process.get("process")
property_name = process.get("property")
if process_name == "jalaliTotsmp" and property_name:
result[property_name] = self._jalali_to_timestamp(value)
elif process_name == "getYearOfDate" and property_name:
result[property_name] = self._extract_year(value)
return result
def _jalali_to_timestamp(self, jalali_date: str) -> Optional[int]:
"""
Convert Jalali date string to timestamp.
Args:
jalali_date: Jalali date string (e.g., "1403/05/06")
Returns:
Unix timestamp or None if conversion fails
"""
try:
# Simple Jalali to Gregorian conversion (this is a basic implementation)
# In production, you might want to use a proper Jalali calendar library
if "/" in jalali_date:
parts = jalali_date.split("/")
if len(parts) == 3:
year, month, day = int(parts[0]), int(parts[1]), int(parts[2])
# Basic conversion (this is simplified)
gregorian_year = year - 621
# This is a very basic conversion - in production use proper library
return int(datetime(gregorian_year, month, day).timestamp())
return None
except Exception:
return None
def _extract_year(self, date_string: str) -> Optional[str]:
"""
Extract year from date string.
Args:
date_string: Date string
Returns:
Year as string or None if extraction fails
"""
try:
if "/" in date_string:
parts = date_string.split("/")
if len(parts) >= 1:
return parts[0]
return None
except Exception:
return None
def _is_valid_date(self, value: Any) -> bool:
"""Check if value is a valid date."""
if isinstance(value, str):
try:
datetime.fromisoformat(value.replace('Z', '+00:00'))
return True
except ValueError:
return False
elif isinstance(value, (int, float)):
try:
datetime.fromtimestamp(value)
return True
except (OSError, ValueError):
return False
return False
def _is_valid_integer(self, value: Any) -> bool:
"""Check if value is a valid integer."""
try:
int(value)
return True
except (ValueError, TypeError):
return False
def _is_valid_float(self, value: Any) -> bool:
"""Check if value is a valid float."""
try:
float(value)
return True
except (ValueError, TypeError):
return False
# def get_export_fields(self) -> List[str]:
# """
# Get list of fields that should be included in exports.
# Returns:
# List of exportable field names
# """
# return [field for field in self.reader.list_fields() if self.reader.get_export_state(field)]
def get_searchable_fields(self) -> List[str]:
"""
Get list of fields that are searchable.
Returns:
List of searchable field names
"""
searchable_types = ["text", "keyword", "search_as_you_type"]
return [field for field in self.reader.list_fields()
if self.reader.get_field_type(field) in searchable_types]
def get_sortable_fields(self) -> List[str]:
"""
Get list of fields that can be used for sorting.
Returns:
List of sortable field names
"""
return [field for field in self.reader.list_fields() if self.reader.is_sortable(field)]
def get_filterable_fields(self) -> List[str]:
"""
Get list of fields that can be used for filtering.
Returns:
List of filterable field names
"""
return [field for field in self.reader.list_fields()
if self.reader.get_field_type(field) in ["keyword", "integer", "long", "float", "double", "boolean",
"date"]]
# def get_field_labels(self) -> Dict[str, str]:
# """
# Get mapping of field names to their Persian labels.
# Returns:
# Dictionary mapping field names to labels
# """
# labels = {}
# for field in self.reader.list_fields():
# label = self.reader.get_field_label(field)
# if label:
# labels[field] = label
# return labels
# ============================================================================
# .....
# ============================================================================
def edition_document(self, document: Dict[str, Any]) -> Dict[str, Any]:
default_value_field = self.reader.get_default_value_field()
for key, value in default_value_field.items():
if key not in document.keys():
document[key] = value
return document
def validate_document(self, document: Dict[str, Any], id: str, check_requirds:bool=True) -> Dict[str, Any]:
"""
Validate an entire document against field configurations.
Args:
document: Document to validate
Returns:
Validation results for all fields
"""
validation_results = {
"valid": True,
"field_validations": {},
"errors": [],
"warnings": []
}
# -----------------------------
# Required check fields
if check_requirds :
for field_name in self.reader.get_required_fields():
if not field_name in document:
if field_name == 'id' and id:
document['id'] = id
else:
validation_results["valid"] = False
validation_results["errors"].append("Field is required but value is None :" + field_name)
if not validation_results["valid"]:
return validation_results, document
# -----------------------------
document_new = {}
for field_name, value in document.items():
if field_name in self.reader.list_fields():
field_validation = self.validate_field_value(field_name, value)
validation_results["field_validations"][field_name] = field_validation
if not field_validation["valid"]:
validation_results["valid"] = False
validation_results["errors"].extend(field_validation["errors"])
else:
document_new[field_name] = field_validation["value"]
validation_results["warnings"].extend(field_validation["warnings"])
return validation_results, document_new
def process_joinning_document(self, document: Dict[str, Any]) -> Dict[str, Any]:
"""
Process an entire document based on field configurations.
Args:
document: Document to process
Returns:
Processed document
"""
processed_doc = {}
for field_name, value in document.items():
if field_name in self.reader.list_fields():
processed_value = self.process_field_value(field_name, value)
if isinstance(processed_value, dict):
processed_doc.update(processed_value)
else:
processed_doc[field_name] = processed_value
else:
processed_doc[field_name] = value
return processed_doc

View File

@ -0,0 +1,705 @@
from __future__ import annotations
import json
from pathlib import Path
from typing import Any, Dict, Optional, List
import os
from app.config.settings import Settings, get_settings
settings: Settings = get_settings()
class MapIndexReader:
"""
Loads per-type configuration from two JSON files:
- mapping.json: The Elasticsearch index mapping for creation
- backend.json: Metadata for each field and index-level behavior
Directory structure:
app/schemas/{type_name}_mapping.json
app/schemas/{type_name}_backend.json
"""
def __init__(self, type_name: str, properties: Optional[Dict]=None, mapping: Optional[Dict]=None) -> None:
self.type_name = type_name
self.base_path = f"{str(settings.base_project)}/app/schemas/"
self._mapping: Dict[str, Any] | None = mapping
self._backend: Dict[str, Any] | None = properties
self._query: Dict[str, Any] | None = None
if not properties :
self.properties = self.backend.get("properties", {})
def _create_map_file(self, type_name: str, sub_folder:Optional[str]='') -> None:
path_file = self.base_path
if sub_folder :
path_file = path_file + sub_folder + '/'
path_file = path_file + f"{type_name}_map.json"
path = Path(path_file)
if not path.exists():
if not self.backend :
return False
properties = self.backend.get("properties", {})
properties_map = {}
for field_name, value in properties.items():
if "fields" in value:
fields = value["fields"]
if "fa" in fields:
fields["fa"]["analyzer"] = "normal_analyzer_fa"
fields["fa"]["search_analyzer"] = "normal_analyzer_fa"
fields["fa"]["search_quote_analyzer"] = "phrase_analyzer_fa"
if "ph" in fields:
fields["ph"]["analyzer"] = "phrase_analyzer_fa"
fields["ph"]["search_analyzer"] = "phrase_analyzer_fa"
fields["ph"]["search_quote_analyzer"] = "phrase_analyzer_fa"
properties_map[field_name] = value
settings = {}
path_settings = self.base_path + f"settings.json"
with open(path_settings, "r", encoding="utf-8") as f:
settings = json.load(f)
json_map = { "mappings": {"date_detection": False, "properties": properties_map}, "settings": settings }
with open(path_file, "w", encoding="utf-8") as file:
json.dump(json_map, file, ensure_ascii=False, indent=2)
def _load_json(self, type_name: str, suffix: str, sub_folder:Optional[str]='') -> Dict[str, Any]:
path_file = self.base_path
if sub_folder :
path_file = path_file + sub_folder + '/'
path_file = path_file + f"{type_name}_{suffix}.json"
path = Path(path_file)
if not path.exists():
if suffix == "map" :
# print("_load_json", type_name)
if not self._create_map_file(type_name, sub_folder) :
return {}
else :
print(f"Config file not found: {path_file}")
raise FileNotFoundError(f"Config file not found: {path_file}")
with path.open("r", encoding="utf-8") as f:
return json.load(f)
@property
def mapping(self) -> Dict[str, Any]:
if self._mapping is None:
self._mapping = self._load_json(self.type_name, "map", "mappings")
return self._mapping
@property
def backend(self) -> Dict[str, Any]:
if self._backend is None:
self._backend = self._load_json(self.type_name, "backend")
return self._backend
@property
def query(self) -> Dict[str, Any]:
if self._query is None:
self._query = self.backend.get("query", {})
return self._query
def get_index_key(self) -> str:
idx = self.backend.get("index", {})
key = idx.get("index_key")
if not key:
raise ValueError("backend.json must include index_key")
return key
def get_index_name(self) -> str:
idx = self.backend.get("index", {})
name = idx.get("name")
if not name:
raise ValueError("backend.json must include index.name")
return name
def get_index_aliases(self) -> List[str]:
idx = self.backend.get("index", {})
aliases = idx.get("aliases", [])
if not isinstance(aliases, list):
raise ValueError("backend.index.aliases must be a list")
return aliases
# def get_field_meta(self, field_name: str) -> Dict[str, Any]:
# if field_name not in self.properties:
# # raise KeyError(f"Field '{field_name}' not defined in backend.json for type '{self.type_name}'")
# return {}
# return self.properties[field_name]
def get_field_prefix_meta(self, field_name: str, prefix_field: str, properties: Optional[Dict]=None) -> bool:
field_name = prefix_field + field_name
return self.get_field_meta(field_name, properties)
def get_default_value_field(self) -> Dict[str, Any]:
"""Get dict of fields that are default value."""
try:
dict_default_value = dict(self.backend.get("validation", {}).get("default_value", {}))
return dict_default_value
except Exception:
return {}
def get_field_meta(self, field_name: str, properties: Optional[Dict]=None) -> bool:
# eg : title
# eg : initial.files
sub_fields = field_name.split('.')
if not properties :
properties = self.properties
sub_field_end = ""
for field in sub_fields :
if field in properties :
properties = properties[field]
sub_field_end = field
else :
return {}
if sub_field_end :
return properties
else:
return properties[field_name]
def list_fields(self) -> List[str]:
return list(self.properties.keys())
def fields_with_property(self, property_name: str, expected_value: Any = True) -> List[str]:
result: List[str] = []
for field_name, meta in self.properties.items():
if meta.get(property_name) == expected_value:
result.append(field_name)
return result
def is_array(self, field_name: str) -> bool:
list_is_array = list(self.backend.get("is_array", []))
if not list_is_array :
return False
return bool( field_name in list_is_array)
def is_required(self, field_name: str) -> bool:
list_is_required = list(self.backend.get("validation", {}).get("required", []))
if not list_is_required :
return False
return bool( field_name in list_is_required)
def is_object(self, field_name: str, properties: Dict) -> bool:
properties = self.get_field_meta(field_name, properties)
if not properties:
return False
return bool(properties.get("properties", False))
def is_sortable(self, field_name: str) -> bool:
field_meta = self.get_field_meta(field_name)
type = field_meta.get("type", "")
if type == "text":
fields = field_meta.get("fields", {})
if "keyword" in fields:
return True, "keyword"
result = ( type == "" or type == "object" or type == "dense_vector")
return not result, ""
# return bool(self.get_field_meta(field_name).get("sortable", False))
def default_sort(self) -> List[Dict[str, Any]]:
return list(self.query.get("default_sort", []))
def default_collapse_field(self) -> Optional[str]:
return self.query.get("collapse_field")
def default_highlight(self) -> Optional[Dict[str, Any]]:
return self.query.get("highlight", {})
def default_aggregation_fields(self) -> Optional[Dict[str, Any]]:
return self.query.get("aggregation_fields", {})
# Enhanced functionality from map_index_reader_1.py
def is_field_valid(self, field_name: str) -> bool:
"""Check if a field exists and is valid in the index configuration."""
try:
return field_name in self.properties
except Exception:
return False
def get_field_joined(self, field_name: str) -> List[str]:
"""Get information about fields that are joined to this field."""
try:
field_meta = self.get_field_meta(field_name)
return field_meta.get("join_to", [])
except (KeyError, Exception):
return []
def needs_keyword_suffix(self, field_name: str) -> bool:
"""Check if a field needs the .keyword suffix for exact matching."""
try:
field_meta = self.get_field_meta(field_name)
field_type = field_meta.get("type", "")
if field_type == "text" and "keyword" in field_meta.get("fields", {}):
return True, field_type, field_meta
return False, field_type, field_meta
# return bool(field_meta.get("needs_keyword", False))
except (KeyError, Exception):
return False, "", {}
def in_include_fields(self, field_name: str) -> bool:
try:
include_fields = self.backend.get("include_fields", [])
return field_name in include_fields
except Exception:
return False
def get_include_fields(self) -> List[str]:
"""Get list of field names that should be included in search output."""
try:
include_fields = self.backend.get("include_fields", [])
return include_fields
except Exception:
return []
def in_exclude_fields(self, field_name: str) -> bool:
try:
exclude_fields = self.backend.get("exclude_fields", [])
return field_name in exclude_fields
except Exception:
return False
def get_exclude_fields(self) -> List[str]:
"""Get list of field names that should be exclude in search output."""
try:
exclude_fields = self.backend.get("exclude_fields", [])
return exclude_fields
except Exception:
return []
def get_filter_key(self, field_name: str) -> bool:
try:
filter_keys = self.query.get("filter_keys", {})
if field_name in filter_keys :
return filter_keys[field_name]
return ""
except Exception:
return ""
def get_sort_key(self, field_name: str) -> bool:
try:
sort_keys = self.backend.get("sort_keys", {})
if field_name in sort_keys :
return sort_keys[field_name]
return {}
except Exception:
return {}
def get_collapse_field(self, field_name: str) -> bool:
try:
if field_name == 'normal' :
return {}
collapse_fields = self.query.get("collapse_fields", {})
if field_name in collapse_fields :
return collapse_fields[field_name]
return {}
except Exception:
return {}
def is_field_no_sort(self, field_name: str) -> bool:
"""Check if a field should not be used for sorting."""
try:
field_meta = self.get_field_meta(field_name)
type = field_meta.get("type", "")
if type == "text":
fields = field_meta.get("fields", {})
if "keyword" in fields:
return False
result = ( type == "" or type == "object" or type == "dense_vector")
return result
except (KeyError, Exception):
return True
# def get_query_boost_fields(self) -> Dict[str, float]:
# """Get fields with query boost values greater than 0."""
# boost_fields = {}
# try:
# for field_name, meta in self.properties.items():
# boost_value = meta.get("query_boost", 0)
# if boost_value > 0:
# boost_fields[field_name] = boost_value
# return boost_fields
# except Exception:
# return {}
def get_field_type(self, field_name: str) -> Optional[str]:
"""Get the Elasticsearch field type for a given field."""
try:
field_meta = self.get_field_meta(field_name)
return field_meta.get("type")
except (KeyError, Exception):
return None
def is_field_highlightable(self, field_name: str) -> bool:
"""Check if a field should be highlighted in search results."""
try:
highlight = self.query.get("highlight")
return bool(field_name in highlight)
except (KeyError, Exception):
return False
def get_required_fields(self) -> List[str]:
"""Get list of fields that are required (not nullable)."""
try:
list_is_required = list(self.backend.get("validation", {}).get("required", []))
return list_is_required
except Exception:
return []
def get_searchable_fields(self) -> List[str]:
"""Get list of fields that are searchable (text or keyword types)."""
try:
searchable_types = ["text", "keyword", "search_as_you_type"]
return [field_name for field_name, meta in self.properties.items()
if meta.get("type") in searchable_types]
except Exception:
return []
def get_query_normal(self, query_value:str, search_type:Optional[str]='normal') -> Optional[list[Dict[str,Any]]]:
try:
normal_fields = self.query.get("normal", {"title":2, "content":1})
query = []
for _type, value in normal_fields.items():
fields = []
for key, boost in value.items():
fields.append(f'{key}^{boost}')
multi_match = {
"multi_match":{
"query": query_value,
"fields": fields,
}
}
if _type == "phrase" and ( search_type == 'normal' or search_type == 'phrase') :
multi_match["multi_match"]["type"] = "phrase"
query.append(multi_match)
elif _type == "match" and search_type == 'normal':
query.append(multi_match)
elif _type == "match" and search_type == 'and' :
multi_match["multi_match"]["operator"] = "and"
query.append(multi_match)
except Exception:
query = []
return query
def get_search_advance_tags(self, index_key: str = None, active_only: bool = True) -> Dict[str, Any]:
"""
Get advanced search tags configuration from backend.json query.advanced_tags section.
Args:
index_key: Optional index key filter (for backward compatibility)
active_only: Whether to return only active tags (for backward compatibility)
Returns:
Dictionary containing advanced search tags configuration with structure:
{
"tags": {
"tag_name": {
"key": "field_path",
"boost_offset": 30,
"collapse_field": "field_name",
"index_parent": "parent_index",
"relation_key": "relation_field"
}
}
}
"""
try:
advanced_tags = self.query.get("advanced_tags", {})
# Filter tags if active_only is True (for backward compatibility)
if active_only:
# In the current implementation, all tags are considered active
# You can add additional filtering logic here if needed
pass
# Return in the expected format
return {
"tags": advanced_tags
}
except Exception as e:
print(f"Error getting search advance tags: {e}")
return {"tags": {}}
def get_advanced_tag_info(self, tag_name: str) -> Optional[Dict[str, Any]]:
"""
Get information about a specific advanced search tag.
Args:
tag_name: Name of the tag to get information for
Returns:
Dictionary containing tag configuration or None if not found
"""
try:
advanced_tags = self.query.get("advanced_tags", {})
return advanced_tags.get(tag_name)
except Exception:
return None
def list_advanced_tags(self) -> List[str]:
"""
Get list of all available advanced search tag names.
Returns:
List of tag names
"""
try:
advanced_tags = self.query.get("advanced_tags", {})
return list(advanced_tags.keys())
except Exception:
return []
def get_advanced_tags_by_field(self, field_path: str) -> List[str]:
"""
Get list of advanced search tags that use a specific field.
Args:
field_path: The field path to search for
Returns:
List of tag names that reference the specified field
"""
try:
advanced_tags = self.query.get("advanced_tags", {})
matching_tags = []
for tag_name, tag_config in advanced_tags.items():
if tag_config.get("key") == field_path:
matching_tags.append(tag_name)
return matching_tags
except Exception:
return []
def get_collapse_fields(self) -> List[str]:
"""
Get list of all collapse fields used in advanced search tags.
Returns:
List of unique collapse field names
"""
try:
advanced_tags = self.query.get("advanced_tags", {})
collapse_fields = set()
for tag_config in advanced_tags.values():
collapse_field = tag_config.get("collapse_field")
if collapse_field:
collapse_fields.add(collapse_field)
return list(collapse_fields)
except Exception:
return []
# # New methods for enhanced field properties
# def get_field_label(self, field_name: str) -> Optional[str]:
# """Get the Persian label for a field."""
# try:
# field_meta = self.get_field_meta(field_name)
# return field_meta.get("label")
# except (KeyError, Exception):
# return None
# def get_filter_key(self, field_name: str) -> str:
# """Get the filter key for a field."""
# try:
# field_meta = self.get_field_meta(field_name)
# return field_meta.get("filter_key", "")
# except (KeyError, Exception):
# return ""
# def can_filter_by_more(self, field_name: str) -> bool:
# """Check if a field can be filtered by multiple values."""
# try:
# field_meta = self.get_field_meta(field_name)
# return bool(field_meta.get("filter_by_more", False))
# except (KeyError, Exception):
# return False
# def get_export_state(self, field_name: str) -> bool:
# """Check if a field should be included in exports."""
# try:
# field_meta = self.get_field_meta(field_name)
# return bool(field_meta.get("export_state", True))
# except (KeyError, Exception):
# return True
def is_auto_id(self, field_name: str) -> bool:
"""Check if a field is auto-generated ID."""
try:
field_meta = self.get_field_meta(field_name)
return bool(field_meta.get("is_autoid", False))
except (KeyError, Exception):
return False
def get_un_repeat_keys(self, field_name: str) -> List[str]:
"""Get unique constraint keys for array/object fields."""
try:
field_meta = self.get_field_meta(field_name)
return field_meta.get("un_repeat_keys", [])
except (KeyError, Exception):
return []
def get_join_processes(self, field_name: str) -> List[Dict[str, Any]]:
"""Get join processes for a field."""
try:
field_joins = self.backend.get("field_joins", {})
if field_name in field_joins :
join_to = field_joins[field_name]
return join_to
return []
except (KeyError, Exception):
return []
def get_processed_fields(self, field_name: str) -> List[str]:
"""Get list of processed fields generated from join processes."""
try:
processes = self.get_join_processes(field_name)
return [process.get("property") for process in processes if process.get("property")]
except Exception:
return []
def get_fields_by_type(self, field_type: str) -> List[str]:
"""Get list of fields with a specific Elasticsearch type."""
try:
return [field_name for field_name, meta in self.properties.items()
if meta.get("type") == field_type]
except Exception:
return []
def get_array_fields(self) -> List[str]:
"""Get list of fields that are arrays."""
try:
return [field_name for field_name, meta in self.properties.items()
if meta.get("array", False)]
except Exception:
return []
def get_object_fields(self) -> List[str]:
"""Get list of fields that are objects."""
try:
return [field_name for field_name, meta in self.properties.items()
if meta.get("properties", False)]
except Exception:
return []
def get_date_fields(self) -> List[str]:
"""Get list of date fields."""
return self.get_fields_by_type("date")
def get_numeric_fields(self) -> List[str]:
"""Get list of numeric fields (integer, long, float, double)."""
try:
numeric_types = ["integer", "long", "float", "double"]
return [field_name for field_name, meta in self.properties.items()
if meta.get("type") in numeric_types]
except Exception:
return []
def get_boolean_fields(self) -> List[str]:
"""Get list of boolean fields."""
return self.get_fields_by_type("boolean")
def get_vector_fields(self) -> List[str]:
"""Get list of vector fields (dense_vector)."""
return self.get_fields_by_type("dense_vector")
def get_field_metadata(self, field_name: str) -> Dict[str, Any]:
"""Get complete metadata for a field including all properties."""
try:
field_meta = self.get_field_meta(field_name)
return {
"name": field_name,
"type": field_meta.get("type"),
"label": field_meta.get("label"),
"return": field_meta.get("return", True),
# "sortable": field_meta.get("sortable", False),
"array": field_meta.get("array", False),
"object": field_meta.get("properties", False),
# "highlight": field_meta.get("highlight", False),
# "query_boost": field_meta.get("query_boost", 0.0),
# "query_normal_boost": field_meta.get("query_normal_boost", 0.0),
# "needs_keyword": field_meta.get("needs_keyword", False),
# "required": field_meta.get("required", False),
# "join_to": field_meta.get("join_to", []),
"filter_key": field_meta.get("filter_key", ""),
"filter_by_more": field_meta.get("filter_by_more", False),
"export_state": field_meta.get("export_state", True),
"is_autoid": field_meta.get("is_autoid", False),
"un_repeat_keys": field_meta.get("un_repeat_keys", [])
}
except (KeyError, Exception):
return {}
def get_index_info(self) -> Dict[str, Any]:
"""Get complete index information."""
try:
index_config = self.backend.get("index", {})
return {
"name": index_config.get("name"),
"aliases": index_config.get("aliases", []),
"index_key": index_config.get("index_key"),
# "file_map": index_config.get("file_map")
}
except Exception:
return {}
def get_query_config(self) -> Dict[str, Any]:
"""Get complete query configuration."""
try:
query_config = self.query
return {
"default_sort": query_config.get("default_sort", []),
"collapse_field": query_config.get("collapse_field"),
"advanced_tags": query_config.get("advanced_tags", {}),
"highlight": query_config.get("highlight", {})
}
except Exception:
return {}
def validate_field_configuration(self, field_name: str) -> Dict[str, Any]:
"""Validate field configuration and return validation results."""
try:
field_meta = self.get_field_meta(field_name)
validation = {
"valid": True,
"errors": [],
"warnings": []
}
# Check required properties
if not field_meta.get("type"):
validation["valid"] = False
validation["errors"].append("Missing field type")
# Check for conflicting properties
if field_meta.get("array", False) and field_meta.get("properties", False):
validation["warnings"].append("Field is both array and object - this may cause issues")
# # Check boost values
# if field_meta.get("query_boost", 0) < 0:
# validation["warnings"].append("Query boost should be non-negative")
# if field_meta.get("query_normal_boost", 0) < 0:
# validation["warnings"].append("Query normal boost should be non-negative")
return validation
except Exception as e:
return {
"valid": False,
"errors": [str(e)],
"warnings": []
}

266
app/core/request_helper.py Normal file
View File

@ -0,0 +1,266 @@
from __future__ import annotations
import json
from typing import Any, Dict, Optional, Tuple, Union
from urllib.parse import parse_qs, urlencode
import httpx
from app.config.settings import get_settings
class RequestHelper:
"""
Helper for communicating with other backend services (HTTP) and optionally SQL-based services.
For SQL, projects can extend this class; here we provide only HTTP helpers to avoid extra runtime deps.
Features:
- Async HTTP client with httpx
- JWT authentication support
- URL query parsing and manipulation
- Comprehensive HTTP method support (GET, POST, PUT, DELETE)
- Service-specific helper methods
- Proper error handling and response parsing
"""
def __init__(self, jwt: Optional[str] = None, timeout_seconds: Optional[int] = None) -> None:
settings = get_settings()
self.jwt = jwt
self.timeout_seconds = timeout_seconds or settings.request_timeout_seconds
self._client: httpx.AsyncClient | None = None
self._sync_client: httpx.Client | None = None
async def _get_async_client(self) -> httpx.AsyncClient:
if self._client is None:
self._client = httpx.AsyncClient(timeout=self.timeout_seconds)
return self._client
def _get_sync_client(self) -> httpx.Client:
if self._sync_client is None:
self._sync_client = httpx.Client(timeout=self.timeout_seconds)
return self._sync_client
def _get_headers(self, custom_headers: Optional[Dict[str, str]] = None) -> Dict[str, str]:
"""Get default headers with optional JWT authentication and custom headers."""
headers = {
"Accept": "application/json",
"Content-Type": "application/json",
}
if self.jwt:
headers["Authorization"] = self.jwt
if custom_headers:
headers.update(custom_headers)
return headers
# Async HTTP methods
async def http_get(self, url: str, params: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
"""Async GET request."""
client = await self._get_async_client()
resp = await client.get(url, params=params, headers=self._get_headers(headers))
resp.raise_for_status()
return resp.json()
async def http_post(self, url: str, json_body: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
"""Async POST request."""
client = await self._get_async_client()
resp = await client.post(url, json=json_body, headers=self._get_headers(headers))
resp.raise_for_status()
return resp.json()
async def http_put(self, url: str, json_body: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
"""Async PUT request."""
client = await self._get_async_client()
resp = await client.put(url, json=json_body, headers=self._get_headers(headers))
resp.raise_for_status()
return resp.json()
async def http_delete(self, url: str, headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
"""Async DELETE request."""
client = await self._get_async_client()
resp = await client.delete(url, headers=self._get_headers(headers))
resp.raise_for_status()
return resp.json()
# Synchronous HTTP methods for backward compatibility
def get(self, url: str, params: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, str]] = None) -> Union[Dict[str, Any], str]:
"""Synchronous GET request."""
return self._request("GET", url, params=params, headers=headers)
def post(self, url: str, body: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, str]] = None) -> Union[Dict[str, Any], str]:
"""Synchronous POST request."""
return self._request("POST", url, body=body, headers=headers)
def put(self, url: str, body: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, str]] = None) -> Union[Dict[str, Any], str]:
"""Synchronous PUT request."""
return self._request("PUT", url, body=body, headers=headers)
def delete(self, url: str, body: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, str]] = None) -> Union[Dict[str, Any], str]:
"""Synchronous DELETE request."""
return self._request("DELETE", url, body=body, headers=headers)
def _request(self, method: str, url: str, body: Optional[Dict[str, Any]] = None,
params: Optional[Dict[str, Any]] = None,
headers: Optional[Dict[str, str]] = None) -> Union[Dict[str, Any], str]:
"""
Internal method for sending synchronous requests with any HTTP method.
"""
client = self._get_sync_client()
try:
# Prepare headers
request_headers = self._get_headers(headers)
# For form data, change content type
if body and not isinstance(body, dict):
request_headers["Content-Type"] = "application/x-www-form-urlencoded"
response = client.request(
method=method.upper(),
url=url,
headers=request_headers,
data=body if body else None,
params=params if params else None,
)
response.raise_for_status()
try:
# Try to parse as JSON
return response.json()
except json.JSONDecodeError:
# If JSON parsing fails, return as text
return response.text
except httpx.RequestException as e:
print(f"{method.upper()} request failed: {e}")
return ""
except Exception as e:
print(f"Unexpected error in {method.upper()} request: {e}")
return ""
# URL query parsing utilities
def parse_url_query(self, query_string: str, query_tag: str = "q",
delimiter: str = "&") -> Tuple[str, Dict[str, str]]:
"""
Parse URL query string and extract the main query and other parameters.
Args:
query_string: The query string to parse
query_tag: The key for the main query parameter
delimiter: The delimiter used to separate query parameters
Returns:
Tuple of (main_query, other_params_dict)
"""
if not query_string:
return "", {}
result = {}
main_query = ""
# Split by delimiter and parse each parameter
items = query_string.split(delimiter) if query_string else []
for item in items:
if not item:
continue
# Split by '=' to get key-value pairs
if "=" in item:
key, value = item.split("=", 1)
if key == query_tag:
main_query = value
else:
result[key] = value
return main_query, result
def build_query_string(self, params: Dict[str, Any]) -> str:
"""Build a query string from a dictionary of parameters."""
return urlencode(params)
def parse_query_params(self, query_string: str) -> Dict[str, str]:
"""Parse query string into a dictionary using urllib.parse."""
if not query_string:
return {}
return dict(parse_qs(query_string, keep_blank_values=True))
# Service-specific helper methods
def get_tanghih_child_ids(self, parents: list, list_url: str) -> list:
"""
Get child IDs for tanghih subjects.
Args:
parents: List of parent IDs
list_url: Base URL for the list service
Returns:
List of child IDs
"""
url = f"{list_url}/subject/get/childs/tanghih"
data = {"parents": parents}
response = self.post(url, data)
if response and isinstance(response, dict) and "meta" in response and response["meta"]:
return response["meta"][0].get("child_ids", [])
return []
def get_child_ids(self, parents: list, list_url: str, item_state: int = 1) -> list:
"""
Get child IDs for subjects.
Args:
parents: List of parent IDs
list_url: Base URL for the list service
item_state: State filter for items
Returns:
List of child IDs
"""
url = f"{list_url}/subject/get/childs"
data = {"parents": parents, "item_state": item_state}
response = self.post(url, data)
if response and isinstance(response, dict) and "meta" in response and response["meta"]:
return response["meta"][0].get("child_ids", [])
return []
# Client lifecycle management
async def close(self) -> None:
"""Close the async HTTP client."""
if self._client is not None:
await self._client.aclose()
self._client = None
def close_sync(self) -> None:
"""Close the synchronous HTTP client."""
if self._sync_client is not None:
self._sync_client.close()
self._sync_client = None
def __enter__(self):
"""Context manager entry for synchronous usage."""
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit for synchronous usage."""
self.close_sync()
async def __aenter__(self):
"""Async context manager entry."""
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""Async context manager exit."""
await self.close()

260
app/core/response_helper.py Normal file
View File

@ -0,0 +1,260 @@
from __future__ import annotations
from typing import Any, Dict, List, Optional
from fastapi.responses import JSONResponse
class ResponseHelper:
"""Normalizes Elasticsearch responses into a unified structure and provides utility methods."""
def __init__(self, elastic_helper=None, reader=None):
self.elastic_helper = elastic_helper
self.reader = reader
self.message = ''
self.status_code = 406
self.status = 1
self.position_id = None
self.is_web_service = True
# Highlight field mappings for repair_result method
# self.hilightField1_ph = 'title_ph'
# self.hilightField1_fa = 'title_fa'
# self.hilightKey1 = 'title'
# self.hilightField2_ph = 'content_ph'
# self.hilightField2_fa = 'content_fa'
# self.hilightKey2 = 'content'
# self.hilightField3_ph = 'description_ph'
# self.hilightField3_fa = 'description_fa'
# self.hilightKey3 = 'description'
# self.current_index_key = None
async def general(self, response):
return response
def return_elastic_list(self, data: Any):
pass
def normalize_search_response(self, response: Dict[str, Any], collapse_field: Optional[str] = None, bookmark_id: Optional[bool] = '', mode:Optional[str]='elastic', user_id: Optional[bool] = '') -> Dict[str, Any]:
"""Normalize Elasticsearch search response into a consistent format."""
took = response.get("took")
shards = response.get("_shards")
hits_section = response.get("hits", {})
total_count = 0
if isinstance(hits_section.get("total"), Dict):
total_count = hits_section.get("total", {}).get("value", 0)
elif isinstance(hits_section.get("total"), int):
total_count = hits_section.get("total", 0)
#add bookmark flag
# if bookmark_id :
# bookmark_ref_ids = []
# bookmark_ref_id_indexs = {}
# for i, h in hits_section.get("hits", []):
# ref_id = h.get("_id", 0)
# source = h.get("_source", {})
# if bookmark_id == 'normal' or bookmark_id == 'id' or bookmark_id == '_id' :
# bookmark_ref_ids.append(ref_id)
# bookmark_ref_id_indexs[ref_id] = i
# else:
# if bookmark_id in source :
# ref_id = source[bookmark_id]
# bookmark_ref_ids.append(source[bookmark_id])
# bookmark_ref_id_indexs[ref_id] = i
# if bookmark_ref_ids :
# self.set_bookmarks_flag(bookmark_ref_ids, bookmark_ref_id_indexs, hits_section, None) # Note: elastic_query_builder needed here
# rename_values of aggregations
aggregations = response.get("aggregations")
# if aggregations and self.reader :
# aggregation_fields = self.reader.default_aggregation_fields()
# user_ids = []
# for agg_name, agg_value in aggregations.items():
# if agg_name in aggregation_fields :
# if not isinstance(aggregation_fields[agg_name], Dict ) :
# continue
# if 'is_value_user_id' in aggregation_fields[agg_name] :
# rename_values = aggregation_fields[agg_name].get('rename_values', {})
# for item in agg_value.get('buckets', []):
# value = item['key']
# if value in rename_values :
# item['title'] = rename_values[value]
# if 'rename_values' in aggregation_fields[agg_name] :
# rename_values = aggregation_fields[agg_name].get('rename_values', {})
# for item in agg_value.get('buckets', []):
# value = item['key']
# if value in rename_values :
# item['title'] = rename_values[value]
result = {}
if mode == 'elastic' :
hits = hits_section.get("hits", [])
print('elastic' , 1)
result = response
# result["count"] = len(hits)
# result["total_count"] = total_count
# result["collapse_field"] = collapse_field
print('elastic' , 2)
# if 'total_collapse' in aggregations :
# result['hits']['total']['value'] = result['aggregations']['total_collapse']['value']
else: # mode == 'normal'
hits = []
for h in hits_section.get("hits", []):
source = h.get("_source", {})
# highlight = self._normalize_highlight(h.get("highlight")) if h.get("highlight") else None
highlight = h.get("highlight")
hits.append({
"id": h.get("_id"),
"index": h.get("_index"),
"score": h.get("_score"),
"source": source,
"highlight": highlight,
"sort": h.get("sort"),
})
result = {
"success": True,
"took": took,
"shards": shards,
"total": total_count,
"total_count": total_count,
"count": len(hits),
"collapse_field": collapse_field,
"hits": hits,
"aggregations": response.get("aggregations"),
}
return result
def normalize_get_response(self, response: Dict[str, Any]) -> Dict[str, Any]:
"""Normalize Elasticsearch get response into a consistent format."""
return {
"success": True,
"found": response.get("found", False),
"id": response.get("_id"),
"index": response.get("_index"),
"source": response.get("_source"),
}
def error(self, message: str, status_code: int = 400, details: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
"""Create a standardized error response."""
return {"success": False, "status_code": status_code, "message": message, "details": details}
def run_error(self, msg: str = 'error', response_code: int = 406, status: int = 1, force_exit: bool = False):
"""Handle error responses with optional force exit."""
if msg == '':
msg = "An error occurred"
self.message = msg
self.status_code = response_code
self.status = status
if force_exit:
more_data = {
"status": self.status,
"message": self.message,
"position": self.position_id,
"data": [],
"meta": None
}
if self.is_web_service:
return JSONResponse(content=more_data, status_code=self.status_code)
return JSONResponse(content={"message": self.message}, status_code=self.status_code)
async def run_error_async(self, message: str, response_code=406, status=1, force_exit=False):
"""Async version of run_error."""
return self.run_error(msg=message, response_code=response_code, status=status, force_exit=force_exit)
def set_bookmarks_flag(self, ref_ids: List[str], ref_ids_indexes: Dict[str, int], results: Dict[str, Any], elastic_query_builder):
"""Set bookmark flags for search results."""
if not self.elastic_helper:
return
params = elastic_query_builder.get_query_bookmarks_flag(ref_ids)
res2 = self.elastic_helper.search(**params)
for res in res2['hits']:
ref_id = res['_source']['ref_id']
if ref_id in ref_ids_indexes:
index = ref_ids_indexes[ref_id]
results['hits'][index]['_source']['tbookmark'] = 1
else:
results['hits'][index]['_source']['tbookmark'] = 0
# def repair_result(self, results: Dict[str, Any], by_bookmarks: bool = False) -> Dict[str, Any]:
# """Repair and enhance search results with highlights and bookmarks."""
# ref_ids = []
# ref_ids_indexes = {}
# for i, hit in enumerate(results['hits']['hits']):
# if by_bookmarks:
# refid = hit['_id']
# if self.current_index_key in ('qasection', 'rgsection'):
# refid = hit['_source']['qanon_id']
# ref_ids.append(refid)
# ref_ids_indexes[refid] = i
# # hit['_source']['tbookmark'] = 0
# if 'highlight' not in hit:
# continue
# highlight = hit['highlight']
# # Process highlight fields
# if self.hilightField1_ph in highlight:
# highlight[self.hilightKey1] = highlight[self.hilightField1_ph]
# elif self.hilightField1_fa in highlight:
# highlight[self.hilightKey1] = highlight[self.hilightField1_fa]
# if self.hilightField2_ph in highlight:
# highlight[self.hilightKey2] = highlight[self.hilightField2_ph]
# elif self.hilightField2_fa in highlight:
# highlight[self.hilightKey2] = highlight[self.hilightField2_fa]
# if self.hilightField3_ph in highlight:
# highlight[self.hilightKey3] = highlight[self.hilightField3_ph]
# elif self.hilightField3_fa in highlight:
# highlight[self.hilightKey3] = highlight[self.hilightField3_fa]
# if by_bookmarks and self.elastic_helper:
# self.set_bookmarks_flag(ref_ids, ref_ids_indexes, results, None) # Note: elastic_query_builder needed here
# if 'aggregations' in results and 'total_collapse' in results['aggregations']:
# results['hits']['total']['value'] = results['aggregations']['total_collapse']['value']
# return results
def merge_related(self, result: Dict[str, Any], related_data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
"""Merge related data into the result."""
if not related_data:
return result
merged = dict(result)
merged["related"] = related_data
return merged
def _normalize_highlight(self, hl: Dict[str, List[str]]) -> Dict[str, List[str]]:
"""Normalize highlight fields by deduplicating while preserving order."""
normalized: Dict[str, List[str]] = {}
for field, frags in hl.items():
field_new = field
if '.ph' in field :
field_new = field.replace('.ph', '')
elif '.fa' in field :
field_new = field.replace('.fa', '')
elif '.ar' in field :
field_new = field.replace('.ar', '')
normalized[field_new] = list(dict.fromkeys(frags)) # de-duplicate while preserving order
return normalized

126
app/main.py Normal file
View File

@ -0,0 +1,126 @@
from fastapi import FastAPI, Request, Response
# from app.permit.permit import FactorySelector
from app.routes.v1.elastic import router as elastic_router
from app.routes.v1.elastic_map import router as elastic_map_router
from app.routes.v1.elastic_admin import router as elastic_admin_router
from app.routes.rag.rag_chat import router as rag_chat
from app.routes.voice.voice import router as voice_routers
from app.routes.tree.tree_base import router as tree_base_routers
from app.config.settings import get_settings, Settings
from app.core.elastic_client_helper import ElasticClientHelper, create_async_elastic_client
from starlette.middleware.base import BaseHTTPMiddleware
from app.core.response_helper import ResponseHelper
from app.lib.general_functions import extract_jwt_user_id
def create_app() -> FastAPI:
app = FastAPI(title="Flexible Elasticsearch Backend", version="0.1.0")
app.state.settings = get_settings()
@app.on_event("startup")
async def on_startup() -> None:
# settings:Settings = app.state.settings
# create_async_redis_client(settings)
app.state.elastic_client = await create_async_elastic_client(app.state.settings)
app.state.elastic_helper = ElasticClientHelper(app.state.elastic_client)
# permit = getattr(app.state, "permit_client", None)
# if permit is None:
# app.state.permit_client = await FactorySelector.get_factory("db",
# type=settings.permit_db_type,
# host=settings.permit_database_host,
# port=settings.permit_database_port,
# username=settings.permit_database_user,
# db_name=settings.permit_database_name,
# )
@app.on_event("shutdown")
async def on_shutdown() -> None:
client = getattr(app.state, "elastic_client", None)
if client is not None:
await client.close()
@app.get("/")
async def Ping():
return "Elasticsearch Backend Service"
@app.get("/ping")
async def Ping():
return "Elasticsearch Backend ping"
app.include_router(elastic_router, prefix="/v1")
app.include_router(rag_chat, prefix="/rag")
app.include_router(voice_routers, prefix="/voice")
app.include_router(tree_base_routers, prefix="/tree")
#---------------------------------------------
# برای نسخه نهایی نیاز است با دسترسی فعال شود
if app.state.settings.is_mode_develop :
app.include_router(elastic_map_router, prefix="/v1/map")
app.include_router(elastic_admin_router, prefix="/v1/admin")
return app
app = create_app()
class TavasiMiddleware(BaseHTTPMiddleware):
# routes_to_reroute = ["/indices/entity/insert/{entity_type}/{data_type}/{ref_key}"]
routes_to_reroute = ["/indices/entity/insert"]
async def test_rewrite(self, request: Request, call_next):
if request.url.path in self.routes_to_reroute:
request.scope['path'] = '/welcome'
headers = dict(request.scope['headers'])
headers[b'custom-header'] = b'my custom header'
request.scope['headers'] = [(k, v) for k, v in headers.items()]
return await request
# return await call_next(request)
async def rewriteUrls(self, request):
request.state.app = app
user_id = extract_jwt_user_id(request, app.state.settings.jwtKey, "HS256" )
if user_id:
# ذخیره موقت در settings یا context
app.state.settings.current_user_id = user_id
return request
async def responseNormal(self, response):
responseHelper = ResponseHelper()
return await responseHelper.general(response)
async def dispatch(self, request: Request, call_next):
# قبل از پردازش درخواست
# print("قبل از پردازش درخواست:", request.url)
await self.rewriteUrls(request)
# پردازش درخواست
response = await call_next(request)
# بعد از پردازش درخواست
response = await self.responseNormal(response)
# print("بعد از پردازش درخواست:", response.status_code)
return response
# @app.middleware("http")
# async def check_permit(request: Request, call_next):
# # client = app.state.permit_client
# # print(await client.ping())
# # result = await client.check_perm(
# # user_id=2, action_tag='item_new'
# # )
# if app.state.settings.enable_back_permition:
# return Response(content={"detail":"The server is temporarily limited."},status_code=503)
# else:
# response = await call_next(request)
# return response
app.add_middleware(TavasiMiddleware)

0
app/permit/__init__.py Normal file
View File

41
app/permit/engine.py Normal file
View File

@ -0,0 +1,41 @@
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
from sqlalchemy.orm import declarative_base
Base = declarative_base()
engine = None
AsyncSessionLocal = None
async def init_database(connection_string):
"""راه‌اندازی اولیه دیتابیس PostgreSQL به صورت Async"""
global engine, AsyncSessionLocal
engine = create_async_engine(
connection_string,
echo=True, # برای دیباگ
future=True,
pool_pre_ping=True,
pool_recycle=300
)
AsyncSessionLocal = async_sessionmaker(
engine,
class_=AsyncSession,
expire_on_commit=False
)
print("✅ PostgreSQL Async database initialized successfully")
async def get_session() -> AsyncSession:
if not AsyncSessionLocal:
raise Exception("Database not initialized. Call init_database() first.")
return AsyncSessionLocal()
async def close_database():
if engine:
await engine.dispose()
print("✅ Database connections closed")

189
app/permit/models.py Normal file
View File

@ -0,0 +1,189 @@
from sqlalchemy import Index, create_engine, Column, Integer, String, Boolean, Text, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from app.permit.engine import Base
class PMDataPermit(Base):
__tablename__ = 'pm_data_permit'
id = Column(Integer, primary_key=True, autoincrement=True)
use_c = Column(Integer, nullable=True)
user_e = Column(Integer, nullable=True)
date_c = Column(String(50), nullable=True) # utf16
date_e = Column(String(50), nullable=True)
deleted_at = Column(String(50), nullable=True)
user_id = Column(Integer, nullable=False)
service_id = Column(String(50), nullable=False)
entity_type = Column(String(50), nullable=True)
entity_id = Column(String(50), nullable=False)
create = Column(Boolean, nullable=True)
read = Column(Boolean, nullable=True)
update = Column(Boolean, nullable=True)
delete = Column(Boolean, nullable=True)
perm_type = Column(Integer, nullable=True, comment='read : 1, write: 10, admin: 500, owner: 1000')
class PMUserSteps(Base):
__tablename__ = 'pm_user_steps'
id = Column(Integer, primary_key=True, autoincrement=True)
user_c = Column(Integer, nullable=True)
user_e = Column(Integer, nullable=True)
date_c = Column(String(50), nullable=True)
date_e = Column(String(50), nullable=True)
deleted_at = Column(String(50), nullable=True)
section_id = Column(Integer, nullable=True)
project_id = Column(Integer, nullable=True, index=True)
step_id = Column(Integer, nullable=True)
user_id = Column(Integer, nullable=True)
perm_type = Column(Integer, nullable=True)
meta = Column(Text, nullable=True)
data_value = Column(String(255), nullable=True, index=True)
class PMFilePermit(Base):
__tablename__ = 'pm_file_permit'
id = Column(Integer, primary_key=True, autoincrement=True)
use_c = Column(Integer, nullable=True)
user_e = Column(Integer, nullable=True)
date_c = Column(String(50), nullable=True)
date_e = Column(String(50), nullable=True)
deleted_at = Column(String(50), nullable=True)
user_id = Column(Integer, nullable=False, comment='شناسه کاربر مورد بحث دسترسی')
service_id = Column(String(50), nullable=False, comment='micro_service_id : repo, message,...')
entity_id = Column(String(50), nullable=False, comment='file_id')
file_type = Column(String(50), nullable=True, comment='jpg, audio, video, owl, ....')
file_name = Column(String(50), nullable=False, comment='نامی که به کاربر نشان داده میشود \\ پیش فرض نام اصلی هست')
file_path = Column(String(255), nullable=False, comment='مسیر لوکال سرور برای دسترسی مستقیم میکروسرویس‌ها')
create = Column(Boolean, nullable=True)
read = Column(Boolean, nullable=True)
update = Column(Boolean, nullable=True)
delete = Column(Boolean, nullable=True)
perm_type = Column(Integer, nullable=True, comment='read : 1, write: 10, admin: 500, owner: 1000')
meta = Column(Text, nullable=True, comment='اطلاعات جانبی ذخیره شده در قالب json یا txt')
class PMProjects(Base):
__tablename__ = 'pm_projects'
id = Column(Integer, primary_key=True, autoincrement=True)
user_c = Column(Integer, nullable=True)
user_e = Column(Integer, nullable=True)
date_c = Column(String(50), nullable=True)
date_e = Column(String(50), nullable=True)
title = Column(String(255), nullable=True)
users = Column(String(255), nullable=True, default='')
organ = Column(String(255), nullable=True)
owner = Column(Integer, nullable=False, default=0)
comment = Column(Text, nullable=True)
deleted_at = Column(String(50), nullable=True)
link = Column(String(255), nullable=True)
admin_users = Column(String(255), nullable=True)
showInDashboard = Column(Boolean, nullable=True, default=True)
tags = Column(String(255), nullable=True, default='')
__table_args__ = (
Index('users', 'users'),
)
class PMRoles(Base):
__tablename__ = 'pm_roles'
id = Column(Integer, primary_key=True, autoincrement=True)
user_c = Column(Integer, nullable=True)
user_e = Column(Integer, nullable=True)
date_c = Column(String(50), nullable=True)
date_e = Column(String(50), nullable=True)
project_id = Column(Integer, nullable=False, index=True)
title = Column(String(255), nullable=True, index=True)
deleted_at = Column(String(50), nullable=True)
class PMSectionRole(Base):
__tablename__ = 'pm_section_role'
id = Column(Integer, primary_key=True, autoincrement=True)
user_c = Column(Integer, nullable=True)
user_e = Column(Integer, nullable=True)
date_c = Column(String(50), nullable=True)
date_e = Column(String(50), nullable=True)
section_id = Column(Integer, nullable=False, index=True)
project_id = Column(Integer, nullable=False, index=True)
role_id = Column(Integer, nullable=False, index=True)
state = Column(Integer, nullable=False, default=0)
deleted_at = Column(String(50), nullable=True)
class PMSectionSteps(Base):
__tablename__ = 'pm_section_steps'
id = Column(Integer, primary_key=True, autoincrement=True)
user_c = Column(Integer, nullable=True)
user_e = Column(Integer, nullable=True)
date_c = Column(String(50), nullable=True)
date_e = Column(String(50), nullable=True)
deleted_at = Column(String(50), nullable=True)
section_id = Column(Integer, nullable=True)
project_id = Column(Integer, nullable=True, index=True)
title = Column(String(50), nullable=True, index=True)
step_key = Column(String(50), nullable=True)
step_order = Column(Integer, nullable=True)
ok_next = Column(Integer, nullable=True)
no_next = Column(Integer, nullable=True)
meta = Column(Text, nullable=True)
schema = Column(Text, nullable=True)
data_type = Column(String(50), nullable=True)
data_options = Column(Text, nullable=True)
class PMSectionUser(Base):
__tablename__ = 'pm_section_user'
id = Column(Integer, primary_key=True, autoincrement=True)
user_c = Column(Integer, nullable=True)
user_e = Column(Integer, nullable=True)
date_c = Column(String(50), nullable=True)
date_e = Column(String(50), nullable=True)
section_id = Column(Integer, nullable=False, index=True)
project_id = Column(Integer, nullable=False, index=True)
user_id = Column(Integer, nullable=False, index=True)
state = Column(Integer, nullable=False, default=0)
deleted_at = Column(String(50), nullable=True)
role_id = Column(Integer, nullable=True)
class PMSections(Base):
__tablename__ = 'pm_sections'
id = Column(Integer, primary_key=True, autoincrement=True)
user_c = Column(Integer, nullable=True)
user_e = Column(Integer, nullable=True)
date_c = Column(String(50), nullable=True)
date_e = Column(String(50), nullable=True)
project_id = Column(Integer, nullable=False, index=True)
section_title = Column(String(255), nullable=True, index=True)
action_title = Column(String(255), nullable=True, index=True)
section_tag = Column(String(50), nullable=True)
action_tag = Column(String(50), nullable=True)
deleted_at = Column(String(50), nullable=True)
class PMUserRole(Base):
__tablename__ = 'pm_user_role'
id = Column(Integer, primary_key=True, autoincrement=True)
user_c = Column(Integer, nullable=True)
user_e = Column(Integer, nullable=True)
date_c = Column(String(50), nullable=True)
date_e = Column(String(50), nullable=True)
user_id = Column(Integer, nullable=False, index=True)
project_id = Column(Integer, nullable=True, index=True)
role_id = Column(Integer, nullable=False, index=True)
state = Column(Integer, nullable=False, default=0)
deleted_at = Column(String(50), nullable=True)

208
app/permit/permit.py Normal file
View File

@ -0,0 +1,208 @@
from __future__ import annotations
from abc import ABC, ABCMeta, abstractmethod
from sqlalchemy.ext.asyncio import AsyncSession
import aiohttp
from app.permit.engine import get_session, init_database
from sqlalchemy import select, and_, or_
from app.permit.models import PMSectionUser, PMSections
# ---------------------- SINGLETON META ----------------------
class SingletonMeta(type):
"""Ensure only one instance of each class exists."""
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
instance = super().__call__(*args, **kwargs)
cls._instances[cls] = instance
return cls._instances[cls]
# ---------------------- ABC WITH SINGLETON SUPPORT ----------------------
class ABCSingletonMeta(ABCMeta, SingletonMeta):
"""Metaclass that combines ABC and Singleton functionality."""
pass
# ---------------------- ABSTRACT CONNECTION ----------------------
class Connection(ABC, metaclass=ABCSingletonMeta):
@abstractmethod
async def ping(self):
"""Check connection status."""
pass
# ---------------------- HTTP CONNECTION ----------------------
class HTTPConnection(Connection, metaclass=SingletonMeta):
def __init__(self, base_url: str):
self.base_url = base_url
self.session: aiohttp.ClientSession | None = None
@classmethod
async def create(cls, base_url: str) -> HTTPConnection:
"""Async factory method for HTTPConnection."""
self = cls(base_url)
self.session = aiohttp.ClientSession(base_url=base_url)
print(f"🌐 HTTPConnection created for {base_url}")
return self
async def ping(self) -> bool:
print(f"🌐 Ping from HTTPConnection: {self.base_url}")
return True
async def check_perm(self, action: str, user: str) -> bool:
print(f"🌐 Checking permission for user '{user}' with action '{action}'")
return True
async def close(self):
if self.session:
await self.session.close()
print("🌐 HTTPConnection session closed")
# ---------------------- DATABASE CONNECTION ----------------------
class DBConnection(Connection, metaclass=SingletonMeta):
def __init__(self, db_type: str, host: str, port: int, username: str, password: str, db_name: str):
self.db_type = db_type
self.host = host
self.port = port
self.username = username
self.password = password
self.db_name = db_name
self.session: AsyncSession | None = None
def get_connection_string(self) -> str:
"""Build a valid async connection string."""
if self.db_type == "mysql":
return f"mysql+aiomysql://{self.username}:{self.password}@{self.host}:{self.port}/{self.db_name}"
elif self.db_type in ("psql", "postgresql"):
return f"postgresql+asyncpg://{self.username}:{self.password}@{self.host}:{self.port}/{self.db_name}"
else:
raise ValueError(f"Unsupported DB type: {self.db_type}")
@classmethod
async def create(
cls,
db_type: str,
host: str,
port: int,
username: str,
password: str,
db_name: str,
) -> DBConnection:
"""Async factory method for DBConnection."""
self = cls(db_type, host, port, username, password, db_name)
conn_str = self.get_connection_string()
await init_database(conn_str)
self.session = await get_session()
print(f"🗄️ Database connection initialized: {conn_str}")
return self
async def ping(self) -> bool:
print(f"🗄️ Ping from DBConnection: {self.host}:{self.port}")
return True
async def check_perm(self, user_id: int, action_tag: str, section_tag: str = None) -> bool:
"""
بررسی دسترسی کاربر با استفاده از ORM
"""
try:
if not self.session:
raise Exception("Database session not initialized")
query = (
select(PMSections.section_tag, PMSections.action_tag)
.join(PMSectionUser, PMSectionUser.section_id == PMSections.id)
.where(
and_(
PMSectionUser.user_id == user_id,
PMSectionUser.deleted_at.is_(None),
PMSections.deleted_at.is_(None),
PMSectionUser.state == 0
)
)
)
result = await self.session.execute(query)
permissions = result.fetchall()
if not permissions:
return False
if section_tag:
for perm in permissions:
if perm.section_tag == section_tag and perm.action_tag == action_tag:
return True
else:
for perm in permissions:
if perm.action_tag == action_tag:
return True
return False
except Exception as e:
print(f"Error checking permission: {str(e)}")
return False
async def close(self):
if self.session:
await self.session.close()
print("🗄️ Database session closed")
# ---------------------- FACTORIES ----------------------
class ConnectionFactory(ABC):
@abstractmethod
async def create_connection(self) -> Connection:
pass
class FactoryHTTPConnection(ConnectionFactory):
def __init__(self, base_url: str):
self.base_url = base_url
async def create_connection(self) -> HTTPConnection:
return await HTTPConnection.create(self.base_url)
class FactoryDBConnection(ConnectionFactory):
def __init__(self, db_type: str, host: str, port: int, username: str, password: str, db_name: str):
self.db_type = db_type
self.host = host
self.port = port
self.username = username
self.password = password
self.db_name = db_name
async def create_connection(self) -> DBConnection:
return await DBConnection.create(
self.db_type,
self.host,
self.port,
self.username,
self.password,
self.db_name,
)
# ---------------------- FACTORY SELECTOR ----------------------
class FactorySelector:
@staticmethod
async def get_factory(connection_type: str, **kwargs) -> ConnectionFactory:
if connection_type == "http":
return await FactoryHTTPConnection(kwargs.get("base_url", "http://localhost")).create_connection()
elif connection_type == "db":
return await FactoryDBConnection(
db_type=kwargs.get("type", "postgresql"),
host=kwargs.get("host", "localhost"),
port=kwargs.get("port", 5432),
username=kwargs.get("username", "admin"),
password=kwargs.get("password", "password"),
db_name=kwargs.get("db_name", "test_db"),
).create_connection()
else:
raise ValueError(f"Unknown connection type: {connection_type}")

2
app/routes/__init__.py Normal file
View File

@ -0,0 +1,2 @@

12
app/routes/rag/models.py Normal file
View File

@ -0,0 +1,12 @@
from __future__ import annotations
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
class RagInsertRequest(BaseModel):
chat_id: Optional[str] = None
title: Optional[str] = None
user_query: str

175
app/routes/rag/rag_chat.py Normal file
View File

@ -0,0 +1,175 @@
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, Request
from app.core.map_index_reader import MapIndexReader
from app.core.elastic_query_builder import ElasticQueryBuilder
from app.core.field_processor import FieldProcessor
from app.core.response_helper import ResponseHelper
from app.routes.v1.models import (
SearchRequest,
InsertRequest,
UpdateByQueryRequest,
DeleteByQueryRequest,
)
from app.routes.rag.models import (
RagInsertRequest,
)
from typing import Any, Dict, List, Optional
import time
from app.routes.v1.elastic import (
insert,
search,
)
import uuid
import requests
from datetime import datetime
from app.config.settings import get_settings, Settings
router = APIRouter(tags=["ragchat"])
settings= get_settings()
def get_elastic_helper(request: Request):
helper = getattr(request.app.state, "elastic_helper", None)
if helper is None:
raise RuntimeError("Elasticsearch helper not initialized")
return helper
@router.post("/{type_name}/credit_refresh")
async def credit_refresh(type_name: str, request: Request):
try:
print("credit_refresh ...->", settings.ai_rag_host)
if settings.ai_rag_host:
url = settings.ai_rag_host + "/" + "credit_refresh"
body = {}
headers = {"accept": "application/json", "Content-Type": "application/json"}
response = requests.request("POST", url, headers=headers, json=body)
return response.text.replace('"', '')
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=str(exc))
@router.post("/{type_name}/insert")
async def insert_rag(type_name: str, payload: RagInsertRequest, request: Request):
time_stamp = int(datetime.now().timestamp())
user_id = 0
document = {
'id' : id,
'title' : title,
'chat_id' : chat_id,
'user_id' : user_id,
'bale_info' : {
"user_name" : "",
"first_name" : "",
"last_name" : ""
},
'user_query' : payload.user_query,
'model_key' : answer.get("model_key", ""),
'retrived_passage' : answer.get("retrived_passage", ""),
'retrived_ref_ids' : answer.get("retrived_ref_ids", []),
'retrived_duration' : int(answer.get("retrived_duration", 0)),
'prompt_type' : answer.get("prompt_type", ""),
'llm_duration' : int(answer.get("llm_duration", 0)),
'full_duration' : int(answer.get("full_duration", 0)),
'time_create' : time_stamp,
'used_ref_ids' : answer.get("used_ref_ids", []),
'prompt_answer' : answer.get("prompt_answer", ""),
'status_text' : answer.get("status_text", ""),
'status' : answer.get("status", False)
}
insertRequest = InsertRequest(id=id, document=document)
response = await insert(type_name, insertRequest, request )
response['answer'] = document
@router.post("/{type_name}/query")
async def query_rag(type_name: str, payload: RagInsertRequest, request: Request):
#
# uid = uuid.uuid4().hex[:8] # فقط ۸ کاراکتر از uuid eg : '9f1c2a7b'
id = uuid.uuid4().hex[:8]
print("insert_rag start ... ", id)
chat_id = payload.chat_id
if not chat_id :
chat_id = uuid.uuid4().hex[:8]
title = payload.title
if not title and payload.user_query :
title = payload.user_query[0:50]
# ---------------------------
answer = {}
is_gpu_service_ready = False
headers = {"accept": "application/json", "Content-Type": "application/json"}
try:
if settings.ai_rag_host_gpu :
url = settings.ai_rag_host_gpu + '/ping'
response = requests.request("GET", url, headers=headers)
# print(response)
# print(response.status_code)
if response.status_code == 200 :
is_gpu_service_ready = True
else :
is_gpu_service_ready = False
except Exception as exc: # noqa: BLE001
is_gpu_service_ready = False
print( " settings.ai_rag_host_gpu ", settings.ai_rag_host_gpu, is_gpu_service_ready)
try:
if payload.user_query :
if is_gpu_service_ready :
url = settings.ai_rag_host_gpu + "/" + "run_chat"
elif settings.ai_rag_host :
url = settings.ai_rag_host + "/" + "run_chatbot"
else :
print(" ******* error in settings.ai_rag_host ...")
print("settings.ai_rag_host ...", url)
body = {"query": payload.user_query, "text":""}
response = requests.request("POST", url, headers=headers, json=body)
# print(response.text)
# print(response.json())
print(response.status_code)
# print(response.headers)
# print("body ", body)
# print("response ", response)
# print(response.text)
data = response.json()
# print(data)
answer = data.get("answer", {} )
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=str(exc))
# ---------------------------
time_stamp = int(datetime.now().timestamp())
user_id = 0
document = {
'id' : id,
'title' : title,
'chat_id' : chat_id,
'user_id' : user_id,
'user_query' : payload.user_query,
'model_key' : answer.get("model_key", ""),
'retrived_passage' : answer.get("retrived_passage", ""),
'retrived_ref_ids' : answer.get("retrived_ref_ids", []),
'retrived_duration' : int(answer.get("retrived_duration", 0)),
'prompt_type' : answer.get("prompt_type", ""),
'llm_duration' : int(answer.get("llm_duration", 0)),
'full_duration' : int(answer.get("full_duration", 0)),
'time_create' : time_stamp,
'used_ref_ids' : answer.get("used_ref_ids", []),
'prompt_answer' : answer.get("prompt_answer", ""),
'status_text' : answer.get("status_text", ""),
'status' : answer.get("status", False)
}
insertRequest = InsertRequest(id=id, document=document)
response = await insert(type_name, insertRequest, request )
response['answer'] = document
return response

View File

@ -0,0 +1,39 @@
from __future__ import annotations
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
# from app.routes.v1.models import (
# SearchRequest,
# # InsertRequest,
# # UpdateByQueryRequest,
# # DeleteByQueryRequest,
# )
class treeSearchRequest(BaseModel):
parent_id: str
version_key: str = 'D49_oss120'
tcode: str = ''
nested: bool = False
query: Optional[str] = ''
all_item: Optional[bool] = False
class treeInsertRequest(BaseModel):
id: Optional[str] = None
version_key: str = 'D49_oss120'
tcode: str
title: str
parent_id: str
child_order: Optional[float] = -1
full_path: Optional[str] = ''
class treeUpdateRequest(BaseModel):
version_key: Optional[str] = None
tcode: Optional[str] = None
title: Optional[str] = None
parent_id: Optional[str] = None
child_order: Optional[float] = None
full_path: Optional[str] = None
content: Optional[str] = None

View File

@ -0,0 +1,346 @@
# ---------------------- modularity
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, Request
from app.core.map_index_reader import MapIndexReader
from app.core.response_helper import ResponseHelper
from app.routes.v1.models import (
SearchRequest,
InsertRequest,
UpdateByQueryRequest,
DeleteByQueryRequest,
)
from app.routes.tree.base_models import (
treeSearchRequest,
treeInsertRequest,
treeUpdateRequest,
)
from typing import Any, Dict, List, Optional
from app.routes.v1.elastic import (
search,
get_by_id,
insert,
update,
delete,
)
import uuid, requests, time, traceback
# ---------------------- global-params
router = APIRouter(tags=["tree"])
# ---------------------- base-Func
def get_elastic_helper(request: Request):
helper = getattr(request.app.state, "elastic_helper", None)
if helper is None:
raise RuntimeError("Elasticsearch helper not initialized")
return helper
# ---------------------- router-Func
# ----------- GET
@router.get("/ping")
async def helloworld(request: Request):
return {"message": " tree base hello-world"}
def sort_children(node):
node["children"].sort(key=lambda x: x["child_order"])
for child in node["children"]:
sort_children(child)
# خروجی بصورت تو در تو بر می گرداند ، یعنی بچه ها داخل فیلد پدر درج میشود
@router.post("/{type_name}/get")
async def search_tree(type_name: str, payload: treeSearchRequest, request: Request):
# print(f"type_name {type_name}", f"payload {payload}", sep="\n")
tcode = payload.tcode
version_key = payload.version_key
parent_id = payload.parent_id
nested = payload.nested
all_item = payload.all_item
query = payload.query
if parent_id == "0":
parent_id = 0
must = [
{"term": {"version_key": version_key}},
{"term": {"tcode": tcode}},
]
if not all_item :
must.append({"term": {"parent_id": parent_id}})
query = {
"size": 10000,
"_source": [
"id",
"title",
"parent_id",
"child_order",
"time_create",
"tcode",
"version_key",
"full_path",
"content",
],
"query": {
"bool": {
"must": must
}
},
"sort": [{"parent_id": "asc"}, {"child_order": "asc"}],
}
if not all_item:
query["query"]["bool"]["must"].append({"term": {"parent_id": parent_id}})
if tcode != "":
query["query"]["bool"]["must"].append({"term": {"tcode": tcode}})
reader = MapIndexReader(type_name) # mj_plan
helper = getattr(request.app.state, "elastic_helper", None)
if helper is None:
raise RuntimeError("Elasticsearch helper not initialized")
# "قانون تشکل‌های مدنی و احزاب"
try:
index_name = reader.get_index_name()
es_res = await helper.search(index_name, query)
# return es_res
es_res = dict(es_res)
items = []
for item in es_res["hits"]["hits"] :
item_new = {
"id": item["_id"],
"title": item["_source"]["title"],
"version_key": item["_source"]["version_key"],
"parent_id": item["_source"]["parent_id"],
"child_order": item["_source"]["child_order"],
"full_path": item["_source"]["full_path"],
"tcode": item["_source"]["tcode"],
"children": [],
}
if "content" in item["_source"] :
item_new["content"] = item["_source"]["content"]
items.append(item_new)
if not nested:
return items
# 2⃣ ساخت دیکشنری lookup بر اساس id برای دسترسی سریع
lookup = {item["id"]: item for item in items}
# 3⃣ ساخت ساختار درختی
root_nodes = []
p = 0
c = 0
for item in items:
parent_id = item["parent_id"]
if parent_id == 0:
# ریشه‌ها
root_nodes.append(item)
# print(f'parent {p}')
p += 1
else:
# print(f'parent_id {parent_id}')
parent = lookup.get(parent_id)
if parent:
parent["children"].append(item)
# print(f'children {c}')
c += 1
for root in root_nodes:
sort_children(root)
# print(f'es_res {result}', )
return root_nodes
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=404, detail=str(traceback.print_exc()))
@router.get("/{type_name}/get/{id}")
async def get_tree(type_name: str, id: str, request: Request):
doc_id = id
response = await get_by_id(type_name, doc_id, request )
return response
@router.post("/{type_name}/insert")
async def insert_tree(type_name: str, payload:treeInsertRequest, request: Request):
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
index_name = reader.get_index_name()
if not payload.id or payload.id == None or payload.id == "None" :
payload.id = 'mp_' + uuid.uuid4().hex[:8]
if not payload.parent_id or payload.parent_id == None:
payload.parent_id = "0"
if not payload.child_order or payload.child_order == -1 :
payload.child_order = get_max_child_order(helper, index_name, payload )
else :
update_next_child_order(helper, index_name, payload )
#?????
if not payload.full_path :
payload.full_path = ''
payload_new = InsertRequest.model_construct()
payload_new.id = payload.id
payload_new.document = payload.model_dump(exclude_none=True)
response = await insert(type_name, payload_new, request )
return response
@router.post("/{type_name}/update/{id}")
async def update_tree(type_name: str, id: str, payload:treeUpdateRequest, request: Request):
# print("################################update_tree ", id)
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
index_name = reader.get_index_name()
# print("################################update_tree ", payload)
if payload.child_order and payload.child_order == -1 :
payload.child_order = get_max_child_order(helper, index_name, payload )
payload_new = InsertRequest.model_construct()
payload_new.id = id
payload_new.document = payload.model_dump(exclude_none=True)
# print("####################################### payload_new ", payload_new)
response = await update(type_name, id, payload_new, request )
return response
return ""
@router.post("/{type_name}/delete/{id}")
async def delete_tree(type_name: str, id: str, request: Request):
response = await delete(type_name, id, request )
return response
@router.post("/{type_name}/move_prev/{to_id}/{id}")
async def move_prev_tree(type_name: str, id: str, to_id: str, request: Request):
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
index_name = reader.get_index_name()
# node1 = await helper.get_by_id(index_name, id)
node2 = await helper.get_by_id(index_name, to_id)
#??????
if id == to_id :
message = " error id "
raise HTTPException(status_code=404, detail=message)
if not node2 :
message = "not found id "
raise HTTPException(status_code=404, detail=message)
payload=node2["_source"]
child_order = get_max_child_order(helper, index_name, payload )
payload_new = InsertRequest()
payload_new.id = id
payload_new.document = {
"parent_id" : to_id,
"child_order" : child_order
}
response = await update(type_name, id, payload_new, request )
return response
@router.post("/{type_name}/move_in/{to_id}/{id}")
async def move_in_tree(type_name: str, id: str, to_id: str, request: Request):
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
index_name = reader.get_index_name()
# node1 = await helper.get_by_id(index_name, id)
node2 = await helper.get_by_id(index_name, to_id)
#??????
if id == to_id :
message = " error id "
raise HTTPException(status_code=404, detail=message)
if not node2 :
message = "not found id "
raise HTTPException(status_code=404, detail=message)
# child_order1 = node1["_source"]["child_order"]
child_order2 = node2["_source"]["child_order"]
# parent_id1 = node1["_source"]["parent_id"]
parent_id2 = node2["_source"]["parent_id"]
payload=node2["_source"]
update_next_child_order(helper, index_name, payload )
payload_new = InsertRequest.model_construct()
payload_new.id = id
payload_new.document = {
"parent_id" : parent_id2,
"child_order" : child_order2
}
response = await update(type_name, id, payload_new, request )
return response
async def get_max_child_order(helper, index_name, payload:treeInsertRequest) :
must = [
{"term": {"version_key": payload.version_key}},
{"term": {"tcode": payload.tcode}},
{"term": {"parent_id": payload.parent_id}}
]
query = {
"size": 1,
"_source": ["child_order"],
"query": {
"bool": {
"must": must
}
},
"sort": [{"child_order": "asc"}],
}
es_res = await helper.search(index_name, query)
child_order = 0
if len(es_res["hits"]["hits"]) > 0 :
child_order = es_res["hits"]["hits"][0]["_source"]["child_order"]
return child_order + 1
async def update_next_child_order(helper, index_name, payload:treeInsertRequest) :
must = [
{"term": {"version_key": payload.version_key}},
{"term": {"tcode": payload.tcode}},
{"term": {"parent_id": payload.parent_id}},
{"range": {"child_order": { "gte": payload.child_order}}}
]
query = {
"query": {
"bool": {
"must": must
}
},
"script": {
"source": "ctx._source.child_order = ctx._source.child_order + 1; ",
"lang": "painless"
}
}
es_res = await helper.update_by_query(index_name, query, True)
return es_res

View File

@ -0,0 +1,2 @@

223
app/routes/v1/elastic.py Normal file
View File

@ -0,0 +1,223 @@
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, Request
from app.core.elastic_client_helper import ElasticClientHelper
from app.core.map_index_reader import MapIndexReader
from app.core.elastic_query_builder import ElasticQueryBuilder
from app.core.field_processor import FieldProcessor
from app.core.response_helper import ResponseHelper
from app.routes.v1.models import (
ExportToFileRequest,
SearchRequest,
InsertRequest,
UpdateByQueryRequest,
DeleteByQueryRequest,
)
from typing import Any, Dict, List, Optional
import time
from app.config.settings import get_settings, Settings
from app.lib.general_functions import is_user_permit_action
router = APIRouter(tags=["elasticsearch"])
settings: Settings = get_settings()
def get_elastic_helper(request: Request):
helper = getattr(request.app.state, "elastic_helper", None)
if helper is None:
raise RuntimeError("Elasticsearch helper not initialized")
return helper
@router.post("/indices/{type_name}/insert")
async def insert(type_name: str, payload: InsertRequest, request: Request):
if not is_user_permit_action(request.state.user_id, f"{type_name}_update", f"{type_name}_insert", request.state.app) :
message = "------ > not access " + str(request.state.user_id) + f" {type_name}_update"
print(message )
raise HTTPException(status_code=400, detail=message)
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
field_processor = FieldProcessor(reader)
validate_doc, document = field_processor.validate_document(payload.document, payload.id)
edition_doc = field_processor.edition_document(payload.document)
document = edition_doc
if not validate_doc["valid"]:
raise HTTPException(status_code=400, detail=str(validate_doc))
processed_doc = field_processor.process_joinning_document(document)
doc = processed_doc
try:
es_res = await helper.index_document(reader.get_index_name(), doc, id=payload.id, refresh="wait_for")
return {"success": True, "result": es_res}
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=str(exc))
@router.post("/indices/{type_name}/update/{id}")
async def update(type_name: str, id: str , payload: InsertRequest, request: Request):
payload.id = id
if not is_user_permit_action(request.state.user_id, f"{type_name}_update", f"{type_name}_insert", request.state.app) :
message = "------ > not access " + str(request.state.user_id) + f" {type_name}_update"
print(message )
raise HTTPException(status_code=400, detail=message)
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
field_processor = FieldProcessor(reader)
validate_doc, document = field_processor.validate_document(payload.document, payload.id, False)
edition_doc = field_processor.edition_document(payload.document)
document = edition_doc
if not validate_doc["valid"]:
raise HTTPException(status_code=400, detail=str(validate_doc))
processed_doc = field_processor.process_joinning_document(document)
doc = processed_doc
try:
es_res = await helper.update_or_index_document(index=reader.get_index_name(), data=doc, document_id=payload.id, operation_type="update", refresh="wait_for")
return {"success": True, "result": es_res}
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail= 'ssss' + str(exc))
@router.post("/indices/{type_name}/delete/{id}")
async def delete(type_name: str, id: str , request: Request):
if not is_user_permit_action(request.state.user_id, f"{type_name}_update", f"{type_name}_delete", request.state.app) :
message = "------ > not access " + str(request.state.user_id) + f" {type_name}_update"
print(message )
raise HTTPException(status_code=400, detail=message)
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
try:
es_res = await helper.delete_document(index=reader.get_index_name(), id=id, refresh="wait_for")
return {"success": True, "result": es_res}
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=str(exc))
@router.get("/indices/{type_name}/{doc_id}")
async def get_by_id(type_name: str, doc_id: str, request: Request):
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
try:
es_res = await helper.get_by_id(reader.get_index_name(), doc_id)
return ResponseHelper().normalize_get_response(es_res)
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=404, detail=str(exc))
#---------------------------------------------------
#--
#-- payload --> body = [ {....}, {....},{....} ]
#--
@router.post("/indices/{type_name}/insert/multi")
async def insertMulti(type_name: str, payload: List[InsertRequest], request: Request):
if not is_user_permit_action(request.state.user_id, f"{type_name}_update", f"{type_name}_insert", request.state.app) :
message = "------ > not access " + str(request.state.user_id) + f" {type_name}_update"
print(message )
raise HTTPException(status_code=400, detail=message)
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
field_processor = FieldProcessor(reader)
list_new = []
list_errors = []
for i, item in enumerate(payload) :
id = item.id if item.id else ''
validate_doc, document = field_processor.validate_document(item.document, id)
if not validate_doc["valid"]:
validate_doc["index"] = i
validate_doc["doc"] = item
list_errors.append(validate_doc)
else:
processed_doc = field_processor.process_joinning_document(document)
if not "id" in processed_doc and id:
processed_doc["id"] = id
list_new.append(processed_doc)
try:
if list_new :
es_res = await helper.bulk_insert(reader.get_index_name(), list_new, refresh="wait_for")
return {"success": True, "validation_errors": list_errors, "result": es_res}
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=str(exc))
#---------------------------------------------------
#--
@router.post("/indices/{type_name}/export-to-file")
async def export_to_file(type_name:str, payload: ExportToFileRequest,request:Request):
print("1 -->", time.time())
reader = MapIndexReader(type_name)
helper :ElasticClientHelper= get_elastic_helper(request)
builder = ElasticQueryBuilder()
result = await helper.export_to_file(
**dict(payload),index_name=reader.get_index_name()
)
return result
@router.post("/indices/{type_name}/search")
async def search(type_name: str, payload: SearchRequest, request: Request):
print("1 -->", time.time())
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
builder = ElasticQueryBuilder()
print("2 -->", time.time())
# print(payload)
body = builder.build_search_query(reader, payload)
print("body -->", body)
try:
es_res = await helper.search(reader.get_index_name(), body)
print("3 -->", time.time())
collapse_field = body.get("collapse", {}).get("field") if body.get("collapse") else None
bookmark_id = payload.bookmark_id if payload.bookmark_id else ''
# print(es_res)
res = ResponseHelper(helper, reader).normalize_search_response(es_res, collapse_field, bookmark_id, payload.mode_response)
print("4 -->", time.time())
return res
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=str(exc))
# @router.post("/indices/{type_name}/list")
# async def search(type_name: str, payload: SearchRequest, request: Request):
# reader = MapIndexReader(type_name)
# helper = get_elastic_helper(request)
# builder = ElasticQueryBuilder()
# body = builder.build_search_query(reader, payload)
# try:
# es_res = await helper.search(reader.get_index_name(), body)
# return ResponseHelper().normalize_search_response(es_res, body.get("collapse", {}).get("field") if body.get("collapse") else None)
# except Exception as exc: # noqa: BLE001
# raise HTTPException(status_code=400, detail=str(exc))
# @router.post("/indices/{type_name}/insert/{entity_type}/{data_type}/{ref_key}")
# @router.post("/indices/{type_name}/insert/favorite/{data_type}/{ref_key}")
# @router.post("/indices/{type_name}/insert/favorite/entity/{ref_key}")
# @router.post("/indices/{type_name}/insert/history/{data_type}/{ref_key}")
# @router.post("/indices/{type_name}/insert/history/{data_type}/{ref_key}")
# @router.post("/indices/{type_name}/insert/")
# async def insert1(type_name: str, payload: InsertRequest, request: Request):
# payload.document.entity_type = entity_type
# payload.document.data_type = data_type
# payload.document.ref_key = ref_key

View File

@ -0,0 +1,62 @@
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, Request
from app.core.map_index_reader import MapIndexReader
from app.core.elastic_query_builder import ElasticQueryBuilder
from app.core.field_processor import FieldProcessor
from app.core.response_helper import ResponseHelper
from app.routes.v1.models import (
SearchRequest,
InsertRequest,
UpdateByQueryRequest,
DeleteByQueryRequest,
)
router = APIRouter(tags=["elasticsearch"])
def get_elastic_helper(request: Request):
helper = getattr(request.app.state, "elastic_helper", None)
if helper is None:
raise RuntimeError("Elasticsearch helper not initialized")
return helper
@router.post("/indices/{type_name}/create-index")
async def create_index(type_name: str, request: Request):
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
try:
res = await helper.create_index_if_not_exists(reader.get_index_name(), reader.mapping)
return ResponseHelper().merge_related({"success": True, "result": res}, None)
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=str(exc))
@router.post("/indices/{type_name}/update-by-query")
async def update_by_query(type_name: str, payload: UpdateByQueryRequest, request: Request):
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
builder = ElasticQueryBuilder()
body = builder.build_update_by_query(reader, payload)
try:
es_res = await helper.update_by_query(reader.get_index_name(), body, refresh=True)
return {"success": True, "result": es_res}
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=str(exc))
@router.post("/indices/{type_name}/delete-by-query")
async def delete_by_query(type_name: str, payload: DeleteByQueryRequest, request: Request):
reader = MapIndexReader(type_name)
helper = get_elastic_helper(request)
builder = ElasticQueryBuilder()
body = builder.build_delete_by_query(reader, payload)
try:
es_res = await helper.delete_by_query(reader.get_index_name(), body, refresh=True)
return {"success": True, "result": es_res}
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=str(exc))

View File

@ -0,0 +1,159 @@
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, Request
from app.core.map_index_reader import MapIndexReader
from app.core.elastic_query_builder import ElasticQueryBuilder
from app.core.field_processor import FieldProcessor
from app.core.response_helper import ResponseHelper
from app.routes.v1.models import (
SearchRequest,
InsertRequest,
UpdateByQueryRequest,
DeleteByQueryRequest,
)
router = APIRouter(tags=["elasticsearch"])
def get_elastic_helper(request: Request):
helper = getattr(request.app.state, "elastic_helper", None)
if helper is None:
raise RuntimeError("Elasticsearch helper not initialized")
return helper
# New enhanced endpoints
@router.get("/indices/{type_name}/schema")
async def get_schema(type_name: str, request: Request):
"""Get complete schema information for a type."""
reader = MapIndexReader(type_name)
try:
return {
"success": True,
"result": {
"index_info": reader.get_index_info(),
"query_config": reader.get_query_config(),
"fields": {field: reader.get_field_metadata(field) for field in reader.list_fields()}
}
}
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=str(exc))
@router.get("/indices/{type_name}/fields")
async def get_fields(type_name: str, request: Request):
"""Get field information for a type."""
reader = MapIndexReader(type_name)
try:
return {
"success": True,
"result": {
"all_fields": reader.list_fields(),
"searchable_fields": reader.get_searchable_fields(),
"sortable_fields": reader.get_sortable_fields(),
"exportable_fields": [f for f in reader.list_fields() if reader.get_export_state(f)],
"array_fields": reader.get_array_fields(),
"object_fields": reader.get_object_fields(),
"date_fields": reader.get_date_fields(),
"numeric_fields": reader.get_numeric_fields(),
"boolean_fields": reader.get_boolean_fields(),
"vector_fields": reader.get_vector_fields()
}
}
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=str(exc))
@router.get("/indices/{type_name}/fields/{field_name}")
async def get_field_info(type_name: str, field_name: str, request: Request):
"""Get detailed information for a specific field."""
reader = MapIndexReader(type_name)
try:
field_meta = reader.get_field_meta(field_name)
return {
"success": True,
"result": {
"field_name": field_name,
"metadata": reader.get_field_metadata(field_name),
# "label": reader.get_field_label(field_name),
"validation": reader.validate_field_configuration(field_name),
"join_processes": reader.get_join_processes(field_name),
"processed_fields": reader.get_processed_fields(field_name)
}
}
except KeyError:
raise HTTPException(status_code=404, detail=f"Field '{field_name}' not found")
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=str(exc))
@router.post("/indices/{type_name}/validate")
async def validate_document(type_name: str, document: dict, request: Request):
"""Validate a document against the schema."""
reader = MapIndexReader(type_name)
field_processor = FieldProcessor(reader)
try:
validation_result = field_processor.validate_document(document)
return {
"success": True,
"result": validation_result
}
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=str(exc))
# @router.post("/indices/{type_name}/process")
# async def process_document(type_name: str, document: dict, request: Request):
# """Process a document based on field configurations."""
# reader = MapIndexReader(type_name)
# field_processor = FieldProcessor(reader)
# try:
# processed_doc = field_processor.process_joinning_document(document)
# return {
# "success": True,
# "result": processed_doc
# }
# except Exception as exc: # noqa: BLE001
# raise HTTPException(status_code=400, detail=str(exc))
@router.get("/indices/{type_name}/advanced-tags")
async def get_advanced_tags(type_name: str, request: Request):
"""Get advanced search tags configuration."""
reader = MapIndexReader(type_name)
try:
return {
"success": True,
"result": {
"tags": reader.get_search_advance_tags(),
"tag_names": reader.list_advanced_tags(),
"collapse_fields": reader.get_collapse_fields()
}
}
except Exception as exc: # noqa: BLE001
raise HTTPException(status_code=400, detail=str(exc))
# @router.get("/indices/{type_name}/labels")
# async def get_field_labels(type_name: str, request: Request):
# """Get field labels mapping."""
# reader = MapIndexReader(type_name)
# try:
# labels = {}
# for field in reader.list_fields():
# label = reader.get_field_label(field)
# if label:
# labels[field] = label
# return {
# "success": True,
# "result": labels
# }
# except Exception as exc: # noqa: BLE001
# raise HTTPException(status_code=400, detail=str(exc))

229
app/routes/v1/models.py Normal file
View File

@ -0,0 +1,229 @@
from __future__ import annotations
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, validator
from typing import Optional, Dict, Any, List
import os
class SearchRequest(BaseModel):
track_total_hits: Optional[bool] = True
mode_response : str = "elastic" # "normal" , "elastic"
query: Optional[str] = ''
search_type: Optional[str] = "normal" # "normal", "phrase" , "and"
filters: Optional[Dict[str, Any]] = None # { "f_ud": 2, "f_cd": "435234234"}
sort: Optional[List[str]] = None # time_edit:desc, lastTitle, ...
from_: int = Field(0, ge=0, alias="from")
size: int = Field(10, ge=0, le=10000)
collapse_field: Optional[str] = ''
bookmark_id: Optional[str] = ''
highlight: Optional[Dict[str, Any]] = None
aggregation_fields: Optional[Dict[str, Any]] = None
# Enhanced fields for new backend.json properties
export_mode: bool = False
validate_fields: bool = True
use_field_boosts: bool = True
aggregation_fields: Optional[List[str]] = None
advanced_search_tags: Optional[List[str]] = None
include_metadata: bool = False
field_type_filter: Optional[str] = None
search_after: Optional[List[Any]] = None
search_fields: Optional[List[str]] = None
default_search_field: str = "_all"
include_fields: Optional[List[str]] = None
exclude_fields: Optional[List[str]] = None
class Config:
populate_by_name = True
class InsertRequest(BaseModel):
id: Optional[str] = None
document: Dict[str, Any]
class UpdateByQueryRequest(BaseModel):
filters: Optional[Dict[str, Any]] = None
set_fields: Optional[Dict[str, Any]] = None
script: Optional[Dict[str, Any]] = None
class DeleteByQueryRequest(BaseModel):
filters: Optional[Dict[str, Any]] = None
class ExportToFileRequest(BaseModel):
path_back: str = Field(
...,
description="Backup directory path",
example="/backup/data"
)
out_name: str = Field(
"",
description="Output file name (default: index_name)",
example="backup_2024",
max_length=255
)
body: Optional[Dict[str, Any]] = Field(
None,
description="Optional search query body",
example={
"query": {
"match_all": {}
}
}
)
fields: Optional[List[str]] = Field(
None,
description="List of fields to include (if empty, include all)",
example=["title", "content", "timestamp"]
)
chunk_size: int = Field(
1000,
description="Number of documents per chunk",
ge=100,
le=10000,
example=1000
)
scroll_timeout: str = Field(
"5m",
description="Scroll timeout for Elasticsearch",
pattern="^[0-9]+[smh]$",
example="5m"
)
max_documents: Optional[int] = Field(
None,
description="Maximum number of documents to export",
ge=1,
le=10000000,
example=50000
)
delay_between_chunks: float = Field(
0.1,
description="Delay between processing chunks in seconds",
ge=0.0,
le=10.0,
example=0.1
)
to_zip: bool = Field(
False,
description="Whether to compress output to ZIP file",
example=True
)
@validator('path_back')
def validate_path_back(cls, v):
"""Validate backup directory path"""
if not v:
raise ValueError('path_back cannot be empty')
# Check if path contains invalid characters
invalid_chars = ['..', '~', '*', '?', '"', '<', '>', '|']
if any(char in v for char in invalid_chars):
raise ValueError(f'Invalid characters in path: {v}')
return v
@validator('out_name')
def validate_out_name(cls, v, values):
"""Validate output file name"""
if not v:
# Use index_name as default
return values.get('index_name', '')
# File name validation
invalid_chars = ['/', '\\', ':', '*', '?', '"', '<', '>', '|']
if any(char in v for char in invalid_chars):
raise ValueError(f'Invalid characters in output name: {v}')
return v
@validator('chunk_size')
def validate_chunk_size(cls, v):
"""Validate chunk size"""
if v < 100:
raise ValueError('chunk_size must be at least 100')
if v > 10000:
raise ValueError('chunk_size cannot exceed 10000')
return v
@validator('scroll_timeout')
def validate_scroll_timeout(cls, v):
"""Validate scroll timeout format"""
import re
pattern = re.compile(r'^[0-9]+[smh]$')
if not pattern.match(v):
raise ValueError('scroll_timeout must be in format: [number][s|m|h] (e.g., 5m, 30s, 1h)')
# Extract number and unit
num = int(v[:-1])
unit = v[-1]
if unit == 's' and num > 3600: # 1 hour
raise ValueError('Scroll timeout in seconds cannot exceed 3600 (1 hour)')
elif unit == 'm' and num > 60: # 1 hour
raise ValueError('Scroll timeout in minutes cannot exceed 60 (1 hour)')
elif unit == 'h' and num > 24: # 1 day
raise ValueError('Scroll timeout in hours cannot exceed 24 (1 day)')
return v
@validator('max_documents')
def validate_max_documents(cls, v):
"""Validate maximum documents"""
if v is not None and v <= 0:
raise ValueError('max_documents must be positive')
return v
@validator('delay_between_chunks')
def validate_delay_between_chunks(cls, v):
"""Validate delay between chunks"""
if v < 0:
raise ValueError('delay_between_chunks cannot be negative')
if v > 10.0:
raise ValueError('delay_between_chunks cannot exceed 10 seconds')
return v
@validator('fields')
def validate_fields(cls, v):
"""Validate fields list"""
if v is not None:
if len(v) == 0:
raise ValueError('fields list cannot be empty')
# Check for duplicate fields
if len(v) != len(set(v)):
raise ValueError('fields list contains duplicates')
# Validate each field name
for field in v:
if not field or not field.strip():
raise ValueError('field name cannot be empty')
if len(field) > 255:
raise ValueError(f'field name too long: {field}')
return v
class Config:
schema_extra = {
"example": {
"path_back": "/backup/data",
"out_name": "backup_2024",
"body": {
"query": {
"match_all": {}
}
},
"fields": ["title", "content", "timestamp"],
"chunk_size": 1000,
"scroll_timeout": "5m",
"max_documents": 50000,
"delay_between_chunks": 0.1,
"to_zip": True
}
}

View File

@ -0,0 +1,16 @@
from __future__ import annotations
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
from app.routes.v1.models import (
SearchRequest,
# InsertRequest,
# UpdateByQueryRequest,
# DeleteByQueryRequest,
)
class voiceSearchRequest(SearchRequest):
query: str = ''

122
app/routes/voice/voice.py Normal file
View File

@ -0,0 +1,122 @@
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, Request
from app.core.map_index_reader import MapIndexReader
from app.core.elastic_query_builder import ElasticQueryBuilder
from app.core.field_processor import FieldProcessor
from app.core.response_helper import ResponseHelper
from app.routes.v1.models import (
SearchRequest,
InsertRequest,
UpdateByQueryRequest,
DeleteByQueryRequest,
)
# from app.routes.voice.models import (
# voiceSearchRequest,
# )
from typing import Any, Dict, List, Optional
import time
from app.routes.v1.elastic import (
search,
)
import uuid
import requests
from app.config.settings import get_settings, Settings
from bs4 import BeautifulSoup
router = APIRouter(tags=["voice"])
@router.post("/{type_name}/search")
async def search_voice(type_name: str, payload: SearchRequest, request: Request):
payload.track_total_hits = True
payload.search_type = "phrase"
# payload.size = 2
response = await search(type_name, payload, request )
# print(response)
took = response.get("took", 0)
aggregations = response.get("aggregations", {})
hits_section = response.get("hits", {})
items = hits_section.get("hits", [])
total = hits_section.get("total", {})
# print(total)
res = []
highlight_ext = []
for item in items:
_id = item.get("_id", 0)
source = item.get("_source", {})
content = source.get("content", "")
time_words = source.get("time_words", "")
highlight = item.get("highlight", [])
highlight_content = []
h_key = ''
if 'content.ph' in highlight :
highlight_content = highlight["content.ph"]
h_key = 'content.ph'
elif 'content.fa' in highlight :
highlight_content = highlight["content.fa"]
h_key = 'content.fa'
elif 'content' in highlight :
highlight_content = highlight["content"]
h_key = 'content'
highlight_ext = []
for i in highlight_content:
soup = BeautifulSoup(i, 'html.parser')
clean = soup.get_text()
start_word = {}
end_word = {}
start_pos = content.find(clean)
if start_pos != -1:
end_pos = start_pos + len(clean)
else :
continue
for word in time_words:
if word["start_offset"] == start_pos:
start_word = word
break
for word in time_words:
if word["end_offset"] == end_pos:
end_word = word
break
highlight_ext.append({
"start": start_word.get("start",""),
"end": end_word.get("end",""),
"start_offset": start_pos,
"end_offset": end_pos
})
if "content" in source :
del source["content"]
if "time_words" in source :
del source["time_words"]
new_item = {
"_id" : _id,
"_source" : source,
"highlight": { 'content' : highlight[h_key] },
# "highlight" : highlight,
"voice_times" : highlight_ext
}
res.append(new_item)
result_end = {
"took" : took,
"status" : 0,
"success": True,
"message" : "",
"hits" : {
"hits": res,
"total": total
}
}
return result_end

View File

@ -0,0 +1,127 @@
{
"index": {
"name": "ai_nlp_word",
"aliases": [],
"index_key": "aiword"
},
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"is_correct": {
"type": "keyword"
},
"nearest_correct_word": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"stem": {
"type": "keyword"
},
"origin": {
"type": "keyword"
},
"word_classes": {
"type": "keyword"
},
"word_tags": {
"type": "keyword"
},
"is_proper_noun": {
"type": "keyword"
},
"ner_description": {
"type": "text"
},
"llm_description": {
"type": "text"
},
"user_description": {
"type": "text"
},
"admin_description": {
"type": "text"
},
"confidence": {
"type": "float"
},
"ref_key": {
"type": "keyword"
},
"language_key": {
"type": "keyword"
},
"domain_tags": {
"type": "keyword"
},
"time_create": {
"type": "date"
},
"time_edit": {
"type": "date"
}
},
"validation": {
"required": [
"title"
],
"default_value":{
"is_correct" : "نامشخص"
}
},
"include_fields": [],
"is_array": [
"word_classes",
"word_tags",
"domain_tags"
],
"query": {
"default_sort": [
{
"_score": "desc"
},
{
"title": "asc"
},
{
"time_edit": "desc"
}
],
"normal": {
"phrase": {
"title": 5,
"nearest_correct_word": 3,
"stem": 2,
"origin": 2
},
"match": {
"title": 3,
"nearest_correct_word": 2,
"stem": 1,
"origin": 1
}
},
"filter_keys": {
"f_wc": "word_classes",
"f_wt": "word_tags",
"f_lk": "language_key",
"f_dt": "domain_tags",
"f_rk": "ref_key"
}
}
}

View File

@ -0,0 +1,216 @@
{
"mappings": {
"date_detection": false,
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"is_correct": {
"type": "keyword"
},
"nearest_correct_word": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"stem": {
"type": "keyword"
},
"origin": {
"type": "keyword"
},
"word_classes": {
"type": "keyword"
},
"word_tags": {
"type": "keyword"
},
"is_proper_noun": {
"type": "keyword"
},
"ner_description": {
"type": "text"
},
"llm_description": {
"type": "text"
},
"user_description": {
"type": "text"
},
"admin_description": {
"type": "text"
},
"confidence": {
"type": "float"
},
"ref_key": {
"type": "keyword"
},
"language_key": {
"type": "keyword"
},
"domain_tags": {
"type": "keyword"
},
"time_create": {
"type": "date"
},
"time_edit": {
"type": "date"
}
}
},
"settings": {
"index.max_result_window": 15000,
"index": {
"analysis": {
"analyzer": {
"phrase_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": []
},
"normal_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": [
"fa_stop"
]
}
},
"filter": {
"fa_stop": {
"type": "stop",
"stopwords": [
"یا",
"را",
"این",
"با",
"آن",
"و",
"در",
"به",
"که",
"از",
"طی",
"پس",
"چه",
"اگر",
"نه",
"آنها",
"هر",
"او",
"ما",
"من",
"تا",
"نیز",
"اما",
"یک",
"بر",
"هم",
"برای",
"کن",
"کرد",
"کردن",
"باش",
"بود",
"بودن",
"شو",
"شد",
"شدن",
"‏دار",
"داشت",
"داشتن",
"‏خواه",
"خواست",
"خواستن",
"‏گوی",
"گفت",
"گفتن",
"‏گیر",
"گرفت",
"گرفتن",
"‏آی",
"آمد",
"آمدن",
"‏توان",
"توانستن",
"‏یاب",
"یافتن",
"‏آور",
"آورد",
"آوردن",
"1",
"2",
"3",
"ص",
"4",
"و",
"5",
"ج",
"6",
"a",
"top",
"href",
"pageno"
],
"char_filter": []
}
},
"char_filter": {
"fa_char_filter": {
"type": "mapping",
"mappings": [
"٠ => 0",
"١ => 1",
"٢ => 2",
"٣ => 3",
"٤ => 4",
"٥ => 5",
"٦ => 6",
"٧ => 7",
"٨ => 8",
"٩ => 9",
"ک => ك",
"ی => ي",
"ة => ه",
"إ => ا",
"أ => ا",
"آ => ا",
"ء => ا",
"َ => ",
"ُ => ",
"ِ => ",
"ّ => ",
"ً => ",
"ٌ => ",
"ٍ => ",
"ْ => "
]
}
}
},
"number_of_shards": "1",
"number_of_replicas": "0"
}
}
}

View File

@ -0,0 +1,217 @@
{
"mappings": {
"date_detection": false,
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "text"
},
"tcode": {
"type": "keyword"
},
"version_key": {
"type": "keyword"
},
"parent_id": {
"type": "keyword"
},
"child_order": {
"type": "float"
},
"full_path": {
"type": "text"
},
"level": {
"type": "integer"
},
"ai_section_ids": {
"type": "keyword"
},
"section_ids": {
"type": "keyword"
},
"user_actions": {
"properties": {
"user_id": {
"type": "integer"
},
"time_edit": {
"type": "date"
},
"description": {
"type": "text"
},
"property_key": {
"type": "keyword"
},
"action_key": {
"type": "keyword"
},
"action_value": {
"type": "text"
},
"action": {
"type": "object"
}
}
},
"time_create": {
"type": "date"
},
"time_edit": {
"type": "date"
}
}
},
"settings": {
"index.max_result_window": 15000,
"index": {
"analysis": {
"analyzer": {
"phrase_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": []
},
"normal_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": [
"fa_stop"
]
}
},
"filter": {
"fa_stop": {
"type": "stop",
"stopwords": [
"یا",
"را",
"این",
"با",
"آن",
"و",
"در",
"به",
"که",
"از",
"طی",
"پس",
"چه",
"اگر",
"نه",
"آنها",
"هر",
"او",
"ما",
"من",
"تا",
"نیز",
"اما",
"یک",
"بر",
"هم",
"برای",
"کن",
"کرد",
"کردن",
"باش",
"بود",
"بودن",
"شو",
"شد",
"شدن",
"‏دار",
"داشت",
"داشتن",
"‏خواه",
"خواست",
"خواستن",
"‏گوی",
"گفت",
"گفتن",
"‏گیر",
"گرفت",
"گرفتن",
"‏آی",
"آمد",
"آمدن",
"‏توان",
"توانستن",
"‏یاب",
"یافتن",
"‏آور",
"آورد",
"آوردن",
"1",
"2",
"3",
"ص",
"4",
"و",
"5",
"ج",
"6",
"a",
"top",
"href",
"pageno"
],
"char_filter": []
}
},
"char_filter": {
"fa_char_filter": {
"type": "mapping",
"mappings": [
"٠ => 0",
"١ => 1",
"٢ => 2",
"٣ => 3",
"٤ => 4",
"٥ => 5",
"٦ => 6",
"٧ => 7",
"٨ => 8",
"٩ => 9",
"ک => ك",
"ی => ي",
"ة => ه",
"إ => ا",
"أ => ا",
"آ => ا",
"ء => ا",
"َ => ",
"ُ => ",
"ِ => ",
"ّ => ",
"ً => ",
"ٌ => ",
"ٍ => ",
"ْ => "
]
}
}
},
"number_of_shards": "1",
"number_of_replicas": "0"
}
}
}

View File

@ -0,0 +1,243 @@
{
"mappings": {
"date_detection": false,
"properties": {
"id": {
"type": "keyword"
},
"plan_id": {
"type": "keyword"
},
"tcode": {
"type": "keyword"
},
"version_key": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "text"
},
"footnotes": {
"properties": {
"text": {
"type": "text"
},
"main_type": {
"type": "keyword"
},
"refrence_ids": {
"type": "keyword"
}
}
},
"section_info": {
"properties": {
"qanon_title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"full_path": {
"type": "text"
},
"ts_date": {
"type": "date"
},
"ts_ref": {
"type": "keyword"
},
"state_etebar": {
"type": "keyword"
},
"content": {
"type": "text"
}
}
},
"user_actions": {
"properties": {
"user_id": {
"type": "integer"
},
"time_edit": {
"type": "date"
},
"description": {
"type": "text"
},
"property_key": {
"type": "keyword"
},
"action_key": {
"type": "keyword"
},
"action_value": {
"type": "text"
},
"action": {
"type": "object"
}
}
},
"time_create": {
"type": "date"
},
"time_edit": {
"type": "date"
}
}
},
"settings": {
"index.max_result_window": 15000,
"index": {
"analysis": {
"analyzer": {
"phrase_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": []
},
"normal_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": [
"fa_stop"
]
}
},
"filter": {
"fa_stop": {
"type": "stop",
"stopwords": [
"یا",
"را",
"این",
"با",
"آن",
"و",
"در",
"به",
"که",
"از",
"طی",
"پس",
"چه",
"اگر",
"نه",
"آنها",
"هر",
"او",
"ما",
"من",
"تا",
"نیز",
"اما",
"یک",
"بر",
"هم",
"برای",
"کن",
"کرد",
"کردن",
"باش",
"بود",
"بودن",
"شو",
"شد",
"شدن",
"‏دار",
"داشت",
"داشتن",
"‏خواه",
"خواست",
"خواستن",
"‏گوی",
"گفت",
"گفتن",
"‏گیر",
"گرفت",
"گرفتن",
"‏آی",
"آمد",
"آمدن",
"‏توان",
"توانستن",
"‏یاب",
"یافتن",
"‏آور",
"آورد",
"آوردن",
"1",
"2",
"3",
"ص",
"4",
"و",
"5",
"ج",
"6",
"a",
"top",
"href",
"pageno"
],
"char_filter": []
}
},
"char_filter": {
"fa_char_filter": {
"type": "mapping",
"mappings": [
"٠ => 0",
"١ => 1",
"٢ => 2",
"٣ => 3",
"٤ => 4",
"٥ => 5",
"٦ => 6",
"٧ => 7",
"٨ => 8",
"٩ => 9",
"ک => ك",
"ی => ي",
"ة => ه",
"إ => ا",
"أ => ا",
"آ => ا",
"ء => ا",
"َ => ",
"ُ => ",
"ِ => ",
"ّ => ",
"ً => ",
"ٌ => ",
"ٍ => ",
"ْ => "
]
}
}
},
"number_of_shards": "1",
"number_of_replicas": "0"
}
}
}

View File

@ -0,0 +1,224 @@
{
"mappings": {
"date_detection": false,
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"ar": {
"type": "text",
"analyzer": "normal_analyzer_arabic",
"search_analyzer": "normal_analyzer_arabic",
"search_quote_analyzer": "phrase_analyzer_arabic"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_fa",
"search_analyzer": "phrase_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
}
}
},
"author": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"main_type": {
"type": "keyword"
},
"begin_date": {
"type": "date"
},
"end_date": {
"type": "date"
},
"begin_year": {
"type": "integer"
},
"tags": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"keywords": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"format": {
"type": "keyword"
}
}
},
"settings": {
"index.max_result_window": 15000,
"index": {
"analysis": {
"analyzer": {
"phrase_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": []
},
"normal_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": [
"fa_stop"
]
}
},
"filter": {
"fa_stop": {
"type": "stop",
"stopwords": [
"یا",
"را",
"این",
"با",
"آن",
"و",
"در",
"به",
"که",
"از",
"طی",
"پس",
"چه",
"اگر",
"نه",
"آنها",
"هر",
"او",
"ما",
"من",
"تا",
"نیز",
"اما",
"یک",
"بر",
"هم",
"برای",
"کن",
"کرد",
"کردن",
"باش",
"بود",
"بودن",
"شو",
"شد",
"شدن",
"‏دار",
"داشت",
"داشتن",
"‏خواه",
"خواست",
"خواستن",
"‏گوی",
"گفت",
"گفتن",
"‏گیر",
"گرفت",
"گرفتن",
"‏آی",
"آمد",
"آمدن",
"‏توان",
"توانستن",
"‏یاب",
"یافتن",
"‏آور",
"آورد",
"آوردن",
"1",
"2",
"3",
"ص",
"4",
"و",
"5",
"ج",
"6",
"a",
"top",
"href",
"pageno"
],
"char_filter": []
}
},
"char_filter": {
"fa_char_filter": {
"type": "mapping",
"mappings": [
"٠ => 0",
"١ => 1",
"٢ => 2",
"٣ => 3",
"٤ => 4",
"٥ => 5",
"٦ => 6",
"٧ => 7",
"٨ => 8",
"٩ => 9",
"ک => ك",
"ی => ي",
"ة => ه",
"إ => ا",
"أ => ا",
"آ => ا",
"ء => ا",
"َ => ",
"ُ => ",
"ِ => ",
"ّ => ",
"ً => ",
"ٌ => ",
"ٍ => ",
"ْ => "
]
}
}
},
"number_of_shards": "1",
"number_of_replicas": "0"
}
}
}

View File

@ -0,0 +1,249 @@
{
"mappings": {
"date_detection": false,
"properties": {
"ref_id": {
"type": "keyword"
},
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"author": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"branch": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"tags": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"begin_year": {
"type": "integer",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 20
}
}
},
"format": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"sound_title": {
"type": "keyword"
},
"sound_link": {
"type": "keyword"
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_fa",
"search_analyzer": "phrase_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
}
}
},
"time_words": {
"properties": {
"start_offset": {
"type": "integer"
},
"end_offset": {
"type": "integer"
},
"word": {
"type": "text"
},
"start": {
"type": "float"
},
"end": {
"type": "float"
}
}
}
}
},
"settings": {
"index.max_result_window": 15000,
"index": {
"analysis": {
"analyzer": {
"phrase_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": []
},
"normal_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": [
"fa_stop"
]
}
},
"filter": {
"fa_stop": {
"type": "stop",
"stopwords": [
"یا",
"را",
"این",
"با",
"آن",
"و",
"در",
"به",
"که",
"از",
"طی",
"پس",
"چه",
"اگر",
"نه",
"آنها",
"هر",
"او",
"ما",
"من",
"تا",
"نیز",
"اما",
"یک",
"بر",
"هم",
"برای",
"کن",
"کرد",
"کردن",
"باش",
"بود",
"بودن",
"شو",
"شد",
"شدن",
"‏دار",
"داشت",
"داشتن",
"‏خواه",
"خواست",
"خواستن",
"‏گوی",
"گفت",
"گفتن",
"‏گیر",
"گرفت",
"گرفتن",
"‏آی",
"آمد",
"آمدن",
"‏توان",
"توانستن",
"‏یاب",
"یافتن",
"‏آور",
"آورد",
"آوردن",
"1",
"2",
"3",
"ص",
"4",
"و",
"5",
"ج",
"6",
"a",
"top",
"href",
"pageno"
],
"char_filter": []
}
},
"char_filter": {
"fa_char_filter": {
"type": "mapping",
"mappings": [
"٠ => 0",
"١ => 1",
"٢ => 2",
"٣ => 3",
"٤ => 4",
"٥ => 5",
"٦ => 6",
"٧ => 7",
"٨ => 8",
"٩ => 9",
"ک => ك",
"ی => ي",
"ة => ه",
"إ => ا",
"أ => ا",
"آ => ا",
"ء => ا",
"َ => ",
"ُ => ",
"ِ => ",
"ّ => ",
"ً => ",
"ٌ => ",
"ٍ => ",
"ْ => "
]
}
}
},
"number_of_shards": "1",
"number_of_replicas": "0"
}
}
}

View File

@ -0,0 +1,201 @@
{
"mappings": {
"date_detection": false,
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"user_id": {
"type": "integer"
},
"user_query": {
"type": "text"
},
"model_key": {
"type": "keyword"
},
"retrived_passage": {
"type": "text"
},
"retrived_ref_ids": {
"type": "keyword"
},
"retrived_duration": {
"type": "integer"
},
"prompt_type": {
"type": "keyword"
},
"llm_duration": {
"type": "integer"
},
"full_duration": {
"type": "integer"
},
"time_create": {
"type": "date"
},
"used_ref_ids": {
"type": "keyword"
},
"prompt_answer": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"settings": {
"index.max_result_window": 15000,
"index": {
"analysis": {
"analyzer": {
"phrase_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": []
},
"normal_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": [
"fa_stop"
]
}
},
"filter": {
"fa_stop": {
"type": "stop",
"stopwords": [
"یا",
"را",
"این",
"با",
"آن",
"و",
"در",
"به",
"که",
"از",
"طی",
"پس",
"چه",
"اگر",
"نه",
"آنها",
"هر",
"او",
"ما",
"من",
"تا",
"نیز",
"اما",
"یک",
"بر",
"هم",
"برای",
"کن",
"کرد",
"کردن",
"باش",
"بود",
"بودن",
"شو",
"شد",
"شدن",
"‏دار",
"داشت",
"داشتن",
"‏خواه",
"خواست",
"خواستن",
"‏گوی",
"گفت",
"گفتن",
"‏گیر",
"گرفت",
"گرفتن",
"‏آی",
"آمد",
"آمدن",
"‏توان",
"توانستن",
"‏یاب",
"یافتن",
"‏آور",
"آورد",
"آوردن",
"1",
"2",
"3",
"ص",
"4",
"و",
"5",
"ج",
"6",
"a",
"top",
"href",
"pageno"
],
"char_filter": []
}
},
"char_filter": {
"fa_char_filter": {
"type": "mapping",
"mappings": [
"٠ => 0",
"١ => 1",
"٢ => 2",
"٣ => 3",
"٤ => 4",
"٥ => 5",
"٦ => 6",
"٧ => 7",
"٨ => 8",
"٩ => 9",
"ک => ك",
"ی => ي",
"ة => ه",
"إ => ا",
"أ => ا",
"آ => ا",
"ء => ا",
"َ => ",
"ُ => ",
"ِ => ",
"ّ => ",
"ً => ",
"ٌ => ",
"ٍ => ",
"ْ => "
]
}
}
},
"number_of_shards": "1",
"number_of_replicas": "0"
}
}
}

View File

@ -0,0 +1,778 @@
{
"mappings": {
"date_detection": false,
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_fa",
"search_analyzer": "phrase_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"title_popular": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_fa",
"search_analyzer": "phrase_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
}
}
},
"embeddings": {
"type": "dense_vector",
"dims": 768,
"index": true,
"similarity": "cosine"
},
"initial": {
"properties": {
"title": {
"type": "text"
},
"content": {
"type": "text"
},
"approuve_type": {
"type": "keyword"
},
"creators": {
"type": "keyword"
},
"signers": {
"type": "keyword"
},
"approuve_number": {
"type": "text"
},
"approuve_date": {
"type": "date"
},
"letter_number": {
"type": "text"
},
"letter_date": {
"type": "date"
},
"letter_signer": {
"type": "keyword"
},
"ministers": {
"type": "keyword"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
},
"receive": {
"properties": {
"term_number": {
"type": "text"
},
"ref_law": {
"type": "keyword"
},
"meet_number": {
"type": "text"
},
"meet_date": {
"type": "date"
},
"register_number": {
"type": "text"
},
"publish_number": {
"type": "text"
},
"handle_type": {
"type": "keyword"
},
"handle_method": {
"type": "keyword"
}
}
},
"refer": {
"properties": {
"refer_date": {
"type": "date"
},
"commission_type": {
"type": "keyword"
},
"commission_main": {
"type": "keyword"
},
"commission_mirrors": {
"type": "keyword"
},
"commission_common": {
"properties": {
"name": {
"type": "keyword"
},
"member_count": {
"type": "integer"
},
"member_names": {
"type": "keyword"
}
}
},
"handle_85": {
"type": "keyword"
}
}
},
"commission_report": {
"properties": {
"agendas": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"meets": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"report_first": {
"properties": {
"approve_type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"report_nexts": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"duration_85": {
"type": "date"
},
"date_85": {
"type": "date"
}
}
}
}
},
"parl_handle": {
"properties": {
"agendas": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"meets": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"agree_names": {
"type": "keyword"
},
"against_names": {
"type": "keyword"
},
"demands": {
"type": "keyword"
},
"demand85_names": {
"type": "keyword"
},
"handle_infos": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"duration_85": {
"type": "date"
},
"date_85": {
"type": "date"
}
}
}
}
},
"before_info": {
"properties": {
"ref_id": {
"type": "keyword"
},
"plan_type": {
"type": "keyword"
},
"discuss_type": {
"type": "keyword"
},
"ministers": {
"type": "keyword"
},
"creators": {
"type": "text"
},
"register_number": {
"type": "text"
},
"prev_number": {
"type": "text"
},
"receipt_date": {
"type": "date"
},
"discuss_date1": {
"type": "date"
},
"discuss_date2": {
"type": "date"
},
"description": {
"type": "text"
},
"commission_type": {
"type": "keyword"
},
"commission_main": {
"type": "keyword"
},
"commission_mirrors": {
"type": "keyword"
},
"content": {
"type": "text"
}
}
},
"letters": {
"properties": {
"type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"order": {
"type": "float"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"description": {
"type": "text"
},
"content": {
"type": "text"
},
"delay_time": {
"type": "text"
},
"expire_date": {
"type": "date"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
},
"counts": {
"properties": {
"key": {
"type": "keyword"
},
"value": {
"type": "text"
}
}
},
"davam_type": {
"type": "keyword"
},
"number_all": {
"type": "text"
},
"number_row": {
"type": "text"
},
"archive_info": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"description": {
"type": "text"
}
}
},
"ts_date_dotic": {
"type": "date"
},
"ts_date1": {
"type": "date"
},
"ts_ref1": {
"type": "keyword"
},
"ts_date2": {
"type": "date"
},
"ts_ref2": {
"type": "keyword"
},
"conformity_rrk": {
"type": "keyword"
},
"conformity_qanonyar": {
"type": "keyword"
},
"content_ocr": {
"type": "text"
},
"qanon_etebar": {
"type": "keyword"
},
"expire_date": {
"type": "date"
},
"exec_date": {
"type": "date"
},
"ref_key": {
"type": "keyword"
},
"ts_date": {
"type": "date"
},
"ts_year": {
"type": "integer"
},
"ts_ref": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"term": {
"type": "keyword"
},
"term_number": {
"type": "integer"
},
"ranking_weight": {
"type": "integer"
},
"main_type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"title_type": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"action_type": {
"type": "keyword"
},
"section_len": {
"type": "integer"
},
"eblagh": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "text"
},
"from": {
"type": "keyword"
}
}
},
"rrk": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "text"
}
}
},
"exceuter_organs": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "keyword"
}
}
},
"receiver_organs": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "keyword"
}
}
},
"sort_date_timestamp": {
"type": "long"
},
"qanon_relations": {
"properties": {
"from_type": {
"type": "keyword"
},
"from_section_id": {
"type": "keyword"
},
"from_section_title": {
"type": "keyword"
},
"rel_type": {
"type": "keyword"
},
"to_type": {
"type": "keyword"
},
"to_id": {
"type": "keyword"
},
"to_title": {
"type": "keyword"
},
"wait": {
"type": "integer"
},
"rel_key": {
"type": "keyword"
}
}
},
"opinion_relations": {
"properties": {
"from_type": {
"type": "keyword"
},
"from_section_id": {
"type": "keyword"
},
"from_section_title": {
"type": "keyword"
},
"rel_type": {
"type": "keyword"
},
"to_type": {
"type": "keyword"
},
"to_id": {
"type": "keyword"
},
"to_title": {
"type": "keyword"
},
"wait": {
"type": "integer"
},
"rel_key": {
"type": "keyword"
}
}
},
"fixed_date": {
"type": "date"
},
"renewal_date": {
"type": "date"
},
"exec_duration": {
"type": "text"
},
"effective_date": {
"type": "date"
},
"relation_organs": {
"type": "keyword"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
},
"type": {
"type": "keyword"
}
}
},
"is_delete": {
"type": "integer"
},
"time_edit": {
"type": "date"
},
"doc_tags": {
"type": "keyword"
},
"doc_states": {
"type": "keyword"
}
}
},
"settings": {
"index.max_result_window": 15000,
"index": {
"analysis": {
"analyzer": {
"phrase_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": []
},
"normal_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": [
"fa_stop"
]
}
},
"filter": {
"fa_stop": {
"type": "stop",
"stopwords": [
"یا",
"را",
"این",
"با",
"آن",
"و",
"در",
"به",
"که",
"از",
"طی",
"پس",
"چه",
"اگر",
"نه",
"آنها",
"هر",
"او",
"ما",
"من",
"تا",
"نیز",
"اما",
"یک",
"بر",
"هم",
"برای",
"کن",
"کرد",
"کردن",
"باش",
"بود",
"بودن",
"شو",
"شد",
"شدن",
"‏دار",
"داشت",
"داشتن",
"‏خواه",
"خواست",
"خواستن",
"‏گوی",
"گفت",
"گفتن",
"‏گیر",
"گرفت",
"گرفتن",
"‏آی",
"آمد",
"آمدن",
"‏توان",
"توانستن",
"‏یاب",
"یافتن",
"‏آور",
"آورد",
"آوردن",
"1",
"2",
"3",
"ص",
"4",
"و",
"5",
"ج",
"6",
"a",
"top",
"href",
"pageno"
],
"char_filter": []
}
},
"char_filter": {
"fa_char_filter": {
"type": "mapping",
"mappings": [
"٠ => 0",
"١ => 1",
"٢ => 2",
"٣ => 3",
"٤ => 4",
"٥ => 5",
"٦ => 6",
"٧ => 7",
"٨ => 8",
"٩ => 9",
"ک => ك",
"ی => ي",
"ة => ه",
"إ => ا",
"أ => ا",
"آ => ا",
"ء => ا",
"َ => ",
"ُ => ",
"ِ => ",
"ّ => ",
"ً => ",
"ٌ => ",
"ٍ => ",
"ْ => "
]
}
}
},
"number_of_shards": "1",
"number_of_replicas": "0"
}
}
}

View File

@ -0,0 +1,774 @@
{
"mappings": {
"date_detection": false,
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_fa",
"search_analyzer": "phrase_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"keyword": {
"type": "keyword"
}
}
},
"title_popular": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_fa",
"search_analyzer": "phrase_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
}
}
},
"embeddings": {
"type": "dense_vector"
},
"initial": {
"properties": {
"title": {
"type": "text"
},
"content": {
"type": "text"
},
"approuve_type": {
"type": "keyword"
},
"creators": {
"type": "keyword"
},
"signers": {
"type": "keyword"
},
"approuve_number": {
"type": "text"
},
"approuve_date": {
"type": "date"
},
"letter_number": {
"type": "text"
},
"letter_date": {
"type": "date"
},
"letter_signer": {
"type": "keyword"
},
"ministers": {
"type": "keyword"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
},
"receive": {
"properties": {
"term_number": {
"type": "text"
},
"ref_law": {
"type": "keyword"
},
"meet_number": {
"type": "text"
},
"meet_date": {
"type": "date"
},
"register_number": {
"type": "text"
},
"publish_number": {
"type": "text"
},
"handle_type": {
"type": "keyword"
},
"handle_method": {
"type": "keyword"
}
}
},
"refer": {
"properties": {
"refer_date": {
"type": "date"
},
"commission_type": {
"type": "keyword"
},
"commission_main": {
"type": "keyword"
},
"commission_mirrors": {
"type": "keyword"
},
"commission_common": {
"properties": {
"name": {
"type": "keyword"
},
"member_count": {
"type": "integer"
},
"member_names": {
"type": "keyword"
}
}
},
"handle_85": {
"type": "keyword"
}
}
},
"commission_report": {
"properties": {
"agendas": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"meets": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"report_first": {
"properties": {
"approve_type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"report_nexts": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"duration_85": {
"type": "date"
},
"date_85": {
"type": "date"
}
}
}
}
},
"parl_handle": {
"properties": {
"agendas": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"meets": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"agree_names": {
"type": "keyword"
},
"against_names": {
"type": "keyword"
},
"demands": {
"type": "keyword"
},
"demand85_names": {
"type": "keyword"
},
"handle_infos": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"duration_85": {
"type": "date"
},
"date_85": {
"type": "date"
}
}
}
}
},
"before_info": {
"properties": {
"ref_id": {
"type": "keyword"
},
"plan_type": {
"type": "keyword"
},
"discuss_type": {
"type": "keyword"
},
"ministers": {
"type": "keyword"
},
"creators": {
"type": "text"
},
"register_number": {
"type": "text"
},
"prev_number": {
"type": "text"
},
"receipt_date": {
"type": "date"
},
"discuss_date1": {
"type": "date"
},
"discuss_date2": {
"type": "date"
},
"description": {
"type": "text"
},
"commission_type": {
"type": "keyword"
},
"commission_main": {
"type": "keyword"
},
"commission_mirrors": {
"type": "keyword"
},
"content": {
"type": "text"
}
}
},
"letters": {
"properties": {
"type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"order": {
"type": "float"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"description": {
"type": "text"
},
"content": {
"type": "text"
},
"delay_time": {
"type": "text"
},
"expire_date": {
"type": "date"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
},
"counts": {
"properties": {
"key": {
"type": "keyword"
},
"value": {
"type": "text"
}
}
},
"davam_type": {
"type": "keyword"
},
"number_all": {
"type": "text"
},
"number_row": {
"type": "text"
},
"archive_info": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"description": {
"type": "text"
}
}
},
"ts_date_dotic": {
"type": "date"
},
"ts_date1": {
"type": "date"
},
"ts_ref1": {
"type": "keyword"
},
"ts_date2": {
"type": "date"
},
"ts_ref2": {
"type": "keyword"
},
"conformity_rrk": {
"type": "keyword"
},
"conformity_qanonyar": {
"type": "keyword"
},
"content_ocr": {
"type": "text"
},
"qanon_etebar": {
"type": "keyword"
},
"expire_date": {
"type": "date"
},
"exec_date": {
"type": "date"
},
"ref_key": {
"type": "keyword"
},
"ts_date": {
"type": "date"
},
"ts_year": {
"type": "integer"
},
"ts_ref": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"term": {
"type": "keyword"
},
"term_number": {
"type": "integer"
},
"ranking_weight": {
"type": "integer"
},
"main_type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"title_type": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"action_type": {
"type": "keyword"
},
"section_len": {
"type": "integer"
},
"eblagh": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "text"
},
"from": {
"type": "keyword"
}
}
},
"rrk": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "text"
}
}
},
"exceuter_organs": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "keyword"
}
}
},
"receiver_organs": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "keyword"
}
}
},
"sort_date_timestamp": {
"type": "long"
},
"qanon_relations": {
"properties": {
"from_type": {
"type": "keyword"
},
"from_section_id": {
"type": "keyword"
},
"from_section_title": {
"type": "keyword"
},
"rel_type": {
"type": "keyword"
},
"to_type": {
"type": "keyword"
},
"to_id": {
"type": "keyword"
},
"to_title": {
"type": "keyword"
},
"wait": {
"type": "integer"
},
"rel_key": {
"type": "keyword"
}
}
},
"opinion_relations": {
"properties": {
"from_type": {
"type": "keyword"
},
"from_section_id": {
"type": "keyword"
},
"from_section_title": {
"type": "keyword"
},
"rel_type": {
"type": "keyword"
},
"to_type": {
"type": "keyword"
},
"to_id": {
"type": "keyword"
},
"to_title": {
"type": "keyword"
},
"wait": {
"type": "integer"
},
"rel_key": {
"type": "keyword"
}
}
},
"fixed_date": {
"type": "date"
},
"renewal_date": {
"type": "date"
},
"exec_duration": {
"type": "text"
},
"effective_date": {
"type": "date"
},
"relation_organs": {
"type": "keyword"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
},
"type": {
"type": "keyword"
}
}
},
"is_delete": {
"type": "integer"
},
"time_edit": {
"type": "date"
},
"doc_tags": {
"type": "keyword"
},
"doc_states": {
"type": "keyword"
}
}
},
"settings": {
"index.max_result_window": 15000,
"index": {
"analysis": {
"analyzer": {
"phrase_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": []
},
"normal_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": [
"fa_stop"
]
}
},
"filter": {
"fa_stop": {
"type": "stop",
"stopwords": [
"یا",
"را",
"این",
"با",
"آن",
"و",
"در",
"به",
"که",
"از",
"طی",
"پس",
"چه",
"اگر",
"نه",
"آنها",
"هر",
"او",
"ما",
"من",
"تا",
"نیز",
"اما",
"یک",
"بر",
"هم",
"برای",
"کن",
"کرد",
"کردن",
"باش",
"بود",
"بودن",
"شو",
"شد",
"شدن",
"‏دار",
"داشت",
"داشتن",
"‏خواه",
"خواست",
"خواستن",
"‏گوی",
"گفت",
"گفتن",
"‏گیر",
"گرفت",
"گرفتن",
"‏آی",
"آمد",
"آمدن",
"‏توان",
"توانستن",
"‏یاب",
"یافتن",
"‏آور",
"آورد",
"آوردن",
"1",
"2",
"3",
"ص",
"4",
"و",
"5",
"ج",
"6",
"a",
"top",
"href",
"pageno"
],
"char_filter": []
}
},
"char_filter": {
"fa_char_filter": {
"type": "mapping",
"mappings": [
"٠ => 0",
"١ => 1",
"٢ => 2",
"٣ => 3",
"٤ => 4",
"٥ => 5",
"٦ => 6",
"٧ => 7",
"٨ => 8",
"٩ => 9",
"ک => ك",
"ی => ي",
"ة => ه",
"إ => ا",
"أ => ا",
"آ => ا",
"ء => ا",
"َ => ",
"ُ => ",
"ِ => ",
"ّ => ",
"ً => ",
"ٌ => ",
"ٍ => ",
"ْ => "
]
}
}
},
"number_of_shards": "1",
"number_of_replicas": "0"
}
}
}

View File

@ -0,0 +1,777 @@
{
"mappings": {
"date_detection": false,
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_fa",
"search_analyzer": "phrase_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"keyword": {
"type": "keyword"
}
}
},
"title_popular": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_fa",
"search_analyzer": "phrase_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
}
}
},
"embeddings": {
"type": "dense_vector"
},
"initial": {
"properties": {
"title": {
"type": "text"
},
"content": {
"type": "text"
},
"approuve_type": {
"type": "keyword"
},
"creators": {
"type": "keyword"
},
"signers": {
"type": "keyword"
},
"approuve_number": {
"type": "text"
},
"approuve_date": {
"type": "date"
},
"letter_number": {
"type": "text"
},
"letter_date": {
"type": "date"
},
"letter_signer": {
"type": "keyword"
},
"ministers": {
"type": "keyword"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
},
"receive": {
"properties": {
"term_number": {
"type": "text"
},
"ref_law": {
"type": "keyword"
},
"meet_number": {
"type": "text"
},
"meet_date": {
"type": "date"
},
"register_number": {
"type": "text"
},
"publish_number": {
"type": "text"
},
"handle_type": {
"type": "keyword"
},
"handle_method": {
"type": "keyword"
}
}
},
"refer": {
"properties": {
"refer_date": {
"type": "date"
},
"commission_type": {
"type": "keyword"
},
"commission_main": {
"type": "keyword"
},
"commission_mirrors": {
"type": "keyword"
},
"commission_common": {
"properties": {
"name": {
"type": "keyword"
},
"member_count": {
"type": "integer"
},
"member_names": {
"type": "keyword"
}
}
},
"handle_85": {
"type": "keyword"
}
}
},
"commission_report": {
"properties": {
"agendas": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"meets": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"report_first": {
"properties": {
"approve_type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"report_nexts": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"duration_85": {
"type": "date"
},
"date_85": {
"type": "date"
}
}
}
}
},
"parl_handle": {
"properties": {
"agendas": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"meets": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"agree_names": {
"type": "keyword"
},
"against_names": {
"type": "keyword"
},
"demands": {
"type": "keyword"
},
"demand85_names": {
"type": "keyword"
},
"handle_infos": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"duration_85": {
"type": "date"
},
"date_85": {
"type": "date"
}
}
}
}
},
"before_info": {
"properties": {
"ref_id": {
"type": "keyword"
},
"plan_type": {
"type": "keyword"
},
"discuss_type": {
"type": "keyword"
},
"ministers": {
"type": "keyword"
},
"creators": {
"type": "text"
},
"register_number": {
"type": "text"
},
"prev_number": {
"type": "text"
},
"receipt_date": {
"type": "date"
},
"discuss_date1": {
"type": "date"
},
"discuss_date2": {
"type": "date"
},
"description": {
"type": "text"
},
"commission_type": {
"type": "keyword"
},
"commission_main": {
"type": "keyword"
},
"commission_mirrors": {
"type": "keyword"
},
"content": {
"type": "text"
}
}
},
"letters": {
"properties": {
"id": {
"type": "keyword"
},
"type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"order": {
"type": "float"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"description": {
"type": "text"
},
"content": {
"type": "text"
},
"delay_time": {
"type": "text"
},
"expire_date": {
"type": "date"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
},
"counts": {
"properties": {
"key": {
"type": "keyword"
},
"value": {
"type": "text"
}
}
},
"davam_type": {
"type": "keyword"
},
"number_all": {
"type": "text"
},
"number_row": {
"type": "text"
},
"archive_info": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"description": {
"type": "text"
}
}
},
"ts_date_dotic": {
"type": "date"
},
"ts_date1": {
"type": "date"
},
"ts_ref1": {
"type": "keyword"
},
"ts_date2": {
"type": "date"
},
"ts_ref2": {
"type": "keyword"
},
"conformity_rrk": {
"type": "keyword"
},
"conformity_qanonyar": {
"type": "keyword"
},
"content_ocr": {
"type": "text"
},
"qanon_etebar": {
"type": "keyword"
},
"expire_date": {
"type": "date"
},
"exec_date": {
"type": "date"
},
"ref_key": {
"type": "keyword"
},
"ts_date": {
"type": "date"
},
"ts_year": {
"type": "integer"
},
"ts_ref": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"term": {
"type": "keyword"
},
"term_number": {
"type": "integer"
},
"ranking_weight": {
"type": "integer"
},
"main_type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"title_type": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"action_type": {
"type": "keyword"
},
"section_len": {
"type": "integer"
},
"eblagh": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "text"
},
"from": {
"type": "keyword"
}
}
},
"rrk": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "text"
}
}
},
"exceuter_organs": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "keyword"
}
}
},
"receiver_organs": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "keyword"
}
}
},
"sort_date_timestamp": {
"type": "long"
},
"qanon_relations": {
"properties": {
"from_type": {
"type": "keyword"
},
"from_section_id": {
"type": "keyword"
},
"from_section_title": {
"type": "keyword"
},
"rel_type": {
"type": "keyword"
},
"to_type": {
"type": "keyword"
},
"to_id": {
"type": "keyword"
},
"to_title": {
"type": "keyword"
},
"wait": {
"type": "integer"
},
"rel_key": {
"type": "keyword"
}
}
},
"opinion_relations": {
"properties": {
"from_type": {
"type": "keyword"
},
"from_section_id": {
"type": "keyword"
},
"from_section_title": {
"type": "keyword"
},
"rel_type": {
"type": "keyword"
},
"to_type": {
"type": "keyword"
},
"to_id": {
"type": "keyword"
},
"to_title": {
"type": "keyword"
},
"wait": {
"type": "integer"
},
"rel_key": {
"type": "keyword"
}
}
},
"fixed_date": {
"type": "date"
},
"renewal_date": {
"type": "date"
},
"exec_duration": {
"type": "text"
},
"effective_date": {
"type": "date"
},
"relation_organs": {
"type": "keyword"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
},
"type": {
"type": "keyword"
}
}
},
"is_delete": {
"type": "integer"
},
"time_edit": {
"type": "date"
},
"doc_tags": {
"type": "keyword"
},
"doc_states": {
"type": "keyword"
}
}
},
"settings": {
"index.max_result_window": 15000,
"index": {
"analysis": {
"analyzer": {
"phrase_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": []
},
"normal_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": [
"fa_stop"
]
}
},
"filter": {
"fa_stop": {
"type": "stop",
"stopwords": [
"یا",
"را",
"این",
"با",
"آن",
"و",
"در",
"به",
"که",
"از",
"طی",
"پس",
"چه",
"اگر",
"نه",
"آنها",
"هر",
"او",
"ما",
"من",
"تا",
"نیز",
"اما",
"یک",
"بر",
"هم",
"برای",
"کن",
"کرد",
"کردن",
"باش",
"بود",
"بودن",
"شو",
"شد",
"شدن",
"‏دار",
"داشت",
"داشتن",
"‏خواه",
"خواست",
"خواستن",
"‏گوی",
"گفت",
"گفتن",
"‏گیر",
"گرفت",
"گرفتن",
"‏آی",
"آمد",
"آمدن",
"‏توان",
"توانستن",
"‏یاب",
"یافتن",
"‏آور",
"آورد",
"آوردن",
"1",
"2",
"3",
"ص",
"4",
"و",
"5",
"ج",
"6",
"a",
"top",
"href",
"pageno"
],
"char_filter": []
}
},
"char_filter": {
"fa_char_filter": {
"type": "mapping",
"mappings": [
"٠ => 0",
"١ => 1",
"٢ => 2",
"٣ => 3",
"٤ => 4",
"٥ => 5",
"٦ => 6",
"٧ => 7",
"٨ => 8",
"٩ => 9",
"ک => ك",
"ی => ي",
"ة => ه",
"إ => ا",
"أ => ا",
"آ => ا",
"ء => ا",
"َ => ",
"ُ => ",
"ِ => ",
"ّ => ",
"ً => ",
"ٌ => ",
"ٍ => ",
"ْ => "
]
}
}
},
"number_of_shards": "1",
"number_of_replicas": "0"
}
}
}

View File

@ -0,0 +1,170 @@
{
"index": {
"name": "mj_qa_domain",
"aliases": [],
"index_key": "mjdomain"
},
"properties": {
"domain_id": {
"type": "keyword"
},
"id": {
"type": "keyword"
},
"main_type": {
"type": "keyword"
},
"relevancies": {
"properties": {
"domain_id": {
"type": "keyword"
},
"domain_name": {
"type": "keyword"
},
"domain_title": {
"type": "keyword"
},
"relevancy_percentage": {
"type": "float"
},
"relevancy_score": {
"type": "float"
}
}
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"top_qanons": {
"properties": {
"importance_score": {
"type": "float"
},
"max_label": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"max_score": {
"type": "float"
},
"qanon_id": {
"type": "keyword"
},
"qanon_title": {
"type": "keyword"
},
"scaled_importance": {
"type": "float"
}
}
},
"general_words": {
"properties": {
"word": {
"type": "keyword"
},
"count": {
"type": "integer"
}
}
},
"own_words": {
"properties": {
"word": {
"type": "keyword"
},
"count": {
"type": "integer"
}
}
},
"code_words": {
"properties": {
"domain_id": {
"type": "keyword"
},
"domain_name": {
"type": "keyword"
},
"shared_count": {
"type": "integer"
},
"union_words": {
"properties": {
"word": {
"type": "keyword"
},
"count": {
"type": "integer"
}
}
},
"delta_words": {
"properties": {
"word": {
"type": "keyword"
},
"count": {
"type": "integer"
}
}
},
"count": {
"type": "integer"
}
}
}
},
"validation": {
"required": []
},
"include_fields": [],
"exclude_fields": [
"general_words", "own_words", "code_words"
],
"is_array": [
"relevancies",
"top_qanons",
"general_words",
"own_words",
"code_words"
],
"query": {
"default_sort": [
{
"_score": "desc"
},
{
"domain_id": "asc"
}
],
"normal": {
"phrase": {
"title": 12,
"relevancies.domain_name": 10,
"top_qanons.qanon_title": 9
},
"match": {
"title": 4,
"relevancies.domain_name": 2,
"top_qanons.qanon_title": 2
}
},
"filter_keys": {
"f_mt": "main_type",
"f_di": "domain_id"
}
}
}

View File

@ -0,0 +1,115 @@
{
"index": {
"name": "mj_plan",
"aliases": [],
"index_key": "mjplan"
},
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "text"
},
"tcode": {
"type": "keyword"
},
"version_key": {
"type": "keyword"
},
"parent_id": {
"type": "keyword"
},
"child_order": {
"type": "float"
},
"full_path": {
"type": "text"
},
"level": {
"type": "integer"
},
"ai_section_ids": {
"type": "keyword"
},
"section_ids": {
"type": "keyword"
},
"user_actions": {
"properties": {
"user_id": {
"type": "integer"
},
"time_edit": {
"type": "date"
},
"description": {
"type": "text"
},
"property_key": {
"type": "keyword"
},
"action_key": {
"type": "keyword"
},
"action_value": {
"type": "text"
},
"action": {
"type": "object"
}
}
},
"time_create": {
"type": "date"
},
"time_edit": {
"type": "date"
}
},
"validation": {
"required": [
"version_key",
"tcode",
"title"
]
},
"include_fields": [],
"exclude_fields": [],
"is_array": [
"section_ids",
"ai_section_ids",
"user_actions"
],
"query": {
"default_sort": [
{
"_score": "desc"
},
{
"time_create": "desc"
}
],
"normal": {
"phrase": {
"title": 6,
"content": 4
},
"match": {
"title": 3,
"content": 2
}
},
"filter_keys": {
}
}
}

View File

@ -0,0 +1,141 @@
{
"index": {
"name": "mj_plan_section",
"aliases": [],
"index_key": "mjpsection"
},
"properties": {
"id": {
"type": "keyword"
},
"plan_id": {
"type": "keyword"
},
"tcode": {
"type": "keyword"
},
"version_key": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "text"
},
"footnotes": {
"properties": {
"text": {
"type": "text"
},
"main_type": {
"type": "keyword"
},
"refrence_ids": {
"type": "keyword"
}
}
},
"section_info": {
"properties": {
"qanon_title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"full_path": {
"type": "text"
},
"ts_date": {
"type": "date"
},
"ts_ref": {
"type": "keyword"
},
"state_etebar": {
"type": "keyword"
},
"content": {
"type": "text"
}
}
},
"user_actions": {
"properties": {
"user_id": {
"type": "integer"
},
"time_edit": {
"type": "date"
},
"description": {
"type": "text"
},
"property_key": {
"type": "keyword"
},
"action_key": {
"type": "keyword"
},
"action_value": {
"type": "text"
},
"action": {
"type": "object"
}
}
},
"time_create": {
"type": "date"
},
"time_edit": {
"type": "date"
}
},
"validation": {
"required": [
"plan_id",
"tcode"
]
},
"include_fields": [],
"exclude_fields": [],
"is_array": [
"footnotes",
"user_actions"
],
"query": {
"default_sort": [
{
"_score": "desc"
},
{
"time_create": "desc"
}
],
"normal": {
"phrase": {
"title": 6,
"content": 4,
"section.content": 4
},
"match": {
"title": 3,
"content": 2,
"section.content": 2
}
},
"filter_keys": {
}
}
}

View File

@ -0,0 +1,86 @@
{
"index": {
"name": "mn_term",
"aliases": [],
"index_key": "mn_term"
},
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_persian",
"search_analyzer": "normal_analyzer_persian",
"search_quote_analyzer": "phrase_analyzer_persian"
},
"ar": {
"type": "text",
"analyzer": "normal_analyzer_arabic",
"search_analyzer": "normal_analyzer_arabic",
"search_quote_analyzer": "phrase_analyzer_arabic"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_persian",
"search_analyzer": "phrase_analyzer_persian",
"search_quote_analyzer": "phrase_analyzer_persian"
}
}
},
"author": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"main_type": {
"type": "keyword"
},
"begin_date": {
"type": "date"
},
"end_date": {
"type": "date"
},
"begin_year": {
"type": "integer"
},
"tags": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"keywords": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"format": {
"type": "keyword"
}
}
}

View File

@ -0,0 +1,154 @@
{
"index": {
"name": "mn_spr_sanad",
"aliases": [],
"index_key": "mnvoice"
},
"properties": {
"ref_id": {
"type": "keyword"
},
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"author": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"branch": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"tags": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"begin_year": {
"type": "integer",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 20
}
}
},
"format": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"sound_title": {
"type": "keyword"
},
"sound_link": {
"type": "keyword"
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text"
},
"ph": {
"type": "text"
}
}
},
"time_words": {
"properties": {
"start_offset": {
"type": "integer"
},
"end_offset": {
"type": "integer"
},
"word": {
"type": "text"
},
"start": {
"type": "float"
},
"end": {
"type": "float"
}
}
}
},
"validation": {
"required": []
},
"include_fields": [],
"is_array": [
"time_words"
],
"query": {
"default_sort": [
{
"_score": "desc"
},
{
"ref_id": "asc"
},
{
"sound_link": "asc"
}
],
"normal": {
"phrase": {
"content.ph": 4
},
"match": {
"content": 2
}
},
"filter_keys": {
"f_au": "author",
"f_tg": "tags",
"f_br": "branch",
"f_fo": "format",
"f_ye": "begin_year"
},
"highlight": {
"pre_tags": [
"<span class='text__orange'>"
],
"post_tags": [
"</span>"
],
"fields": {
"title.fa": {},
"title.ph": {},
"content.fa": {},
"content.ph": {}
}
}
}
}

View File

@ -0,0 +1,275 @@
{
"main_type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"id": {
"type": "keyword"
},
"person_code": {
"type": "keyword"
},
"research_code": {
"type": "keyword"
},
"meet_code": {
"type": "keyword"
},
"old_meet_id": {
"type": "integer"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"meet_no": {
"type": "integer"
},
"term_info": {
"properties": {
"id": {
"type": "keyword"
},
"format": {
"type": "keyword"
},
"author": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"subtitle": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"subjects": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"allwords": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"tags": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"keywords": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"verb": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"sanad_year": {
"type": "integer"
},
"sanad_date": {
"type": "date"
},
"amplify": {
"type": "text"
},
"ralation": {
"type": "keyword"
},
"city": {
"type": "keyword"
},
"place": {
"type": "keyword"
},
"address": {
"type": "text"
},
"audience": {
"type": "text"
},
"attendees": {
"type": "text"
},
"report_info": {
"properties": {
"film_count": {
"type": "integer"
},
"sound_count": {
"type": "integer"
},
"photo_count": {
"type": "integer"
},
"file_count": {
"type": "integer"
},
"is_address": {
"type": "integer"
},
"is_mindex": {
"type": "integer"
},
"is_mintro": {
"type": "integer"
},
"is_sanad_data1": {
"type": "integer"
},
"is_sanad_data2": {
"type": "integer"
}
}
},
"mindex": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_persian",
"search_analyzer": "normal_analyzer_persian",
"search_quote_analyzer": "phrase_analyzer_persian"
},
"ar": {
"type": "text",
"analyzer": "normal_analyzer_arabic",
"search_analyzer": "normal_analyzer_arabic",
"search_quote_analyzer": "phrase_analyzer_arabic"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_persian",
"search_analyzer": "phrase_analyzer_persian",
"search_quote_analyzer": "phrase_analyzer_persian"
}
}
},
"mintro": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_persian",
"search_analyzer": "normal_analyzer_persian",
"search_quote_analyzer": "phrase_analyzer_persian"
},
"ar": {
"type": "text",
"analyzer": "normal_analyzer_arabic",
"search_analyzer": "normal_analyzer_arabic",
"search_quote_analyzer": "phrase_analyzer_arabic"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_persian",
"search_analyzer": "phrase_analyzer_persian",
"search_quote_analyzer": "phrase_analyzer_persian"
}
}
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_persian",
"search_analyzer": "normal_analyzer_persian",
"search_quote_analyzer": "phrase_analyzer_persian"
},
"ar": {
"type": "text",
"analyzer": "normal_analyzer_arabic",
"search_analyzer": "normal_analyzer_arabic",
"search_quote_analyzer": "phrase_analyzer_arabic"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_persian",
"search_analyzer": "phrase_analyzer_persian",
"search_quote_analyzer": "phrase_analyzer_persian"
}
}
},
"completion": {
"type": "completion",
"analyzer": "simple",
"preserve_separators": true,
"preserve_position_increments": true,
"max_input_length": 50
},
"sort_date_timestamp": {
"type": "long"
},
"permit_tags": {
"type": "keyword"
},
"resource_info": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_persian",
"search_analyzer": "normal_analyzer_persian",
"search_quote_analyzer": "phrase_analyzer_persian"
}
}
}
}

View File

@ -0,0 +1,130 @@
{
"main_type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_persian",
"search_analyzer": "normal_analyzer_persian",
"search_quote_analyzer": "phrase_analyzer_persian"
},
"ar": {
"type": "text",
"analyzer": "normal_analyzer_arabic",
"search_analyzer": "normal_analyzer_arabic",
"search_quote_analyzer": "phrase_analyzer_arabic"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_persian",
"search_analyzer": "phrase_analyzer_persian",
"search_quote_analyzer": "phrase_analyzer_persian"
}
}
},
"permit_tags": {
"type": "keyword"
},
"search_state": {
"type": "keyword"
},
"user_create": {
"type": "keyword"
},
"time_create": {
"type": "date"
},
"time_edit": {
"type": "date"
},
"file_links": {
"properties": {
"title": {
"type": "keyword"
},
"link": {
"type": "text"
},
"type": {
"type": "text"
},
"description": {
"type": "text"
}
}
},
"meet_info": {
"properties": {
"id": {
"type": "keyword"
},
"format": {
"type": "keyword"
},
"author": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"term_info": {
"properties": {
"id": {
"type": "keyword"
},
"format": {
"type": "keyword"
},
"author": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}

View File

@ -0,0 +1,208 @@
{
"main_type": {
"type": "keyword"
},
"id": {
"type": "keyword"
},
"html": {
"type": "text"
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_fa",
"search_analyzer": "phrase_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
}
}
},
"meet_info": {
"properties": {
"id": {
"type": "keyword"
},
"format": {
"type": "keyword"
},
"author": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"term_info": {
"properties": {
"id": {
"type": "keyword"
},
"format": {
"type": "keyword"
},
"author": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"tree_info": {
"properties": {
"parent_id": {
"type": "keyword"
},
"child_order": {
"type": "double"
},
"level": {
"type": "integer"
},
"full_path": {
"type": "text"
},
"title": {
"type": "text"
},
"path_headings": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"content_len": {
"type": "integer"
},
"word_len": {
"type": "integer"
},
"tags": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"keywords": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"sort_date_timestamp": {
"type": "long"
},
"nlp_parses": {
"properties": {
"main_type": {
"type": "keyword"
},
"nlp_type": {
"type": "keyword"
},
"begin": {
"type": "integer"
},
"end": {
"type": "integer"
},
"text": {
"type": "text"
},
"referes": {
"type": "keyword"
},
"dependency_infos": {
"type": "nested"
}
}
},
"embeddings": {
"type": "dense_vector",
"dims": 768,
"index": true,
"similarity": "cosine"
},
"file_links": {
"properties": {
"title": {
"type": "keyword"
},
"link": {
"type": "text"
},
"type": {
"type": "text"
}
}
},
"time_edit": {
"type": "date"
},
"user_edit": {
"type": "integer"
},
"user_logs": {
"properties": {
"id": {
"type": "keyword"
},
"user_id": {
"type": "integer"
},
"username": {
"type": "keyword"
},
"time_edit": {
"type": "date"
},
"property": {
"type": "keyword"
}
}
}
}

View File

@ -0,0 +1,79 @@
{
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text",
"analyzer": "normal_analyzer_persian",
"search_analyzer": "normal_analyzer_persian",
"search_quote_analyzer": "phrase_analyzer_persian"
},
"ar": {
"type": "text",
"analyzer": "normal_analyzer_arabic",
"search_analyzer": "normal_analyzer_arabic",
"search_quote_analyzer": "phrase_analyzer_arabic"
},
"ph": {
"type": "text",
"analyzer": "phrase_analyzer_persian",
"search_analyzer": "phrase_analyzer_persian",
"search_quote_analyzer": "phrase_analyzer_persian"
}
}
},
"author": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"main_type": {
"type": "keyword"
},
"begin_date": {
"type": "date"
},
"end_date": {
"type": "date"
},
"begin_year": {
"type": "integer"
},
"tags": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"keywords": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"format": {
"type": "keyword"
}
}

View File

@ -0,0 +1,49 @@
{
"index": {
"name": "mn_group",
"aliases": [],
"index_key": "mngroup"
},
"properties": {
"id": {
"type": "keyword"
},
"main_type": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "content"
},
"user_id": {
"type": "integer"
},
"time_create": {
"type": "date"
},
"time_edit": {
"type": "date"
}
},
"validation": {
"required": [
],
"default_value": {
}
},
"include_fields": [],
"exclude_fields": [],
"is_array": [
]
}

View File

@ -0,0 +1,49 @@
{
"index": {
"name": "mn_meet",
"aliases": [],
"index_key": "mnmeet"
},
"properties": {
"id": {
"type": "keyword"
},
"main_type": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "content"
},
"user_id": {
"type": "integer"
},
"time_create": {
"type": "date"
},
"time_edit": {
"type": "date"
}
},
"validation": {
"required": [
],
"default_value": {
}
},
"include_fields": [],
"exclude_fields": [],
"is_array": [
]
}

View File

@ -0,0 +1,207 @@
{
"index_name": "mj_qa_qanon",
"file_map": "mj_qa_qanon.json",
"index_key": "qaqanon",
"fields": [
{
"key": "id",
"label": "شناسه",
"is_array": 0,
"is_sortable": 1,
"is_need_keyword": 0,
"filter_key": "",
"filter_by_more": 0,
"export_state": 1,
"is_autoid": 0
},
{
"key": "title",
"lable": "عنوان",
"is_sortable": 1,
"is_need_keyword": 1,
"query_normal_boost": 3
},
{
"key": "content",
"lable": "متن اصلی",
"is_sortable": 1,
"is_need_keyword": 1,
"query_normal_boost": 1
},
{
"key": "embeddings",
"label": "بردار معنایی",
"export_state": 0
},
{
"key": "qanon_etebar",
"lable": "اعتبار قانون",
"is_sortable": 1
},
{
"key": "expire_date",
"lable": "تاریخ انقضاء",
"is_sortable": 1
},
{
"key": "fixed_date",
"lable": "تاریخ دائمی شدن",
"is_sortable": 1
},
{
"key": "renewal_date",
"lable": "تاریخ تمدید",
"is_sortable": 1
},
{
"key": "renewal_date",
"lable": "تاریخ تمدید",
"is_sortable": 1
},
{
"key": "exec_duration",
"lable": "مدت اجراء"
},
{
"key": "ref_key",
"lable": "منبع اخذ داده",
"is_sortable": 1,
"export_state": 0
},
{
"key": "ts_date",
"lable": "تاریخ تصویب",
"is_sortable": 1,
"join_to": [
{
"process": "jalaliTotsmp",
"property": "sort_date_timestamp",
"desc": "jalaliTotsmp(\"ts_date\",\"/\")"
},
{
"process": "getYearOfDate",
"property": "ts_year",
"desc": "1403/05/06 --> 1403"
}
]
},
{
"key": "ts_year",
"lable": "سال تصویب",
"is_sortable": 1
},
{
"key": "ts_ref",
"lable": "مرجع تصویب",
"is_sortable": 1,
"is_need_keyword": 1
},
{
"key": "term",
"lable": "عنوان دوره",
"is_sortable": 1
},
{
"key": "term_number",
"lable": "شماره دوره",
"is_sortable": 1
},
{
"key": "main_type",
"lable": "نوع اصلی/تبعی",
"is_sortable": 1
},
{
"key": "sub_type",
"lable": "نوع",
"is_sortable": 1
},
{
"key": "title_type",
"lable": "نوع قالب",
"is_sortable": 1,
"is_need_keyword": 1
},
{
"key": "section_len",
"lable": "تعداد حرف",
"export_state": 0
},
{
"key": "exceuter_organs",
"lable": "سازمان مجری",
"is_array": 1,
"is_object": 1,
"un_repeat_keys": ["id"]
},
{
"key": "receiver_organs",
"lable": "سازمان‌های دریافت کننده",
"is_array": 1,
"is_object": 1,
"un_repeat_keys": ["id"]
},
{
"key": "sort_date_timestamp",
"lable": "تاریخ مرتب سازی",
"export_state": 0
},
{
"key": "qanon_relations",
"lable": "قوانین مرتبط",
"is_array": 1,
"is_object": 1,
"un_repeat_keys": ["to_id", "from_section_id", "from_type"],
"export_state": 0
},
{
"key": "opinion_relations",
"lable": "آراء مرتبط",
"is_array": 1,
"is_object": 1,
"un_repeat_keys": ["to_id", "from_section_id", "from_type"],
"export_state": 0
},
{
"key": "before_info",
"lable": "اطلاعات پیشا قانون",
"is_array": 0,
"is_object": 1,
"un_repeat_keys": ["ref_id"],
"export_state": 0
},
{
"key": "letters",
"lable": "ارجاعات و ابلاغات",
"is_array": 1,
"is_object": 1,
"un_repeat_keys": ["type", "date", "number"],
"export_state": 0
},
{
"key": "is_delete",
"lable": "وضعیت حذف",
"is_sortable": 1,
"export_state": 0
},
{
"key": "time_edit",
"lable": "زمان ویرایش",
"is_sortable": 1
},
{
"key": "doc_tags",
"lable": "ویژگیهای سند",
"is_sortable": 1,
"is_array": 1,
"is_object": 0
},
{
"key": "doc_states",
"lable": "برچسبهای وضعیت سند",
"is_sortable": 1,
"is_array": 1,
"is_object": 0
}
]
}

View File

@ -0,0 +1,345 @@
{
"index": {
"name": "test_index",
"aliases": [
"test_index_alias"
],
"index_key": "test",
"file_map": "backend_map.json"
},
"properties": {
"id": {
"type": "keyword",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": true,
"join_to": [],
"label": "شناسه",
"filter_key": "",
"is_autoid": false
},
"title": {
"type": "text",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": true,
"query_boost": 2.0,
"query_normal_boost": 3.0,
"needs_keyword": true,
"required": true,
"join_to": [],
"label": "عنوان",
"filter_key": "",
"is_autoid": false
},
"content": {
"type": "text",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": true,
"query_boost": 1.0,
"query_normal_boost": 1.0,
"needs_keyword": true,
"required": false,
"join_to": [],
"label": "متن اصلی",
"filter_key": "",
"is_autoid": false
},
"embeddings": {
"type": "dense_vector",
"return": false,
"sortable": false,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "بردار معنایی",
"filter_key": "",
"is_autoid": false
},
"category": {
"type": "keyword",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 1.5,
"query_normal_boost": 1.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "دسته‌بندی",
"filter_key": "",
"is_autoid": false
},
"tags": {
"type": "keyword",
"return": true,
"sortable": false,
"array": true,
"object": false,
"highlight": false,
"query_boost": 1.0,
"query_normal_boost": 0.8,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "برچسب‌ها",
"filter_key": "",
"is_autoid": false
},
"author": {
"type": "object",
"return": true,
"sortable": false,
"array": false,
"object": true,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "نویسنده",
"filter_key": "",
"is_autoid": false,
"un_repeat_keys": [
"id"
]
},
"status": {
"type": "keyword",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "وضعیت",
"filter_key": "",
"is_autoid": false
},
"created_date": {
"type": "date",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [
{
"process": "jalaliTotsmp",
"property": "sort_date_timestamp",
"desc": "jalaliTotsmp(\"created_date\",\"/\")"
},
{
"process": "getYearOfDate",
"property": "created_year",
"desc": "1403/05/06 --> 1403"
}
],
"label": "تاریخ ایجاد",
"filter_key": "",
"is_autoid": false
},
"modified_date": {
"type": "date",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "تاریخ ویرایش",
"filter_key": "",
"is_autoid": false
},
"sort_date_timestamp": {
"type": "long",
"return": false,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "تاریخ مرتب سازی",
"filter_key": "",
"is_autoid": false
},
"created_year": {
"type": "keyword",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "سال ایجاد",
"filter_key": "",
"is_autoid": false
},
"priority": {
"type": "integer",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "اولویت",
"filter_key": "",
"is_autoid": false
},
"is_deleted": {
"type": "boolean",
"return": false,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "وضعیت حذف",
"filter_key": "",
"is_autoid": false
},
"metadata": {
"type": "object",
"return": true,
"sortable": false,
"array": false,
"object": true,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "متادیتا",
"filter_key": "",
"is_autoid": false
},
"related_items": {
"type": "object",
"return": true,
"sortable": false,
"array": true,
"object": true,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "موارد مرتبط",
"filter_key": "",
"is_autoid": false,
"un_repeat_keys": [
"id",
"type"
]
}
},
"query": {
"default_sort": [
{
"_score": "desc"
},
{
"created_date": "desc"
}
],
"collapse_field": null,
"advanced_tags": {
"تاریخ": {
"key": "sort_date_timestamp",
"boost_offset": 30,
"collapse_field": "id"
},
"عنوان": {
"key": "title",
"collapse_field": "id"
},
"متن": {
"key": "content",
"collapse_field": ""
},
"دسته‌بندی": {
"key": "category",
"collapse_field": "id"
},
"برچسب": {
"key": "tags",
"collapse_field": "id"
},
"نویسنده": {
"key": "author.name",
"collapse_field": "id"
},
"سال": {
"key": "created_year",
"boost_offset": 30,
"collapse_field": "id"
},
"اولویت": {
"key": "priority",
"collapse_field": "id"
}
},
"highlight": {
"pre_tags": [
"<em>"
],
"post_tags": [
"</em>"
],
"fields": {
"title": {},
"content": {}
}
}
}
}

View File

@ -0,0 +1,479 @@
{
"index": {
"name": "mj_qa_qanon",
"aliases": [],
"index_key": "qaqanon",
"file_map": "qaqanon_map.json"
},
"fields": {
"id": {
"type": "keyword",
"label": "شناسه",
"sortable": true,
"required": true
},
"title": {
"type": "text",
"label": "عنوان",
"sortable": true,
"highlight": true,
"query_boost": 2.0,
"query_normal_boost": 3.0,
"needs_keyword": true,
"required": true,
"sub_fields": [
"fa",
"ph",
"keyword"
]
},
"title_popular": {
"type": "text",
"label": "عنوان مشهور",
"highlight": true,
"query_boost": 2.0,
"query_normal_boost": 3.0
},
"content": {
"type": "text",
"label": "متن اصلی",
"highlight": true,
"query_boost": 1.0,
"query_normal_boost": 1.0,
"sub_fields": [
"fa",
"ph"
]
},
"embeddings": {
"type": "dense_vector",
"label": "بردار معنایی",
"return": false
},
"initial": {
"type": "properties",
"label": "مرحله تقدیم",
"object": true,
"fields": {
"title": {
"type": "text",
"label": "عنوان ابتدائی"
},
"content": {
"type": "text",
"label": "متن ابتدائی"
},
"approuve_type": {
"type": "keyword",
"label": "نوع مصوبه"
},
"creators": {
"type": "keyword",
"array": true,
"label": "اسامی نمایندگان"
},
"signers": {
"type": "keyword",
"array": true,
"label": "اسامی سایر نمایندگان امضاء کننده"
},
"approuve_number": {
"type": "text",
"label": ""
},
"approuve_date": {
"type": "date",
"label": ""
},
"letter_number": {
"type": "text",
"label": ""
},
"letter_date": {
"type": "date",
"label": ""
},
"letter_signer": {
"type": "keyword",
"label": ""
},
"ministers": {
"type": "keyword",
"label": ""
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
},
"category": {
"type": "keyword",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 1.5,
"query_normal_boost": 1.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "دسته‌بندی",
"filter_key": "",
"is_autoid": false
},
"tags": {
"type": "keyword",
"return": true,
"sortable": false,
"array": true,
"object": false,
"highlight": false,
"query_boost": 1.0,
"query_normal_boost": 0.8,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "برچسب‌ها",
"filter_key": "",
"is_autoid": false
},
"author": {
"type": "object",
"return": true,
"sortable": false,
"array": false,
"object": true,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "نویسنده",
"filter_key": "",
"is_autoid": false,
"un_repeat_keys": [
"id"
]
},
"status": {
"type": "keyword",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "وضعیت",
"filter_key": "",
"is_autoid": false
},
"created_date": {
"type": "date",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [
{
"process": "jalaliTotsmp",
"property": "sort_date_timestamp",
"desc": "jalaliTotsmp(\"created_date\",\"/\")"
},
{
"process": "getYearOfDate",
"property": "created_year",
"desc": "1403/05/06 --> 1403"
}
],
"label": "تاریخ ایجاد",
"filter_key": "",
"is_autoid": false
},
"modified_date": {
"type": "date",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "تاریخ ویرایش",
"filter_key": "",
"is_autoid": false
},
"sort_date_timestamp": {
"type": "long",
"return": false,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "تاریخ مرتب سازی",
"filter_key": "",
"is_autoid": false
},
"created_year": {
"type": "keyword",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "سال ایجاد",
"filter_key": "",
"is_autoid": false
},
"priority": {
"type": "integer",
"return": true,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "اولویت",
"filter_key": "",
"is_autoid": false
},
"is_deleted": {
"type": "boolean",
"return": false,
"sortable": true,
"array": false,
"object": false,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "وضعیت حذف",
"filter_key": "",
"is_autoid": false
},
"metadata": {
"type": "object",
"return": true,
"sortable": false,
"array": false,
"object": true,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "متادیتا",
"filter_key": "",
"is_autoid": false
},
"related_items": {
"type": "object",
"return": true,
"sortable": false,
"array": true,
"object": true,
"highlight": false,
"query_boost": 0.0,
"query_normal_boost": 0.0,
"needs_keyword": false,
"required": false,
"join_to": [],
"label": "موارد مرتبط",
"filter_key": "",
"is_autoid": false,
"un_repeat_keys": [
"id",
"type"
]
}
},
"query": {
"default_sort": [
{
"_score": "desc"
},
{
"created_date": "desc"
}
],
"collapse_field": null,
"normal": [
{
"match_phrase": {
"title.ph": {
"boost": 12,
"query": "@query_value"
}
}
},
{
"match_phrase": {
"tags": {
"boost": 10,
"query": "@query_value"
}
}
},
{
"match_phrase": {
"categories": {
"boost": 10,
"query": "@query_value"
}
}
},
{
"match_phrase": {
"content.ph": {
"boost": 9,
"query": "@query_value"
}
}
},
{
"match": {
"title.fa": {
"boost": 4,
"query": "@query_value"
}
}
},
{
"match": {
"title.fa_syn": {
"boost": 2,
"query": "@query_value"
}
}
},
{
"match": {
"tags": {
"boost": 3,
"query": "@query_value"
}
}
},
{
"match": {
"categories": {
"boost": 3,
"query": "@query_value"
}
}
},
{
"match": {
"te_ref": {
"boost": 3,
"query": "@query_value"
}
}
},
{
"match": {
"content.fa": {
"boost": 3,
"query": "@query_value"
}
}
},
{
"match": {
"content.fa_syn": {
"boost": 1,
"query": "@query_value"
}
}
}
],
"advanced_tags": {
"تاریخ": {
"key": "sort_date_timestamp",
"boost_offset": 30,
"collapse_field": "id"
},
"عنوان": {
"key": "title",
"collapse_field": "id"
},
"متن": {
"key": "content",
"collapse_field": ""
},
"دسته‌بندی": {
"key": "category",
"collapse_field": "id"
},
"برچسب": {
"key": "tags",
"collapse_field": "id"
},
"نویسنده": {
"key": "author.name",
"collapse_field": "id"
},
"سال": {
"key": "created_year",
"boost_offset": 30,
"collapse_field": "id"
},
"اولویت": {
"key": "priority",
"collapse_field": "id"
}
},
"highlight": {
"pre_tags": [
"<em>"
],
"post_tags": [
"</em>"
],
"fields": {
"title": {},
"content": {}
}
}
}
}

View File

@ -0,0 +1,121 @@
{
"index": {
"name": "mj_qa_chat",
"aliases": [],
"index_key": "qachat"
},
"properties": {
"id": {
"type": "keyword"
},
"chat_id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"user_id": {
"type": "integer"
},
"user_query": {
"type": "text"
},
"model_key": {
"type": "keyword"
},
"retrived_passage": {
"type": "text"
},
"retrived_ref_ids": {
"type": "keyword"
},
"retrived_duration": {
"type": "integer"
},
"prompt_type": {
"type": "keyword"
},
"llm_duration": {
"type": "integer"
},
"full_duration": {
"type": "integer"
},
"time_create": {
"type": "date"
},
"used_ref_ids": {
"type": "keyword"
},
"status_text": {
"type": "keyword"
},
"status": {
"type": "integer"
},
"prompt_answer": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
},
"validation": {
"required": [
"user_query",
"model_key"
],
"default_value": {
"prompt_type" : "question"
}
},
"include_fields": [],
"exclude_fields": [],
"is_array": [
"retrived_ref_ids",
"used_ref_ids"
],
"query": {
"default_sort": [
{
"_score": "desc"
},
{
"time_create": "desc"
},
{
"user_id": "asc"
}
],
"normal": {
"phrase": {
"title": 5,
"user_query": 5,
"prompt_answer": 4
},
"match": {
"title": 3,
"user_query": 2,
"prompt_answer": 1
}
},
"filter_keys": {
"f_ud": "user_id",
"f_cd": "chat_id",
"f_tt": "title",
"f_pt": "prompt_type",
"f_urd": "used_ref_ids",
"f_md": "model_key",
"f_rrd": "retrived_ref_ids"
}
}
}

View File

@ -0,0 +1,135 @@
{
"index": {
"name": "mj_qa_plan",
"aliases": [],
"index_key": "qaplan"
},
"properties": {
"plan_id": {
"type": "keyword"
},
"id": {
"type": "keyword"
},
"urgency_state": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"introduction": {
"type": "text"
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text"
},
"ph": {
"type": "text"
}
}
},
"result_sections": {
"properties": {
"number": {
"type": "integer"
},
"title": {
"type": "float"
},
"content": {
"type": "text"
},
"notes": {
"type": "text"
},
"reasons": {
"type": "text"
},
"relation_sections": {
"type": "nested"
}
}
},
"ai_result": {
"properties": {
"start_offset": {
"type": "integer"
},
"end_offset": {
"type": "integer"
},
"word": {
"type": "text"
},
"start": {
"type": "float"
},
"end": {
"type": "float"
}
}
},
"time_create": {
"type": "date"
},
"user_id": {
"type": "integer"
},
"status_code": {
"type": "integer"
},
"status_message": {
"type": "text"
}
},
"validation": {
"required": []
},
"include_fields": [],
"is_array": [],
"query": {
"default_sort": [
{
"_score": "desc"
},
{
"ref_id": "asc"
},
{
"sound_link": "asc"
}
],
"normal": {
"phrase": {
"content.ph": 4
},
"match": {
"content": 2
}
},
"filter_keys": {},
"highlight": {
"pre_tags": [
"<span class='text__orange'>"
],
"post_tags": [
"</span>"
],
"fields": {
"title.fa": {},
"title.ph": {},
"content.fa": {},
"content.ph": {}
}
}
}
}

17
app/schemas/qaqanon.text Normal file
View File

@ -0,0 +1,17 @@
امضاء به دستگاه اجرایی
ابلاغ به رئیس جمهور
اصلاحیه ابلاغیه
ابلاغیه استنکافی
ارسال رئیس جمهور به روزنامه رسمی
انتشار روزنامه رسمی
لازم الاجراء شدن -اصلاح متن مغایر
text -> date :
expire_date
exec_date
ts_date

View File

@ -0,0 +1,879 @@
{
"index": {
"name": "mj_qa_qanon",
"aliases": [],
"index_key": "qaqanon",
"file_map": "qaqanon_map.json"
},
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"fa": {
"type": "text"
},
"ph": {
"type": "text"
},
"keyword": {
"type": "keyword"
}
}
},
"title_popular": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text"
},
"ph": {
"type": "text"
}
}
},
"embeddings": {
"type": "dense_vector",
"dims": 768,
"index": true,
"similarity": "cosine"
},
"initial": {
"properties": {
"title": {
"type": "text"
},
"content": {
"type": "text"
},
"approuve_type": {
"type": "keyword"
},
"creators": {
"type": "keyword"
},
"signers": {
"type": "keyword"
},
"approuve_number": {
"type": "text"
},
"approuve_date": {
"type": "date"
},
"letter_number": {
"type": "text"
},
"letter_date": {
"type": "date"
},
"letter_signer": {
"type": "keyword"
},
"ministers": {
"type": "keyword"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
},
"receive": {
"properties": {
"term_number": {
"type": "text"
},
"ref_law": {
"type": "keyword"
},
"meet_number": {
"type": "text"
},
"meet_date": {
"type": "date"
},
"register_number": {
"type": "text"
},
"publish_number": {
"type": "text"
},
"handle_type": {
"type": "keyword"
},
"handle_method": {
"type": "keyword"
}
}
},
"refer": {
"properties": {
"refer_date": {
"type": "date"
},
"commission_type": {
"type": "keyword"
},
"commission_main": {
"type": "keyword"
},
"commission_mirrors": {
"type": "keyword"
},
"commission_common": {
"properties": {
"name": {
"type": "keyword"
},
"member_count": {
"type": "integer"
},
"member_names": {
"type": "keyword"
}
}
},
"handle_85": {
"type": "keyword"
}
}
},
"commission_report": {
"properties": {
"agendas": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"meets": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"report_first": {
"properties": {
"approve_type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"report_nexts": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"duration_85": {
"type": "date"
},
"date_85": {
"type": "date"
}
}
}
}
},
"parl_handle": {
"properties": {
"agendas": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"meets": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"agree_names": {
"type": "keyword"
},
"against_names": {
"type": "keyword"
},
"demands": {
"type": "keyword"
},
"demand85_names": {
"type": "keyword"
},
"handle_infos": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"duration_85": {
"type": "date"
},
"date_85": {
"type": "date"
}
}
}
}
},
"before_info": {
"properties": {
"ref_id": {
"type": "keyword"
},
"plan_type": {
"type": "keyword"
},
"discuss_type": {
"type": "keyword"
},
"ministers": {
"type": "keyword"
},
"creators": {
"type": "text"
},
"register_number": {
"type": "text"
},
"prev_number": {
"type": "text"
},
"receipt_date": {
"type": "date"
},
"discuss_date1": {
"type": "date"
},
"discuss_date2": {
"type": "date"
},
"description": {
"type": "text"
},
"commission_type": {
"type": "keyword"
},
"commission_main": {
"type": "keyword"
},
"commission_mirrors": {
"type": "keyword"
},
"content": {
"type": "text"
}
}
},
"letters": {
"properties": {
"type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"order": {
"type": "float"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"description": {
"type": "text"
},
"content": {
"type": "text"
},
"delay_time": {
"type": "text"
},
"expire_date": {
"type": "date"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
},
"counts": {
"properties": {
"key": {
"type": "keyword"
},
"value": {
"type": "text"
}
}
},
"davam_type": {
"type": "keyword"
},
"number_all": {
"type": "text"
},
"number_row": {
"type": "text"
},
"archive_info": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"description": {
"type": "text"
}
}
},
"ts_date_dotic": {
"type": "date"
},
"ts_date1": {
"type": "date"
},
"ts_ref1": {
"type": "keyword"
},
"ts_date2": {
"type": "date"
},
"ts_ref2": {
"type": "keyword"
},
"conformity_rrk": {
"type": "keyword"
},
"conformity_qanonyar": {
"type": "keyword"
},
"content_ocr": {
"type": "text"
},
"qanon_etebar": {
"type": "keyword"
},
"expire_date": {
"type": "date"
},
"exec_date": {
"type": "date"
},
"ref_key": {
"type": "keyword"
},
"ts_date": {
"type": "date"
},
"ts_year": {
"type": "integer"
},
"ts_ref": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"term": {
"type": "keyword"
},
"term_number": {
"type": "integer"
},
"ranking_weight": {
"type": "integer"
},
"main_type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"title_type": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"action_type": {
"type": "keyword"
},
"section_len": {
"type": "integer"
},
"eblagh": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "text"
},
"from": {
"type": "keyword"
}
}
},
"rrk": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "text"
}
}
},
"exceuter_organs": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "keyword"
}
}
},
"receiver_organs": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "keyword"
}
}
},
"sort_date_timestamp": {
"type": "long"
},
"qanon_relations": {
"properties": {
"from_type": {
"type": "keyword"
},
"from_section_id": {
"type": "keyword"
},
"from_section_title": {
"type": "keyword"
},
"rel_type": {
"type": "keyword"
},
"to_type": {
"type": "keyword"
},
"to_id": {
"type": "keyword"
},
"to_title": {
"type": "keyword"
},
"wait": {
"type": "integer"
},
"rel_key": {
"type": "keyword"
}
}
},
"opinion_relations": {
"properties": {
"from_type": {
"type": "keyword"
},
"from_section_id": {
"type": "keyword"
},
"from_section_title": {
"type": "keyword"
},
"rel_type": {
"type": "keyword"
},
"to_type": {
"type": "keyword"
},
"to_id": {
"type": "keyword"
},
"to_title": {
"type": "keyword"
},
"wait": {
"type": "integer"
},
"rel_key": {
"type": "keyword"
}
}
},
"fixed_date": {
"type": "date"
},
"renewal_date": {
"type": "date"
},
"exec_duration": {
"type": "text"
},
"effective_date": {
"type": "date"
},
"relation_organs": {
"type": "keyword"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
},
"type": {
"type": "keyword"
}
}
},
"is_delete": {
"type": "integer"
},
"time_edit": {
"type": "date"
},
"doc_tags": {
"type": "keyword"
},
"doc_states": {
"type": "keyword"
}
},
"validation": {
"required": [
"id",
"title"
]
},
"include_fields": [
"id",
"title",
"title_popular",
"sort_date_timestamp",
"time_edit",
"ts_date",
"ts_ref",
"content",
"doc_states"
],
"exclude_fields": [
"embeddings"
],
"field_joins": {
"ts_date": [
{
"process": "dateTotsmp",
"property": "sort_date_timestamp",
"desc": "dateTotsmp(\"ts_date\",\"/\")"
},
{
"process": "getYearOfDate",
"property": "ts_year",
"desc": "1403/05/06 --> 1403"
}
]
},
"is_array": [
"initial.creators",
"initial.signers",
"initial.ministers",
"initial.files",
"refer.commission_mirrors",
"refer.commission_common",
"refer.commission_common.member_names",
"commission_report.agendas",
"commission_report.meets",
"commission_report.report_nexts",
"parl_handle.agendas",
"parl_handle.meets",
"parl_handle.agree_names",
"parl_handle.against_names",
"parl_handle.demands",
"parl_handle.demand85_names",
"parl_handle.handle_infos",
"before_info.ministers",
"before_info.creators",
"before_info.commission_mirrors",
"letters",
"letters.files",
"counts",
"exceuter_organs",
"receiver_organs",
"qanon_relations",
"opinion_relations",
"relation_organs",
"files",
"doc_states",
"doc_tags"
],
"is_autoid": [],
"query": {
"default_sort": [
{
"_score": "desc"
},
{
"ts_date": "desc"
}
],
"aggregation_fields": {
"ai-codes.label": 100,
"code-ai.label": 100,
"ts_ref": 100,
"state_etebar": 200,
"qanon_etebar": 200,
"sub_type": 100,
"other_type": 100,
"ts_year": 100,
"qanon_title": 100,
"topics.title": 100,
"title_type": 20,
"tvalid_state.tstate": 20,
"tcode_mains.label": 20,
"tcode_subs.label": 20,
"user_edit": {
"size": 100,
"is_value_user_id": true
},
"user_actions.action_key": {
"size": 100,
"name": "user_actions",
"rename_values": {
"expert_tvalid_state": "کارشناس-اعتبارسنجی",
"supervisor_tvalid_state": "ناظر-اعتبارسنجی",
"manager_tvalid_state": "مدیر-اعتبارسنجی",
"expert_tcode_mains": "کارشناس-کداصلی",
"expert_tcode_subs": "کارشناس-کد فرعی",
"supervisor_tcode_mains": "ناظر-کداصلی",
"supervisor_tcode_subs": "ناظر-کد فرعی",
"manager_tcode_mains": "مدیر-کداصلی",
"manager_tcode_subs": "مدیر-کد فرعی"
}
}
},
"collapse_fields": {
"qanon_id": {
"sort": {
"child_order": "asc"
},
"size": 5
}
},
"normal": {
"phrase": {
"title.ph": 12,
"title_popular": 10,
"content.ph": 9
},
"match": {
"title.ph": 4,
"title_popular": 2,
"content.ph": 2
}
},
"sort_keys": {
"lasttitle": {
"sort_date_timestamp": "desc"
},
"firsttitle": {
"sort_date_timestamp": "asc"
},
"title": {
"qanon_title": "asc"
}
},
"filter_keys": {
"f_rk": "ref_key",
"f_td": "ts_date",
"f_ey": "eb_year",
"f_ws": "work_state.title",
"f_wt": "work_tags.title",
"f_tr": "ts_ref.keyword",
"f_qty": "qanon_type",
"f_se": "state_etebar",
"f_qe": "qanon_etebar",
"f_st": "state_tanghih",
"f_ty": "ts_year",
"f_qt": "qanon_title.keyword",
"f_tt": "title_type",
"f_ttk": "title_type.keyword",
"f_tp": "topics.title",
"f_eo": "exceuter_organs.title",
"f_ro": "receiver_organs.title",
"f_tg": "tags.keyword",
"f_ct": "categories.keyword",
"f_mn": "majles_name",
"f_tn": "term_number",
"f_ed": "error_date.keyword",
"f_mb": "meet_number",
"f_tm": "talker.name",
"f_to": "talker.organ",
"f_md": "meet_date.keyword",
"f_dn": "dastur_number",
"f_mc": "member_count",
"f_nt": "content_type",
"f_mt": "main_type",
"f_sty": "sub_type",
"f_ot": "other_type.keyword",
"f_cal": "code-ai.label",
"f_vs": "tvalid_state.tstate",
"f_cm": "tcode_mains.label",
"f_cs": "tcode_subs.label",
"f_ua": "user_actions.action_key",
"f_ue": "user_edit",
"fr_mid": "tcode_mains.id",
"fr_sid": "tcode_subs.id",
"qanon_id": "qanon_id",
"mqanon_tp": {
"type": "api",
"key": "topics.id",
"service": "tlist",
"url": "/subject/get/childs",
"result": "meta.child_ids",
"body": {
"parents": "@value",
"item_state": 1
}
}
},
"advanced_tags": {
"تاریخ": {
"key": "sort_date_timestamp",
"boost_offset": 30,
"collapse_field": "id"
},
"عنوان": {
"key": "title",
"collapse_field": "id"
},
"متن": {
"key": "content",
"collapse_field": ""
},
"دسته‌بندی": {
"key": "category",
"collapse_field": "id"
},
"برچسب": {
"key": "tags",
"collapse_field": "id"
},
"نویسنده": {
"key": "author.name",
"collapse_field": "id"
},
"سال": {
"key": "created_year",
"boost_offset": 30,
"collapse_field": "id"
},
"اولویت": {
"key": "priority",
"collapse_field": "id"
}
},
"highlight": {
"pre_tags": [
"<span class='text__orange'>"
],
"post_tags": [
"</span>"
],
"fields": {
"title.fa": {},
"title.ph": {},
"qanon_title.fa": {},
"qanon_title.ph": {},
"title_popular": {},
"content.fa": {},
"content.ph": {}
}
}
}
}

View File

@ -0,0 +1,963 @@
{
"index": {
"name": "mj_qa_section",
"aliases": [],
"index_key": "qasection",
"file_map": "qasection_map.json"
},
"properties": {
"id": {
"type": "keyword"
},
"html": {
"type": "text"
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text"
},
"ph": {
"type": "text"
}
}
},
"qanon_title": {
"type": "text",
"fields": {
"fa": {
"type": "text"
},
"ph": {
"type": "text"
},
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"qanon_etebar": {
"type": "keyword"
},
"expire_date": {
"type": "keyword"
},
"ref_key": {
"type": "keyword"
},
"ts_date": {
"type": "text"
},
"ts_year": {
"type": "integer"
},
"ts_ref": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"state_etebar": {
"type": "keyword"
},
"state_tanghih": {
"type": "keyword"
},
"main_type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"other_type": {
"type": "keyword"
},
"qanon_id": {
"type": "keyword"
},
"phase_id": {
"type": "keyword"
},
"parent_id": {
"type": "keyword"
},
"child_order": {
"type": "double"
},
"other_info": {
"properties": {
"level": {
"type": "integer"
},
"full_path": {
"type": "text"
},
"number_text": {
"type": "text"
},
"hierarchy": {
"type": "text"
}
}
},
"qanon_type": {
"type": "keyword"
},
"title_type": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content_len": {
"type": "integer"
},
"word_len": {
"type": "integer"
},
"content_emb": {
"type": "text"
},
"content_normal": {
"type": "text"
},
"nlp_tags": {
"type": "keyword"
},
"nlp_childs": {
"properties": {
"id": {
"type": "keyword"
},
"word_len": {
"type": "integer"
}
}
},
"path_headings": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"level": {
"type": "integer"
},
"simplifies": {
"type": "text"
},
"ai_keywords": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"topics": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "keyword"
}
}
},
"categories": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"tags": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"keywords": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"sort_date_timestamp": {
"type": "long"
},
"nlp_parses": {
"properties": {
"main_type": {
"type": "keyword"
},
"nlp_type": {
"type": "keyword"
},
"begin": {
"type": "integer"
},
"end": {
"type": "integer"
},
"text": {
"type": "text"
},
"referes": {
"type": "keyword"
},
"dependency_infos": {
"type": "nested"
}
}
},
"ref_state": {
"properties": {
"id": {
"type": "keyword"
},
"qanon_id": {
"type": "keyword"
},
"qanon_title": {
"type": "keyword"
},
"qanon_date": {
"type": "text"
},
"section_id": {
"type": "keyword"
},
"section_title": {
"type": "keyword"
},
"ref_key": {
"type": "keyword"
},
"effect_type": {
"type": "keyword"
},
"new_text": {
"type": "text"
},
"description": {
"type": "text"
},
"prev_text": {
"type": "text"
}
}
},
"code-ai": {
"properties": {
"label": {
"type": "keyword"
},
"score": {
"type": "float"
}
}
},
"history_codes": {
"type": "nested",
"properties": {
"version_key": {
"type": "keyword"
},
"sub_key": {
"type": "keyword"
},
"label": {
"type": "keyword"
},
"score": {
"type": "float"
},
"description": {
"type": "text"
}
}
},
"ai_code": {
"properties": {
"label": {
"type": "keyword"
},
"score": {
"type": "float"
}
}
},
"ai_codes": {
"properties": {
"label": {
"type": "keyword"
},
"score": {
"type": "float"
}
}
},
"embeddings": {
"type": "dense_vector",
"dims": 768,
"index": true,
"similarity": "cosine"
},
"tvalid_state": {
"properties": {
"tstate": {
"type": "keyword"
},
"description": {
"type": "text"
},
"treason_type": {
"type": "keyword"
},
"treason": {
"type": "text",
"fielddata": true
},
"dependency_infos": {
"type": "nested"
}
}
},
"tcode_mains": {
"properties": {
"label": {
"type": "keyword"
},
"id": {
"type": "keyword"
}
}
},
"tcode_subs": {
"properties": {
"label": {
"type": "keyword"
},
"id": {
"type": "keyword"
}
}
},
"tcode": {
"properties": {
"mains": {
"type": "keyword"
},
"subs": {
"type": "keyword"
},
"mains_old": {
"type": "keyword"
},
"subs_old": {
"type": "keyword"
}
}
},
"model_work_tags": {
"properties": {
"title": {
"type": "keyword"
},
"description": {
"type": "text"
},
"degree": {
"type": "integer"
}
}
},
"model_work_state": {
"properties": {
"title": {
"type": "keyword"
},
"description": {
"type": "text"
}
}
},
"model_rules": {
"properties": {
"id": {
"type": "keyword"
},
"text": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"text_ners": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"template_desc": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"degree": {
"type": "integer"
},
"work_tags": {
"type": "keyword"
},
"main_type": {
"type": "keyword"
},
"description": {
"type": "text"
}
}
},
"model_rules_logics": {
"properties": {
"id": {
"type": "keyword"
},
"rules-id": {
"type": "keyword"
},
"text": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"main_type": {
"type": "keyword"
},
"description": {
"type": "text"
}
}
},
"model_rules_logics_triples": {
"properties": {
"id": {
"type": "keyword"
},
"rules-id": {
"type": "keyword"
},
"rules_logics-id": {
"type": "keyword"
},
"text": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"description": {
"type": "text"
}
}
},
"model_rules_spins": {
"properties": {
"id": {
"type": "keyword"
},
"rules-id": {
"type": "keyword"
},
"text": {
"type": "text",
"fielddata": true
},
"description": {
"type": "text"
}
}
},
"model_files": {
"properties": {
"id": {
"type": "keyword"
},
"file": {
"type": "keyword"
},
"file_name": {
"type": "keyword"
},
"title": {
"type": "text"
}
}
},
"graph_context": {
"properties": {
"repalces": {
"type": "text"
},
"delimiter_points": {
"type": "integer"
},
"section_ids": {
"type": "keyword"
},
"context": {
"type": "text"
},
"tags": {
"type": "keyword"
}
}
},
"graph_models": {
"properties": {
"id": {
"type": "keyword"
},
"OWL": {
"type": "text"
},
"rule_type": {
"type": "keyword"
},
"rule": {
"type": "text"
},
"labels": {
"type": "nested"
},
"description": {
"type": "text"
}
}
},
"ai_conflict_rules": {
"properties": {
"id": {
"type": "keyword"
},
"rule_type": {
"type": "keyword"
},
"rule": {
"type": "text"
},
"relation_rules": {
"type": "keyword"
},
"confilit_rules": {
"type": "keyword"
},
"confilit_sections": {
"type": "keyword"
},
"conflict_state": {
"type": "keyword"
},
"description": {
"type": "text"
}
}
},
"file_links": {
"properties": {
"title": {
"type": "text"
},
"link": {
"type": "text"
}
}
},
"is_delete": {
"type": "integer"
},
"time_edit": {
"type": "date"
},
"edit_type": {
"type": "keyword"
},
"mirror_type": {
"type": "keyword"
},
"doc_states": {
"type": "keyword"
},
"doc_tags": {
"type": "keyword"
},
"user_edit": {
"type": "integer"
},
"user_actions": {
"properties": {
"user_id": {
"type": "integer"
},
"time_edit": {
"type": "date"
},
"description": {
"type": "text"
},
"step_order": {
"type": "integer"
},
"wizard_key": {
"type": "keyword"
},
"property_key": {
"type": "keyword"
},
"action_key": {
"type": "keyword"
},
"action_value": {
"type": "text"
},
"action": {
"type": "object"
}
}
},
"user_logs": {
"properties": {
"id": {
"type": "keyword"
},
"user_id": {
"type": "integer"
},
"username": {
"type": "keyword"
},
"time_edit": {
"type": "date"
},
"property": {
"type": "keyword"
}
}
}
},
"validation": {
"required": [
"id",
"qanon_title",
"qanon_id",
"ts_ref",
"ts_date"
]
},
"include_fields": [
"id",
"content",
"ts_date",
"qanon_id",
"qanon_title",
"ts_ref",
"section_id",
"child_order",
"other_info",
"qanon_type",
"other_type",
"sort_date_timestamp",
"state_etebar",
"title",
"categories",
"topics",
"tags",
"ai_code",
"ai_codes",
"title_type"
],
"exclude_fields": [
"embeddings"
],
"field_joins": {
"ts_date": [
{
"process": "dateTotsmp",
"property": "sort_date_timestamp",
"desc": "dateTotsmp(\"ts_date\",\"/\")"
},
{
"process": "getYearOfDate",
"property": "ts_year",
"desc": "1403/05/06 --> 1403"
}
],
"content": [
{
"process": "len",
"property": "content_len",
"desc": "len(\"content\")"
},
{
"process": "countWords",
"property": "word_len",
"desc": "lenWords()"
}
]
},
"is_array": [
"nlp_tags",
"path_headings",
"simplifies",
"ai_keywords",
"topics",
"categories",
"tags",
"keywords",
"nlp_parses",
"nlp_parses.referes",
"ref_state",
"history_codes",
"ai_codes",
"tcode_mains",
"tcode_subs",
"model_work_tags",
"model_rules",
"model_rules_logics",
"model_rules_logics_triples",
"model_rules_spins",
"model_files",
"graph_models",
"ai_conflict_rules",
"file_links",
"doc_states",
"doc_tags",
"user_actions",
"user_logs"
],
"is_autoid": [],
"query": {
"default_sort": [
{
"_score": "desc"
},
{
"ts_date": "sort_date_timestamp"
}
],
"aggregation_fields": {
"ai-codes.label": 100,
"code-ai.label": 100,
"ts_ref": 100,
"state_etebar": 200,
"qanon_etebar": 200,
"sub_type": 100,
"other_type": 100,
"ts_year": 100,
"qanon_title": 100,
"topics.title": 100,
"title_type": 20,
"tvalid_state.tstate": 20,
"tcode_mains.label": 20,
"tcode_subs.label": 20,
"user_edit": {
"size": 100,
"is_value_user_id": true
},
"user_actions.action_key": {
"size": 100,
"name": "user_actions",
"rename_values": {
"expert_tvalid_state": "کارشناس-اعتبارسنجی",
"supervisor_tvalid_state": "ناظر-اعتبارسنجی",
"manager_tvalid_state": "مدیر-اعتبارسنجی",
"expert_tcode_mains": "کارشناس-کداصلی",
"expert_tcode_subs": "کارشناس-کد فرعی",
"supervisor_tcode_mains": "ناظر-کداصلی",
"supervisor_tcode_subs": "ناظر-کد فرعی",
"manager_tcode_mains": "مدیر-کداصلی",
"manager_tcode_subs": "مدیر-کد فرعی"
}
}
},
"collapse_fields": {
"qanon_id": {
"sort": {
"child_order": "asc"
},
"size": 5
}
},
"normal": {
"phrase": {
"title.ph": 12,
"title_popular": 10,
"content.ph": 9
},
"match": {
"title.ph": 4,
"title_popular": 2,
"content.ph": 2
}
},
"sort_keys": {
"lasttitle": {
"sort_date_timestamp": "desc"
},
"firsttitle": {
"sort_date_timestamp": "asc"
},
"title": {
"qanon_title": "asc"
}
},
"filter_keys": {
"f_rk": "ref_key",
"f_td": "ts_date",
"f_ey": "eb_year",
"f_ws": "work_state.title",
"f_wt": "work_tags.title",
"f_tr": "ts_ref.keyword",
"f_qty": "qanon_type",
"f_se": "state_etebar",
"f_qe": "qanon_etebar",
"f_st": "state_tanghih",
"f_ty": "ts_year",
"f_qt": "qanon_title.keyword",
"f_tt": "title_type",
"f_ttk": "title_type.keyword",
"f_tp": "topics.title",
"f_eo": "exceuter_organs.title",
"f_ro": "receiver_organs.title",
"f_tg": "tags.keyword",
"f_ct": "categories.keyword",
"f_mn": "majles_name",
"f_tn": "term_number",
"f_ed": "error_date.keyword",
"f_mb": "meet_number",
"f_tm": "talker.name",
"f_to": "talker.organ",
"f_md": "meet_date.keyword",
"f_dn": "dastur_number",
"f_mc": "member_count",
"f_nt": "content_type",
"f_mt": "main_type",
"f_sty": "sub_type",
"f_ot": "other_type.keyword",
"f_cal": "code-ai.label",
"f_vs": "tvalid_state.tstate",
"f_cm": "tcode_mains.label",
"f_cs": "tcode_subs.label",
"f_ua": "user_actions.action_key",
"f_ue": "user_edit",
"fr_mid": "tcode_mains.id",
"fr_sid": "tcode_subs.id",
"qanon_id": "qanon_id",
"mqanon_tp": {
"type": "api",
"key": "topics.id",
"service": "tlist",
"url": "/subject/get/childs",
"result": "meta.child_ids",
"body": {
"parents": "@value",
"item_state": 1
}
}
},
"advanced_tags": {
"تاریخ": {
"key": "sort_date_timestamp",
"boost_offset": 30,
"collapse_field": "id"
},
"عنوان": {
"key": "title",
"collapse_field": "id"
},
"متن": {
"key": "content",
"collapse_field": ""
},
"دسته‌بندی": {
"key": "category",
"collapse_field": "id"
},
"برچسب": {
"key": "tags",
"collapse_field": "id"
},
"نویسنده": {
"key": "author.name",
"collapse_field": "id"
},
"سال": {
"key": "created_year",
"boost_offset": 30,
"collapse_field": "id"
},
"اولویت": {
"key": "priority",
"collapse_field": "id"
}
},
"highlight": {
"pre_tags": [
"<span class="text__orange">"
],
"post_tags": [
"</span>"
],
"fields": {
"title.fa": {},
"title.ph": {},
"qanon_title.fa": {},
"qanon_title.ph": {},
"title_popular": {},
"content.fa": {},
"content.ph": {}
}
}
}
}

140
app/schemas/settings.json Normal file
View File

@ -0,0 +1,140 @@
{
"index.max_result_window": 15000,
"index": {
"analysis": {
"analyzer": {
"phrase_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": []
},
"normal_analyzer_fa": {
"type": "custom",
"tokenizer": "standard",
"char_filter": [
"fa_char_filter"
],
"filter": [
"fa_stop"
]
}
},
"filter": {
"fa_stop": {
"type": "stop",
"stopwords": [
"یا",
"را",
"این",
"با",
"آن",
"و",
"در",
"به",
"که",
"از",
"طی",
"پس",
"چه",
"اگر",
"نه",
"آنها",
"هر",
"او",
"ما",
"من",
"تا",
"نیز",
"اما",
"یک",
"بر",
"هم",
"برای",
"کن",
"کرد",
"کردن",
"باش",
"بود",
"بودن",
"شو",
"شد",
"شدن",
"‏دار",
"داشت",
"داشتن",
"‏خواه",
"خواست",
"خواستن",
"‏گوی",
"گفت",
"گفتن",
"‏گیر",
"گرفت",
"گرفتن",
"‏آی",
"آمد",
"آمدن",
"‏توان",
"توانستن",
"‏یاب",
"یافتن",
"‏آور",
"آورد",
"آوردن",
"1",
"2",
"3",
"ص",
"4",
"و",
"5",
"ج",
"6",
"a",
"top",
"href",
"pageno"
],
"char_filter": []
}
},
"char_filter": {
"fa_char_filter": {
"type": "mapping",
"mappings": [
"٠ => 0",
"١ => 1",
"٢ => 2",
"٣ => 3",
"٤ => 4",
"٥ => 5",
"٦ => 6",
"٧ => 7",
"٨ => 8",
"٩ => 9",
"ک => ك",
"ی => ي",
"ة => ه",
"إ => ا",
"أ => ا",
"آ => ا",
"ء => ا",
"َ => ",
"ُ => ",
"ِ => ",
"ّ => ",
"ً => ",
"ٌ => ",
"ٍ => ",
"ْ => "
]
}
}
},
"number_of_shards": "1",
"number_of_replicas": "0"
}
}

View File

@ -0,0 +1,888 @@
{
"index": {
"name": "tt_test",
"aliases": [],
"index_key": "test"
},
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"fa": {
"type": "text"
},
"ph": {
"type": "text"
},
"keyword": {
"type": "keyword"
}
}
},
"title_popular": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text"
},
"ph": {
"type": "text"
}
}
},
"embeddings": {
"type": "dense_vector"
},
"initial": {
"properties": {
"title": {
"type": "text"
},
"content": {
"type": "text"
},
"approuve_type": {
"type": "keyword"
},
"creators": {
"type": "keyword"
},
"signers": {
"type": "keyword"
},
"approuve_number": {
"type": "text"
},
"approuve_date": {
"type": "date"
},
"letter_number": {
"type": "text"
},
"letter_date": {
"type": "date"
},
"letter_signer": {
"type": "keyword"
},
"ministers": {
"type": "keyword"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
},
"receive": {
"properties": {
"term_number": {
"type": "text"
},
"ref_law": {
"type": "keyword"
},
"meet_number": {
"type": "text"
},
"meet_date": {
"type": "date"
},
"register_number": {
"type": "text"
},
"publish_number": {
"type": "text"
},
"handle_type": {
"type": "keyword"
},
"handle_method": {
"type": "keyword"
}
}
},
"refer": {
"properties": {
"refer_date": {
"type": "date"
},
"commission_type": {
"type": "keyword"
},
"commission_main": {
"type": "keyword"
},
"commission_mirrors": {
"type": "keyword"
},
"commission_common": {
"properties": {
"name": {
"type": "keyword"
},
"member_count": {
"type": "integer"
},
"member_names": {
"type": "keyword"
}
}
},
"handle_85": {
"type": "keyword"
}
}
},
"commission_report": {
"properties": {
"agendas": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"meets": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"report_first": {
"properties": {
"approve_type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"report_nexts": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"duration_85": {
"type": "date"
},
"date_85": {
"type": "date"
}
}
}
}
},
"parl_handle": {
"properties": {
"agendas": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"meets": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"agree_names": {
"type": "keyword"
},
"against_names": {
"type": "keyword"
},
"demands": {
"type": "keyword"
},
"demand85_names": {
"type": "keyword"
},
"handle_infos": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"duration_85": {
"type": "date"
},
"date_85": {
"type": "date"
}
}
}
}
},
"before_info": {
"properties": {
"ref_id": {
"type": "keyword"
},
"plan_type": {
"type": "keyword"
},
"discuss_type": {
"type": "keyword"
},
"ministers": {
"type": "keyword"
},
"creators": {
"type": "text"
},
"register_number": {
"type": "text"
},
"prev_number": {
"type": "text"
},
"receipt_date": {
"type": "date"
},
"discuss_date1": {
"type": "date"
},
"discuss_date2": {
"type": "date"
},
"description": {
"type": "text"
},
"commission_type": {
"type": "keyword"
},
"commission_main": {
"type": "keyword"
},
"commission_mirrors": {
"type": "keyword"
},
"content": {
"type": "text"
}
}
},
"letters": {
"properties": {
"id": {
"type": "keyword"
},
"type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"order": {
"type": "float"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"description": {
"type": "text"
},
"content": {
"type": "text"
},
"delay_time": {
"type": "text"
},
"expire_date": {
"type": "date"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
},
"counts": {
"properties": {
"key": {
"type": "keyword"
},
"value": {
"type": "text"
}
}
},
"davam_type": {
"type": "keyword"
},
"number_all": {
"type": "text"
},
"number_row": {
"type": "text"
},
"archive_info": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"description": {
"type": "text"
}
}
},
"ts_date_dotic": {
"type": "date"
},
"ts_date1": {
"type": "date"
},
"ts_ref1": {
"type": "keyword"
},
"ts_date2": {
"type": "date"
},
"ts_ref2": {
"type": "keyword"
},
"conformity_rrk": {
"type": "keyword"
},
"conformity_qanonyar": {
"type": "keyword"
},
"content_ocr": {
"type": "text"
},
"qanon_etebar": {
"type": "keyword"
},
"expire_date": {
"type": "date"
},
"exec_date": {
"type": "date"
},
"ref_key": {
"type": "keyword"
},
"ts_date": {
"type": "date"
},
"ts_year": {
"type": "integer"
},
"ts_ref": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"term": {
"type": "keyword"
},
"term_number": {
"type": "integer"
},
"ranking_weight": {
"type": "integer"
},
"main_type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"title_type": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"action_type": {
"type": "keyword"
},
"section_len": {
"type": "integer"
},
"eblagh": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "text"
},
"from": {
"type": "keyword"
}
}
},
"rrk": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "text"
}
}
},
"exceuter_organs": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "keyword"
}
}
},
"receiver_organs": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "keyword"
}
}
},
"sort_date_timestamp": {
"type": "long"
},
"qanon_relations": {
"properties": {
"from_type": {
"type": "keyword"
},
"from_section_id": {
"type": "keyword"
},
"from_section_title": {
"type": "keyword"
},
"rel_type": {
"type": "keyword"
},
"to_type": {
"type": "keyword"
},
"to_id": {
"type": "keyword"
},
"to_title": {
"type": "keyword"
},
"wait": {
"type": "integer"
},
"rel_key": {
"type": "keyword"
}
}
},
"opinion_relations": {
"properties": {
"from_type": {
"type": "keyword"
},
"from_section_id": {
"type": "keyword"
},
"from_section_title": {
"type": "keyword"
},
"rel_type": {
"type": "keyword"
},
"to_type": {
"type": "keyword"
},
"to_id": {
"type": "keyword"
},
"to_title": {
"type": "keyword"
},
"wait": {
"type": "integer"
},
"rel_key": {
"type": "keyword"
}
}
},
"fixed_date": {
"type": "date"
},
"renewal_date": {
"type": "date"
},
"exec_duration": {
"type": "text"
},
"effective_date": {
"type": "date"
},
"relation_organs": {
"type": "keyword"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
},
"type": {
"type": "keyword"
}
}
},
"is_delete": {
"type": "integer"
},
"time_edit": {
"type": "date"
},
"doc_tags": {
"type": "keyword"
},
"doc_states": {
"type": "keyword"
}
},
"validation": {
"required": [
"id",
"title"
],
"default_value": {
"prompt_type": "question"
}
},
"include_fields": [
"id",
"title",
"title_popular",
"sort_date_timestamp",
"time_edit",
"ts_date",
"ts_ref",
"content",
"doc_states"
],
"exclude_fields": [
"embeddings"
],
"field_joins": {
"ts_date": [
{
"process": "dateTotsmp",
"property": "sort_date_timestamp",
"desc": "dateTotsmp(\"ts_date\",\"/\")"
},
{
"process": "getYearOfDate",
"property": "ts_year",
"desc": "1403/05/06 --> 1403"
}
]
},
"is_array": [
"initial.creators",
"initial.signers",
"initial.ministers",
"initial.files",
"refer.commission_mirrors",
"refer.commission_common",
"refer.commission_common.member_names",
"commission_report.agendas",
"commission_report.meets",
"commission_report.report_nexts",
"parl_handle.agendas",
"parl_handle.meets",
"parl_handle.agree_names",
"parl_handle.against_names",
"parl_handle.demands",
"parl_handle.demand85_names",
"parl_handle.handle_infos",
"before_info.ministers",
"before_info.creators",
"before_info.commission_mirrors",
"letters",
"letters.files",
"counts",
"exceuter_organs",
"receiver_organs",
"qanon_relations",
"opinion_relations",
"relation_organs",
"files",
"doc_states",
"doc_tags"
],
"is_autoid": [
"letters"
],
"query": {
"default_sort": [
{
"_score": "desc"
},
{
"ts_date": "desc"
}
],
"aggregation_fields": {
"ai-codes.label": 100,
"code-ai.label": 100,
"ts_ref": 100,
"state_etebar": 200,
"qanon_etebar": 200,
"sub_type": 100,
"other_type": 100,
"ts_year": 100,
"qanon_title": 100,
"topics.title": 100,
"title_type": 20,
"tvalid_state.tstate": 20,
"tcode_mains.label": 20,
"tcode_subs.label": 20,
"user_edit": {
"size": 100,
"is_value_user_id": true
},
"user_actions.action_key": {
"size": 100,
"name": "user_actions",
"rename_values": {
"expert_tvalid_state": "کارشناس-اعتبارسنجی",
"supervisor_tvalid_state": "ناظر-اعتبارسنجی",
"manager_tvalid_state": "مدیر-اعتبارسنجی",
"expert_tcode_mains": "کارشناس-کداصلی",
"expert_tcode_subs": "کارشناس-کد فرعی",
"supervisor_tcode_mains": "ناظر-کداصلی",
"supervisor_tcode_subs": "ناظر-کد فرعی",
"manager_tcode_mains": "مدیر-کداصلی",
"manager_tcode_subs": "مدیر-کد فرعی"
}
}
},
"collapse_fields": {
"qanon_id": {
"sort": {
"child_order": "asc"
},
"size": 5
}
},
"normal": {
"phrase": {
"title.ph": 12,
"title_popular": 10,
"content.ph": 9
},
"match": {
"title.ph": 4,
"title_popular": 2,
"content.ph": 2
}
},
"sort_keys": {
"lasttitle": {
"sort_date_timestamp": "desc"
},
"firsttitle": {
"sort_date_timestamp": "asc"
},
"title": {
"qanon_title": "asc"
}
},
"filter_keys": {
"f_rk": "ref_key",
"f_td": "ts_date",
"f_ey": "eb_year",
"f_ws": "work_state.title",
"f_wt": "work_tags.title",
"f_tr": "ts_ref.keyword",
"f_qty": "qanon_type",
"f_se": "state_etebar",
"f_qe": "qanon_etebar",
"f_st": "state_tanghih",
"f_ty": "ts_year",
"f_qt": "qanon_title.keyword",
"f_tt": "title_type",
"f_ttk": "title_type.keyword",
"f_tp": "topics.title",
"f_eo": "exceuter_organs.title",
"f_ro": "receiver_organs.title",
"f_tg": "tags.keyword",
"f_ct": "categories.keyword",
"f_mn": "majles_name",
"f_tn": "term_number",
"f_ed": "error_date.keyword",
"f_mb": "meet_number",
"f_tm": "talker.name",
"f_to": "talker.organ",
"f_md": "meet_date.keyword",
"f_dn": "dastur_number",
"f_mc": "member_count",
"f_nt": "content_type",
"f_mt": "main_type",
"f_sty": "sub_type",
"f_ot": "other_type.keyword",
"f_cal": "code-ai.label",
"f_vs": "tvalid_state.tstate",
"f_cm": "tcode_mains.label",
"f_cs": "tcode_subs.label",
"f_ua": "user_actions.action_key",
"f_ue": "user_edit",
"fr_mid": "tcode_mains.id",
"fr_sid": "tcode_subs.id",
"qanon_id": "qanon_id",
"mqanon_tp": {
"type": "api",
"key": "topics.id",
"service": "tlist",
"url": "/subject/get/childs",
"result": "meta.child_ids",
"body": {
"parents": "@value",
"item_state": 1
}
}
},
"advanced_tags": {
"تاریخ": {
"key": "sort_date_timestamp",
"boost_offset": 30,
"collapse_field": "id"
},
"عنوان": {
"key": "title",
"collapse_field": "id"
},
"متن": {
"key": "content",
"collapse_field": ""
},
"دسته‌بندی": {
"key": "category",
"collapse_field": "id"
},
"برچسب": {
"key": "tags",
"collapse_field": "id"
},
"نویسنده": {
"key": "author.name",
"collapse_field": "id"
},
"سال": {
"key": "created_year",
"boost_offset": 30,
"collapse_field": "id"
},
"اولویت": {
"key": "priority",
"collapse_field": "id"
}
},
"highlight": {
"pre_tags": [
"<span class='text__orange'>"
],
"post_tags": [
"</span>"
],
"fields": {
"title.fa": {},
"title.ph": {},
"qanon_title.fa": {},
"qanon_title.ph": {},
"title_popular": {},
"content.fa": {},
"content.ph": {}
}
},
"search_all_fields":{
"title": "title",
"content": "prompt_answer"
}
}
}

View File

@ -0,0 +1,890 @@
{
"index": {
"name": "use_for_test",
"aliases": [],
"index_key": "use_for_test"
},
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"fields": {
"fa": {
"type": "text"
},
"ph": {
"type": "text"
},
"keyword": {
"type": "keyword"
}
}
},
"title_popular": {
"type": "text",
"analyzer": "normal_analyzer_fa",
"search_analyzer": "normal_analyzer_fa",
"search_quote_analyzer": "phrase_analyzer_fa"
},
"content": {
"type": "text",
"fields": {
"fa": {
"type": "text"
},
"ph": {
"type": "text"
}
}
},
"embeddings": {
"type": "dense_vector"
},
"initial": {
"properties": {
"title": {
"type": "text"
},
"content": {
"type": "text"
},
"approuve_type": {
"type": "keyword"
},
"creators": {
"type": "keyword"
},
"signers": {
"type": "keyword"
},
"approuve_number": {
"type": "text"
},
"approuve_date": {
"type": "date"
},
"letter_number": {
"type": "text"
},
"letter_date": {
"type": "date"
},
"letter_signer": {
"type": "keyword"
},
"ministers": {
"type": "keyword"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
},
"receive": {
"properties": {
"term_number": {
"type": "text"
},
"ref_law": {
"type": "keyword"
},
"meet_number": {
"type": "text"
},
"meet_date": {
"type": "date"
},
"register_number": {
"type": "text"
},
"publish_number": {
"type": "text"
},
"handle_type": {
"type": "keyword"
},
"handle_method": {
"type": "keyword"
}
}
},
"refer": {
"properties": {
"refer_date": {
"type": "date"
},
"commission_type": {
"type": "keyword"
},
"commission_main": {
"type": "keyword"
},
"commission_mirrors": {
"type": "keyword"
},
"commission_common": {
"properties": {
"name": {
"type": "keyword"
},
"member_count": {
"type": "integer"
},
"member_names": {
"type": "keyword"
}
}
},
"handle_85": {
"type": "keyword"
}
}
},
"commission_report": {
"properties": {
"agendas": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"meets": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"report_first": {
"properties": {
"approve_type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"report_nexts": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"duration_85": {
"type": "date"
},
"date_85": {
"type": "date"
}
}
}
}
},
"parl_handle": {
"properties": {
"agendas": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"meets": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "date"
}
}
},
"agree_names": {
"type": "keyword"
},
"against_names": {
"type": "keyword"
},
"demands": {
"type": "keyword"
},
"demand85_names": {
"type": "keyword"
},
"handle_infos": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"duration_85": {
"type": "date"
},
"date_85": {
"type": "date"
}
}
}
}
},
"before_info": {
"properties": {
"ref_id": {
"type": "keyword"
},
"plan_type": {
"type": "keyword"
},
"discuss_type": {
"type": "keyword"
},
"ministers": {
"type": "keyword"
},
"creators": {
"type": "text"
},
"register_number": {
"type": "text"
},
"prev_number": {
"type": "text"
},
"receipt_date": {
"type": "date"
},
"discuss_date1": {
"type": "date"
},
"discuss_date2": {
"type": "date"
},
"description": {
"type": "text"
},
"commission_type": {
"type": "keyword"
},
"commission_main": {
"type": "keyword"
},
"commission_mirrors": {
"type": "keyword"
},
"content": {
"type": "text"
}
}
},
"letters": {
"properties": {
"id": {
"type": "keyword"
},
"type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"order": {
"type": "float"
},
"number": {
"type": "text"
},
"date": {
"type": "date"
},
"description": {
"type": "text"
},
"content": {
"type": "text"
},
"delay_time": {
"type": "text"
},
"expire_date": {
"type": "date"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
}
}
}
}
},
"counts": {
"properties": {
"key": {
"type": "keyword"
},
"value": {
"type": "text"
}
}
},
"davam_type": {
"type": "keyword"
},
"number_all": {
"type": "text"
},
"number_row": {
"type": "text"
},
"archive_info": {
"properties": {
"type": {
"type": "keyword"
},
"number": {
"type": "text"
},
"description": {
"type": "text"
}
}
},
"ts_date_dotic": {
"type": "date"
},
"ts_date1": {
"type": "date"
},
"ts_ref1": {
"type": "keyword"
},
"ts_date2": {
"type": "date"
},
"ts_ref2": {
"type": "keyword"
},
"conformity_rrk": {
"type": "keyword"
},
"conformity_qanonyar": {
"type": "keyword"
},
"content_ocr": {
"type": "text"
},
"qanon_etebar": {
"type": "keyword"
},
"expire_date": {
"type": "date"
},
"exec_date": {
"type": "date"
},
"ref_key": {
"type": "keyword"
},
"ts_date": {
"type": "date"
},
"ts_year": {
"type": "integer"
},
"ts_ref": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"term": {
"type": "keyword"
},
"term_number": {
"type": "integer"
},
"ranking_weight": {
"type": "integer"
},
"main_type": {
"type": "keyword"
},
"sub_type": {
"type": "keyword"
},
"title_type": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"action_type": {
"type": "keyword"
},
"section_len": {
"type": "integer"
},
"eblagh": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "text"
},
"from": {
"type": "keyword"
}
}
},
"rrk": {
"properties": {
"number": {
"type": "text"
},
"date": {
"type": "text"
}
}
},
"exceuter_organs": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "keyword"
}
}
},
"receiver_organs": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "keyword"
}
}
},
"sort_date_timestamp": {
"type": "long"
},
"qanon_relations": {
"properties": {
"from_type": {
"type": "keyword"
},
"from_section_id": {
"type": "keyword"
},
"from_section_title": {
"type": "keyword"
},
"rel_type": {
"type": "keyword"
},
"to_type": {
"type": "keyword"
},
"to_id": {
"type": "keyword"
},
"to_title": {
"type": "keyword"
},
"wait": {
"type": "integer"
},
"rel_key": {
"type": "keyword"
}
}
},
"opinion_relations": {
"properties": {
"from_type": {
"type": "keyword"
},
"from_section_id": {
"type": "keyword"
},
"from_section_title": {
"type": "keyword"
},
"rel_type": {
"type": "keyword"
},
"to_type": {
"type": "keyword"
},
"to_id": {
"type": "keyword"
},
"to_title": {
"type": "keyword"
},
"wait": {
"type": "integer"
},
"rel_key": {
"type": "keyword"
}
}
},
"fixed_date": {
"type": "date"
},
"renewal_date": {
"type": "date"
},
"exec_duration": {
"type": "text"
},
"effective_date": {
"type": "date"
},
"relation_organs": {
"type": "keyword"
},
"files": {
"properties": {
"path": {
"type": "text"
},
"file_name": {
"type": "text"
},
"title": {
"type": "text"
},
"type": {
"type": "keyword"
}
}
},
"is_delete": {
"type": "integer"
},
"time_edit": {
"type": "date"
},
"doc_tags": {
"type": "keyword"
},
"doc_states": {
"type": "keyword"
}
},
"validation": {
"required": [
"id",
"title"
],
"default_value": {
"prompt_type": "question"
}
},
"include_fields": [
"id",
"title",
"title_popular",
"sort_date_timestamp",
"time_edit",
"ts_date",
"ts_ref",
"content",
"doc_states"
],
"exclude_fields": [
"embeddings"
],
"field_joins": {
"ts_date": [
{
"process": "dateTotsmp",
"property": "sort_date_timestamp",
"desc": "dateTotsmp(\"ts_date\",\"/\")"
},
{
"process": "getYearOfDate",
"property": "ts_year",
"desc": "1403/05/06 --> 1403"
}
]
},
"is_array": [
"initial.creators",
"initial.signers",
"initial.ministers",
"initial.files",
"refer.commission_mirrors",
"refer.commission_common",
"refer.commission_common.member_names",
"commission_report.agendas",
"commission_report.meets",
"commission_report.report_nexts",
"parl_handle.agendas",
"parl_handle.meets",
"parl_handle.agree_names",
"parl_handle.against_names",
"parl_handle.demands",
"parl_handle.demand85_names",
"parl_handle.handle_infos",
"before_info.ministers",
"before_info.creators",
"before_info.commission_mirrors",
"letters",
"letters.files",
"counts",
"exceuter_organs",
"receiver_organs",
"qanon_relations",
"opinion_relations",
"relation_organs",
"files",
"doc_states",
"doc_tags"
],
"is_autoid": [
"letters"
],
"query": {
"default_sort": [
{
"_score": "desc"
},
{
"ts_date": "desc"
}
],
"aggregation_fields": {
"ai-codes.label": 100,
"code-ai.label": 100,
"ts_ref": 100,
"state_etebar": 200,
"qanon_etebar": 200,
"sub_type": 100,
"other_type": 100,
"ts_year": 100,
"qanon_title": 100,
"topics.title": 100,
"title_type": 20,
"tvalid_state.tstate": 20,
"tcode_mains.label": 20,
"tcode_subs.label": 20,
"user_edit": {
"size": 100,
"is_value_user_id": true
},
"user_actions.action_key": {
"size": 100,
"name": "user_actions",
"rename_values": {
"expert_tvalid_state": "کارشناس-اعتبارسنجی",
"supervisor_tvalid_state": "ناظر-اعتبارسنجی",
"manager_tvalid_state": "مدیر-اعتبارسنجی",
"expert_tcode_mains": "کارشناس-کداصلی",
"expert_tcode_subs": "کارشناس-کد فرعی",
"supervisor_tcode_mains": "ناظر-کداصلی",
"supervisor_tcode_subs": "ناظر-کد فرعی",
"manager_tcode_mains": "مدیر-کداصلی",
"manager_tcode_subs": "مدیر-کد فرعی"
}
}
},
"collapse_fields": {
"qanon_id": {
"sort": {
"child_order": "asc"
},
"size": 5
}
},
"normal": {
"phrase": {
"title.ph": 12,
"title_popular": 10,
"content.ph": 9
},
"match": {
"title.ph": 4,
"title_popular": 2,
"content.ph": 2
}
},
"sort_keys": {
"lasttitle": {
"sort_date_timestamp": "desc"
},
"firsttitle": {
"sort_date_timestamp": "asc"
},
"title": {
"qanon_title": "asc"
}
},
"filter_keys": {
"f_i":"id",
"f_cat":"category",
"f_rk": "ref_key",
"f_td": "ts_date",
"f_ey": "eb_year",
"f_ws": "work_state.title",
"f_wt": "work_tags.title",
"f_tr": "ts_ref.keyword",
"f_qty": "qanon_type",
"f_se": "state_etebar",
"f_qe": "qanon_etebar",
"f_st": "state_tanghih",
"f_ty": "ts_year",
"f_qt": "qanon_title.keyword",
"f_tt": "title_type",
"f_ttk": "title_type.keyword",
"f_tp": "topics.title",
"f_eo": "exceuter_organs.title",
"f_ro": "receiver_organs.title",
"f_tg": "tags.keyword",
"f_ct": "categories.keyword",
"f_mn": "majles_name",
"f_tn": "term_number",
"f_ed": "error_date.keyword",
"f_mb": "meet_number",
"f_tm": "talker.name",
"f_to": "talker.organ",
"f_md": "meet_date.keyword",
"f_dn": "dastur_number",
"f_mc": "member_count",
"f_nt": "content_type",
"f_mt": "main_type",
"f_sty": "sub_type",
"f_ot": "other_type.keyword",
"f_cal": "code-ai.label",
"f_vs": "tvalid_state.tstate",
"f_cm": "tcode_mains.label",
"f_cs": "tcode_subs.label",
"f_ua": "user_actions.action_key",
"f_ue": "user_edit",
"fr_mid": "tcode_mains.id",
"fr_sid": "tcode_subs.id",
"qanon_id": "qanon_id",
"mqanon_tp": {
"type": "api",
"key": "topics.id",
"service": "tlist",
"url": "/subject/get/childs",
"result": "meta.child_ids",
"body": {
"parents": "@value",
"item_state": 1
}
}
},
"advanced_tags": {
"تاریخ": {
"key": "sort_date_timestamp",
"boost_offset": 30,
"collapse_field": "id"
},
"عنوان": {
"key": "title",
"collapse_field": "id"
},
"متن": {
"key": "content",
"collapse_field": ""
},
"دسته‌بندی": {
"key": "category",
"collapse_field": "id"
},
"برچسب": {
"key": "tags",
"collapse_field": "id"
},
"نویسنده": {
"key": "author.name",
"collapse_field": "id"
},
"سال": {
"key": "created_year",
"boost_offset": 30,
"collapse_field": "id"
},
"اولویت": {
"key": "priority",
"collapse_field": "id"
}
},
"highlight": {
"pre_tags": [
"<span class='text__orange'>"
],
"post_tags": [
"</span>"
],
"fields": {
"title.fa": {},
"title.ph": {},
"qanon_title.fa": {},
"qanon_title.ph": {},
"title_popular": {},
"content.fa": {},
"content.ph": {}
}
},
"search_all_fields":{
"title": "title",
"content": "prompt_answer"
}
}
}