I am using a Groovy library call ws-lite for web service testing. The way it works is it takes a closure and generate XML and send it to a web service end point.
See below for a simple example of what this closure looks like:
def bookXml = {
books {
book(available: "20", id: "1") {
title("Don Xijote")
author(id: "1", "Manuel De Cervantes")
}
book(available: "14", id: "2") {
title("Catcher in the Rye")
author(id: "2", "JD Salinger")
}
book(available: "13", id: "3") {
title("Alice in Wonderland")
author(id: "3", "Lewis Carroll")
}
}
}
Will generate XML in the request as below:
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book available="20" id="1">
<title>Don Xijote</title>
<author id="1">Manuel De Cervantes</author>
</book>
<book available="14" id="2">
<title>Catcher in the Rye</title>
<author id="2">JD Salinger</author>
</book>
<book available="13" id="3">
<title>Alice in Wonderland</title>
<author id="3">Lewis Carroll</author>
</book>
</books>
In order to make my clients more flexible, I normally pass the data structure from my test to the client as a map:
def bookMap = [
books: [[
id : "1",
available: "20",
title : "Don Xijote",
author : [
id : "1",
name: "Manuel De Cervantes"
]
], [
id : "2",
available: "14",
title : "Catcher in the Rye",
author : [
id : "2",
name: "JD Salinger"
]
], [
id : "3",
available: "13",
title : "Alice in Wonderland",
author : [
id : "3",
name: "Lewis Carroll"
]
]
]
]
This is how the client looks like now:
def bookXml = {
books {
bookMap.books.book.each {
book(available: it.available, id: it.id) {
title(it.available.title)
author(id: it.author.id, it.author.name)
}
}
}
}
One thing I want to do is in the bookXml closure, is there a way that I can take out a tag, if the value in my data structure is null?
For example, if title of my first book is null in the map, then in the closure, it won't create this tag title for book one.
I know how this can be done in groovy collection using collectentries for map and collect for list, but I don't know much about transforming closure.
Can you please share some insight with me?
Thanks.
I do not have much knowledge of builders, but it seems that the question is about how to ignore keys will null values in a map.
This can be achieved by using the each() method with a two-arg closure. The two arguments passed to the closure in this case will be each entry's key and value.
To demonstrate -
def book = [
id : "1",
available: "20",
title : null
]
book.each {key, value->
if (value) {
println "$key->$value"
}
}
I highly doubt you can do what you want in a simple way. If you are not into ASTs, then a closure is not a data structure which you can manipulate easily.
IMO, you should make your input map consistent before passing it to bookXml. Other than that, stick to #diveshpremdeep answer.
Related
Below is my mongo db json file.
I want to read the 'topic' element from array 'Addtasks' for each its object element in .hbs(handle bar) file when the route /addTask is called in express.
{
"_id" : ObjectId("5f2313cb1351d606046660fd"),
"email" : "mike#g.com",
"name" : "Mike Tyson",
"Addtasks" : [
{
"otherdetails" : "haha great!",
"website" : "asad.com",
"keywords" : "article importance, article generation, article quality",
"words" : 1000,
"topic" : "How article is generated?",
"_id" : ObjectId("5f2314011351d606046660ff")
},
{
"otherdetails" : "Not much thanks!",
"website" : "abcdxyz.co.in",
"keywords" : "niggas are great, yo whatsup!",
"words" : 2000,
"topic" : "whats your name nigga?",
"_id" : ObjectId("5f23142d1351d60604666101")
}
],
}
You can use the second arg to .find() to project the values you want.
https://mongoplayground.net/p/LLuM1sd5Raq
db.collection.find({}, {
"Addtasks.topic": 1,
name: 1, // Add whatever other fields you need as well, remove if not
email: 1,
})
Yields:
[
{
"Addtasks": [
{
"topic": "How article is generated?"
},
{
"topic": "whats your name nigga?"
}
],
"_id": ObjectId("5f2313cb1351d606046660fd"),
"email": "mike#g.com",
"name": "Mike Tyson"
}
]
If you pass in your data something like this::
res.render('mytemplate.hbs', { items: jsObject.Addtasks });
then your hbs could look like this:
<ul>
{{#each items}}
<li>{{this.topic}}</li>
{{/each}}
</ul>
(Somewhat unrelated, but that json-file is not valid JSON. JSON and javascript objects are not the same thing. )
For some reason I am not able to create JSON object in Groovy using JSONBuilder
Here is what I have but it comes back {}:
import groovy.json.JsonBuilder
JsonBuilder builder = new JsonBuilder()
builder {
name "Name"
description "Description"
type "schedule type"
schedule {
recurrenceType "one time"
start "${startDateTime}"
end "${endDateTime}"
}
scope {
entities ["${applicationId}"]
matches [
{
tags [
{
key "key name"
context "some context"
}
]
}
]
}
}
Does anyone know a simple way to create JSON object with nested elements?
I tend to find JsonOutput to be simpler to use for data that is already constructed. Yours would look like this:
groovy.json.JsonOutput.toJson(
[name: "Name",
description: "Description",
type: "schedule type",
schedule: [
recurrenceType: "one time",
start: "${startDateTime}",
end: "${endDateTime}"
],
scope: [
entities: ["${applicationId}"],
matches: [
[
tags: [
[
key: "key name",
context: "some context"
]
]
]
]
]]
)
If you are creating a JSON from Groovy objects, then you can use; JsonOutput
And if you have several values to pass and create a JSON object, then you can use; JsonGenerator
Or you can use JsonBuilder or StreamingJsonBuilder
check the groovy documentation
my schema looks like
{
qty:{
property1:{
//something
}
property2:[{
size:40,
color:"black",
enabled:"true"
}]
}
}
property 2 is array what i want to do is update those array object whose enabled is true in single query
I tried writing the following query
db.col.update({
"qty.property2.enabled" = "true"
}, {
"qty.property2.color" = "green"
}, callback)
but it is not working
error:
[main] Error: can't have . in field names [qty.pro.size]
db.col.update({"qty.property2.enabled":"true"},{$set: {'qty.property2.$.color': 'green'}}, {multi: true})
this is the way to update element inside array.
equal sign '=' cannot be used inside object
updating array is done using $
Alternative solution for multiple conditions:
db.foo.update({
_id:"i1",
replies: { $elemMatch:{
_id: "s2",
update_password: "abc"
}}
},
{
"$set" : {"replies.$.text" : "blah"}
}
);
Why
So I was looking for similar solution as this question, but in my case I needed array element to match multiple conditions and using currently provided answers resulted in changes to wrong fields.
If you need to match multiple fields, for example let say we have element like this:
{
"_id" : ObjectId("i1"),
"replies": [
{
"_id" : ObjectId("s1"),
"update_password": "abc",
"text": "some stuff"
},
{
"_id" : ObjectId("s2"),
"update_password": "abc",
"text": "some stuff"
}
]
}
Trying to do update by
db.foo.update({
_id:"i1",
"replies._id":"s2",
"replies.update_password": "abc"
},
{
"$set" : {"replies.$.text" : "blah"}
}
);
Would result in updating to field that only matches one condition, for example it would update s1 because it matches update_password condition, which is clearly wrong. I might have did something wrong, but $elemMatch solution solved any problems like that.
Suppose your documet looks like this.
{
"_id" : ObjectId("4f9808648859c65d"),
"array" : [
{"text" : "foo", "value" : 11},
{"text" : "foo", "value" : 22},
{"text" : "foobar", "value" : 33}
]
}
then your query will be
db.foo.update({"array.value" : 22}, {"$set" : {"array.$.text" : "blah"}})
where first curly brackets represents query criteria and second one sets the new value.
I'm new to ArangoDB and a growing fan already. Among many things we need to translate many-to-many relations into graphs, and query efficiently in there.
However I can't seem to reproduce the behaviour in NEIGHBORS as described in the cookbook
under "Using Edge Collections".
After I insert data and run:
FOR b IN books RETURN { book: b, authors: NEIGHBORS(books, written, b._id, 'inbound') }
[
{
"book" : {
"_id" : "books/10519631898915",
"_key" : "10519631898915",
"_rev" : "10519631898915",
"title" : "The beauty of JOINS"
},
"authors" : [ ]
}
]
Empty authors list! I tried this instead:
FOR b IN books RETURN { book: b, authors: NEIGHBORS(authors, written, b._id, 'inbound') }
[
{
"book" : {
"_id" : "books/10519631898915",
"_key" : "10519631898915",
"_rev" : "10519631898915",
"title" : "The beauty of JOINS"
},
"authors" : [
"authors/10519474612515",
"authors/10519475792163"
]
}
]
Which returns the _id list. None of those return what I need as in the cookbook, which is the expected edge/vertex structure.
(All has been tested in 2.6.9)
How is the use of NEIGHBORS intended and how do I get to my goal in pure AQL?
Is there a standard documentation of NEIGHBORS (and other graph AQL features) somewhere with description and type of each argument as well as return value?
Have you tried the includeData option for NEIGHBORS?
FOR b IN books RETURN { book: b, authors: NEIGHBORS(authors, written, b._id, 'inbound', [], {includeData: true}) }
That worked in my test.
It will be way more performant then PATHS on large datasets (PATHS computes much more irrelevant information)
Note: The empty array [] is used to define edges that should be followed only. With an empty array we follow all edges, but you could also follow special edges f.e. {label: "written"} instead of [].
Right, I found one solution:
FOR p IN PATHS(books, written, 'inbound')
RETURN p.destination
Result:
Warnings:
[1577], 'collection 'books' used as expression operand'
Result:
[
{
"_id": "books/10519631898915",
"_rev": "10519631898915",
"_key": "10519631898915",
"title": "The beauty of JOINS"
},
{
"_id": "authors/10519474612515",
"_rev": "10519474612515",
"_key": "10519474612515",
"name": {
"first": "John",
"last": "Doe"
}
},
{
"_id": "authors/10519475792163",
"_rev": "10519475792163",
"_key": "10519475792163",
"name": {
"first": "Maxima",
"last": "Musterfrau"
}
}
]
It gets the destination vertices at least, but it doesn't seem right since I get a warning and the source vertex is included as a destination.
Further elaboration and suggestions are very welcome.
UPDATE (2017): NEIGHBORS is no longer supported in AQL 3.x
Instead of
NEIGHBORS(books, written, b._id, 'inbound')
you could write a sub-query:
(FOR v IN 1..1 INBOUND b written RETURN v)
I have a MongoDb document like this
"schools":
[
"name" : "University",
"classes" :
[
{
"name":"Chem",
"teachers":
[
"Joe",
"Bill"
]
},
{
"name":"Math",
"teachers":
[
"Julie",
"Phil"
]
},
],
// More schools/classes/teachers here
]
How do I add a new teacher to the Math class?
(I'm writing this in node.js)
For the specific case you listed, you can do it like this:
myDocument.schools[0].classes[1].teachers.push("A new teacher");
myDocument.save();
For general cases (eg, add teacher to a class named "xyz"), you'd have to loop through the appropriate array(s) to find the item you're looking for.