跳轉到內容

XQuery/Freebase

來自華夏公益教科書

您希望訪問一個使用 REST 作為查詢引數獲取 JSON 文件並返回 JSON 文件作為結果的 Web 服務。此示例使用 Freebase,一個流行的開放資料庫,被 Google 收購。

基本示例

[編輯 | 編輯原始碼]

我們將建立一個 Freebase 查詢,該查詢將返回藝術家 ID 為“/en/bob_dylan”的前三張專輯的名稱。

我們將使用 EXPath http 客戶端庫並使用 URI 傳送 HTTP GET 請求到 Freebase 伺服器以對我們的查詢進行編碼。

示例程式碼

[編輯 | 編輯原始碼]
xquery version "3.0";

let $freebase:="https://www.googleapis.com/freebase/v1/mqlread?query="

let $queries:= 
   (
     '[{
        "type": "/music/album",
        "name":null,
        "artist":{"id":"/en/bob_dylan"},
        "limit":3
      }]','cursor'
   )
 
let $responses := http:send-request(<http:request
     href="{$freebase || string-join(for $q in $queries return encode-for-uri($q),'&amp;')}"
     method="get"/>)
                           
return
<results>
  {if ($responses[1]/@status ne '200')
     then
         <failure>{$responses[1]}</failure>
     else
       <success>
         {util:base64-decode($responses[2])}
         {'' (: todo - use string to JSON serializer lib here :) }
       </success>
  }
</results>

示例結果

[編輯 | 編輯原始碼]

返回的結果是 JSON 格式的字串。我已經添加了空格以顯示結果的結構

<results>
   <success>
     {"cursor": "eNp9js1qxDAMhB-ml5hiVrKUWBJl6XuYHNz80EBJQtIuyz59nW7p3joXgUajb7qvbV82ix2wpcWoTbMhREIAZWrz3NsT-LIBFmkILVha99txuFwM2mqcrkNv9-GnPRtJYCmJhjU2wsE-Xnx1mVZTFWTS8PgWbHvWACFLwQOUCI4Cv0LMg_SDPzdYK2oQl96n-bMwnasO0E-toygz_UFQaqbA9PD4P8hdnT8fOaiFnEvrthTICZGCgp5e45tGZB6ZQKJmBCSAbyilTh0=",
   "result": 
   [
      {
         "artist": {"id": "/en/bob_dylan"}, 
         "name": "Blood on the Tracks", 
         "type": "/music/album"
       },
       {
         "artist": {"id": "/en/bob_dylan"},
         "name": "Love and Theft",
         "type": "/music/album"
       },
       {
           "artist": {"id": "/en/bob_dylan"}, 
           "name": "Highway 61 Revisited",
           "type": "/music/album"
       }
   ]}
   </success>
</results>

Freebase API 金鑰

[編輯 | 編輯原始碼]

以下是描述獲取 Freebase API 金鑰過程的部落格連結

http://anchetawern.github.io/blog/2013/02/11/getting-started-with-freebase-api/

使用遊標獲取更多資料

[編輯 | 編輯原始碼]

預設的 Freebase 查詢限制了返回的結果數量。Freebase 提供資料庫遊標以方便檢索整個查詢結果集。您要求在查詢結果中返回一個遊標(參見下面的示例 API 呼叫以獲取初始請求的形式),它充當指向下一組查詢結果的連結。

https://developers.google.com/freebase/v1/mql-overview#querying-with-cursor-paging-results

透過提供從先前呼叫返回的遊標值,可以獲得下一組結果。除此之外,您還將獲得另一個指向下一組的遊標。當檢索到最後一組結果時,遊標將設定為字串“false”。

Freebase 查詢概述網頁上的示例包含 Python 程式碼示例,該示例呼叫庫來為您處理所有遊標處理工作。

https://developers.google.com/freebase/v1/mql-overview#looping-through-cursor-results

但是,透過少量尾遞迴,可以在 XQuery 中輕鬆實現相同的功能。我們將使用以下 MQL 查詢作為示例,該查詢返回所有電影及其 Netflix ID。

[{
  "type": "/film/film",
  "name": null,
  "netflix_id": []
}]

關於 MQL 的一些簡要評論。您透過提供欄位名稱和 null 值來要求某事。Null 將被實際值替換。但是,如果該欄位可以具有多個值,MQL 將返回一個數組並導致您的 null 查詢出錯。即使您期望一個單一值,這也會發生,因此您可以使用空陣列的符號而不是 null 來避免此問題,如上面的查詢所示。

您可以將上面的查詢貼上到 http://www.freebase.com/query 以檢視結果(我們將處理程式碼示例中的遊標)。現在進入程式碼,它假設 XQuery 3.0

xquery version "3.0";
import module namespace xqjson="http://xqilla.sourceforge.net/lib/xqjson";

Freebase 返回 JSON,因此我們使用上述包將其轉換為 XML。從 eXist 中,您可以透過在 eXist 包管理器中單擊它來安裝該包,您可以從 eXist 儀表板訪問該包管理器。

我們為查詢宣告一個變數。

declare variable $mqlQuery {'[{
   "type": "/film/film",
  "name": null,
  "netflix_id": []
}]'};

declare variable $freebase {'https://www.googleapis.com/freebase/v1/mqlread'};
declare variable $key {obtain an API key from freebase and puts it's value here'};

由於我們將進行尾遞迴,因此我們需要將 API 呼叫放入一個函式中,該函式將進行 API 呼叫並將結果儲存在資料庫中。

  declare function local:freebaseCall($cursor as xs:string,$i as xs:integer)

此函式將採用兩個引數。第一個是遊標,第二個是為自動遞增的唯一檔名提供配置的整數。

declare function local:freebaseCall($cursor as xs:string, $i as xs:integer)
{
  if ($cursor eq 'false')

    (: termination condition :)
    then $i || ' pages loaded'
    else

     let $params :=  ('query=' || encode-for-uri($mqlQuery), 'key=' ||
       $key, 'cursor=' || encode-for-uri($cursor))

     (: Above uri encodes the parameters to the API call - we have three the
       MQL query, the API key and the cursor :)

      let $href := $freebase || '?' || string-join($params, '&amp;')

     (: This constructs the API call - again thanks to Michael Westbay for
      showing the correct way to do this by string joining the parameters
      with a separator of &amp; :)

     let $responses :=
       http:send-request(<http:request href="{$href}" method="get"/>)
      (: Make the API call. :)

    return
        if ($responses[1]/@status ne '200')
            then <failure
                   href="{xmldb:decode-uri(xs:anyURI($href))}">{$responses[1]}
                </failure>
            else

        let $jsonResponse:= util:base64-decode($responses[2])
        (: Standard EXPATH http error checking - don't forget to base64 decode
           the body of the response.
         :)

         let $freebaseXML:= xqjson:parse-json($jsonResponse)

    (: Convert the returned JSON to XML because we are going to construct an
       http PUT to store it in our xml db. :)

      let $movieData := http:send-request(
         <http:request
            href="{concat(path to store the data in your repostiory,$i,'.xml')}"
               username="username"
               password="password"
               auth-method="basic"
               send-authorization="true"
               method="put">
              <http:header name="Connection" value="close"/>
              <http:body media-type="application/xml"/>              
          </http:request>,
          (),
          <batch cursor="{$cursor}">{transform:transform($freebaseXML,doc(identity.xsl'),())}
          </batch>)

         (: Standard EXPATH PUT request. On the last line we are wrapping the
            returned XML with an element that carries the value of the cursor that
            was used to obtain the page. Identity.xsl is of course the standard
            XSLT identity transform, you can use it as a placeholder for the
            insertion of your own custom transform.
         :)

          return local:freebaseCall($freebaseXML//data(pair[@name="cursor"]), $i + 1)

         (: Finally the tail recursive call. We extract the cursor from the
            returned JSON for parameter 1 and increment $i to give us a unique
            document name for the next page to store.

};

要啟動一切,請將空字串作為初始遊標值傳遞並初始化您的計數器

   local:freebaseCall('',1)

或者,您可以使用遊標對函式呼叫進行預處理,以從您停止的地方重新開始檢索。

   local:freebaseCall($freebaseXML//data(pair[@name="cursor"]), $i + 1)};

原始示例由 Ihe Onwuka 和 Michael Westbay 於 2014 年 3 月在 eXist 列表中提供

參考文獻

[編輯 | 編輯原始碼]
華夏公益教科書