ClassMethod OnPage() As %Status [ ServerOnly = 1 ]
{
    
    //just the query string...
    set qs=%request.CgiEnvs("QUERY_STRING")
        
    //SOLUTION 1: $piece only
    set externalCenterCode=$p(qs,":")	
    set startDateRange=$p($p(qs,":authoredOn=le",2),":")
    set endDataRange=$p($p(qs,":authoredOn=ge",2),":")

    
    //SOLUTION 2: generic solution if params grow	
    for i=1:1:$l(qs,":") {
        set nvp=$p(qs,":",i),name=$p(nvp,"=",1),value=$p(nvp,"=",2)
        //fix the quirks
        if value="" set value="name",name="ecc"
        if name="authoredOn" set name=$e(value,1,2),value=$e(value,3,*)
        set params(name)=value
    }

    //SOLUTION 3: regex(ish) solution
    set code=$p(qs,":")
    set loc=$locate(qs,"le\d{4}-\d{2}-\d{2}")
    set start=$e(qs,loc+2,loc+11)
    set loc=$locate(qs,"ge\d{4}-\d{2}-\d{2}")
    set end=$e(qs,loc+2,loc+11)


    //some helper code to dump the variables into the CSP page
    write !,"<pre>"
    zwrite
    //use this to take a good look at the request object...
    zwrite %request
    write !,"</pre>"
    quit $$$OK
}

Here are three solutions and a couple of inline tips, including your request for regex example

I wouldn't worry too much about using $piece, its very common to use it in this way

Eduards comment above also has a fourth suggestion to use $lfs (list from string) which is also commonly used as a way of piecing out data

I think you might have missed my point about the adapter being non standard.

E.g. what if the adapter has code like this...

Method OnTask() As %Status
{
    //Set tSC = ..BusinessHost.ProcessInput($$$NULLOREF)
    Set tSC = ..BusinessHost.General($$$NULLOREF)
    Set ..BusinessHost.%WaitForNextCallInterval=1
    Quit tSC
}

The normal sequence of

OnTask() → ProcessInput() → OnProcessInput()

becomes

OnTask() → General()

Of course this then raises the question, how does the interval then work as it would most likely only run once.

Main point is to check if the adapter has hard wired it in directly. It's the only logical explanation outside of this being an observer error.

Hi Scott,

My understanding is that search table classes are maintained in Ens.DocClassMap. When you do a purge the Ens.MessageHeader appears to have the responsibility for calling RemoveSearchTableEntries() on the Ens.SearchTableBase which uses indirection to call RemoveIndex() on the class name maintained in the DocClassMap.

The docs briefly mention DocClassMap...

The Ensemble classes use this class to remove search table entries when message bodies are deleted.

https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=EEDI_search_tables#EEDI_search_tables_mgmt

Short answer, purges will automatically include new search tables even if you scheduled the purge before creating the search table.

Hi Kurro,

Some random suggestions without being able to see the implementation details...

1. Enable the SOAP log and compare the request and response headers to SOAP UI to see what is different

https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GSOAP_debug#GSOAP_debug_info_soap_log

2. Does changing the SOAP version make any difference

https://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SOAP.WebClient#PROPERTY_SoapVersion

3. The error is probably raised in this block of code, might be worth working backwards from there, its at the end of DoSOAPRequest in %SOAP.WebClient

    Set responseContentType=$zcvt($piece(response.ContentType,";",1),"L")
    If ..SoapBinary {
        If (responseContentType'="application/octet-stream") Quit $$$ERROR($$$SOAPUnexpectedType,response.ContentType)
    } Else {
        If (responseContentType'="text/xml") && 
           (responseContentType'="application/soap+xml") &&
           (responseContentType'="multipart/related") {
            Quit $$$ERROR($$$SOAPUnexpectedType,response.ContentType)
        }
    }

4. If your running out of ideas then maybe reinstall / restart the server code (is it .NET by any chance)?

It depends what you really need.

SYSTEM MODE

There are existing global flags that are available through various classes. For instance SystemMode() will provide system wide constants for Live, Test and Dev...

https://docs.intersystems.com/healthconnectlatest/csp/documatic/%25CSP.Documatic.cls?&LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.Version

The SystemMode() is admin configured via the Memory and Startup screen on the management portal.

LOOKUP TABLES

For some global settings, particularly business related, it would be good practice to use lookup tables and not a global...

https://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls?&LIBRARY=ENSLIB&PRIVATE=1&CLASSNAME=Ens.Util.LookupTable

UTIL FUNCTIONS

Also, if you are not aware of them yet, you should take a look at creating your own Ens util functions which are automatically inherited into routers and DTL's and provide a good place to stash various levels of global and business value / logic...

https://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls?&LIBRARY=ENSLIB&PRIVATE=1&CLASSNAME=Ens.Util.FunctionSet

https://docs.intersystems.com/latest/csp/docbook/Doc.View.cls?KEY=EBUS_utility_functions

Hi Michael,

1. Copy the entire first source OBX to the first target OBX
2. Loop over the source OBX group
3. In the loop, if the loop key is greater than 1, append the source OBX 5 value to the first target OBX 5 with a preceding delimiter

It should look something like this...

If the source is...

OBX|1||||Alpha|
OBX|2||||Bravo|
OBX|3||||Charlie|


Then your target will output as...

OBX|1||||Alpha Bravo Charlie|

Hi David,

The ruleset including assign actions are evaluated before the send actions.

In this instance, if both rules are true then the first assign will be overwritten by the second assign, before both send actions are processed.

I guess you could work around this by having two different property names and testing for the existence of a value in the DTL.

It feels like there is a better solution altogether to suggest, but its not clear what the real world use case is here.

Sean.

Hi Chip.

In general, there is a danger of the tail (OOP) wagging the dog (ORM).

Polymorphism itself is a funny old thing. When it comes to true abstract things, its more easy to understand where its benefits come from. I always like to use IDataReader as a strong example of explaining Polymorphism. Where it becomes a bit murky is with the daft examples that try and teach Polymorphism, such as Cat and Horse implement Animal. It never really makes any real world sense, and as soon as you mix database storage it can go from murky to bizarre.

If we simplify it down to just shared data attributes then we can see how Person might make sense, but this type of design will have a trade off somewhere else in the architecture. You could have a Person table, and many concrete role looking implementations of Person, but then other areas such as SQL reporting can become really complex.

From the perspective of the example provided there are well established design patterns, we would have a Patient class and a separate Staff and Role class. Staff might share similarities to Patient, but we would try and solve this through composition over inheritance (and polymorphism), so for instance Address could be a shared thing. In this sense there is no problem that a Doctor can also be a Patient. There is duplication of storage here, but its a small trade off from other aspects of the architecture. There is also the aspect that something like Address has a different context anyway, a Patient Address would be home, whilst a Doctors Address would be work, and here the two database pools don't mix that well, hence why they tend to be two separate data entities.

That all said, its still an interesting question, and perhaps it just needs a better real world use case to explore it further...

There are a few ways to test the existence of a sub-string in a string...

$find(string,tag)

string[tag

$match(string,tagPatternMatcher)

$lf(list,tag)

Given...

set string="a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z"

and testing for "x"

within a tight loop with a million tests

$find  = .004609

[      = .004813

$match = .008951

$lf    = .023201

$find is marginally quicker than the contains operator with a million operations in .004609 seconds, whilst the other two methods are relatively slower. I say relative because they are still quick in their own rights.

A wider question might not be which is the fastest, but how it's implemented with multiple tag tests. For instance a single variable creation is almost as expensive as the string test itself, and if you wrapped it all in a re-usable method, the method call would be 10x as expensive.

If you are dealing with two strings in the first instance, then a tight loop on $find is going to be your most optimum option. The other functions are probably just as performent in the right context, but if you have to dissect the strings to implement them, then the cost is not the test, but in the other operations that you have to do first. 

The IDE (or enhanced code editor if you like) is actually dog food for my own standards based UI library.

In terms of building web based editors I've been building them from scratch for years so I am fairly comfortable with the level of difficulty. There is a screen shot below of one.

And yes, TTD will be one of the last features, but, I do want to prove the concept up front and inject any design decisions into the foundations so that I don't end up with lots of code re-factoring.

I've not used Monaco, mainly ACE. Perhaps there is some synergy between the two projects to use Monaco.

Any help with the original question would be much appreciated.