
    Ari:                       d dl Z d dlZd dlZd dlmZ d dlmZmZmZm	Z	 d dl
mZmZmZ d dl
Z
d dlZd dlZd dlZd dl d dlmZ d dlZd dlZd dlZd dlZd dlZd dlZd dlZd dlmZ  ej<                         Zg dZ g d	Z!g d
Z"e"g dz  Z"e"g dz  Z"e"dgz  Z"e"g dz  Z"g dZ#g dZ$ e%e e!z   e"z   e$z   e#z         Z"g dZ&g dZ' e%e e!z   e$z         Z(g dZ)ddZ*ddZ+ G d dej                  jX                  jZ                        Z-y)    N)
namedtuple)ListSetDictOptional)	dataclassfieldfields)*)SQLAlchemyDataDriver)BeautifulSoup)   и   в   во   не   что   он   на   я   с   со   как   а   то   все   она   так   его   но   да   ты   к   у   же   вы   за   бы   по   только   ее   мне   было   вот   от   меня   еще   нет   о   из   ему   теперь
   когда   даже   ну
   вдруг   ли   если   уже   или   ни   быть   был   него   до   вас   нибудь
   опять   уж   вам   ведь   там
   потом   себя   ничего   ей
   может   они   тут   где   есть   надо   ней   для   мы   тебя   их   чем   была   сам   чтоб   без
   будто   чего   раз   тоже   себе   под
   будет   ж
   тогда   кто   этот   того   потому
   этого
   какой   совсем   ним
   здесь   этом   один
   почти   мой   тем
   чтобы   нее   сейчас   были   куда
   зачем   всех   никогда
   можно   при   наконецu   два   об   другой   хоть
   после   надu   больше   тот
   через   эти   нас   про
   всего   них
   какаяu
   много
   развеu   три   эту   моя   впрочемu   хорошо   свою   этой
   перед   иногда
   лучше   чуть   томu   нельзя
   такой   им
   более   всегда   конечно   всю
   между)imemymyselfweourours	ourselvesyouyouryoursyourself
yourselveshehimhishimselfsheherhersherselfititsitselftheythemtheirtheirs
themselveswhatwhichwhowhomthisthatthesethoseamisarewaswerebebeenbeinghavehashadhavingdodoesdiddoingaantheandbutiforbecauseasuntilwhileofatbyforwithaboutagainstbetweenintothroughduringbeforeafterabovebelowtofromupdowninoutonoffoverunderagainfurtherthenonceheretherewhenwherewhyhowallanybotheachfewmoremostothersomesuchnonornotonlyownsamesothantooverystcanwilljustdonshouldnow)u
   сделаu   делu   сделu   дела   этr   r   r   r   r   r   r   r   r   r   r   r   u   всёr   r   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   r   r   r?   r-   r.   r   u   ещёr1   r2   r3   r   r5   r6   r7   r8   r9   r:   r;   r<   r=   r>   r?   r?   r   rB   r%   rD   u   нибытьrE   rF   r%   rH   rI   rJ   rK   rL   r   u   мочьrO   rP   rQ   rR   rS   r   rU   rV   r!   rO   rY   r?   r[   r\   r]   r^   r   r`   ra   rK   rc   r?   re   rf   rg   rh   r   rj      этоrl   rm   rO   ro   r  rq   rr   rs   rt   ru   r   rw   r?   ry   rz      весьr|   r}   r~   r   r2   r   r   r   r   r   r   rh   rV   r   r   rO   rl   r   rh   rs   r   u   свойrh   r   r   r   r   r   rO   r   r   r   r   r   ).u
   такоеri   r   r   r   rW   rM   rZ   r   rd   r   r+   r4   rn   rA   r0   r   r   rk   rb   r{   r   rG   r   rN   rC   r*   r   r   rp   rx   r/   r   rT   r   r   r,   r   rX   rD   r   r   r_   r@   r   rv   )u
   нужноu   хотяu   затоu   именноu   вообщеu   тудаu   такиu   делатьu   помнитьu
   менееu   оноu
   кромеu   твойu   откудаu   считать   внеu   тойu   каждыйu
   никтоu   которыйu
   затемu   вопросu   обаu   длит)rl   r   u   ещu   нибu   мочr  r     веr"  u   своr"  r   u   нужнu   хот   затu
   вообщu   тудr   u   менr   u   кромu   твоr!  r   u   каждu   никтu
   которr#  r   )	r   rl   u   почемуry   rg   rQ   r6   u   можешьrY   )docwwwhttphttpsmailto)u
   авторu   бизнес-процессu   владелецu   датu   исполнителu   контрагентu   логическu
   отделu   постановщикu   приоритетu   проектu   процессu
   спискu   стандартнu   статусu   тип)approvbaseclosedefaultdocumentopentasku   бизнесu   документu
   задачu   задачаu   закрытu
   обычнu   системu   созданu   черновик)textml_textnametagscommentsaddon_fieldskey_phrasesc                    ddl m}m} ddlm}  ||       }|j                         D ]-  \  }}||j                  vr|j                   |||            }/ t        |j                  |j                         ddi            S )Nr   )r0  	bindparam)
postgresql)valueliteral_bindsT)dialectcompile_kwargs)sqlalchemy.sqlr0  r8  sqlalchemy.dialectsr9  items_bindparams
bindparamsstrcompiler<  )sqlparamsr0  r8  r9  stmtkvs           ./cmf/models/cmf_full_search.pysql_debug_not_workrK  ?   sy    ..9D||~ 4!D$$$__Yq234 t||J$6$6$8Z^H_|`aa    c                    | }|j                         D ]w  \  }}t        |t        t        f      rt	        j
                  d| d| d|      };|t	        j
                  d| dd|      }Yt	        j
                  d| dd| d|      }y |S )Nz(:z([^1-9a-zA-Z_]))z\2zNULL\2"z"\2)r@  
isinstanceintfloatresub)rE  rF  resrH  rI  s        rJ  	sql_debugrU  J   s    
C 	F1a#u&&&Bqc!12s"IsCCY&&Bqc!12vDC &&Bqc!12q3KEC	F JrL  c                      e Zd ZdZdZg dZdZg dZed        Z	ed        Z
ed        Zed	        Zed
        Zed'd       Zed(d       Zed)d       Zed        Zed        Ze	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d*d       Zed        Zed        Zed        Zedej0                  j2                  fd       Zed+d       Ze ed      d,d              Zed        Ze edddd      d               Z ed        Z!e	 	 d-d        Z"e	 	 d.d!       Z#e	 	 	 	 	 	 d/	 	 	 d0d"       Z$ed#        Z%ed1d$       Z&ed2d%       Z'eed&               Z(y)3CmfFullSearchuC   
    Сервис полнотекстового поиска.
    russian)r2  text_renderr0  
text_draftr3  	parent_idtree_parent_id
project_idcmf_created_atcmf_modified_atcodecmf_deletedcmf_archivedloginemailphonephone_internalphone_mobilephone_2phone_assistant
ip_addressemail_2birthdayT)fulltext_searchrun_force_reindexindex_statsc                 t    t        |d      j                  d      j                  d      j                         S )Nzhtml.parser
ignore)errors)r   get_textencodedecode)clstext_with_htmls     rJ  
strip_htmlzCmfFullSearch.strip_html   s2    ^];DDTJQQYaQbiikkrL  c                     d}d}t        |      |k  r|gS t        dt        |      |      D cg c]  }||||z   |z     }}|S c c}w )Ni d   r   )lenrange)rw  r0  text_len_maxoverlapr   rT  s         rJ  _split_size_to_partsz"CmfFullSearch._split_size_to_parts   s\     t9|#6M7<QD	<7XY!tAanW,-YY
 Zs   Ac                     dj                  t        j                  d|      D cg c]  }|st        |      dk  s| c}      S c c}w )N z\s2   )joinrR  splitr|  rw  r0  ws      rJ  	_strip_50zCmfFullSearch._strip_50   s6    xxBHHUD$9OqQ3q6B;OPPOs   AAAc                     dj                  t        j                  d|      D cg c]  }|j                         t        vs| c}      S c c}w )Nr  z[ \s<>|&\/%@!`+=;.])r  rR  r  lowerALL_STOP_WORDSr  s      rJ  _strip_not_wordzCmfFullSearch._strip_not_word   s;     xxBHH-CT$Jnqaggi_mNmnoons   A Ac                     dd l }d}d| d| }| j                  j                  j                         j	                  |j                  d      d|i      j                         }|S )Nr   zCmfFullSearchLock:_zKSELECT pg_try_advisory_xact_lock(('x' || md5(:lock_key))::bit(64)::bigint);lock_key)
sqlalchemydp_ddSessionexecuter0  scalar)rw  obj_idsapart_nor  rT  s         rJ  _fullsearch_obj_lock_get_pgz)CmfFullSearch._fullsearch_obj_lock_get_pg   sk    'xq	:ffjj  "**277  4A  ,BD
 68 	 
rL  c                 p   t        j                          }t        j                  d|        d|z   }|st        d      dd l}| j
                  j                  }|j                  |       }|j                  }|j                         }	| j                  |      }
|
st        j                  d| d       y d}|j                  |j                  j                  g      j                  |j                  j                  |k(        j                  |j                  j                   |k(        j#                         }|	j%                  |      j'                         }|rv|d   }|j)                         j                  |j                  j                  |k(        j+                  d|j,                  j/                               }|	j%                  |       nvt        j                  d	|        | j1                         }|j3                         j+                  ||d|j,                  j/                         ||
      }|	j%                  |       |r+ddlm}  |t8        j:                  j<                  d|id       t        j                          |z
  dkD  r.t        j                  dt        j                          |z
          y y )Nzcmf_full_search: mark_dirty zDEBUG_MSG: empty obj_idr   )cmf_full_search: _text_search_sql_insert  lockedT)is_dirtydirty_atz#cmf_full_search: mark_dirty insert )idr  r  r  r  obj_name)schedule_deferred_jobr     )kwargs	countdowng?zPROF mark_dirty() got )timegdebug
ValueErrorr  r  data_driverdp_model_cls	__table__r  r  selectcr  r   r  r  with_for_updater  firstupdatevaluesfuncr  gen_idinsertcmf.includer  models	CmfPersoncelery_full_search_index)rw  r  
fast_index	debug_msgprof_str  ddsa_modeltabler  rT  r  get_stmtget_resid_update_stmtinsert_stmtr  s                     rJ  mark_dirty_deferzCmfFullSearch.mark_dirty_defer   s,   ))+	.vh78!I-	|--VV??3'""JJL--f5GG?xwOPIIuwwzzl#uww~~/0uww'12 "	 	 ))H%++-!*CU577::,-V!WW[[]    IIk"GG9&BC**,CV!!WW[[]#&    IIk"9!&"2"2"K"KU]_eTfrst99; 4'GG,TYY[7-B,CDE (rL  c                     |r| j                  ||      S t        j                  j                  |      }|r
|d   dk(  rd}||dt        j                  |<   t	        t        j                        dk\  rt        d       y y )N)r  r  Tr  r  i'  u   DEV: WARNING! Большое количество блокировок может падать на некоторых инсталяциях)r  r  deferred_fullsearch_dirty_listgetr|  	cmf_alert)rw  r  r  forcer  prevs         rJ  
mark_dirtyzCmfFullSearch.mark_dirty   s     '':'FF//33F;D&,JBL[d3e((0q//0E9  f  g :rL  c                 l   |ri t         _        y t         j                  sy t         j                  }t        |      dkD  rt        dt        |              t	        |j                               D ]9  }||   }| j                  ||j                  d      |j                  d             ; i t         _        y )Nr  ua   DEV: CmfFullSearch помечено грязными слишком много объектов: r  r  r  )r  r  r|  r  sortedkeysr  r  )rw  
only_clean
dirty_listr  r  s        rJ  apply_deferred_dirtyz"CmfFullSearch.apply_deferred_dirty   s    /1A,//55
z?Ryz}  I  {J  zK  L  MZ__./ 	qF'F  FJJ|4LX^XbXbcnXo p	q ,.(rL  c                    |st        d      dd l}| j                  j                  }|j	                  |       }|j
                  }|j                         }|j                  |j                  j                  g      j                  |j                  j                  |k(        j                  |j                  j                  dk(        j                         }|j                  |      j                         }|rv|d   }	|j!                         j                  |j                  j                  |	k(        j#                  dd|j$                  j'                               }
|j                  |
       | j                  j)                          y )Nr  r   F)r  is_dirty_patchr  )r  r  r  r  r  r  r  r  r  r  r   r  r  r  r  r  r  r  r  r  commitrw  r  r  r  r  r  r  r  r  r  r  s              rJ  
mark_cleanzCmfFullSearch.mark_clean
  s    |--VV??3'""JJL IIuwwzzl#uww~~/0uww!+, "	 	 ))H%++-!*CU577::,-V"#(WW[[]    IIk"rL  c                 8   |st        d      dd l}| j                  j                  }|j	                  |       }|j
                  }|j                         }|j                  |j                  j                  g      j                  |j                  j                  |k(        j                  |j                  j                  dk(        j                         }|j                  |      j                         }|r|d   }	|j!                         j                  |j                  j                  |	k(        j#                  |j$                  j'                         t)        j*                  d      z         }
|j                  |
       | j                  j-                          y )Nr  r      )hours)delay_error_reindex)r  r  r  r  r  r  r  r  r  r  r   r  r  r  r  r  r  r  r  r  datetime	timedeltar  r  s              rJ  mark_delay_error_reindexz&CmfFullSearch.mark_delay_error_reindex)  s+   |--VV??3'""JJL IIuwwzzl#uww~~/0uww!+, "	 	 ))H%++-!*CU577::,-V(*8J8JQR8S(S    IIk"rL  Nc&                 
   |dk(  r,| j                  |       | j                  j                          y | j                  |      d d }|}&d}'|r|'|z  }'|	r|'d|	z   z  }'|
r|'d|
z   z  }'|!r|'d|!z   z  }'|&d|!z   z  }&| j	                  |'      }(| j                  |(      }(|dk(  r'|rA| j                  |      })| j                  |)      }*|d d  d|*d    })| j	                  |)      }+n|d d  })|)g}*| j	                  |)      }+|r%|}| j	                  |      },| j                  |,      },nd }d },|r(| j                  |      }|d d }| j	                  |      }-nd }d }-|r(| j                  |      }.|.d d }.| j	                  |.      }/nd }.d }/|r1t        |      }0| j                  |0      d d }0| j	                  |0      }1nd }0d }1|!r|!}!|!}2n%d }!d }2n |d	k(  rd g}*d }+d }d },d }d }-d }.d }/d }0d }1d }!d }2d })d}3|r|}3|!r|!d d dz   |3z   }3d}4|r5|4dj                  d
 t        |j                               D              d d z  }4|r|4d|d d z   z  }4|4d|z   z  }4| j                  |3      }3| j                  |4      }4| j	                  |3      }5| j	                  |4      }6|5j                  d      }7|6j                  d      }8|7|8z   }9|9dk  rn|9dz
  }:|7dkD  rr|8dk\  rEdj                  |5j                         d d       }5dj                  |6j                         d d       }6ndj                  |5j                         d d|8z
         }5|6}6nq|7dk  r(|5}5dj                  |6j                         d d|7z
         }6nDdj                  |5j                         d d       }5dj                  |6j                         d d       }6|dz   |3z   dz   |4z   };|;d d };|;j                         };| j	                  |;      }< | j                  d<i ddd|d|d|d|d|d|d|d|d|d|d|d|d|d|d |d!|d"|d#|d$|d%)d&+d'|&d(|(d)|d*,d+|d,-d-|;d.|<d/.d0/d10d21d3| d4|!d52d6|"d7|#d8|$d9|% t        *      d:kD  rt        |*d:d  d:      D ]  \  }=}>|d d  d|> })| j	                  |)      }+ | j                  d<i d|=d|d|d|d|d|d|d|d|d|d|d|d|d|d|d |d!|d"|d#|d$|d%|)d&|+d-d d.d d'|&d(d d)d d*d d+d d,d d/d d0d d1|0d2|1d3| d4d d5d d6|"d7|#d8|$d9|%  | j!                  |t        |*      ;       | j                  j                          y )=N	9_disablei   r  z DZQTN 0_full_indexr   i 1_namec              3       K   | ]  }|  y wN ).0rI  s     rJ  	<genexpr>z-CmfFullSearch.index_object.<locals>.<genexpr>  s     *Z1*Zs   i  u
    Тэги i>  i  i*  i:  r  r  	obj_modelobj_parent_idobj_tree_parent_idobj_project_idobj_company_idobj_created_atobj_modified_atobj_codeobj_deletedobj_archivedobj_owner_nameobj_author_nameobj_modified_by_nameobj_responsible_namesobj_hrefobj_logic_type_codeobj_activity_codeobj_status_typeobj_texttext_for_vecr  name_for_vecobj_tagstags_for_vecobj_result_textresult_text_for_vecobj_ml_textml_text_for_vecobj_commentscomments_for_vecobj_addon_fieldsobj_addon_fields_for_vecobj_user_ratingobj_key_phraseskey_phrases_for_vecobj_breadcrumbsobj_related_person_loginsobj_user_portal_topobj_client_portal_topr  )delete_from_partnor  )'_text_search_sql_delete_obj_with_childsr  r  r  r  r  rC  r  setr  countr  r  _text_search_sql_insertr|  	enumerate_text_search_sql_delete_partno)?rw  
model_namer  r  r2  r0  text_prefixtext_suffixfull_search_typecompany_nameproject_namemodel_verbose_namer  comments_textr  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  obj_addon_fields_dictr	  r
  r  r  r  r  r  
smart_namer  r  obj_text_listr  r   r  obj_comments_textcomments_text_for_vecr  r  r  obj_ml_text_mainobj_ml_text_suffixml_text_main_for_vecml_text_suffix_for_vecmain_token_lensuffix_token_lentotal_token_lenoversize_lenr  r  r  obj_text_parts?                                                                  rJ  index_objectzCmfFullSearch.index_objectF  s   T {*77?FFMMO mmK0$7 
$J#,,J#,,J#//J	O33H**:6}}\2~-==. # 8 8 B)%401=3C2DE"228<)%401!)
"228<#"228<"}}\:#"%--"@"1'6":&)&9&9/&J#"&&*#$'MM-$@!$5gv$>!(+(;(;<M(N%$(!(,%$#&'<#= #&==1A#B7F#K +.+>+>?O+P(#' +/("1&5# #'&*#)!FMLHL"O"& $$(!#'+$"O"&H #.u5;>NN #((*Zc:O:V:V:X6Y*Z"Z[`\`"aa,%4"@@cM11==)9: ]]+=>"223CD!$!4!45G!H-33C8177<(+;; V#  +V3L&#u,+.884H4N4N4PQXRX4Y+Z(-0XX6L6R6R6TU[V[6\-]*+.884H4N4N4PQqR`aqRq4r+s(-C*&(';$),2H2N2N2PQoR`aoRo2p)q& (+xx0D0J0J0LWf0U'V$),2H2N2N2PQWRW2X)Y& "C'*::S@CUU!'6* "'')--k: 	$## 	
A 	
f 	

 	
'	
<N	
_m	
 M	
)	
;J	
 	
 ,7	
 FR	
 cq	
 ,		
 CW		

 #8	

 CK	
 !4	
 HY	
 kz	
 	
 -9	
 	
 -9	
 	
 -9	
 ,	
 BU	
 $	
 6E	
 +	
 >S	
 .	
 Ia	
 ,	
  ,!	
  BU!	
" ,#	
$ '@%	
& !4'	
( #8)	
, }!*3M!"4Eq*I &)%401=/B"228<+++ G F V` "/DVgu GU#1CR & 4? NZ ky %4	 K_	
 +@
 KS )< Pa sB & 5A !% 7; & 59 " 15 %) ?C "& 9= &6 Qi %4  %)!  ?C!" %4#$ /H%& )<'( +@)	4 	**6c-FX*YrL  c                 p   |st        d      dd l}| j                  j                  }|j	                  |       }|j
                  }|j                         }|j                  |      j                  |j                  j                  |k(  |j                  j                  |k(  z        }|j                  |       y Nr  r   )r  r  r  r  r  r  r  deleter   r  r  r  r  )rw  r  r  r  r  r  r  del_stmts           rJ  r  z5CmfFullSearch._text_search_sql_delete_obj_with_childsC  s     |--VV??3'""JJL99U#))WW""f,WW^^v%'
 	
		(rL  c                    |st        d      dd l}| j                  j                  }|j	                  |       }|j
                  }|j                         }|j                  |      j                  |j                  j                  |k(        j                  |j                  j                  |k\        }|j                  |       y r/  )r  r  r  r  r  r  r  r0  r   r  r  r  r  )	rw  r  r  r  r  r  r  r  r1  s	            rJ  r  z,CmfFullSearch._text_search_sql_delete_partnoW  s    |-- VV??3'""JJL IIeU577>>V+,U577??&889 	 	
		(rL  c*           	         t         j                  d|        |st        d      dd l}*| j                  j
                  }+|+j                  |       },|,j                  }-|+j                         }.| j                  |      }/|/st         j                  d| d       y |*j                  |-j                  j                  g      j                  |-j                  j                  |k(        j                  |-j                  j                  |k(        j!                         }0d }1|r|j#                  d      d   }1t%        |.j'                  |0            }2|2rt)        |2      dkD  rt+        d| d| d	d
       |2d   d   }3 |-j-                         j                  |-j                  j                  |3k(        j.                  d<i d|ddddd|d|d|d|d|d|d|d|!d|d|1d|d|d|d|d|	d|*j0                  j2                  j5                  | j6                  |      d |*j0                  j2                  j5                  | j6                  |      d!|*j0                  j2                  j5                  | j6                  |      d"|*j0                  j2                  j5                  | j6                  |      d#|*j0                  j2                  j5                  | j6                  |      d$|*j0                  j2                  j5                  | j6                  |       d%|*j0                  j2                  j5                  | j6                  |"      d&|
d'|d(|d)|d*|d+|d,|d-|d.|d/|d0|d1|#d2|$d3|*j0                  j2                  j5                  | j6                  |%      d4|&d5|'d6|*j0                  j2                  j5                  | j6                  |'      d7|(d8|)}4|.j'                  |4       |3S t         j                  d9|        | j9                         }3 |-j;                         j.                  d<i d:|3ddddd;|d|d|d|d|d|d|d|d|d|!d|d|1d|d|d|d|d|	d|*j0                  j2                  j5                  | j6                  |      d |*j0                  j2                  j5                  | j6                  |      d!|*j0                  j2                  j5                  | j6                  |      d"|*j0                  j2                  j5                  | j6                  |      d#|*j0                  j2                  j5                  | j6                  |      d$|*j0                  j2                  j5                  | j6                  |       d%|*j0                  j2                  j5                  | j6                  |"      d&|
d'|d(|d)|d*|d+|d,|d-|d.|d/|d0|d1|#d2|$d3|*j0                  j2                  j5                  | j6                  |%      d4|&d5|'d6|*j0                  j2                  j5                  | j6                  |'      d7|(d8|)}5|.j'                  |5       |3S )=Nr  r  r   r  :r  u   Для объекта z	(part_no=u   ) задублировались записи в поиске. Обратитесь в техническую поддержку!Tabortr  r  Fr  r  r  r  r  r  r  r  r  r  obj_parent_modelr  r  r  r  r  name_tsvectortext_tsvectorml_text_tsvectortags_tsvectorresult_text_tsvectorcomments_tsvectoraddon_fields_tsvectorr  r  r  r  r  r  r  r  r  r  r  r	  r
  key_phrases_tsvectorr  r  "obj_related_person_logins_tsvectorr  r  z0cmf_full_search: _text_search_sql_insert insert r  r  r  )r  r  r  r  r  r  r  r  r  r  r  r  r  r   r  r  r  r  listr  r|  r  r  r  rE  r  to_tsvector_fts_configr  r  )6rw  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r  r  r  r  r  r  r	  r
  r  r  r  r  r  r  r  r  r  r  rT  r  r7  r  r  r  r  s6                                                         rJ  r  z%CmfFullSearch._text_search_sql_insertj  s   ( 	
;F8DE|-- VV??3'""JJL--f5GG?xwOP IIuwwzzl#U577>>V+,U577??g-._	 	  ,2237:qyy*+7|a26()G9  MN  O  W[  \!*Q-CuwwzzS()/ "/ #	/
 $)/ (/ &/ &/ !,/ &/ %4/ "./ &6/ #0/ &6/  (:!/" $2#/$ $2%/& $2'/( %4)/* #%&&++"9"9#//<"X+/, #%&&++"9"9#//<"X-/. &(VV[[%<%<S__o%^//0 #%&&++"9"9#//<"X1/2 *,)@)@Re)f3/4 ')ffkk&=&=cooO_&`5/6 +-&&++*A*A#//Sk*l7/8 &9/: !,;/< ".=/> $2?/@ %4A/B *>C/D +@E/F &G/H )<I/J '8K/L %4M/N %4O/P %4Q/R *,)@)@Re)fS/T %4U/V /HW/X 8:vv{{7N7Ns`y7zY/Z )<[/\ +@]/ d IIk"p 
m GGFvhOP**,C00 #0 $)	0
 $0 "0 (0 &0 &0 !,0 &0 %40 ".0 &60 #00  &6!0" (:#0$ $2%0& $2'0( $2)0* %4+0, #%&&++"9"9#//<"X-0. #%&&++"9"9#//<"X/00 &(VV[[%<%<S__o%^102 #%&&++"9"9#//<"X304 *,)@)@Re)f506 ')ffkk&=&=cooO_&`708 +-&&++*A*A#//Sk*l90: &;0< !,=0> ".?0@ $2A0B %4C0D *>E0F +@G0H &I0J )<K0L '8M0N %4O0P %4Q0R %4S0T *,)@)@Re)fU0V %4W0X /HY0Z 8:vv{{7N7Ns`y7z[0\ )<]0^ +@_0 f IIk"
rL  objc                     |j                   sy | j                  D ](  }|dk(  r	||j                  v s||   j                  s( y y )Nr_  T)full_searchrequired_fieldsr
   
is_changed)rw  rD  
field_names      rJ  is_obj_need_reindexz!CmfFullSearch.is_obj_need_reindex  sJ     -- 	J..SZZ'C
O,F,F		rL  c           
         t         j                  j                  j                         D ]*  }|j                  s|j
                  }|r||vr$d}t        j                  |      }|j                  }|rdg}	 t        j                         }	|j                  ||||z   gdgdd      }
|
s|
D ]J  }|r6t        j                  j                  |j                  j                  d       ;|j                          L |t!        |
      z  }| j"                  j%                          t&        j)                  d| d| d	t        j                         |	z
  d
d        y )Nr   r  Tr^  )r
   sliceorder_byinclude_archivedinclude_deleted)r  zCmfFullSearch.reindex_models: r4  z, z0.3z sec)cmfr  	CmfEntityiter_subclassesrF  
class_namecmfutilget_model_by_namefull_search_preload_fieldsr  rA  rW  r  r  r:  full_search_indexr|  r  r  r  r  )rw  models_listcommit_everylazy	model_clsr  offsetmodel
field_listtsobj_listrD  s               rJ  reindex_modelszCmfFullSearch.reindex_models  sQ   --==? 	lI(("--Jz<F--j9E99J"V
YY[ ::%!6,#67./%)$( &   # 0C,,77D7Q--/	0
 #h-'8AfXRPTPYPYP[^`P`adOeeijk' 	lrL  )	only_oncec                    | r|st        dd       t        j                  d       t        j                  j                          t        j                  }|j                  j                  }|j                  |      }|j                  }|j                         }d}| rd}t        d| dd	d
       	 d| d| d}|j                  |      }	t        |	      }	|j                          t!        |	      rt        dd	d
       nt                y [)Nu   Поиск после обновления должен работать сразу, с приемлемым качеством без переиндексацииTr5  zRun run_force_reindexr  r  zrun_force_reindex Process z=true: r  )endflushz
                WITH cte AS (
                SELECT
                    id as id
                FROM   cmf_full_search
                WHERE  z=false AND part_no=0
                ORDER BY dirty_at DESC
                LIMIT  1000
                )
                UPDATE cmf_full_search s
                SET
                    zt = true
                FROM   cte
                WHERE  cte.id = s.id
                RETURNING s.id;
            .)r  r  r  r  CmfAccessListcheck_admin_moderW  r  r  r  r  r  printr  rA  r  r|  )

from_patchi_do_manual_migrationrw  r  r  r  r  dirty_fieldrE  rT  s
             rJ  rn  zCmfFullSearch.run_force_reindex:  s    3   |  EI  J	')--/""VV??3'""JJL !*K*;-w?RtT
 $} % !M "C  ))C.Cs)CHHJ3xcr.1 rL  c                     t         j                  j                          | j                         }| j                  d      }||dS )NT)r  )totaldirty_count)r  rg  rh  r  )rw  rn  ro  s      rJ  ro  zCmfFullSearch.index_statsz  sA    --/		iii. &
 	
rL  z	@minutely   )rb  
system_jobschedulepriorityc                  d	   t         j                  d       t        j                  j	                         rt         j                  d       y t        j                  d       t        j
                         } d}g }	 t        j                  j                  dgg ddg d	g d
gdddt        j                  j                         t        j                  d      z
  gg dgdddt        j                  j                         gg dggdgddg      }|snpi }|D ]H  }t        j                  |j                        }||vrg ||<   ||   j                  |j                         J |j!                         D ]D  \  }}t         j                  d| dt#        |              t%        |       |D ]	  }|j'                  |j(                  dd|gd      }	|dz  }|	st        j                  j'                  |g d      }
d|
j*                   d|
j,                   d|
j                   d |
j.                   d!|
j0                   
}t         j                  |t2        j4                  "       t        j                  j7                  |       t         j                  d#|	j*                          	 |	j9                           G t        j
                         | z
  d&kD  rut        j                  j                         jG                         jH                  }|dk\  r |dk  rt        j
                         | z
  d'kD  rnTt         j                  d(| d)       n:tE                t         j                  d*| d+       t        j                  d       |rQt         j                  d,t2        j4                  "       |D ]  \  }}t         j                  | d-|        ! |d   d   t         j                  d.t        j
                         | z
          y # t:        $ r}t         j                  d$| d|	 d%| t2        j4                  "       t         j                  t=        j>                                |j                  ||g       tA                t        j                  jC                  |       tE                Y d }~d }~ww xY w)/NzStart cron_index_dirtyuJ   Не индексируем поскольку запущен импорт   r   Tr  )r  =r   OR)r  rv  T)r  rv  Tr  <   )minutes)r  rv  Nr  )r  rv  Nz	-dirty_at   )r
   filterrM  rL  zcron_index_dirty process r  r  rv  )r
   r|  rO  r  )r  r  r  r  r  )r  r
   u   cron_index_dirty: Не индексируем поскольку объект не найден или удален в бд fullsearch id:z
 obj name:z	 obj id: z
 dirty_at:z
 is_dirty:)levelzobj_id=u#   ERROR! При индексации u"    произошла ошибка:    -   ua   cron_index_dirty превышен лимит времени 20 секунд обработано u    объектовu&   cron_index_dirty обработано u    объектов, sleep 1u2   При индексации были ошибки:z: zEnd cron_index_dirty at )%r  r  rT  enable_import_modeimport_is_runningr  sleepr  rW  slistr  r  r  get_model_by_idr  appendr@  r|  ri  r  rV  r  r  r  r  loggingERRORr  rW  	Exception	traceback
format_exccmf_rollbackr  
cmf_commit
astimezonehour)stn
exceptionsobj_id_listobj_ids_by_modelr  r]  id_listr  rD  
search_rawerr_strer  s                 rJ  cron_index_dirtyzCmfFullSearch.cron_index_dirty  su    	
()%%779GG`a

1YY[
 ..44XJ*=+/1HJg*h+/-7h>O>O>S>S>UX`XjXjstXu>u,v,C+,
 *.-BCIZIZI^I^I`,a,N)*.(" 1<}-.G' 5 5K( !% >//> 00.0$U+ '..v}}=	> #3"8"8": %w3E7!CL>JKg" %C))5+K+KUY[^`cTdvz){CFA%+%9%9%=%=S  RF%=  &G
%11;z*J]J]I^^ghrhyhygz  {E  FP  FY  FY  EZ Z,,6,?,?+@#B w}}=,,TTUXY GGgcffX./%--/%%6 yy{R"$((,,.99;@@19 yy{R'",GG  AB  @C  CT  U  VLGG<QC?YZ[JJqMM P GGHQXQ^Q^G_$ 'Q3%r!&'Q-""	*499;r>*:;<; % %"EcU!C5Prstru v  F  L  L  M	 4 4 67"))3(3$,,EEcJ"%s    P  	R/	BR**R/c                 h    t        j                  j                  j                  j                  |i |S r  )rP  r  cmf_search_engineCmfSearchEngine
fts_search)rw  argsr  s      rJ  rm  zCmfFullSearch.fulltext_search  s)    zz++;;FFWPVWWrL  c
                     g S r  )rI  rL  addon_filterr  archiveddeletedtree_parent_filterformatr  _build_tags_filtertag_name#_build_related_person_logins_filterALLOWED_FIELDSr  rP  r  rW  r  r  r  r  rR  rS  tuplequery_qstop_words
query_likequery_obj_coderA  )*rw  model_name_intsquery_strforce_field_nameage_dayslabelforce_sliceinclude_attachmentsearchmarkrI  rL  r[  modified_by_nameresponsible_namelogic_type_codestatus_typemodified_at_beforemodified_at_afterr  r  user_rating
owner_nameauthor_namer  include_attachment_filtertags_filtertags_params
rpl_filter
rpl_paramssearch_fieldheadline_fieldtsvector_field
slice_from	slice_forage_days_subquery
max_partnotsquery_str_h1tsquery_str_h2ts_rank_normalizerE  
found_objss*                                             rJ  search_oncezCmfFullSearch.search_once  s	     rL  c
                 	   dj                  |j                               }
|xs |	j                  }|t        vrt	        d| d       d| }d| }|dk(  s|dk(  rd}| d	}d
 } |||      \  }}}d}d}d}d}d}d}|rd| d}d| d}|rd| d}d| d}|rd| d}d| d}d}|	j
                  r5t        |	j
                  j                  d            dk\  r|dk(  rd}nd| d}|r|}n|	j                  }|	j                  j                  dd       }|	j                  j                  d      }|	j                  j                  dd       }|	j                  j                  d d       }|	j                  j                  d!d       }|	j                  j                  d"      } |	j                  j                  d#      }!|	j                  }"|	j                  }#|	j                  j                  d$      }$|	j                  j                  d%d       }%|	j                  j                  d&|%      }&|%xs |&}%|	j                  }'|'r d'j                  d(j                  |'            nd}'|	j                  j                  d)      xs g }(| j                  |(      \  })}*|r| j                  |      \  }+},nM|	j                  j                  d*      r.| j                  |	j                  j                  d*            \  }+},nd}+i },|d+   }-|d,   |d+   z
  }.d}/|rt!        |      }d-| d.}/d+}0|d/k(  rd0}0t#        t%        j&                  d1d|      j                               }1d2j                  |1t#        t(              z
        }2d2j                  |1t#        t(              z
        }3|2}4|3}5d}6d }7|	j*                  r)d2j                  |	j*                  j                               }7 d3j                  dFi |'|||/||)|6|+|||||||d4}8i d5|	j,                  d6|d7|	j.                  d8|2d9|3d:|4d;|5d<|-d=|.d>t1        |      d?|d@|7xs d dA|	j
                  xs d dB|	j2                  xs d dC|	j4                  dD|0d||%|&||||| |!|"|#|$|||dE|*|,}9t6        j8                  j:                  j<                  j?                         jA                  |8|9      jC                         }:tE        |:      S )GNz & 6   Недопустимое значение field_name: Tr5  obj_r2  r1  r  	_tsvectorc                    g }g }g }d}|dgk(  rd}t         j                  d   |   }t         j                  d   |   }t        t        j                  dd|       j                               D ]  }|t         j                  vs|t         j                  |   vr|j                  |       <t         j                  |   |   |kD  r|j                  |       gt         j                  |   |   |kD  r|j                  |       |j                  |        dj                  |      dj                  |      dj                  |      fS )	NdiCmfTasktiFST_FRQ_MIDFST_FRQ_LOW[()!|&']r   | )APPFTS_STAT_WORDSr  rR  rS  r  r  r  )	tsqueryr  
query_high	query_mid	query_lowselectorfrq_midfrq_lowtokens	            rJ  tsquery2idfqueryz<CmfFullSearch.search_once_top_bm25.<locals>.tsquery2idfquery  s2   JIIH+((7AG((7AGRVVJsG<BBDE - 2 22hcFXFXY^F_6_$$U+''.x87B$$U+''.x87B$$U+%%e,- ::j)JJy)JJy)* *rL  0z&ts_rank(setweight(name_tsvector,'C')||z, :query_high)zts_rank(setweight(z,'C'), :query_high)z&ts_rank(setweight(name_tsvector,'B')||z, :query_mid)z,'B'), :query_mid)z&ts_rank(setweight(name_tsvector,'A')||z, :query_low)z,'A'), :query_low)r  r     r  z\ or (obj_ml_text LIKE '%' || :query_like || '%' or obj_text LIKE '%' || :query_like || '%') z or z  LIKE '%' || :query_like || '%' r[  r  r  r  r  r  r  r  r  r   AND obj_tree_parent_id IN ('{}')','r  related_person_loginr   r  $obj_modified_at > now() - interval '
 days' ANDr0  @B r  r  uD  

            SET gin_fuzzy_search_limit=100000;
            SET default_text_search_config = 'russian';

            SELECT
                subs.*,
                subs.obj_name as title,
                ts_headline(
                    'russian',
                    substring(COALESCE(subs.headline_raw, subs.obj_name) FROM 0 FOR 100000),
                    plainto_tsquery(:param_search_query),
                    'MaxFragments=1,MaxWords=25,MinWords=24'
                ) AS headline
            FROM (
                (
                    SELECT
                        obj_id,
                        obj_code,
                        {headline_field} as headline_raw,
                        obj_model,
                        --obj_name || ' - ' || rank as title,
                        obj_breadcrumbs as breadcrumbs,
                        obj_project_id as obj_project_id,
                        obj_parent_id as obj_parent_id,
                        obj_related_person_logins as obj_related_person_logins,
                        :label || (CASE WHEN EXTRACT(days from (now()-obj_modified_at)) < 3*365 then 'R' else '' end) as label,
                        --- Множители сбросили на 1 тк выше уже есть умножение через A B C D но можно убрать A B C D Сделать только С а множители расставить уже здесь
                        {txt_tsrank_high_string} + {txt_tsrank_mid_string} * 1 + {txt_tsrank_low_string} * 1 as rank,
                        EXTRACT(days from (now()-obj_modified_at)) as age_days,
                        {tsvector_field} as result_tsvector, {search_field} as result_text, name_tsvector, obj_name, obj_modified_at,
                        obj_user_portal_top, obj_client_portal_top,
                        'flow_main' as mark
                    FROM cmf_full_search
                    WHERE
                        -- obj_code='DOC-006092' and
                        {age_days_subquery}
                        ({search_field} != '' and {search_field} is not null) and
                        (
                           ( {tsvector_field} @@ :txt_tsquery_str_where_OR
--ниже спорная строка часть данных может не найтись сложная проблема релевантности текста и имени
                             and ts_rank({tsvector_field}, :txt_tsquery_str_rank_OR) > 0.0001
                           )
                         OR
                           (:query_like is not null and  {search_field} LIKE '%' || :query_like || '%')
                         )

                        -- Это делаем, чтобы исключить отсюда результаты из top1
                        -- но это может привести к тому, что по top1 ранк низкий а здесь высокий, но мы его исключили
                        -- and not(name_tsvector @@ :tsquery_str_where_OR)

                        and ((obj_model IN :model_name_in))
                        and (:query_qstop_words_str is null or not(text_tsvector @@ :query_qstop_words_str))
                        and part_no <= :max_partno
                        and ( :parent_id is null or obj_project_id = :parent_id )
                        AND ( :owner_name IS NULL OR obj_owner_name = :owner_name )
                        AND ( :author_name IS NULL OR obj_author_name = :author_name )
                        AND ( :modified_by_name IS NULL OR obj_modified_by_name = :modified_by_name)
                        and ( :responsible_name is null or obj_responsible_names ILIKE '%' || :responsible_name || '%' )
                        {tags_filter}
                        {rpl_filter}
                        {tree_parent_filter}
                        and ( :logic_type_code is null or obj_logic_type_code = :logic_type_code )
                        and ( :status_type is null or obj_status_type = :status_type )
                        AND ( :modified_at_before IS NULL OR obj_modified_at <= :modified_at_before )
                        AND ( :modified_at_after IS NULL OR obj_modified_at >= :modified_at_after )
                        and ( :archived is null or obj_archived = :archived )
                        AND ( :deleted IS NULL OR obj_deleted = :deleted )
                        and ( :user_rating is null or obj_user_rating >= :user_rating)
                        and 1=1
                    ORDER BY rank desc
                    LIMIT :slice_for OFFSET :slice_from
                )
            UNION ALL
                (
                    SELECT
                        obj_id,
                        obj_code,
                        COALESCE({headline_field}, obj_name) as headline_raw,
                        obj_model,
                        --obj_name || ' - ' || rank as title,
                        obj_breadcrumbs as breadcrumbs,
                        obj_project_id as obj_project_id,
                        obj_parent_id as obj_parent_id,
                        obj_related_person_logins as obj_related_person_logins,
                        :label || (CASE WHEN EXTRACT(days from (now()-obj_modified_at)) < 3*365 then 'R' else '' end) as label,
                        --- Множители сбросили на 1 тк выше уже есть умножение через A B C D но можно убрать A B C D Сделать только С а множители расставить уже здесь
                        {txt_tsrank_high_string} + {txt_tsrank_mid_string} * 1 + {txt_tsrank_low_string} * 1 as rank,
                        EXTRACT(days from (now()-obj_modified_at)) as age_days,
                        ml_text_tsvector as result_tsvector, obj_text as result_text, name_tsvector, obj_name, obj_modified_at,
                        obj_user_portal_top, obj_client_portal_top,
                        'flow_attach' as mark
                    FROM cmf_full_search
                    WHERE
                        {age_days_subquery}
                        (obj_ml_text != '' and obj_ml_text is not null)
                        and (obj_parent_model != 'CmfPerson')
                        and (
                            ( ml_text_tsvector @@ :txt_tsquery_str_where_OR
--ниже спорная строка часть данных может не найтись сложная проблема релевантности текста и имени
                             and ts_rank(ml_text_tsvector, :txt_tsquery_str_rank_OR) > 0.0001
                            )
                            OR (:query_like is not null and  obj_text LIKE '%' || :query_like || '%')
                            OR (:query_like is not null and  obj_name LIKE '%' || :query_like || '%')
                            OR (:query_like is not null and  obj_ml_text LIKE '%' || :query_like || '%')


                         )

                        and ('{tsvector_field}' = 'ml_text_tsvector')
                        -- Это делаем, чтобы исключить отсюда результаты из top1
                        -- но это может привести к тому, что по top1 ранк низкий а здесь высокий, но мы его исключили
                        -- and not(name_tsvector @@ :tsquery_str_where_OR)

                        and (obj_parent_model IN :model_name_in and obj_model = 'CmfAttachment')
                        and (:query_qstop_words_str is null or not(ml_text_tsvector @@ :query_qstop_words_str))
                        and part_no <= :max_partno
                        and ( :parent_id is null or obj_project_id = :parent_id )
                        AND ( :owner_name IS NULL OR obj_owner_name = :owner_name )
                        AND ( :author_name IS NULL OR obj_author_name = :author_name )
                        AND ( :modified_by_name IS NULL OR obj_modified_by_name = :modified_by_name)
                        and ( :responsible_name is null or obj_responsible_names ILIKE '%' || :responsible_name || '%' )
                        {tags_filter}
                        {rpl_filter}
                        {tree_parent_filter}
                        and ( :logic_type_code is null or obj_logic_type_code = :logic_type_code )
                        and ( :status_type is null or obj_status_type = :status_type )
                        AND ( :modified_at_before IS NULL OR obj_modified_at <= :modified_at_before )
                        AND ( :modified_at_after IS NULL OR obj_modified_at >= :modified_at_after )
                        and ( :archived is null or obj_archived = :archived )
                        AND ( :deleted IS NULL OR obj_deleted = :deleted )
                        and ( :user_rating is null or obj_user_rating >= :user_rating)
                        and 1=1
                    ORDER BY rank desc
                    LIMIT 20 OFFSET :slice_from/:slice_for*4
                )
            UNION ALL

                (
                    SELECT
                        obj_id,
                        obj_code,
                        ts_rank(setweight(name_tsvector,'C') || {tsvector_field}, :tsquery_str_rank_OR)|| obj_name ||' '|| {headline_field} as headline_raw,
                        obj_model,
                        --obj_name as title,
                        obj_breadcrumbs as breadcrumbs,
                        obj_project_id as obj_project_id,
                        obj_parent_id as obj_parent_id,
                        obj_related_person_logins as obj_related_person_logins,
                        :label || (CASE WHEN EXTRACT(days from (now()-obj_modified_at)) < 3*365 then 'R' else '' end) as label,
                        -- (ts_rank(setweight(name_tsvector,'C') || ml_text_tsvector, :tsquery_str_rank_OR, {ts_rank_normalize}))
                        -- * (CASE WHEN EXTRACT(days from (now()-obj_modified_at)) < 3*365 then
                        --             ((3*365-EXTRACT(days from (now()-obj_modified_at)))/30/100+1)
                        --   else 1 end) as rank,
                        --- Множители сбросили на 1 тк выше уже есть умножение через A B C D но можно убрать A B C D Сделать только С а множители расставить уже здесь
                        {tsrank_high_string} + {tsrank_mid_string} * 1 + {tsrank_low_string} * 1 as rank,
                        EXTRACT(days from (now()-obj_modified_at)) as age_days,
                        COALESCE({tsvector_field}, name_tsvector) as result_tsvector, {search_field} as result_text, name_tsvector, obj_name, obj_modified_at,
                        obj_user_portal_top, obj_client_portal_top,
                        'flow_name' as mark
                    FROM cmf_full_search
                    WHERE
                        {age_days_subquery}
                        ({search_field} != '' and {search_field} is not null) and
                        (
                           (
                             name_tsvector @@ :tsquery_str_where_OR
                             and ts_rank(setweight(name_tsvector,'C') || {tsvector_field}, :tsquery_str_rank_OR) > 0.0001
                           )
                         OR
                           (:query_like is not null and {search_field} LIKE '%' || :query_like || '%')
                         -- <% медленная и на ml_text запускать не вариант 10сек
                         -- experimental
                         OR (:search_query_wo_qstop  <<% obj_name)
                        )
                        and (obj_model IN :model_name_in)
                        and (:query_qstop_words_str is null or not(text_tsvector @@ :query_qstop_words_str))
                        and part_no <= :max_partno
                        and ( :parent_id is null or obj_project_id = :parent_id )
                        AND ( :owner_name IS NULL OR obj_owner_name = :owner_name )
                        AND ( :author_name IS NULL OR obj_author_name = :author_name )
                        AND ( :modified_by_name IS NULL OR obj_modified_by_name = :modified_by_name)
                        and ( :responsible_name is null or obj_responsible_names ILIKE '%' || :responsible_name || '%' )
                        {tags_filter}
                        {rpl_filter}
                        {tree_parent_filter}
                        and ( :logic_type_code is null or obj_logic_type_code = :logic_type_code )
                        and ( :status_type is null or obj_status_type = :status_type )
                        AND ( :modified_at_before IS NULL OR obj_modified_at <= :modified_at_before )
                        AND ( :modified_at_after IS NULL OR obj_modified_at >= :modified_at_after )
                        and ( :archived is null or obj_archived = :archived )
                        AND ( :deleted IS NULL OR obj_deleted = :deleted )
                        and ( :user_rating is null or obj_user_rating >= :user_rating)
                        and 1=1
                    ORDER BY rank desc
                    LIMIT :slice_for OFFSET :slice_from
                )
            UNION ALL








                /* Дополнительно проверяем Полное совпадение CODE или ID или ilike выдаем с высоким рангом 100
                   в теории можно объединить с выше через CASE LIKE или с once_top
                */
                (
                    SELECT
                        obj_id,
                        obj_code,
                        obj_name ||' '|| obj_code || ' ' || obj_id || ' '  || {headline_field} as headline_raw,
                        obj_model,
                        --obj_name,
                        obj_breadcrumbs as breadcrumbs,
                        obj_project_id as obj_project_id,
                        obj_parent_id as obj_parent_id,
                        obj_related_person_logins as obj_related_person_logins,
                        :label || (CASE WHEN EXTRACT(days from (now()-obj_modified_at)) < 365 then 'R' else '' end) as label,
                        1000 as rank,
                        -- 100
                        -- * (CASE WHEN EXTRACT(days from (now()-obj_modified_at)) < 365 then
                        --            (365-EXTRACT(days from (now()-obj_modified_at)))/30+1 else 1 end) as rank,
                        EXTRACT(days from (now()-obj_modified_at)) as age_days,
                        {tsvector_field} as result_tsvector, {search_field} as result_text, name_tsvector, obj_name, obj_modified_at,
                        obj_user_portal_top, obj_client_portal_top,
                        'flow_idcodelike' as mark
                    FROM cmf_full_search
                    WHERE
                        ({search_field} != '' and {search_field} is not null) and
                        /* Дополнительно проверяем полное совпадение CODE ID ML_TEXT >2WORDS по ilike с высоким рангом */
                        (   (:query_obj_code is not null
                                 and (
                                       -- не дает бонуса возможно даже хуже obj_code = :query_obj_code or
                                      obj_code LIKE '%' || :query_obj_code )
                            )
                            or (:query_obj_id is not null and obj_id = :query_obj_id)
                            {idcodelike_like_string}
                        )
                        and (:query_qstop_words_str is null or not({tsvector_field} @@ :query_qstop_words_str))
                        and obj_model IN :model_name_in
                        and part_no <= :max_partno
                        and ( :parent_id is null or obj_project_id = :parent_id )
                        AND ( :owner_name IS NULL OR obj_owner_name = :owner_name )
                        AND ( :author_name IS NULL OR obj_author_name = :author_name )
                        AND ( :modified_by_name IS NULL OR obj_modified_by_name = :modified_by_name)
                        and ( :responsible_name is null or obj_responsible_names ILIKE '%' || :responsible_name || '%' )
                        {tags_filter}
                        {rpl_filter}
                        {tree_parent_filter}
                        and ( :logic_type_code is null or obj_logic_type_code = :logic_type_code )
                        and ( :status_type is null or obj_status_type = :status_type )
                        AND ( :modified_at_before IS NULL OR obj_modified_at <= :modified_at_before )
                        AND ( :modified_at_after IS NULL OR obj_modified_at >= :modified_at_after )
                        and ( :archived is null or obj_archived = :archived )
                        AND ( :deleted IS NULL OR obj_deleted = :deleted )
                        and 1=1
                    /*ORDER BY obj_id desc -- не будем делать, т.к. тормозить будет*/
                    -- хак 50 здесь предполагает что запросы делаются по 50 и будем давать больше лайкответов на след слайсах
                    LIMIT 2+:slice_from/25
                )
            ) as subs
        )r  r  r  r  r  r  r  r  tsrank_high_stringtsrank_mid_stringtsrank_low_stringtxt_tsrank_high_stringtxt_tsrank_mid_stringtxt_tsrank_low_stringidcodelike_like_stringparam_search_queryparam_querysearch_query_wo_qstoptsquery_str_rank_ORtsquery_str_where_ORtxt_tsquery_str_rank_ORtxt_tsquery_str_where_ORr  r  r  r  query_qstop_words_strr  query_obj_idr  r  )r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  )#r  r  rI  r  r  r  r|  rL  r  r  r  r  r  r  r  r  rP  r  rR  rS  EVA_OR_QUERY_STOP_WORDS_NORMr  search_queryr  r  r  r  r  rW  r  r  r  r  fetchallrA  );rw  r  queryr  r  r  r  force_related_person_loginr  r  param_tsqueryrI  r  r  r  r  r  r  r  r  r  r  r  r  r  r  rL  r[  r  r  r  r  r  r  r  r  r  r  r  r  
tags_namesr  r  r  r  r  r  r  r  query_token_listr  r  r  r  r  r  rE  rF  r  s;                                                              rJ  search_once_top_bm25z"CmfFullSearch.search_once_top_bm25  s-    

5;;=1%:):):
^+Nzl[cghj\*
|,:#:'N&<y1	*2 ,<E=+Q(
Iy !$ # #%KNK[[i!l);N;KK^%a"$J>JZZg j(:>:JJ\$_!$J>JZZg j(:>:JJ\$_! "$V%6%6%<%<S%A!Ba!G}, .J&-1,?_)b&ELLE''++K>	!..223EF!..223EtL --112CTJ))--mTB#00445IJ"//334GH??..))--m< ((,,\4@
))--mZH.;
#66j|?FFuzzRdGef  CE((,,Z8>B
#&#9#9*#E [%%(%L%LMg%h"J
  $$%;<%(%L%LVM`M`MdMde{M|%}"J
JJ1X
!HuQx'	(m8!EhZzZ
 J rvvje<BBDE  %zz*:SA]=^*^_$zz*:SA]=^*^_"5#7    $##$)JJv/G/G/M/M/O$P!H HP FQXR #5(,!2,&!2$"4!2!2&<%:%:&<
QXt!
&(;(; !
5!
#f&B&B!
 "#6!
 #$8	!

 &'>!
 '(@!
 *!
 !
 U=1!
 U!
 $%:%Bd!
 &++3t!
 V008D!
 f33!
 *!
  !!
" %& 0 0.&"4!2 &$""=!
> ?!
@ A!
F )),,88@@BJJ3PVW``b
 JrL  c                 v   |xs |j                   }|j                  j                  d      }	|j                  j                  d      }
|j                  j                  d      }|j                  j                  d      }|j                  j                  d      }|j                  j                  d      }|j                  j                  d      }|j                  j                  d      }|j                  j                  d	d       }|j                  j                  d
|      }|xs |}|j                  }|r dj	                  dj                  |            nd}|j                  j                  d      xs g }| j                  |      \  }}|r| j                  |      \  }}nM|j                  j                  d      r.| j                  |j                  j                  d            \  }}nd}i }d}|rt        d       |x}}d}|t        vrt        d| d       d| }|j                  sd}d}n.|j                  d   }|j                  d   |j                  d   z
  }d}|rt        |      }d| d}|dk(  rdnd}d| d| d | d!| d"| d#| d#| d#| d$}t        j                  j                  j                  j!                         j#                  |i d%|d&|d't%        |      d(|d)|d|	d	|d
|d|
d|d|d|d|d|d*|j&                  d+|j(                  d|||      } t+        |       }!|!S ),Nr[  r  r  r  r  r  r  r  r  r  r  r  r  r  r  z
            AND ( :responsible_name IS NULL OR obj_responsible_names ILIKE '%' || :responsible_name || '%' )
            AND ( :owner_name IS NULL OR obj_owner_name = :owner_name )
        z:Param related_user is deprecated. Use related_person_loginz
            AND (
                :responsible_name IS NULL
                OR obj_responsible_names ILIKE '%' || :responsible_name || '%'
                OR obj_owner_name = :owner_name
            )
        r  Tr5  r  r   r{  r  r  r  r0  r  a  
            SELECT
                obj_id,
                obj_code,
                obj_model,
                obj_name as title,
                obj_breadcrumbs as breadcrumbs,
                :label || (CASE WHEN EXTRACT(DAYS FROM (NOW() - obj_modified_at)) < 365 then 'R' else '' end) as label,
                100 / (EXTRACT(DAYS FROM (NOW() - obj_modified_at)) + 1) as rank,
                EXTRACT(DAYS FROM (NOW() - obj_modified_at)) as age_days,
                 as ,
                substring(` FROM 0 FOR 240) as headline
            FROM cmf_full_search
            WHERE
                a]  
                obj_model IN :model_name_in
                AND part_no <= :max_partno
                AND ( :parent_id IS NULL OR obj_parent_id = :parent_id )
                AND ( :author_name IS NULL OR obj_author_name = :author_name )
                AND ( :modified_by_name IS NULL OR obj_modified_by_name = :modified_by_name)
                z
                a  
                AND ( :logic_type_code IS NULL OR obj_logic_type_code = :logic_type_code )
                AND ( :status_type IS NULL OR obj_status_type = :status_type )
                AND ( :modified_at_before IS NULL OR obj_modified_at <= :modified_at_before )
                AND ( :modified_at_after IS NULL OR obj_modified_at >= :modified_at_after )
                AND ( :archived IS NULL OR obj_archived = :archived )
                AND ( :deleted IS NULL OR obj_deleted = :deleted )
                and ( :user_rating is null or obj_user_rating >= :user_rating)
                AND obj_modified_at is not null
            ORDER BY obj_modified_at DESC
            LIMIT :slice_for OFFSET :slice_from;
        r  r  r  r  r  r  r  )rI  r  r  r  r  r  r  r  r  r  fullsearch_slicerP  r  rW  r  r  r  r  r  r  r  rA  )"rw  r  related_userr  r  r  r  r  rI  r[  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  owner_and_responsible_filterr  r  r  r  r  rE  r  all_objss"                                     rJ  filter_oncezCmfFullSearch.filter_once  s4    &:):):
''++K8	!..223EF!..223EF --112CD))--m<#00445IJ"//334GH))--m<((,,\4@
))--mZH.;
#66j|?FFuzzRdGef  CE((,,Z8>B
#&#9#9*#E [%%(%L%LMg%h"J
  $$%;<%(%L%LVM`M`MdMde{M|%}"J
JJ($ RS,88J),( ^+Nzl[cghj\*&&JI003J//2V5L5LQ5OOI8}H"FxjPZ [ *f 4W!

 d<. 1'. ) ## $ .. /  #$ %	/"H )),,88@@BJJ3 Q
*Q
Q
 U=1Q
 U	Q

 *Q
 Q
 *Q
 ;Q
  0Q
  0Q
 Q
 ;Q
 !"4Q
  !2Q
 Q
  v~~!Q
" ;#Q
$ %Q
& 'Q
 
* 
#rL  c                 ,   t        t        j                  j                        }| j	                  |      \  }}d}d}|rt        |      }d| d}d}d}d}	d}
dj                  g d	| d
| d| d| d| d| d
| d| d| d| d| d
| d| d| d| d      }t        j                  j                  j                  j                         j                  ||	|
|d|      j                         }t        |      S )Nr     r  r  EMTr  r   r{  a  
            (SELECT
                obj_id,
                obj_code,
                obj_model,
                obj_name as title,
                obj_breadcrumbs as breadcrumbs,
                :label || (CASE WHEN EXTRACT(DAYS FROM (NOW() - obj_modified_at)) < 365 then 'R' else '' end) as label,
                100 / (EXTRACT(DAYS FROM (NOW() - obj_modified_at)) + 1) as rank,
                EXTRACT(DAYS FROM (NOW() - obj_modified_at)) as age_days,
                r  r	  r
  zW
                obj_model = 'CmfTask' AND
                part_no = 0
                u  
                AND obj_modified_at is not null /* Временный хак пока не поправят багу TEM-1625047513 */
            ORDER BY obj_modified_at DESC
            LIMIT :slice_for OFFSET :slice_from)
            UNION
            (SELECT
                obj_id,
                obj_code,
                obj_model,
                obj_name as title,
                obj_breadcrumbs as breadcrumbs,
                :label || (CASE WHEN EXTRACT(DAYS FROM (NOW() - obj_modified_at)) < 365 then 'R' else '' end) as label,
                100 / (EXTRACT(DAYS FROM (NOW() - obj_modified_at)) + 1) as rank,
                EXTRACT(DAYS FROM (NOW() - obj_modified_at)) as age_days,
                z[
                obj_model = 'CmfDocument' AND
                part_no = 0
                z
                obj_model != 'CmfDocument' AND
                obj_model != 'CmfTask' AND
                part_no = 0
                u   
                AND obj_modified_at is not null /* Временный хак пока не поправят багу TEM-1625047513 */
            ORDER BY obj_modified_at DESC
            LIMIT :slice_for OFFSET :slice_from);
            )r  r  r  )rC  r  current_userrc  r  rP  r  r  rW  r  r  r  r  r   rA  )rw  r  r  r  r  r  r  r  r  r  r  rE  r  s                rJ  filter_empty_topzCmfFullSearch.filter_empty_topu  so    !5!56!$!H!HI]!^
J8}H"FxjPZ [$
	@ @ 
 @ @ #@ $0.@1@ (.@)@ ##@$@" #@"#@> ?@> #?@> $0.?@>1?@@ (.A@@)A@F ##G@F$G@L M@LM@h i@h #i@h $0.i@h1i@j (.k@j)k@p ##q@p$q@x y@xy@D )),,88@@BJJ3$"Q
 	Q
 
 8: 	 JrL  c                 X    i }d}| r!d}ddj                  d | D               d|d<   ||fS )Nr  z AND obj_tags ~* :tags_re(^| |,)(|c              3   F   K   | ]  }t        j                  |        y wr  rR  escape)r  r  s     rJ  r  z3CmfFullSearch._build_tags_filter.<locals>.<genexpr>  s     8hQY89L8hs   !)( |,|$)tags_re)r  )r  r  r  s      rJ  r  z CmfFullSearch._build_tags_filter  sF     5K'/8h]g8h0h/iiq%rK	"K''rL  c                 V    i }d}| r d}t        j                  |       } d|  d|d<   ||fS )Nr  z; AND obj_related_person_logins ~* :related_person_logins_rer  r  related_person_logins_rer  )
user_loginr  r  s      rJ  r  z1CmfFullSearch._build_related_person_logins_filter  sC     

VJ:.J7?
|85TJ12:%%rL  c                    t        |       dk  ry g }|d d D ]i  }|d   j                  d      s|d   j                  d      s,i }|d   |d<   |j                  dd       r|d   d   |d<   nd |d<   |j                  |       k t        j                         }||_        d	|_        | |_        t        j                  j                  |_        d
|_        t        j                         5  |j!                          d d d        y # 1 sw Y   y xY w)Nr  r{  r  zCmfDocument:zCmfTask:r  parentr]  r  F)r|  
startswithr  r  r  CmfSearchStatr`  actionr  r  r  r  	person_id
aggregatedrT  disable_aclsave)r  obj_dictst_dictrecst_recstats         rJ  _do_calc_statisticsz!CmfFullSearch._do_calc_statistics  s    |q CR= 	#CI((8CI<P<PQ[<\F"4yF8wwx&'*8}T':|$'+|$NN6"	# ##%(**  " 	IIK	 	 	s   &D  D	)Fr  )FFr  )F)!NNNr  NNNNNFFNNNNNNNNNNNNNNNNNNNNFF)Nr{  F)FF)NNr  NFNr  )NNr  NNFN)NNNNr  N)r  
str | Noner  r/  )r  z	list[str]returntuple[str, dict[str, str]])r  rC  r0  r1  ))__name__
__module____qualname____doc__rC  rG  	api_allowapi_methodsclassmethodry  r  r  r  r  r  r  r  r  r  r-  r  r  r  rP  r  	BaseModelrJ  ra  staticmethodcmf_deferred_jobrn  ro  r  rm  r  r  r  r  r  r  r.  r  rL  rJ  rW  rW  Z   s<    KO IK, l l  $ Q Q p p   :F :Fx g g . .  <  8  ' !"""&!#Mz zx  &  $ _ _B 	cjj&:&: 	 	 l lB %; & ;| 	
 	
 VWX]= Y ]=@ X X Y[JLn  n ^ SU`dj  j Z  &*37F #F %1	F FR X  X t ( ( & &    rL  rW  )NN).r  r  mathcollectionsr   typingr   r   r   r   dataclassesr   r	   r
   r  sys	itertoolsr  cmf.data_providers.sqlalchemyr   cmf.fields.cmf_full_searchrP  cmf.models.cmf_search_engineenchantstring	pymorphy3rR  r  bs4r   MorphAnalyzermorphRUSSIAN_STOP_WORDSENGLISH_STOP_WORDSr  PROMPT_STOP_WORDS_NORMTECH_STOP_WORDSr  EVA_ARTIFACT_KEYS_STOP_WORDSEVA_ARTIFACT_VALUES_STOP_WORDSr  r  rK  rU  cmf_full_searchrW  r  rL  rJ  <module>rR     sK      " , , 0 0   
   > ! #    	   	 	! @  A 
  W    !\  \    !c  c   ,    !G  G  K :"#58J#JMi#il{#{  U  $U   V   M  "x  '*<<NO_b mCJJ..<< mrL  