首页 > 解决方案 > 如何在查询控制台的上下文中运行 MarkLogic RestAPI 调用?

问题描述

我想分析对自定义函数的 RestAPI 调用。看来我有几个选择。请评论每一个:

  1. 从给定应用程序服务器端口的访问日志或请求日志中提取 RestAPI 调用并将其复制到查询控制台。我不知道这是否可能,除非我使用xdmp:http-postor xdmp.httpPostAPI 调用来调用 RestAPI 调用。我只是不确定配置一个 Rest 调用是否有意义。
  2. 我有一个自定义的“zsearch”模块/库来处理搜索请求并压缩结果。我想我可以将return:planandreturn:metrics 选项添加到函数中搜索查询的参数中zsearch:get() ,但我不明白在下面的代码示例中这样做的语法,我想硬编码返回调用。有人可以提供一个例子吗?

此外,此模块利用/MarkLogic/rest-api/models/search-model-query.xqy处的 search-model-query Rest API 调用。我想知道这个模块的位置,因为我想查看它并向它添加一些日志记录。我不确定为什么代码使用search-model-query.xqy库而不是search:search调用。似乎该search:search调用将比从数据库模块内进行的后续 Rest API 调用更有效。

我还想了解将此代码从使用search-model-query.xqy 移植https://docs.marklogic.com/search:searchsearch:search中描述的功能需要什么。

这是zsearch代码:


module namespace zsearch = "http://marklogic.com/rest-api/resource/zsearch";

import module namespace lid = "http://marklogic.com/util/log-id"
  at "/MarkLogic/appservices/utils/log-id.xqy";

import module namespace searchmodq = "http://marklogic.com/rest-api/models/search-model-query"
  at "/MarkLogic/rest-api/models/search-model-query.xqy";

import module namespace sut = "http://marklogic.com/rest-api/lib/search-util"
  at "/MarkLogic/rest-api/lib/search-util.xqy";

import module namespace eput = "http://marklogic.com/rest-api/lib/endpoint-util"
  at "/MarkLogic/rest-api/lib/endpoint-util.xqy";

declare default function namespace "http://www.w3.org/2005/xpath-functions";

declare option xdmp:mapping "false";
(:
declare option xdmp:transaction-mode "auto";
:)

declare private function zsearch:response-callback(
  $response-type as xs:string?
) as empty-sequence()
{
 eput:response-type-callback($response-type),

 let $timestamp := xdmp:request-timestamp()
 return if (exists($timestamp)) then eput:add-response-header("ML-Effective-Timestamp",string($timestamp)) else ()
 
};

declare private function zsearch:query-parameter(
  $paramMap   as map:map,
  $params     as map:map,
  $name       as xs:string,
  $required   as xs:boolean,
  $repeatable as xs:boolean,
  $allowed    as xs:string*,
  $privileges as xs:string*,
  $converter  as function(*)?
) as map:map
{
  let $values := map:get($params,$name)
  return (
      if (not($required) or exists($values[. ne ""])) then ()
      else error((),"REST-REQUIREDPARAM",$name),

      if (empty($values))
      then $paramMap
      else (
          if (empty($privileges) or xdmp:has-privilege($privileges, "execute")) then ()
          else error((),"REST-INVALIDPARAM", concat(
              $name," parameter requires at least one privilege: ",string-join($privileges,", ")
              )),

          if ($repeatable or count($values) lt 2) then ()
          else error((),"REST-REPEATEDPARAM",$name),

          map:with($paramMap,$name,
              if (empty($allowed) and empty($converter))
              then $values
              else
                  let $fname  :=
                      if (empty($converter)) then ()
                      else function-name($converter)
                  let $fns    :=
                      if (empty($fname)) then ()
                      else namespace-uri-from-QName($fname)
                  let $flocal :=
                      if (not($fns = ("http://www.w3.org/2001/XMLSchema"))) then ()
                      else local-name-from-QName($fname)
                  for $val in $values
                  let $converted :=
                      if (empty($converter))
                      then $val
                      else if (empty($flocal) or xdmp:castable-as($fns,$flocal,$val))
                      then $converter($val)
                      else error((),"REST-INVALIDPARAM", concat(
                          $name, " parameter not convertible to xs:",
                          local-name-from-QName($fname)," value: ",string($val)
                          ))
                  return
                      if (exists($allowed) and not($converted = $allowed))
                      then error((),"REST-INVALIDTYPE",
                          ($name, $val, "is not one of", string-join($allowed, "|")))
                      else $converted
              )
          )
      )
};

declare private function zsearch:validate-parameter-names(
  $paramMap        as map:map,
  $params          as map:map,
  $ignore-patterns as xs:string*
) as xs:string*
{
  for $name in map:keys($params)
  return
      if (map:contains($paramMap,$name)) then ()
      else zsearch:check-ignore($name,$ignore-patterns)
};

declare private function zsearch:check-ignore(
  $name            as xs:string,
  $ignore-patterns as xs:string*
) as xs:string?
{
  if (exists($ignore-patterns) and exists(
      for $pattern in $ignore-patterns
      return
          if (matches($name,$pattern))
          then true()
          else ()
      ))
  then ()
  else $name
};

declare private function zsearch:compress(
  $data            as document-node()*
) as document-node()
{
  let $zip := xdmp:zip-create(
      <parts xmlns="xdmp:zip"><part>result.json</part></parts>,
      ($data)
  )
  return document { $zip }
};

declare function zsearch:get($context as map:map, $params as map:map) as document-node()* {

  let $headers   := eput:get-request-headers()
  let $accept    := eput:get-accept-types($headers)
  let $method    := eput:get-request-method($headers)
  let $env       := eput:response-callback-map(zsearch:response-callback#1)
  let $paramMap  := map:new()
      =>zsearch:query-parameter($params,"q",false(),false(),(),(),())
      =>zsearch:query-parameter($params,"category",false(),true(),("content","metadata","collections","permissions","properties","quality","metadata-values"),(),())
      =>zsearch:query-parameter($params,"format",false(),false(),("json","xml"),(),())
      =>zsearch:query-parameter($params,"start",false(),false(),(),(),xs:unsignedLong#1)
      =>zsearch:query-parameter($params,"pageLength",false(),false(),(),(),xs:unsignedLong#1)
      =>zsearch:query-parameter($params,"options",false(),false(),(),(),())
      =>zsearch:query-parameter($params,"collection",false(),true(),(),(),())
      =>zsearch:query-parameter($params,"directory",false(),false(),(),(),())
      =>zsearch:query-parameter($params,"view",false(),true(),("none","results","metadata","facets","all"),(),())
      =>zsearch:query-parameter($params,"txid",false(),false(),(),(),())
      =>zsearch:query-parameter($params,"database",false(),false(),(),(),())
      =>zsearch:query-parameter($params,"forest-name",false(),true(),(),(),())
      =>zsearch:query-parameter($params,"transform",false(),false(),(),(),())
      =>zsearch:query-parameter($params,"timestamp",false(),false(),(),(),())
      =>zsearch:query-parameter($params,"trace",false(),true(),(),"http://marklogic.com/xdmp/privileges/rest-tracer",())
  let $extra-names := zsearch:validate-parameter-names(
      if ($method = ("GET", "HEAD"))
          then zsearch:query-parameter($paramMap,$params,"structuredQuery",false(),false(),(),(),())
          else $paramMap,
      $params,
      ('name')
      )
  return (
      if (empty($extra-names)) then ()
      else error((),"REST-UNSUPPORTEDPARAM", concat(
          "invalid parameters: ",string-join($extra-names,", ")
          )),

      lid:enable(map:get($paramMap,"trace")),

      if (searchmodq:check-untraced()) then ()
      else lid:log(
          $searchmodq:trace-id,"zearch",
          map:entry("method",$method)=> map:with("headers",$headers)=> map:with("parameters",$paramMap)
          ),

      if (empty($accept)) then ()
      else if ($accept = ("application/json", "text/json", "application/xml", "text/xml")) then
          if (map:contains($paramMap,"category"))
          then error((),"REST-UNSUPPORTEDPARAM",
              "Can use the 'category' parameter only with multipart/mixed accept")
          else
              let $view := map:get($paramMap,"view")
              return
                  if (not($view eq "none")) then ()
                  else error((),"REST-UNSUPPORTEDPARAM",
                      "Can use the 'none' value for the 'view' parameter only with multipart/mixed accept")
      else if (starts-with(head($accept),"multipart/mixed"))
      then map:put($env,"add-header",eput:add-response-header#2)
      else error((), "REST-UNACCEPTABLETYPE", string-join($accept,", ")),

      let $_ := 1
      return (
          let $response :=  searchmodq:search-get($headers,$paramMap,$env)
          let $has-matches := map:get($env, "has-matches")
          return
              if (exists($response)) then 
(:
                  let $_ := map:put($context,"output-type","application/octet-stream") 
:)
                  let $_ := xdmp:set-response-content-type("application/octet-stream")
                  return zsearch:compress($response)
              else if ($has-matches) then ()
              else xdmp:set-response-code(404,"Not Found")
      )
  )
};```

标签: marklogic

解决方案


您可以使用xdmp:http-get()xdmp:http-post()点击实际端点并通过 HTTP 使用适当的操作调用它。您可以在选项中指定return-planand :return-metrics

xdmp:http-get("http://localhost:8000/LATEST/config/resources/zsearch",
  <options xmlns="xdmp:http">
    <authentication method="basic">
      <username>myname</username>
      <password>mypassword</password>
      <return-metrics>true</return-metrics>
      <return-plan>true</return-plan>
    </authentication>
  </options>)

您还可以导入已安装的 REST 模块并以编程方式调用它的方法,而不是通过 HTTP 端点:

   import module namespace zsearch = "http://marklogic.com/rest-api/resource/zsearch" 
     at "/marklogic.rest.resource/zsearch/assets/resource.xqy";
   let $context as map:map := map:new()
   let $params as map:map := map:new()
   return
     zsearch:get($context, $params)

你也可以使用xdmp:plan()xdmp:query-meters()

该模块/MarkLogic/rest-api/models/search-model-query.xqy可以在任何 MarkLogic 服务器的 MarkLogic 安装中找到。它位于安装位置下/Modules/MarkLogic/rest-api/models/search-model-query.xqy


推荐阅读