2012-04-24 30 views
11

Tôi muốn tạo bộ đếm trong xquery. nỗ lực ban đầu của tôi trông giống như sau: Kết quảCập nhật bộ đếm trong XQuery

let $count := 0 
for $prod in $collection 
let $count := $count + 1 
return 
<counter>{$count }</counter> 

dự kiến:

<counter>1</counter> 
<counter>2</counter> 
<counter>3</counter> 

kết quả thực tế:

<counter>1</counter> 
<counter>1</counter> 
<counter>1</counter> 

Các $count biến hoặc không cập nhật hoặc được đặt lại. Tại sao tôi không thể gán lại một biến hiện tại? Điều gì sẽ là một cách tốt hơn để có được kết quả mong muốn?

Trả lời

6

biến Immutable

XQuery là một functional programming language, trong đó bao gồm giữa những người khác biến bất biến, vì vậy bạn không thể thay đổi giá trị của một biến. Mặt khác, một bộ sưu tập các chức năng mạnh mẽ có sẵn cho bạn, giải quyết rất nhiều vấn đề lập trình hàng ngày.

let $count := 0 
for $prod in $collection] 
    let $count := $count + 1 
return 
<counter>{$count }</counter> 

let $count trong dòng 1 định nghĩa biến này trong tất cả các phạm vi, được tất cả các dòng trong trường hợp này như sau. let $count trong dòng 3 xác định $count mới là 0+1, hợp lệ trong tất cả các dòng sau trong khối mã này - không được xác định. Vì vậy, bạn thực sự tăng $count ba lần một, nhưng loại bỏ kết quả ngay lập tức.

BaseX 'thông tin truy vấn cho thấy phiên bản tối ưu hóa truy vấn này là

for $prod in $collection 
    return element { "counter" } { 1 } 

Giải pháp

Để có được tổng số phần tử trong $collection, bạn chỉ có thể sử dụng

return count($collection) 

Để biết danh sách các hàm XQuery, bạn có thể xem XQuery part of functx chứa cả một danh sách vui vẻ của XQuery ctions và một số chức năng hữu ích khác có thể được đưa vào như một module.

19

Hãy thử sử dụng 'at':

for $d at $p in $collection 
return 
element counter { $p } 

này sẽ cung cấp cho bạn vị trí của mỗi '$ d'. Nếu bạn muốn sử dụng điều này cùng với mệnh đề order by, điều này sẽ không hoạt động vì vị trí dựa trên thứ tự ban đầu, không phải trên kết quả sắp xếp. Để khắc phục điều này, chỉ cần lưu kết quả được sắp xếp của biểu thức FLWOR trong một biến và sử dụng mệnh đề at trong FLWOR thứ hai chỉ lặp lại kết quả đầu tiên được sắp xếp.

let $sortResult := for $item in $collection 
        order by $item/id 
        return $item 

for $sortItem at $position in $sortResult 
return <item position="{$position}"> ... </item> 
+0

Cảm ơn rất nhiều. Việc này ổn với tôi. –

+0

Nó hoạt động cho tôi quá Cảm ơn người đàn ông! bạn tiết kiệm trong ngày! – axy108

6

Như @Ranon nói, tất cả XQuery giá trị là không thay đổi, vì vậy bạn không thể cập nhật một biến. Nhưng nếu bạn thực sự cần một số có thể cập nhật (không nên quá thường xuyên), bạn có thể sử dụng đệ quy:

declare function local:loop($seq, $count) { 
    if(empty($seq)) then() 
    else 
    let $prod := $seq[1], 
     $count := $count + 1 
    return (
     <count>{ $count }</count>, 
     local:loop($seq[position() > 1], $count) 
    ) 
}; 

local:loop($collection, 0) 

Điều này hoạt động chính xác như bạn dự định với ví dụ của bạn.

Trong XQuery 3.0 một phiên bản tổng quát hơn của chức năng này thậm chí còn được định nghĩa trong thư viện chuẩn: fn:fold-right($f, $zero, $seq)

Điều đó nói rằng, trong ví dụ của bạn, bạn chắc chắn nên sử dụng at $count như thể hiện bởi @tohuwawohu.

6

Tất cả các giải pháp trên là hợp lệ nhưng tôi muốn đề cập đến mà bạn có thể sử dụng phần mở rộng XQuery Scripting để thiết lập giá trị biến:

variable $count := 0; 

for $prod in (1 to 10) 
return { 
    $count := $count + 1; 
    <counter>{$count}</counter> 
} 

Bạn có thể thử ví dụ này sống ở http://www.zorba-xquery.com/html/demo#twh+3sJfRpHhZR8pHhOdsmqOTvQ=

+1

Đây có phải là điều XQuery 3.0 không? Có lẽ tốt để đề cập đến. – derickson

+2

XQuery Scripting là một phần mở rộng được cung cấp bởi một số triển khai XQuery. Nó đặc biệt tốt nếu bạn muốn thực hiện cả hai cập nhật và trả về các phần tử cùng một lúc (ví dụ: đối với các dịch vụ web), nhưng ngăn chặn nhiều tối ưu có thể. Bạn nên dính vào XQuery cơ bản bất cứ khi nào có thể. –

+0

Ví dụ trực tiếp đã hết hạn (404) :( –

6

cụ thể để MarkLogic bạn cũng có thể sử dụng xdmp:set. Nhưng điều này phá vỡ các giả định ngôn ngữ chức năng, vì vậy hãy sử dụng nó một cách thận trọng.

http://docs.marklogic.com/5.0doc/docapp.xqy#display.xqy?fname=http://pubs/5.0doc/apidoc/ExsltBuiltins.xml&category=Extension&function=xdmp:set

Đối với một ví dụ về xdmp:set trong mã thực tế, phân tích cú pháp tìm kiếm https://github.com/mblakele/xqysp/blob/master/src/xqysp.xqy có thể hữu ích.

2

Tôi nghĩ rằng bạn đang tìm kiếm cái gì đó như:

XQUERY:

for $x in (1 to 10) 
return 
    <counter>{$x}</counter> 

OUTPUT:

<counter>1</counter> 
<counter>2</counter> 
<counter>3</counter> 
<counter>4</counter> 
<counter>5</counter> 
<counter>6</counter> 
<counter>7</counter> 
<counter>8</counter> 
<counter>9</counter> 
<counter>10</counter> 
4

Sử dụng xdmp:set thay vì truy vấn dưới đây

let $count := 0 
for $prod in (1 to 4) 
return (xdmp:set($count,number($count+1)) ,<counter>{$count }</counter> 
Các vấn đề liên quan