David Hockenbroch · Jul 11, 2024 go to post

As Robert said, the data and indexes are both stored in global trees. The structure of the trees for indices and the trees for general data storage are different, though, and that's where the efficiency comes from.

The data will be stored in the ^EMPLOYEED global. You'll see a bunch of data in that global like:

^EMPLOYEED(1) = $lb("001","ABC",20)

^EMPLOYEED(2) = $ib("002","AAA",21)

Those are lists of every property of that class, indexed by the ID column.

The indexes, on the other hand, will look more like:

^EMPLOYEEI("IDX_EMPID","001",1)

^EMPLOYEEI("IDX_EMPID","002",2)

This allows for a much more efficient search to find the ID of the records that match your search criteria, similar to if you opened a terminal and put in:

write $ORDER(^EMPLOYEEI("IDX_EMPID","001",""))

Which would give you row 1, then it could more easily look up row 1 in the original without having to go through every record in the table in order searching for it.

In your system management portal, you can run your query and check the query plan, then run it again telling it to ignore indexes like this:

SELECT * FROM %IGNOREINDEX * EMPLOYEE WHERE EMPID="005"

This will tell it to ignore all indexes on the table. You can check that query plan and compare it to the original to see the differences in how the query finds data and how well it performs.

David Hockenbroch · Jul 11, 2024 go to post

We have an ERP system that runs on hundreds of ZEN pages, and we still love it. We just recognize that at some point, we're probably going to have to do some things to maintain it ourselves. I'd love to see it come back if that was an option.

David Hockenbroch · Jul 9, 2024 go to post

These are stored as a relationship to %Dictionary.PropertyDefinition, so you can access them like this:

select * from %dictionary.propertydefinition where parent = 'Your class name here'

David Hockenbroch · Jun 19, 2024 go to post

I'm a big fan of try/catch, but I'm an even bigger fan of "if it ain't broke, don't fix it." This is one of those things that will sound simple, but you'll end up finding out that there are multiple different ztraps at different stack levels and an occasional etrap here and there, and then you end up rewriting way more code than you thought.

David Hockenbroch · Jun 18, 2024 go to post

If you are accessing them through IIS now (i.e. using port 80) then you just need to get the SSL certificate set up in IIS and you'll be able to access those URLs using HTTPS. Keep in mind that HTTPS by default uses port 443, so you may have to make a firewall change accordingly.

You may also want to consider setting up a URL rewrite so that requests that come in as HTTP are redirected to their new HTTPS versions.

David Hockenbroch · Jun 3, 2024 go to post

Are you using a CSP or a Zen page? In one of your comments you mentioned Zen.

That's also not the correct syntax for calling a class method. It would be:

#server(##class(MyOrders.WO).retGetWO(wo))#;

That's assuming this is all in the class MyOrders.WO.

David Hockenbroch · May 14, 2024 go to post

If you download the MongoDB JDBC driver, you can use it to set up an SQL Gateway connection in your system management portal. Once you've done that, you can use that to do your query, or you can link the tables in MongoDB so you can query them more directly.

David Hockenbroch · Apr 29, 2024 go to post

I don't think there is a really good way to do exactly this through JDBC, but you can handle this using the JDBC URL if you do a little setup beforehand.

First, in the SMP, System Administration, Configuration, System Configuration, Namespaces, click on Create New Namespace. Give it a name - let's say "SAMPLE" and have it copy from your original namespace (probably USER).

Second, go into a terminal, switch to the new namespace and use the command:

w $SYSTEM.SQL.Schema.SetDefault("SAMPLE",.oldval,1)

You should get a 1 confirming that the change was made successfully.

At this point, when you make your JDBC connection, connecting to either namespace will work on the same database, but connecting to the SAMPLE namespace will use the sample schema, and the USER namespace will use the SQLUser schema.

David Hockenbroch · Apr 24, 2024 go to post

@Cristiano Silva, Rochdi is using Ensemble 2018. Does that version automatically configure IIS if you install it after IIS? I could be wrong, but I thought that automatic configuration was only in more recent versions of IRIS.
 

David Hockenbroch · Apr 19, 2024 go to post

I've never actually used Open Exchange before. I'm going to learn how to do that, and then I will do that.

David Hockenbroch · Apr 19, 2024 go to post

If you instead use:

do attachObj.%Set("contentType","application/pdf")

Does that give you the result you expect?

David Hockenbroch · Apr 16, 2024 go to post

If that's the issue, you could also try the following, and it will post to that location using the parameters you specified with InsertFormData:

set AuthToken = "/B2C_1A_client_credentials_signIn/oauth2/v2.0/token"
set tSc = AuthToken.Post()
David Hockenbroch · Apr 9, 2024 go to post

You can use the if $i to do some stuff within loops too. For example, if you enter the following to commands in a terminal:

set a = 5
for{if $i(a,-1){w a,!}else{quit}}

This will subtract 1 from a and write its value until it reaches 0, then quit the loop. Of course you could just as easily use for a=4:-1:1 {w a,!} to produce the same output a little more nicely, so you don't see that as much.

David Hockenbroch · Apr 9, 2024 go to post

You will have to initially set it to something, yes. Once you set the value in the onchange, if you want it to also run the query again, I believe you have to tell it to do that. You could make that part of your method too, though.

David Hockenbroch · Apr 8, 2024 go to post

You can create a text input and write a method that sets the maxRows property based on what the user puts into that input that fires in the input's onchange.

David Hockenbroch · Apr 2, 2024 go to post

The document's original source file name is the name the file had when it was received. %f is a shorthand for using that in the file name. If you wanted them all to be named "myfile" with the timestamp on the end, you'd change the Filename property to myfile_%Q%!+(_a)

David Hockenbroch · Apr 2, 2024 go to post

In SQL, you can use the following query:

select * from %dictionary.compiledclass where primarysuper like '%abc.test.cls%'

David Hockenbroch · Apr 1, 2024 go to post

Queries to linked tables will fail if there is an issue with the connection, but when it's up, you've got the current data. Copying it over would help with potential connection issues, though you would still have a problem if the connection can't be established when you're trying to copy the data over. But if you're doing that once a day, your data is going to be a day old at times. I think the question you need to be asking is, for your specific application, how important is it that the data you're getting from the external database is current?

David Hockenbroch · Apr 1, 2024 go to post

Your login request technically is an unauthenticated request, which means it uses the account UnknownUser. Since the web application /api/atelier requires user permission on the %Development resource, the request is failing. You could address this by either removing that restriction from the web app or by assigning the %Developer role to UnknownUser. In my opinion, though, neither of those is really ideal.

David Hockenbroch · Mar 27, 2024 go to post

I've seen this before when the browser has something cached from the old version. Have you cleared your browser's cached files?

David Hockenbroch · Mar 27, 2024 go to post

When you see something that starts with a ..# that's a parameter that's defined in a class. If you look at the source of the %CSP.REST class, you'll see:

Parameter HTTP401UNAUTHORIZED As %String = "401 Unauthorized";

So you could try either set the %response.Status "401 Unauthorized" or add that parameter to your class and use it as you have before in %CSP.REST classes.

David Hockenbroch · Mar 26, 2024 go to post

If you want to do all files in a directory, including recursively going into all subfolders, the command should be something like:

zip -r test001.zip /path/to/folder/

If you're trying to do this from inside ObjectScript, you'd have to use $ZF -100 to do that:

set args(1) = "-r"
set args(2) = "test001.zip"
set args(3) = "/path/to/folder/"
do $ZF(-100,"","zip",.args)
David Hockenbroch · Mar 26, 2024 go to post

gzip and zip are not the same thing. If you are trying to create a .zip, you need to use zip.

It's probably not finding your test001.zip because it's creating a test001.zip.gz.

David Hockenbroch · Mar 26, 2024 go to post

.gz and .zip files are two very, very different things. You can't just rename a gz to zip and expect it to work.

David Hockenbroch · Mar 25, 2024 go to post

We haven't tried using it for ObjectScript, but we do have some customers who use Crystal Reports who have tried using it to help set those up, and it has been very questionable for that. It definitely doesn't understand what's where in the database structure, and even basic formula fields seem to be far more complicated than necessary.

David Hockenbroch · Mar 22, 2024 go to post

If you want to treat them as lists, you probably want to use $LISTFROMSTRING along with $LISTGET, $LISTFIND, and $LISTLENGTH. All of which can be abbreviated to their initials, by the way - $LFS, $LG, $LF, $LL.

set y = $LFS("Red,Green,Orange,Yellow")
set x = $LFS("Purple,Black,Yellow,Pink")
for i=1:1:$LL(x){
    if $LF(y,$LG(x,i)) > 0 { 
        write $LG(x,i)_" found in both lists!",!
        //Or whatever else you want to do when you find a match here . . .
    }
}
David Hockenbroch · Mar 18, 2024 go to post

By the time I started learning ObjectScript, I had already been exposed to varying degrees to Java, javascript, PHP, C#, C++, Visual Basic, Python, and ActionScript, so to me it was different, but I was kind of used to finding my way around the quirks of a new object-oriented language.

One thing that does make it more difficult with ObjectScript in this community, though, is that so many things can be abbreviated in code, and that makes it harder for beginners to read and follow up on. For example, you might see {}.%New() or {}.%FromJSON(). It might take some time to figure out that if you want to look up further documentation on what that is, you have to look at the %Library.DynamicObject class, and that you could also use ##class(%Library.DynamicObject).%New(). Commands like set, do and for get shortened to s, d, and f. Functions like $ZDATETIME get shortened to $ZDT. We mention things like $$$ThrowStatus assuming you'll know that if you're writing a routine, you have to have #include %occStatus at the top to use that.

It's something that, once you figure it out, it's easy to understand, but those of us writing articles on here could also do a better job of writing our code samples to be readable.

David Hockenbroch · Mar 12, 2024 go to post

If it's null, your code might not even be getting to the Post method. Are you running this in the terminal, and are you getting any other errors there? Is your RTLS SSL configuration set up in the management portal?

Also, when the response comes back, it's JSON, so if you want to get just the token, you'd have to:

set tokenObj = ##class(%Library.DynamicObject).%FromJSON(AuthToken.HttpResponse.Data)
set AuthTokenValue = tokenObj.%Get("access_token")

David Hockenbroch · Feb 15, 2024 go to post

If you're using a dynamic object to set the value, there is an optional third argument to the %Set method where you can specify the data type. So if you use myobject.%Set("testingID",1234567,"number") it will be added as a number.