📜 ⬆️ ⬇️

DB. Directories. MUMPS examples (Caché Object Script) 2

In the last article, we looked at an example directory on MUMPS (Caché Object Script). The global structures and the retrieve method were disassembled. We have learned the simplest operation - obtaining the name of an element by a known identifier. The structures considered were single-level. Polls and comments, after the article, showed that the topic as a whole is interesting. Today we look at examples of building indexes for reference books. All codes / identifiers / global names are real. The main idea of ​​these articles is the exchange of knowledge / experience in the development and design of live databases .

Briefly recall the main points of the first part :

At the request of 4dmonster , full versions of commands will be published in the examples. ( write instead of w , etc.)

Let's refresh the available data globals:
^Dictionary("Vehicle","TransmissionType",1,0,"UpdateTime")="62086,66625" ^Dictionary("Vehicle","TransmissionType",1,0,"uid")=888 ^Dictionary("Vehicle","TransmissionType",2,0,"UpdateTime")="62086,66625" ^Dictionary("Vehicle","TransmissionType",2,0,"uid")=888 ^NameDictionaryElement(1,"partUri",0)="akp" ^NameDictionaryElement(1,"partUri",0,"UpdateTime")="62086,66625" ^NameDictionaryElement(1,"ru",0)="" ^NameDictionaryElement(1,"ru",0,"UpdateTime")="62086,66625" ^NameDictionaryElement(2,"partUri",0)="meh" ^NameDictionaryElement(2,"partUri",0,"UpdateTime")="62086,66625" ^NameDictionaryElement(2,"ru",0)="" ^NameDictionaryElement(2,"ru",0,"UpdateTime")="62086,66625" 

Global ^ Dictionary - contains all elements of reference books and their properties, global ^ NameDictionaryElement - contains names of elements of reference books in all languages.
')
Create global Ctrl + C / V
Set command - sets the value of a variable (local or global).
 set ^Dictionary("Vehicle","TransmissionType",1,0,"UpdateTime")="62086,66625" set ^Dictionary("Vehicle","TransmissionType",1,0,"uid")=888 set ^Dictionary("Vehicle","TransmissionType",2,0,"UpdateTime")="62086,66625" set ^Dictionary("Vehicle","TransmissionType",2,0,"uid")=888 set ^NameDictionaryElement(1,"partUri",0)="akp" set ^NameDictionaryElement(1,"partUri",0,"UpdateTime")="62086,66625" set ^NameDictionaryElement(1,"ru",0)="" set ^NameDictionaryElement(1,"ru",0,"UpdateTime")="62086,66625" set ^NameDictionaryElement(2,"partUri",0)="meh" set ^NameDictionaryElement(2,"partUri",0,"UpdateTime")="62086,66625" set ^NameDictionaryElement(2,"ru",0)="" set ^NameDictionaryElement(2,"ru",0,"UpdateTime")="62086,66625" 


And now let's see how the directory index can be arranged, and see what it is for.

Definition:


An index is a number, letter, or other combination of characters indicating the location of an element, or group of elements , in the aggregate. The element may contain other elements. The same applies to the index - the index is always composite , even if it consists of one or less parts.

We analyze the components of the global index in Caché
Remember the analysis of the word in the composition of the lessons of the Russian language? Where prefix, root, sufix, ending is searched. Here is something very similar. First we need the word (global / local variable). Let our word be ^ | "MONTOLOGY" | NameDictionaryElement (1, "ru", 0) . Between the vertical sticks | specified namespace | - it is necessary for the maximum completeness of the demonstration of the composition of the global index (variable). Recall the meaning of our word - execute the write command:

 MONTOLOGY>write ^|"MONTOLOGY"|NameDictionaryElement(1,"ru",0)  MONTOLOGY> 

Our word ^ | "MONTOLOGY" | NameDictionaryElement (1, "ru", 0) means automatic transmission (automatic transmission).

In addition to the word, for the analysis we need the system function $ name () , which returns the name of the variable in the canonical form. Set command - sets the value of a variable (local or global). Create a local variable slovo and demonstrate the operation of the $ name () function. In one line, execute two set and write commands (commands separated by at least one space):

 MONTOLOGY>set slovo=$name(^|"MONTOLOGY"|NameDictionaryElement(1,"ru",0)) write slovo ^|"MONTOLOGY"|NameDictionaryElement(1,"ru",0) MONTOLOGY> 


If now, having only the slovo variable, we will need to re-read (or write down) the meaning of our word - we can use the indirection operator @

Example:
 MONTOLOGY>write @slovo  MONTOLOGY> 

So, the slovo variable contains the name of our word ^ | "MONTOLOGY" | NameDictionaryElement (1, "ru", 0) in the canonical form. We proceed to his analysis. Use the $ qsubscript () function . Using a for loop, you can shorten the record and the explanation. The write command and the for loop are written on one line in order to execute this command from the terminal:

 MONTOLOGY>write ": "_slovo,! for i=-1:1:3 { write "  "_i_"- : "_$qsubscript(slovo,i),! } : ^|"MONTOLOGY"|NameDictionaryElement(1,"ru",0)  -1- : MONTOLOGY  0- : ^NameDictionaryElement  1- : 1  2- : ru  3- : 0 MONTOLOGY> 

The underscore " _ " is a concatenation character (stitching together strings). The exclamation mark " ! " Inside the write command is a newline. If to be completely accurate, the limit value of the end of the cycle: the number 3 - must be replaced by the function $ qlength (slovo) . It returns the maximum index number of the variable. However, for simplicity, I just wrote 3 .

So we see:
  • The 1st index is the name of the namespace;
  • The 0th index is, in fact, the name of the global (up to the parentheses);
  • 1st, 2nd, 3rd (and further) are global indexes in brackets.

That is, a variable (local or global) in itself necessarily contains several indices. In order to get the value of a variable, we need to know the values ​​of all its composite indices.

Preventing confusion with the word index, note the following:


Directory Indexes


Indexes are needed to quickly find information. Depending on the type of search, different indexes may be required. Consider an example of an index for the simplest search by value.

We assume that the indices of the global ^ IndexDictionary will mean the following:
  1. ontology (rough classification of reference books) - our ontology Vehicle (vehicles)
  2. directory name - TransmissionType (transmission type)
  3. element property name
  4. element property value (or language name, if previous index = "name" )
  5. reference element identifier (or lower-case element name, if the previous index is the language name)
  6. reference item identifier (only if there was an element name at the previous level, for all other properties except “name” , this index is absent)

Print the contents of the global ^ IndexDictionary execute the command:
zwrite ^ IndexDictionary ("Vehicle", "TransmissionType")
I remind you that “MONTOLOGY” is the name of the namespace.

 MONTOLOGY>zwrite ^IndexDictionary("Vehicle","TransmissionType") ^IndexDictionary("Vehicle","TransmissionType","name","partUri","akp",1)=1 ^IndexDictionary("Vehicle","TransmissionType","name","partUri","meh",2)=1 ^IndexDictionary("Vehicle","TransmissionType","name","ru","",1)=1 ^IndexDictionary("Vehicle","TransmissionType","name","ru","",2)=1 ^IndexDictionary("Vehicle","TransmissionType","uid",888,1)=1 ^IndexDictionary("Vehicle","TransmissionType","uid",888,2)=1 MONTOLOGY> 

Create Global Ctrl + C / V
 set ^IndexDictionary("Vehicle","TransmissionType","name","partUri","akp",1)=1 set ^IndexDictionary("Vehicle","TransmissionType","name","partUri","meh",2)=1 set ^IndexDictionary("Vehicle","TransmissionType","name","ru","",1)=1 set ^IndexDictionary("Vehicle","TransmissionType","name","ru","",2)=1 set ^IndexDictionary("Vehicle","TransmissionType","uid",888,1)=1 set ^IndexDictionary("Vehicle","TransmissionType","uid",888,2)=1 


Let's take a closer look at the structure of the index global. The values ​​of indexed variables do not carry any semantic load, therefore, for simplicity, they are equal to one everywhere. All the information we are interested in is contained solely in the indices. Note that the first and second index of the global ^ IndexDictionary , coincides with the first and second index of the global ^ Dictionary . The values ​​of the properties of elements go only at the 4th or 5th (for names) level. Since our global should provide fast search by value, its very structure implies certain search conditions. All preliminary indexes are known initially. That is, before we start looking for something, we already know: the ontology, the type of the directory element, the name of the property and its value. Obviously, such an index global is not suitable for analyzing full-text user requests. But in this case - it is not required. Let's try to find in the global directory element (s) of the property value. We need a $ order () function that returns the next variable index at a given level. If there is no next index, the function will return the empty string "" . Let us demonstrate the operation of the $ order () function:

 MONTOLOGY>write $order(^IndexDictionary("Vehicle","TransmissionType","")) name MONTOLOGY>write $order(^IndexDictionary("Vehicle","TransmissionType","name")) uid MONTOLOGY>write $order(^IndexDictionary("Vehicle","TransmissionType","uid")) MONTOLOGY> 

That is, the next index on the third level, after the empty string "" , is the first occurring value of the index of the third level - "name" . The next index on the third level after “name” is “uid” . The next index on the third level after the "uid" is not, an empty line is printed. In order to find the first identifier (perhaps the name is not unique) of the TransmissionType directory element with the Russian name “mech”, simply run the following command:

 MONTOLOGY>write $order(^IndexDictionary("Vehicle","TransmissionType","name","ru","","")) 2 MONTOLOGY> 

retrieveListByIndex


We write a subroutine to display a list of directory elements by the value of any property (except the name).

 #; -------------------------------------------------------------------------------------------------- #;   #; -------------------------------------------------------------------------------------------------- Dictionary #; -------------------------------------------------------------------------------------------------- #;      . #; -------------------------------------------------------------------------------------------------- retrieveListByIndex(ontology,type,index,value) set id="" for { set id=$o(^IndexDictionary(ontology,type,index,value,id)) quit:id="" write id,! } quit #; -------------------------------------------------------------------------------------------------- 

Note that for {} is an infinite loop. The exit condition is described inside it by the quit: id = "" construct
As soon as id becomes an empty string (and this happens when the next index on the fifth level is not present), the subroutine will exit the loop. Find all elements of the TransmissionType help file added / edited by the user 888 (uid = 888):

 MONTOLOGY>do retrieveListByIndex^Dictionary("Vehicle","TransmissionType","uid",888) 1 2 MONTOLOGY> 

We modify our subroutine in order for it to additionally be able to search for elements whose names begin with a certain substring. Expand the output format (add the name of the element). I will give the program code of the Dictionary along with the retrieve prod from the last part:

Dictionary code:
 #; -------------------------------------------------------------------------------------------------- #;   #; -------------------------------------------------------------------------------------------------- Dictionary #; -------------------------------------------------------------------------------------------------- #;   . #; -------------------------------------------------------------------------------------------------- retrieve(id,lang="ru",version=0) quit $get(^NameDictionaryElement(id,lang,version),"") #; -------------------------------------------------------------------------------------------------- #;      . #; -------------------------------------------------------------------------------------------------- retrieveListByIndex(ontology,type,index,value,str="",lang="ru") #;     ,       set str=$zconvert(str,"L") set id="" for { #;  ()    set id=$order(^IndexDictionary(ontology,type,index,value,id)) #;  ,        quit:id="" #;   set name=$$retrieve(id,lang) #;      str if $extract($zconvert(name,"L"),1,$length(str))=str { #;  (  ) write id_" "_name,! } } quit #; -------------------------------------------------------------------------------------------------- 

Let me explain the new features and designs used:
  • $ zconvert () - formats a string depending on the parameter passed (L - means translate to lower case);
  • $ length () - returns the length of the string;
  • $ extract () - selects a substring from a string with a specific starting and ending position. In our case, starting with the first character and ending with a long str at the input of the subroutine.
  • set name = $$ retrieve (id, lang) - calling the subroutine from the current program the program name can be omitted, although the construction set name = $$ retrieve ^ Dictionary (id, lang) will return the same result.


Let's try to look at the result of our program:

 MONTOLOGY>do retrieveListByIndex^Dictionary("Vehicle","TransmissionType","uid",888) 1  2  MONTOLOGY>do retrieveListByIndex^Dictionary("Vehicle","TransmissionType","uid",888,"A") MONTOLOGY>do retrieveListByIndex^Dictionary("Vehicle","TransmissionType","uid",888,"") 2  MONTOLOGY>do retrieveListByIndex^Dictionary("Vehicle","TransmissionType","uid",888,"") 1  MONTOLOGY>do retrieveListByIndex^Dictionary("Vehicle","TransmissionType","uid",888,"") 2  MONTOLOGY>do retrieveListByIndex^Dictionary("Vehicle","TransmissionType","uid",888,"","partUri") MONTOLOGY>do retrieveListByIndex^Dictionary("Vehicle","TransmissionType","uid",888,"m","partUri") 2 meh MONTOLOGY>do retrieveListByIndex^Dictionary("Vehicle","TransmissionType","uid",888,"","partUri") 1 akp 2 meh MONTOLOGY> 

I want to immediately note that the search engine for a name starting with a given substring, described in retrieveListByIndex , is not the most effective, it is just an option. In more detail this topic will be covered further.

So, in our reference subsystem, there are already three important significant global:


Obviously, to efficiently update / delete directory elements - one more global will be needed - which stores backlinks. This is necessary in order not to perform any search when deleting data (when updating, the minimum necessary checks will be performed). Let's call this global RefsDictionary . Print all the backlinks for elements 1 and 2 :

 MONTOLOGY>zwrite ^RefsDictionary(1),^RefsDictionary(2) ^RefsDictionary(1,"^|""MONTOLOGY""|IndexDictionary(""Vehicle"",""TransmissionType"",""name"",""partUri"",""akp"",1)")=1 ^RefsDictionary(1,"^|""MONTOLOGY""|IndexDictionary(""Vehicle"",""TransmissionType"",""name"",""ru"","""",1)")=1 ^RefsDictionary(1,"^|""MONTOLOGY""|IndexDictionary(""Vehicle"",""TransmissionType"",""uid"",888,1)")=1 ^RefsDictionary(2,"^|""MONTOLOGY""|IndexDictionary(""Vehicle"",""TransmissionType"",""name"",""partUri"",""meh"",2)")=1 ^RefsDictionary(2,"^|""MONTOLOGY""|IndexDictionary(""Vehicle"",""TransmissionType"",""name"",""ru"","""",2)")=1 ^RefsDictionary(2,"^|""MONTOLOGY""|IndexDictionary(""Vehicle"",""TransmissionType"",""uid"",888,2)")=1 MONTOLOGY> 

Create Global Ctrl + C / V
 set ^RefsDictionary(1,"^|""MONTOLOGY""|IndexDictionary(""Vehicle"",""TransmissionType"",""name"",""partUri"",""akp"",1)")=1 set ^RefsDictionary(1,"^|""MONTOLOGY""|IndexDictionary(""Vehicle"",""TransmissionType"",""name"",""ru"","""",1)")=1 set ^RefsDictionary(1,"^|""MONTOLOGY""|IndexDictionary(""Vehicle"",""TransmissionType"",""uid"",888,1)")=1 set ^RefsDictionary(2,"^|""MONTOLOGY""|IndexDictionary(""Vehicle"",""TransmissionType"",""name"",""partUri"",""meh"",2)")=1 set ^RefsDictionary(2,"^|""MONTOLOGY""|IndexDictionary(""Vehicle"",""TransmissionType"",""name"",""ru"","""",2)")=1 set ^RefsDictionary(2,"^|""MONTOLOGY""|IndexDictionary(""Vehicle"",""TransmissionType"",""uid"",888,2)")=1 


I think some of you, dear readers, have already guessed why ^ RefsDictionary is needed and why it has such a structure. More on this in the next article.

Thanks for attention.

I will be glad to questions, comments and suggestions.

PS:

I have practically no experience in writing technical articles, so “attempts at Habré to show that the globals are cool” didn’t stop, but just move at low speed. Perhaps, on the May holidays, I can spend more time on this topic.

Source: https://habr.com/ru/post/177319/


All Articles