Your points are well taken. I would like to add some thoughts:

Parent/Child relationship is an interesting concept, but it does not do well with larger volumes of data as you can't make use of bitmap indices in the child class. Embedding documents or serial classes on the other hand fully support bitmap indices which are important if you are operating on a larger set.

Data type handling can be designed in a very flexible way. Your suggestion of using generic %String properties is one option to deal with flexible data types with Caché Persistent Objects. But you get no support of the backend for any complex values you store in such a property. You have to write code to serialize/deserialize your complex values and - even more important - you can't index sub-values as they are no properties. This may be suited for some use cases, but not for others.

To answer your question about documenting schemas: Many developers just document sample JSON documents and explain what their purpose is. We offer no additional tooling for this yet, but we are working on tools that allow you to understand what a collection looks like. This is an area that will improve over time.

Ben,

Caché Objects don't come with exact the same benefits and I am happy to briefly discuss the differences and similarities. Actually, every time I talk about Caché Objects, I mean Caché Persistent Classes.

  • Flexibility

This one is simple. Caché Objects have a fixed schema. It can be changed for sure, but you have to potentially migrate your data if you still want to access it consistently. The impact depends on the type of the schema change, of course. If you just add a property, you are fine. Even some type changes may not require a data migration.

  • Sparseness

Caché Objects are persisted by making use of a storage strategy. By default, each property gets a slot in a $List structure. $List is optimized for sequentially accessing elements, not for random access, which is fine for a fixed schema world. You usually want to load all top-level values most of the time anyway. Therefore, the $List serialization is optimized for dense data.

Assume an object has 100 properties and only properties 1,10,25,50,75 and 100 are filled. That is sparse data. With the $List serialization we have to jump through the empty buckets to read the six values we actually are interested in. That is a waste of time. Also, we are storing 94 empty buckets on disk. That is a waste of space, not much, but it can add up if your data is very sparse. 

Document stores embrace serialization formats that are optimized for storing sparse data in a compact form and for random access.

  • Hierarchical

Caché Objects can either link to instances of other classes (persistent class includes a property where the type points to another persistent class) or they can embed instances of another class (persistent class includes a property where the type points to another serial class).

A document can embed another structure, which is similar to our serial class implementation because the data is actually physically stored together. One physical read of a document can retrieve every information you are interested in if it is designed correctly.

You cannot compare embedding with a link to another table/class as the data is stored separately and usually requires access to separate blocks.

  • Dynamic Types

Properties of a Caché Object have a type. I can't take a timestamp and store it in a property with the type Sample.Person. The Object and SQL layer will validate values and ensure type safety for me.

Document keys are not associated with a type at all. Only a value is. I can take two documents that have different types for the same key and store them in the same collection. This is an example of such two documents:

set person1 = {"name":"Stefan Wittmann"}

set person2 = {"name":{"first":"Stefan","last":"Wittmann","middle":null}}

I can't simply model this with classes. person1 would require a %String property while person2 requires a link to a serial class.

 

I hope this sheds some light on the individual benefits. Obviously, this comes with a price. Your application code has to do more validation as the backend allows you to work without a schema. There is always a cost involved.

Q2: It is against core REST architecture principles, so I hope not.

That is not entirely true. Filters and action descriptions are commonly exposed as URL parameters as they serve to either specify an action or limit the working set. But you are still operating on the same resource identity.

What you can observe is that REST is just a best practice leveraging HTML. You will find any possible implementation of REST interfaces out there, some are true to the original spirit, some are not.

Anyway, you can fight endless wars about REST  interfaces, but one thing is for sure: URL parameters are commonly used for REST interfaces.

To answer the original question: I don't see a need to add URL parameters to the URL map. They would only add value if we added them as method arguments, but I think that would be very confusing as path variables are passed as arguments at the moment.

You can use the %request.Data property to retrieve the URL parameters.

I have never actually tried to pass slashes as parameters, but I would guess that it should work if you encode them. I am aware that some systems drop these requests, because it is easy to inject malicious code this way.

Are you receiving a 404 and use Apache? If so, you may have to enable  the AllowEncodedSlashes directive:

http://httpd.apache.org/docs/current/mod/core.html#allowencodedslashes

I only managed to get my code down to 207 characters and it's even missing the whitespace->0 mapping, I saw that too late. Butwhoneedswhitespacestodayanyway?

In case you are curious here is my code:

 #define a(%x) $LG(o,$a($E(s,%x,%x))-96)
 s (x,o)="" f m=2:1:9{s r="" f n=1:1:$S(m=7:4,m=9:4,1:3){s r=r_m,o=o_$LB(r)}} f j=1:1:$L(s)-1{s x=x_$$$a(j) s:$E($$$a(j+1),1,1)=$E(x,$L(x),*) x=x_" "} q x_$$$a($L(s))

The first part actually builds a $LIST with the mapping from characters to the corresponding keypad sequence. The second part loops over the input string and does a lookup for the keypad sequence. A look-ahead checks if I have to add a whitespace to indicate a pause. Because of the look-ahead I have to treat the last character conversion outside of my main loop.

Tricks I used:

  • Replace if statements with $SELECT
  • Use SET statement to set multiple variables to the same values
  • Built a macro for code you need multiple times (I need the lookup three times, in my main loop, for the look-ahead and for the last character conversion) 

Not an optimal algorithm and it's even lacking a piece, but still it is a fun exercise.

Indeed. Starting with Caché 2016.2 you can call $toJSON() on registered and persistent objects and we will convert it with a standard projection logic into a dynamic entity (%Object or %Array) and serialize it as JSON with a $toJSON() call.

If you want to modify your object before you output it to JSON you can first call $compose on your registered object to convert it to a dynamic object, modify it to your needs and then call $toJSON() on your modified dynamic object.

Later versions will introduce more sophisticated means to influence the standard behavior of $compose. 

The addition of the JSON_OBJECT and JSON_ARRAY SQL functions allows you to easily create JSON from a SQL query as well, as Kenneth pointed out. 

You cannot use macros in Zen runtime expression this documentation chapter covers runtime expressions:

http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=...

If this question is actually about localizing text in Zen, please take a look at this documentation chapter:

http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=...

Localize your text in %OnGetJSResources() and load it on the client-side via zenText(id).

I would write it like this:

    set array =  []

    while (result.Next()) {
        set object = { 
                "data":{
                    "id":result.Data("ID"),
                    "reg":result.Data("Registration"),
                    "snNum":result.Data("SatNavVehNumber")
                }
            }
          do array.$push(object)
    }

You can directly embed your values as Caché Object Script and that makes your code look pretty close like the desired outcome. This approach makes it very simple to build complex JSON structures and still know what you are doing.

Steve, the correct approach is to either

a) subclass the plugin in question and generate a different HTML base or invoke additional beautify code

b) build your own plugin if you want to support a new layout object

What you describe sounds like a specialized $navbar component, so you may want to subclass the bootstrap plugin and register a specialized $navbar widget, e.g. $mynavbar.

I am not able to view the image, but in general Object Synchronization can be used by multiple servers. The architecture is designed in such a way, that usually one node is taking the role of a master and all other nodes become clients to this master.

You can implement other schemes as well, as indicated by this paragraph in the documentation:

For object synchronization, the idea of client and server is by convention only. For any two databases, you can perform bidirectional updates; if there are more than two databases, you can choose what scheme you use to update all of them (such as local databases synchronizing with a main database independently).

There is no support for bi-directional updates at the same time, so you have to sync one direction first and after that happened you can sync the other way around. The more complex your mesh becomes the harder it becomes to resolve conflicts.

That's why the documentation recommends staying with a master / client scheme, as this reduces the complexity of resolving conflicts.

Also, you have to be aware that Object Synchronization is not built for real-time updates. You are synchronizing at discrete intervals.

When the user refreshes the page, the template defined in the parameter TEMPLATECLASS is loaded. Also, the default keys as specified in your XData block will be used to retrieve your layout and our data (initialLayoutKey and initialDocumentKey).

You can use the documentView's callback onload and load another template and set correct keys before the documentView actually starts the setup process.

Assuming the following documentView definition:

<mojo:documentView id="mainView"
onload="zenPage.initPage();"
initialDocumentKey="home"
initialLayoutKey="home"
ongetlayout = "return zenPage.getContent('layout',key,criteria);"
ongetdata = "return zenPage.getContent('data',key,criteria);"
>
<mojo:jQM-1.3.2-PageManager jQueryAnimation="flip" onPageShow="zenPage.onPageShow(layoutkey,documentkey);">
<mojo:jQM-1.3.2-Helper/>
<mojo:googleMaps-3-Helper/>
<mojo:HTML5Helper/>
<mojo:mojoDefaultHelper/>
</mojo:jQM-1.3.2-PageManager>
</mojo:documentView>

You can define the following method initPage:

ClientMethod initPage() [ Language = javascript ]
{
    zenPage.gotoArea('the area identifier you want to load')
    zen('mainView').initialLayoutKey = 'the layout key you want to use';
    zen('mainView').initalDocumentKey = 'the data key you want to use';
}

Please note that you are first loading your template and then set the initialLayoutKey and initialDocumentKey values, as the documentView hasn't actually done any work yet.

It is on you to store any relevant state somewhere locally, e.g. in a cookie or the localStorage. You can retrieve that state in your initPage() method and initialize your page accordingly.

Not sure why this is happening. This is working fine for me:

SELECT now() as tnow, max('2016-05-13 08:51:16') as latest, DATEDIFF (s,now(), max('2016-05-13 08:51:16')) as difference
FROM Sample.Person

tnow                                   latest                                  difference
2016-05-13 13:38:08    2016-05-13 08:51:16    -17212
2016-05-13 13:38:42    2016-05-13 08:51:16    -17246
2016-05-13 13:38:56    2016-05-13 08:51:16    -17260

I am using the function now().

Let me address your questions. The node.js module files are currently released and shipped with a Caché kit. Our mid-term goal is to make external binding files available via the native package managers of the corresponding environment. For node.js, we are talking about npm, for Java Maven and for .NET NuGet. That being said we are not there yet. I will see what we can do in the short-term.

Let's talk about support for specific versions.

Support for Node.js  4.2.x has been introduced with Caché 2016.2. If you grab a Windows field test kit you can find the binding file here: <install-dir>\bin\cache421.node.

Support for Node.js 5.x.x is already implemented and is currently triaged for a release with Caché 2016.3. But v5 is not a very important release for production use.

If you take a look at the long term support plan from the node team (https://github.com/nodejs/LTS/), there is a nice picture at the bottom describing that 4.x will be on long-term support (LTS) until April 2017. v6 becomes LTS from October 2016 until April 2018.

The v6 release blog post recommends staying on 4.x if you require stability or move to 6.x if you can upgrade. Avoid 5.x. https://nodejs.org/en/blog/release/v6.0.0/

We are currently working on implementing support for 6.x.x