TMGRPC1C ;TMG/kst-RPC Functions for CPRS for Search functionality ;4/28/10 ; 5/18/10 12:57pm ;;1.0;TMG-LIB;**1**;4/28/10 ; ;"TMG RPC FUNCTIONS ; ;"Copyright Kevin Toppenberg MD 4/28/10 ;"Released under GNU General Public License (GPL) ;" ;"======================================================================= ;" RPC -- Public Functions. ;"======================================================================= ;"SRCH(OUT,FILENUM,STR) --A search function, to support calls by RPC from CPRS ;"======================================================================= ;"PRIVATE API FUNCTIONS ;"======================================================================= ; ;"======================================================================= ;"======================================================================= ;"Dependencies: ;" DIC, TMGDEBUG ;"======================================================================= ;"======================================================================= ; ; ; SRCH(OUT,FILENUM,STR) ; ;"Purpose: A search function, to support calls by RPC from CPRS ;"Input: OUT-- Pass by reference. AN OUT PARAMETER. ;" FILENUM -- The target file number that resulting IENs will be in ;" STR -- This is a logic string for searching. See details below. ;"Results: OUT is filled in. Format: ;" OUT(0)=1 for success, or -1^Error Message ;" OUT(IEN)="" ;" OUT(IEN)="" ;"Search string examples: ;" 8925:.02(.01="SMITH,JOHN") ;" 1234:.01(.03in"32..55") <-- this is a range test ;" 1234:.99((.01="SMITH,JOHN") OR (.01="SMITH,BILL")) AND 4567:.01(.02'="4/2/10") NOT (1["HAPPY") ;" -- File specifier. To specify searching in a file OTHER THAN target filenumber, an optional ;" FILENUM:FLD[:FLD[:FLD...]] may be specified. However, ultimately, this must point back ;" to the target filenumber. E.g. Search in file 8925, but for each entry found, use the IEN ;" specified by FLD (or FLDA:FLDB or FLDA:FLDB:FLDC:...) ;" FILENUM:(...) ;" The logic is read from left to right, honoring parentheses. If a filenumber ;" is not specified, then the last specified filenumber is used. ;" E.g. 1234:.01( LogicA ) OR 234:.99( LogicB ) AND ( LogicC ) ;" LogicA fields refer to file 1234:.01. ;" LogicB fields refer to file 234:.99 ;" LogicA fields refer to file 234:.99 (last specified file number) ;" E.g. 5678:.01( (LogicA1) OR 5432:.88(LogicA2) NOT (LogicA3) ) or (LogicB) ;" LogicA1 fields refer to file 5678:.01 ;" LogicA2 fields refer to file 5432:.88 ;" LogicA3 fields refer to file 5432:.88 (last specified file number inside parentheses) ;" LogicB fields refer to file 5678 (last specified file number at same parentheses level) ;" -- Each individual search term must be enclosed in parentheses, and may contain sub-terms ;" enclosed in nested parentheses ;" -- Each individual search term is comprised of: ;" FIELD then COMPARATOR then VALUE ;" 1. FIELDS -- can be name or number. This is for currently active file (see below) ;" may also be FIELDA:FIELDB:... when FIELDA is a pointer, then FIELDB ;" is taken from the pointed-to field. ;" 2. COMPARATOR -- can be: ;" "=" -- means exact match ;" "'=", "!=", "<>", -- any of these means 'Does not equal' ;" ">=", "'<" -- means greater-than-or-equal-to (same as not-less-than) ;" "<=", "'>" -- means less-than-or-equal-to (same sa not-greater-than) ;" "in","IN","In" -- means field is in specified rage (see Value below) ;" "[" -- means 'contains'. Interpreted as follows: ;" -- For Word processor (WP) fields, this means that any line in the entire field ;" can contain search term, to be matched positive. ;" -- For free text field, then just text of field is searched. ;" -- For Date fields .... (FINISH THIS...) ;" -- For Sets ... (FINISH THIS...) ;" 3. VALUE -- The search term to search for. Should be in quotes. ;" Note: if comparator is "IN", then syntax is "Value1..Value2" ;" There should be a ".." between the two values. ;" -- Logical combiners of separate search terms allowed are: ;" "OR" or "|" or "||" ;" "AND" or "&" or "&&" ;" "NOT" or "!" or "'" or "ANDNOT" ;" -- Logic short-circuiting is applied. The algorhythm will try to identify the elements ;" of the search that will be the fastest, and then work from that set, to make the overall ;" search better. ;" -- Searching of subfiles is not currently supported. IMPLEMENT LATER.... ;"Results: None ;" NEW ARRAY,RESULT SET FILENUM=$GET(FILENUM) SET ARRAY("FILE")=FILENUM SET OUT(0)=1 ;"Default to success SET RESULT=$$PARSESTR(FILENUM,STR,.ARRAY) ZWR ARRAY DO PressToCont^TMGUSRIF quit IF +RESULT=-1 SET OUT(0)=RESULT GOTO SRCHDN SET RESULT=$$OPTIMIZ(.ARRAY) IF +RESULT=-1 SET OUT(0)=RESULT GOTO SRCHDN SET RESULT=$$DOSRCH(.ARRAY,.OUT) IF +RESULT=-1 SET OUT(0)=RESULT GOTO SRCHDN SRCHDN QUIT ; ; PARSESTR(FILENUM,STR,ARRAY,FNUMPTR) ; ;"Purpose: To take user input, validate it, and parse into an formatted array ;"Input: STR: This is the user input string. Format as documented in SRCH() above. ;" ARRAY -- PASS BY REFERENCE. An OUT PARAMETER. Format as follows. ;" ARRAY(1,"FNUMPTR")= FNUM:FLDA[:FLDB[:FLDC...]] FNUM is filenumber that ;" contain search field, and then fields used to point ;" back to *TARGET* FILENUM for entire search ;" ARRAY(1,"FLD")=Fieldnumber to search ;" ARRAY(1,"COMP")=Comparator, will be "=", "'=", "'<", or "'>", "[" ;" ARRAY(1,"SRCH")=The value of to be used in search. ;" ARRAY(2,...) The second search term. ;" ARRAY(3,...) The third search term (which is comprised of sub terms) ;" ARRAY(3,1,... The first subterm (same format as higher level) ;" ARRAY(3,2,... The second subterm (same format as higher level) ;" ARRAY(n,...) The N'th search term. ;" ARRAY("SETCOMP",i)= NumA^Combiner^NumB ;" NumA and NumB refer to seach term number (e.g. 1, 2, ... n above) ;" If NumA="#", then it means 'the resulting set of results so far' ;" Combiner will be "AND", "OR", or "NOT" ;" i is the index variable, and logic should be evaluated in numerical order ;" FNUMPTR: Will be used when calling self reiteratively. Leave blank in first call. ;" DON'T pass by reference. This is 'FileNum:FLD[:FLD[:FLD...]] specifier ;"Results: 1 if OK, or -1^Message if error during processing. ; NEW SUBSTRA,SUBSTRB,POS NEW RESULT SET RESULT=1 ;"default to success NEW TERMNUM SET TERMNUM=0 SET FNUMPTR=$GET(FNUMPTR,FILENUM) NEW LOGICNUM SET LOGICNUM=0 NEW DONE SET DONE=0 FOR DO QUIT:(DONE=1)!(+RESULT=-1) . NEW TEMPARRAY . SET TERMNUM=TERMNUM+1 . ;"--- Get file number, if any . SET STR=$$TRIM^XLFSTR(STR) . IF +$PIECE(STR,"(",1)>0 DO QUIT:(+RESULT=-1) . . SET FNUMPTR=$PIECE(STR,"(",1) ;"Convert 1234:.01:.02:(...) --> 1234:.01:.02: . . IF $EXTRACT(FNUMPTR,$LENGTH(FNUMPTR))=":" SET FNUMPTR=$EXTRACT(FNUMPTR,1,$LENGTH(FNUMPTR)-1) . . IF $$FNPTR(FNUMPTR)'=FILENUM DO QUIT . . . SET RESULT="-1^'"_FNUMPTR_"' points to file #"_$$FNPTR(FNUMPTR)_", not file #"_FILENUM_" as expected" . ;"Split STR --> SUBSTRA + SUBSTRB . SET SUBSTRA=$$MATCHXTR^TMGSTUTL(STR,"(") . IF SUBSTRA="" SET DONE=1 QUIT . SET POS=$FIND(STR,SUBSTRA) ;"Return pos of following character . SET SUBSTRB=$EXTRACT(STR,POS+1,9999) ;"Should be " [LOGICTERM] [SearchTerm]..." . ;"Process SUBSTRA, either directly if single term, or recursively if compound term. . IF $$HNQTSUB^TMGSTUTL(SUBSTRA,"(") DO . . SET RESULT=$$PARSESTR(FILENUM,SUBSTRA,.TEMPARRAY,FNUMPTR) . ELSE DO . . SET RESULT=$$PARSE1(FILENUM,SUBSTRA,FNUMPTR,.TEMPARRAY) . IF +RESULT=-1 QUIT . MERGE ARRAY(TERMNUM)=TEMPARRAY . ;"Now get Logic term connecting this to next term (if any) . SET SUBSTRB=$$TRIM^XLFSTR(SUBSTRB) ;"Remove opening (and closing) spaces . NEW LOGICTERM SET LOGICTERM=$$UP^XLFSTR($PIECE(SUBSTRB," ",1)) . IF LOGICTERM="" SET DONE=1 QUIT . IF (LOGICTERM="|")!(LOGICTERM="||") SET LOGICTERM="OR" . ELSE IF (LOGICTERM="&")!(LOGICTERM="&&") SET LOGICTERM="AND" . ELSE IF (LOGICTERM="!")!(LOGICTERM="'")!(LOGICTERM="ANDNOT") SET LOGICTERM="NOT" . IF (LOGICTERM="AND")!(LOGICTERM="OR")!(LOGICTERM="NOT") DO . . NEW CURSET SET CURSET=$SELECT(TERMNUM=1:"1",1:"#") . . SET LOGICNUM=LOGICNUM+1 . . SET ARRAY("SETCOMP",LOGICNUM)=CURSET_"^"_LOGICTERM_"^"_(TERMNUM+1) ;"will check later that TERMNUM+1 is supplied . ELSE DO QUIT . . SET RESULT="-1^Bad logic term. Expect 'AND', 'OR', or 'NOT'. Found: ["_LOGICTERM_"]" . SET STR=$PIECE(SUBSTRB," ",2,999) QUIT RESULT ; ; FNPTR(FNUMPTR) ; ;"Puprose: To resolve a FNUMPTR, finding ultimate target file ;"Input: FNUMPTR: Format: FNUM:FLDA[:FLDB[:FLDC...]] FNUM is filenumber that ;" contain search field, and then fields used to point to *TARGET* FILENUM ;"Results: -1^Error message if error, otherwise returns pointed to file NEW RESULT,FILE,FLD,I,DONE SET FILE=+$GET(FNUMPTR) SET RESULT=0 SET DONE=0 FOR I=2:1:999 DO QUIT:(+RESULT=-1)!(DONE=1) . SET FLD=$PIECE(FNUMPTR,":",I) . IF FLD="" SET DONE=1 QUIT . IF $DATA(^DD(FILE,FLD,0))=0 DO QUIT . . SET RESULT="-1^Field ["_FLD_"] was not found in file ["_FILE_"]" . NEW FLDTYPE SET FLDTYPE=$PIECE(^DD(+FILE,+FLD,0),"^",2) . IF FLDTYPE'["P" DO QUIT . . SET RESULT="-1^Field ["_FLD_"] does not point to another file." . SET FILE=+$PIECE(FLDTYPE,"P",2) SET RESULT=FILE QUIT RESULT ; PARSE1(FILENUM,STR,FNUMPTR,ARRAY) ; ;"Purpose: Parse a simple search term (e.g. .01="SMITH,JOHN"). Also validate that field exists in file. ;"Input: FILENUM -- The TARGET filenumber that the entire search is referencing. ;" STR: This is part of the user input string to parse ;" FNUMPTR: FNUM:FLDA[:FLDB[:FLDC...]] FNUM is filenumber that contain search field, and then ;" fields used to point back to *TARGET* FILENUM for entire search ;" ARRAY -- PASS BY REFERENCE. An OUT PARAMETER. Format as follows. ;" ARRAY("FNUMPTR")=Filenumber that contains field) ;" ARRAY("FLD")=Fieldnumber to search ;" ARRAY("COMP")=Comparator, will be "=", "'=", "'<", or "'>", "[","IN" ;" ARRAY("SRCH")=The value of to be used in search. ;"NOTE: If field specifies a DATE, then the search value will be converted to FileMan format ;"Results: 1 if OK, or -1^Message if error during processing. ;" NEW RESULT SET RESULT=1 ;"default to success NEW SAV SET SAV=STR SET STR=$$TRIM^XLFSTR($GET(STR)) SET ARRAY("FNUMPTR")=FNUMPTR NEW FLD SET FLD=+STR IF $DATA(^DD(+FNUMPTR,FLD,0))=0 DO GOTO PS1DN . SET RESULT="-1^Field ["_FLD_"] was not found in file ["_+FNUMPTR_"]" FOR DO QUIT:(+STR=0) . SET STR=$EXTRACT(STR,$LENGTH(+STR)+1,9999) ;"Strip off field number . IF $EXTRACT(STR,1)=":" DO ;"Handle '.02:.99:.01' format, as Fileman does. . . SET STR=$$TrimL^TMGSTUTL(STR,":") . . IF +STR>0 SET FLD=FLD_":"_+STR SET ARRAY("FLD")=FLD NEW FLDTYPE SET FLDTYPE=$PIECE(^DD(+FNUMPTR,+FLD,0),"^",2) IF (FLD[":"),(FLDTYPE'["P") DO GOTO PS1DN . SET RESULT="-1^Found fields ["_FLD_"], however field "_+FLD_" is not a pointer in file "_FILENUM IF FLDTYPE["M" DO GOTO PS1DN . SET RESULT="-1^Searches in fields that are MULTIPLES not supported" NEW COMP SET COMP=$PIECE(STR,"""",1) SET COMP=$$UP^XLFSTR($$TRIM^XLFSTR(COMP)) IF (COMP="!=")!(COMP="<>") SET COMP="'=" ELSE IF (COMP=">=") SET COMP="'<" ELSE IF (COMP="<=") SET COMP="'>" NEW NOT SET NOT=$EXTRACT(COMP,1) IF NOT'="'" SET NOT="" IF (COMP="=")!(COMP="[")!(COMP="IN")!(COMP="<")!(COMP=">") DO . SET ARRAY("COMP")=NOT_COMP ELSE DO GOTO PS1DN . SET RESULT="-1^Comparator ["_COMP_"] is not valid" NEW SRCH SET SRCH=$PIECE(STR,"""",2,999) ;"Will strip off opening quotes IF $EXTRACT(SRCH,$LENGTH(SRCH))="""" SET SRCH=$EXTRACT(SRCH,1,$LENGTH(SRCH)-1) ;"Strip closing quote IF FLDTYPE["D" DO ;"Convert search value into a FM date (internal format) . NEW ADATE SET ADATE=SRCH . NEW TEMPRSLT SET TEMPRSLT="" . FOR QUIT:(ADATE="")!(+RESULT=-1) DO . . NEW X,Y,%DT . . SET %DT="T" . . SET X=$PIECE(ADATE,"..",1) . . SET ADATE=$PIECE(ADATE,"..",2) . . DO ^%DT . . IF Y=-1 DO QUIT . . . SET RESULT="-1^Invalid date: ["_X_"]" . . . SET SRCH="",ADATE="" . . IF TEMPRSLT'="" SET TEMPRSLT=TEMPRSLT_".." . . SET TEMPRSLT=TEMPRSLT_Y . SET SRCH=TEMPRSLT ELSE IF FLDTYPE["S" DO ;"Convert FM SET type into internal format . NEW OUT,TMGMSG . DO VAL^DIE(+FNUMPTR,"+1,",FLD,"E",SRCH,.OUT,,"TMGMSG") . SET SRCH=$GET(OUT) IF SRCH'="" SET ARRAY("SRCH")=SRCH ELSE DO GOTO PS1DN . SET RESULT="-1^Search value is invalid" ; PS1DN IF +RESULT=-1 SET RESULT=RESULT_", found in ["_SAV_"]" QUIT RESULT ; SRCHSUB(RESULT,TERMS) ; ;"Purpose: A search function, to support calls by RPC from CPRS ;"Input: RESULT-- Pass by reference. AN OUT PARAMETER. ;" TERMS -- Pass by reference. Contains search terms. Format ;" TERMS("FILE")=FileNumber ;" TERMS(Field)=Comparator^SearchValue ;" TERMS(Field)=Comparator^SearchValue ;" -- Allowed Comparators: "=","[","<", ;"Results: RESULT is filled in. Format: ;" RESULT(0)=1 for success, or -1^Error Message ;" RESULT(IEN)="" ;" RESULT(IEN)="" ;"NOTE: When multiple fields are specfied, then search results will combine terms ;" in an AND fashion. I.e. results only returned that match ALL specified terms. ; QUIT