为什么我无法捕获 MarkLogic 请求中的某些异常?
Why can’t I catch certain exceptions in a MarkLogic request?
我有一些练习 “invalid values” setting on an element range index 的代码。在本例中,我在我的数据库中的 onDate
元素上配置了一个 dateTime
元素范围索引(这将应用于 XML 元素和 JSON 属性)。我已经将该索引设置为拒绝无效值。此设置意味着如果我尝试设置 onDate
元素的值并且它不可转换为 dateTime
或为 null(JSON 或 [=16 中的 null
=] 在 XML) 中,我的更新将失败。 (相反的行为是完全忽略无效值。)
我在 MarkLogic 8.0-4 的服务器端 JavaScript 中尝试了以下代码:
'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;
我本以为我的请求会成功并以 1 === errors.length
结束,因为只有第二个插入会失败,因为 'asdf'
不能作为 dateTime
进行转换并且它不为空。但是,我收到 XDMP-RANGEINDEX
错误并且交易失败。为什么我的 try
/catch
在这里不起作用?
问题在于 MarkLogic 如何处理更新事务。 MarkLogic 不是在每次 xdmp.docuentInsert(…)
调用时实际更改数据,而是将所有更新排队并在请求结束时自动应用它们。 (这也是为什么您在同一事务中看不到数据库更新的原因。)因此,直到循环执行并且数据库尝试提交排队的事务之后才会抛出错误。这种行为在 XQuery 中是相同的(稍微简化):
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)
}
为了同步捕获错误,您需要将每个更新放入其自己的事务中。一般来说,这种方法比 MarkLogic 的默认事务处理要慢得多,而且会占用大量资源。但是,它在这里只是为了说明幕后发生的事情,并且可以在特定用例中派上用场,比如这个。
在下面的示例中,我使用 xdmp.invokeFunction()
在父请求的单独事务中“调用”一个函数。 (First-class functions for the win!)这允许完全应用更新(或回滚错误)并允许调用模块查看更新(或错误)。我将低级 xdmp.invokeFunction()
包装在我自己的 applyAs()
函数中以提供一些细节,例如将函数参数正确传递给柯里化函数。
'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;
}
}
}
我有一些练习 “invalid values” setting on an element range index 的代码。在本例中,我在我的数据库中的 onDate
元素上配置了一个 dateTime
元素范围索引(这将应用于 XML 元素和 JSON 属性)。我已经将该索引设置为拒绝无效值。此设置意味着如果我尝试设置 onDate
元素的值并且它不可转换为 dateTime
或为 null(JSON 或 [=16 中的 null
=] 在 XML) 中,我的更新将失败。 (相反的行为是完全忽略无效值。)
我在 MarkLogic 8.0-4 的服务器端 JavaScript 中尝试了以下代码:
'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;
我本以为我的请求会成功并以 1 === errors.length
结束,因为只有第二个插入会失败,因为 'asdf'
不能作为 dateTime
进行转换并且它不为空。但是,我收到 XDMP-RANGEINDEX
错误并且交易失败。为什么我的 try
/catch
在这里不起作用?
问题在于 MarkLogic 如何处理更新事务。 MarkLogic 不是在每次 xdmp.docuentInsert(…)
调用时实际更改数据,而是将所有更新排队并在请求结束时自动应用它们。 (这也是为什么您在同一事务中看不到数据库更新的原因。)因此,直到循环执行并且数据库尝试提交排队的事务之后才会抛出错误。这种行为在 XQuery 中是相同的(稍微简化):
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)
}
为了同步捕获错误,您需要将每个更新放入其自己的事务中。一般来说,这种方法比 MarkLogic 的默认事务处理要慢得多,而且会占用大量资源。但是,它在这里只是为了说明幕后发生的事情,并且可以在特定用例中派上用场,比如这个。
在下面的示例中,我使用 xdmp.invokeFunction()
在父请求的单独事务中“调用”一个函数。 (First-class functions for the win!)这允许完全应用更新(或回滚错误)并允许调用模块查看更新(或错误)。我将低级 xdmp.invokeFunction()
包装在我自己的 applyAs()
函数中以提供一些细节,例如将函数参数正确传递给柯里化函数。
'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;
}
}
}