2016-03-01 4 views
8

Ich habe einen Code, der die “invalid values” setting on an element range index ausübt. In diesem Fall habe ich einen dateTime Elementbereichsindex für das Element onDate in meiner Datenbank konfiguriert (dies gilt sowohl für XML-Elemente als auch für JSON-Eigenschaften). Ich habe diesen Index so eingestellt, dass er ungültige Werte zurückweist. Diese Einstellung bedeutet, wenn ich versuche, den Wert eines onDate Elements zu setzen und es ist nicht zu einem dateTime oder Null (literal null in JSON oder xsi:nil="true" in XML) castable, meine Aktualisierung wird fehlschlagen. (Das entgegengesetzte Verhalten ist völlig ungültige Werte zu ignorieren.)Warum kann ich bestimmte Ausnahmen in einer MarkLogic-Anfrage nicht abfangen?

ich den folgenden Code in Server-Side JavaScript in Marklogic versuchte 8,0-4:

'use strict'; 
declareUpdate(); 
var errors = []; 
var inputs = { 
'/37107-valid.json': (new Date()).toISOString(), 
'/37107-invalid.json': 'asdf', // Should throw an error 
'/37107-null.json': null 
}; 

for(var uri in inputs) { 
try { 
    xdmp.documentInsert(
    uri, 
    { 'onDate': inputs[uri] }, 
    xdmp.defaultPermissions(), 
    ['37107'] // Collections 
    ); 
} catch(err) { 
    errors.push(err); 
} 
} 
errors.length; 

Ich würde mein Wunsch zum Erfolg zu erwarten und zu beenden mit 1 === errors.length, weil nur der zweite Einsatz fehlgeschlagen wäre, weil 'asdf' nicht als dateTime gießbar ist und es nicht null ist. Jedoch bekomme ich stattdessen einen XDMP-RANGEINDEX Fehler und meine Transaktion schlägt fehl. Warum funktioniert meine try/catch hier nicht?

Antwort

13

Das Problem ist, wie MarkLogic Update-Transaktionen verarbeitet. Anstatt die Daten tatsächlich mit jedem xdmp.docuentInsert(…) Aufruf zu ändern, reiht MarkLogic alle Aktualisierungen ein und wendet sie am Ende der Anforderung atomar an. (Dies ist auch der Grund, warum Datenbankaktualisierungen nicht innerhalb derselben Transaktion angezeigt werden können.) Daher wird der Fehler erst nach der Ausführung der Schleife ausgelöst, und die Datenbank versucht, die eingereihten Transaktionen zu übernehmen. Dieses Verhalten ist das gleiche in XQuery (etwas vereinfacht):

let $uris := (
'/37107-valid.xml', 
'/37107-invalid.xml', 
'/37107-null.xml' 
) 
let $docs := (
<onDate>{fn:current-dateTime()}</onDate>, 
<onDate>asdf</onDate>, 
<onDate xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> 
) 
return 
for $uri at $i in $uris 
return 
    try { 
    xdmp:document-insert($uri, $docs[$i],(), ('37107')) 
    } catch($err) { 
    xdmp:log($err) 
    } 

Um synchron um die Fehler zu fangen, würden Sie jedes Update in seine eigene Transaktion setzen müssen. Im Allgemeinen ist dieser Ansatz wesentlich langsamer und ressourcenintensiver als die Standard-Transaktionsverarbeitung von MarkLogic. Es ist jedoch illustrativ, um zu demonstrieren, was unter den Abdeckungen passiert, und kann für bestimmte Anwendungsfälle wie diese nützlich sein.

Im folgenden Beispiel verwende ich xdmp.invokeFunction(), um eine Funktion in einer separaten Transaktion von der übergeordneten Anforderung "aufzurufen". (Erstklassige Funktionen für den Gewinn!) Dadurch können die Updates vollständig angewendet (oder mit einem Fehler zurückgesetzt) ​​und das aufrufende Modul die Updates (oder Fehler) sehen. Ich habe die Low-Level-xdmp.invokeFunction() in meiner eigenen applyAs()-Funktion, um einige Feinheiten, wie korrekt übergeben Funktionsargumente an die Curry-Funktion.

'use strict'; 

var errors = []; 
var inputs = { 
'/37107-valid.json': (new Date()).toISOString(), 
'/37107-invalid.json': 'asdf', 
'/37107-null.json': null 
}; 

var insert = applyAs(
function(uri, value) { 
    return xdmp.documentInsert(
    uri, 
    { 'onDate': inputs[uri] }, 
    xdmp.defaultPermissions(), 
    ['37107'] 
    ); 
}, 
{ isolation: 'different-transaction', transactionMode: 'update' }, 
'one' 
); 

for(var uri in inputs) { 
try { 
    insert(uri, inputs[uri]); 
} catch(err) { 
    errors.push(err); 
} 
} 
errors.length; // Correctly returns 1 


// <https://gist.github.com/jmakeig/0a331823ad9a458167f6> 
function applyAs(fct, options, returnType /* 'many', 'one', 'iterable' (default) */) { 
    options = options || {}; 
    return function() { 
    var params = Array.prototype.slice.call(arguments); 
    // Curry the function to include the params by closure. 
    // xdmp.invokeFunction requires that invoked functions have 
    // an arity of zero. 
    var f = (function() { 
     return fct.apply(null, params); 
    }).bind(this); 
    // Allow passing in user name, rather than id 
    if(options.user) { options.userId = xdmp.user(options.user); delete options.user; } 
    // Allow the functions themselves to declare their transaction mode 
    if(fct.transactionMode && !(options.transactionMode)) { options.transactionMode = fct.transactionMode; } 
    var result = xdmp.invokeFunction(f, options); // xdmp.invokeFunction returns a ValueIterator 
    switch(returnType) { 
     case 'one': 
     // return fn.head(result); // 8.0-5 
     return result.next().value; 
     case 'many': 
     return result.toArray(); 
     case 'iterable': 
     default: 
     return result; 
    } 
    } 
} 
+0

Jetzt müssen Sie Ihre Antwort akzeptieren;) – joemfb

+0

Ich stelle mir vor Stimmzettel Füllung zu vermeiden, können Sie Ihre eigene Antwort für zwei Tage nicht akzeptieren. –