I am new to xquery, and I am trying to use a collection to reload my webpage and keep some information. My problem is after I create the collection and save my node using (sausalito) the collection stays alive even afterI close the program. Next time I use the collection it has nodes already in it. I only need the collection to save a node, then reload website and delete node. Problem is that I am not able to delete the collection or the nodes. I tried using delete-nodes() and other methods from http://www.zorba-xquery.com/doc/zorba-1.4.0/zorba/xqdoc/xhtml/www.zorba-xquery.com_modules_xqddf.html#delete-index-1
What I have
declare collection resultview:collection as node()*;
declare variable $resultview:collection as xs:QName := xs:QName("resultview:collection");
declare sequential function resultview:add($allMovies as element(movies))
{
for $movie in $allMovies
return xqddf:insert-nodes($resultview:collection, $allMovies);
fn:trace(xqddf:collection($resultview:collection), "Collection data: "),
exit returning resultview:list();
};
declare sequential function resultview:deleteList() {
let $a := ""
return xqddf:delete-index($resultview:collection);
exit returning resultview:list();
};
if I do understand you correctly, this should work:
declare collection resultview:collection as node()*;
declare variable $resultview:collection as xs:QName := xs:QName("resultview:collection");
declare sequential function resultview:add($allMovies as element(movies))
{
xqddf:insert-nodes($resultview:collection, $allMovies);
resultview:list();
};
declare sequential function resultview:deleteList() {
xqddf:delete-nodes(
$resultview:collection,
xqddf:collection($resultview:collection));
resultview:list();
};
use delete-nodes instead of delete-index (the latter deletes a complete index and not a node at a specific index position).
does that help?
Related
I'm trying to create a loop to read, for example, 4200 users from 1000 to 1000 but I can't get it to cut when it reaches the end. I tried it with if, for and I couldn't do it.
I have programmed in JAVA but with Groovy I see that the structure is different.
urlUsers = urlUsers.concat("/1/1000");
List<UserConnectorObject> usersList = null;
while({
gesdenResponse = GesdenUtils.sendHttpRequest(urlUsers, "LOOKUP", null,
request.getMetaData()?.getLogin(), request.getMetaData()?.getPassword());
log.info("Users data in JSON: "+gesdenResponse.getOutput())
usersList = GesdenUtils.fromJSON(gesdenResponse.getOutput(), GesdenConstants.USER_IDENTITY_KEY);
usersList.size() == 10;
log.info("List size in JSON "+usersList.size());
}()) continue
Groovy has lots of loop structures, but it is crucial to separate the regular ones (lang built-ins) and the api functions which take closure as an argument
take closure - no plain way to escape
If you want to iterate from A to B users, you can use, for instance,
(10..20).each { userNo -> // Here you will have all 10 iterations
if ( userNo == 5) {
return
}
}
If something outrageous happens in the loop body and you cannot use return to escape, as loop boddy is a closure (separate function) and this resurn just exits this closure. Next iteration will happen just after.
use regular lang built-in loop structures - make use of break/continue
for (int userNo in 1..10) { // Here you will have only 5 iterations
if (userNo == 5) {
break
}
}
It looks like your closure always return falsy because there is no explicit return, and the last statement evaluated is the call to log.info(String) which returns void.
Use an explicit return or move/delete the log statement.
I have the following code that returns an array of array of results I try to use distinct-values to remove duplicates and it does nothing I have tried also removing using looping functions by comparing values with no success.
I have tried converting to "xs anyAtomicType" and using distinct values
I have tried putting in json array and extracting the sub-array
I have tried tokenizing, xdmp quote, string-before/after and many others
declare function local:verify-user-uri($dir as xs:string)
{
for $each in cts:uris($dir, ())
let $uIds := (for $d in $each
where contains($d, "/profile.xml")
return $d)
return $uIds
};
I get back duplicated result in form of:
/users/123-343-/profile.xml
/users/122-222-/profile.xml
/users/123-343-/profile.xml
/users/122-222-/profile.xml
/users/123-343-/profile.xml
/users/122-222-/profile.xml
I am expecting:
/users/123-343-/profile.xml
/users/122-222-/profile.xml
Is it possible that you have simply invoked this function 3 times and didn't realize it?
You have declared $dir to be a single xs:string. If your $dir happened to be a sequence of strings of the same directory, or if you otherwise invoked the function 3 times with the directory variable.
It can easily happen with function mapping enabled (default behavior). https://docs.marklogic.com/guide/xquery/enhanced#id_55459
There are a couple of things that you can do as a diagnostic:
1.) Remove the explicit type on the $dir parameter in the function:
declare function local:verify-user-uri($dir)
{
for $each in cts:uris($dir, ())
let $uIds := (for $d in $each
where contains($d, "/profile.xml")
return $d)
return $uIds
};
do you get an error executing cts:uris() that looks like this:
[1.0-ml] XDMP-ARGTYPE: )err:XPT0004) cts:uris(("/users/", "/users/", "/users/"), ()) -- arg1 is not of type xs:string?
2.) try disabling function mapping by adding the following to the prolog:
declare option xdmp:mapping "false";
and see if you then get an invalid coercion error like:
[1.0-ml] XDMP-AS (err:XPTY0004) $dir as xs:string -- Invalid coersion ("/users/", "/users/", "/users/") as xs:string
3.) You could also add something to the end of the sequence of values returned from the function to indicate how many times it has executed:
declare function local:verify-user-uri($dir as xs:string)
{
for $each in cts:uris($dir, ())
let $uIds := (for $d in $each
where contains($d, "/profile.xml")
return $d)
return $uIds, "#"
};
And see how many times you see "#" in the result. If more than one, you are invoking the function multiple times.
Next to the good suggestions from Mads, I notice a couple of other things about your code:
It doesn't make sense to iterate over $each as it contains one uri only. Keep in mind that a FLWOR statement ends with a return, which tells what should be the result per item
Beware that the first arg to cts:uris only marks a start, not an end. If you feed in /aaa/, you also get back /bbb/ etc, though not vice versa.
To be honest, I think you are looking for cts:uri-match() instead, which would reduce your function to a one-liner:
declare function local:verify-user-uri($dir as xs:string) {
cts:uri-match($dir || "*/profile.xml")
};
HTH!
PS: I do recommend always disabling function mapping as Mads recommends. It can prevent a lot of confusion.
Is there a way to pass the variables down to a nested callback without passing them to each function along the way unnecessarily? The problem is that I need to call getValueFromTable1 to get one value of the database. Take that result and add another variable from the original list and send that to a getValueFromTable2 to get the second piece of information from the database, and then finally take that result2 with the userID from the top level function and use that to do a DB insert.
I know I could do a more complex DB query with joins and such so that I get all my information at once and then just call one function, but my "getValueFromTable1" and "getValueFromTable2" are generic functions that get a set of data from the database that I can reuse in more than one place hence why I am trying to do it this way.
The problem I get is that node JS doesn't have the itemList in scope when i call
itemList[i].item2
And I am not passing item2 into function2 because function2 does not need it for its own purpose and it would make it take a variable it doesn't need.
doDatabaseInsert(itemList, userID) {
for(var i=0; i < itemList.length; i++) {
getValueFromTable1(itemList[i].item1, function(results) {
getValueFromTable2(results, itemList[i].item2, function(results2) {
//Finally do stuff with all the information
//Do DB insert statement with userID, and results2 into table 3
}
}
}
}
You can't do that with a regular for loop because you are referencing i from inside an asynchronous callback, where the value of i is already itemList.length because the for loop finished long ago.
Try this instead:
itemList.forEach(function(item) {
getValueFromTable1(item.item1, function(results) {
getValueFromTable2(results, item.item2, function(results2) {
//Finally do stuff with all the information
//Do DB insert statement with userID, and results2 into table 3
});
});
});
I was able to store functions into a table. But now I have no idea of how to invoke them. The final table will have about 100 calls, so if possible, I'd like to invoke them as if in a foreach loop. Thanks!
Here is how the table was defined:
game_level_hints = game_level_hints or {}
game_level_hints.levels = {}
game_level_hints.levels["level0"] = function()
return
{
[on_scene("scene0")] =
{
talk("hint0"),
talk("hint1"),
talk("hint2")
},
[on_scene("scene1")] =
{
talk("hint0"),
talk("hint1"),
talk("hint2")
}
}
end
Aaand the function definitions:
function on_scene(sceneId)
-- some code
return sceneId
end
function talk(areaId)
-- some code
return areaId
end
EDIT:
I modified the functions so they'll have a little more context. Basically, they return strings now. And what I was hoping to happen is that at then end of invoking the functions, I'll have a table (ideally the levels table) containing all these strings.
Short answer: to call a function (reference) stored in an array, you just add (parameters), as you'd normally do:
local function func(a,b,c) return a,b,c end
local a = {myfunc = func}
print(a.myfunc(3,4,5)) -- prints 3,4,5
In fact, you can simplify this to
local a = {myfunc = function(a,b,c) return a,b,c end}
print(a.myfunc(3,4,5)) -- prints 3,4,5
Long answer: You don't describe what your expected results are, but what you wrote is likely not to do what you expect it to do. Take this fragment:
game_level_hints.levels["level0"] = function()
return
{
[on_scene("scene0")] =
{
talk("hint0"),
}
}
end
[This paragraph no longer applies after the question has been updated] You reference on_scene and talk functions, but you don't "store" those functions in the table (since you explicitly referenced them in your question, I presume the question is about these functions). You actually call these functions and store the values they return (they both return nil), so when this fragment is executed, you get "table index is nil" error as you are trying to store nil using nil as the index.
If you want to call the function you stored in game_level_hints.levels["level0"], you just do game_level_hints.levels["level0"]()
Using what you guys answered and commented, I was able to come up with the following code as a solution:
asd = game_level_hints.levels["level0"]()
Now, asd contains the area strings I need. Although ideally, I intended to be able to access the data like:
asd[1][1]
accessing it like:
asd["scene0"][1]
to retrieve the area data would suffice. I'll just have to work around the keys.
Thanks, guys.
It's not really clear what you're trying to do. Inside your anonymous function, you're returning a table that uses on_scene's return value as keys. But your on_scene doesn't return anything. Same thing for talk.
I'm going to assume that you wanted on_scene and talk to get called when invoking each levels in your game_level_hints table.
If so, this is how you can do it:
local maxlevel = 99
for i = 0, maxlevel do
game_level_hints.levels["level" .. i] = function()
on_scene("scene" .. i)
talk("hint" .. i)
end
end
-- ...
for levelname, levelfunc in pairs(game_level_hints.levels) do
levelfunc()
end
I'm writing long digit arythmetics. This is a function for adding to longint long binary digits. I need to output the sum inside the function, to debug it. How could I do it, without creating new variables?
function add(var s1,s2:bindata;shift:longint):bindata;
var l,i:longint;
o:boolean;
begin
writeln(s1.len,' - ',s2.len);
o:=false;
l:=max(s1.len,s2.len);
add.len:=0;
for i:=1 to l do begin
if o then Begin
if s1.data[i+shift] then Begin
if (s2.data[i]) then add.data[i+shift]:=true
Else add.data[i+shift]:=false;
End
else if s2.data[i] then add.data[i+shift]:=false
else Begin
add.data[i+shift]:=true;
o:=false;
End;
End
Else Begin
if s1.data[i+shift] then Begin
if s2.data[i] then
Begin
add.data[i+shift]:=false;
o:=true;
End
Else add.data[i+shift]:=true;
End
else if s2.data[i] then add.data[i+shift]:=true
else add.data[i+shift]:=false;
End;
output(add); //Can I output a variable?
end;
add.len:=l;
if o then Begin
inc(add.len);
add.data[add.len]:=true;
End;
end;
You are accumulating the result of the function within the function result variable, which is generally fine, but uses an outdated style, and leads to exactly the problem you're facing here. You're trying to report an intermediate value of the function result, and to do that, you're trying to reference the name of the function, add. When you do that, though, the compiler interprets it as an attempt to report the function itself, rather than the expected return value of this particular invocation of the function. You'll get the address of the function, if output is defined to accept function addresses; otherwise, you'll get a compiler error.
If your compiler offers a certain common language extension, then you should use the implicit Result variable to refer to the intermediate return value instead of continuing to refer to it by the function name. Since Result is declared implicitly, you wouldn't have to create any other variables. The compiler automatically recognizes Result and uses it as an alias for the function's return value. Simply find every place you write add within the function and replace it with Result. For example:
if o then begin
Inc(Result.len);
Result.data[Result.len] := True;
end;
Turbo Pascal, Free Pascal, GNU Pascal, and Delphi all support the implicit Result variable, but if you've managed to get stuck with a compiler that doesn't offer that extension, then you have no choice but to declare another variable. You could name it Result, and then implement your function with one additional line at the end, like so:
function add(var s1, s2: bindata; shift: longint): bindata;
var
l, i: longint;
o: boolean;
Result: bindata;
begin
{
Previous function body goes here, but with
`add` replaced by `Result`
}
{ Finally, append this line to copy Result into the
function's return value immediately before returning. }
add := Result;
end;