how to loop over a property of type array of object - rythm

I'm a total rythm newbie and got stuck on one of my simple tests (tested here: http://fiddle.rythmengine.com/)
I guess it's easiest to explain the problem in a simple example:
My JSON input args:
{myObj:
{name: "test", values: [ {id: 1}, {id: 2} ]
}
}
so the values property is an array of object - and those objects only have one property id.
my test-template:
#args Object myObj
Hello #myObj.name#
#for (Object v: myObj.values) {
#// this fails
}
this fails:
org.rythmengine.exception.CompileException: values cannot be resolved or is not a field
what is wrong here?
i.e. outside of the loop I can access myObj.values
#args Object myObj
Hello #myObj.name#
#myObj.values#

You declared myObj as Object which doesn't have the value property at all.
Solution:
Make your JSON string look like:
{
name: "test", values: [ {id: 1}, {id: 2} ]
}
Your template code:
#args String name, Map[] values
Hello #name#
#for (Map v: values) {
#v.get("id")
}
Result:
Hello test
1
2
Tested on http://fiddle.rythmengine.com/

Related

Mongodb - Updating nested object array without loosing attributes

How do I update or if not existent create a nested object array with one query? The update should only touch existing properties.
So I have a documents structure like this:
[
{
_id: ObjectId("61c296c1fe6de82e5eafd9e1"),
id: 1235,
name: 'Package',
type: 'product',
exampleOuter: 'test1',
size: [
{
length: '10cm',
height: '13cm',
exampleInner: 'test2',
},
{
length: '23cm',
height: '15cm',
exampleInner: 'test3',
}
]
},
...
]
I try to update this given object by the id. Lets assume I have a second object for ease lets call it: "updateObject" and I just want to update those fields from the original object that are existing in the updateObject. If the id is not found in the document than a new object from the updateObject should be created.
[
{
id: 1235,
name: 'Package2',
size: [
{
length: '20cm',
height: '23cm'
},
{
length: '33cm',
height: '25cm'
}
]
}
]
The normal behavior of MongoDB is that the attributes type, exampleOuter are gone in the original object after performing this query since they are not existing in the updateObject. I prevent that from happening by using $setwhich looks like this: {$set: updateObject}. But this doesn't work for the object array in the size attribute. They are all loosing the exampleInner because it is not existing in the updateObject.
I first tried to wrap every object in the size array with a $set to achieve the desired behavior but obv that didn't work.
After reasearch i got the tip using the dot notation and an index. So my updateObject looks like this:
[
{
id: 1235,
name: 'Package2',
size.0.length: '20cm',
size.0.height: '23cm',
size.1.length: '33cm',
size.1.height: '25cm',
}
]
Which works fantastic for the update part but if the id of this new updateObject is not in the document than this will create a new object - which is correct but instead of an array of objects it will create an object with the index 0, 1, ... as the key size.
So the new created document looks like this:
size: {
0: {
length: '10cm',
},
1: {
length: '23cm',
}
}
Am I on the right track with this or is there a complete other way to go? If it's the right approach how do I make sure that the if not existend objects the size attribute is created as an array and not as an object with the key as index?

How to access the properties of a query result in Mongo

I can find a document on my database. A call to:
subject = await Subject.find({ name: 'Math' });
res.send(subject);
returns the document correctly:
{
"topics": [],
"_id": "5ab71fe102863b28e8fd1a3a",
"name": "Math",
"__v": 0
}
The problem is when I try to access the properties of subject. Any of the following calls returns nothing:
res.send(subject._id);
res.send(subject.name);
I've tried subject.toObject() and subject.toArray() but I receive an error:
(node:2068) UnhandledPromiseRejectionWarning: TypeError: subject.toObject is not a function
Any help will be appreciated. Thanks!
NB:
before res.send(subject), I called console.log(subject) and the output is:
[ { topics: [],
_id: 5ab71fe102863b28e8fd1a3a,
name: 'cocei5',
__v: 0 } ]
That is because find method in MongoDB always returns an array.
subject = await Subject.find({ name: 'Math' });
So in above line the Subject.find({name: 'Math'}) is returning an array. Which you are storing in subject variable. if you are getting only single object from DB then you might access the object properties by using subject[0].propertyName.
like if you want to send just an id you can do it by
res.send(subject[0]._id);
You can always use the es6 destructuring feature to get the first element returned in the array, as long as you are sure the result will always be on the 0th index.
const [subject] = await Subject.find({ name: 'Math' });
res.send(subject._id);
res.send(subject.name);
ref: Destructuring arrays and objects
Details for find api
OR you can either use
const subject = await Subject.findOne({ name: 'Math' });
res.send(subject._id);
res.send(subject.name);
As findOne return object whereas find returns an array of objects.
ref: Details for findOne api

Freeform subobject in json-schema

I am drafting an API documentation with swagger.io and is trying to make it fit to our use case. The system is going to receive and process data from all sources, and they would each have different sets of fields.
While the product of the processing share the same schema, we want to include the input in the schema too for reference purpose. For instance, given
{
"foo": "bar"
"bar": "baz"
}
The product of the processing is
{
"original": {
"foo": "bar",
"bar": "baz"
}
"processed": {
"stdFieldA": "bar",
"stdFieldB": "baz"
}
}
Assuming for each input from different sources, we end up having stdFieldA and stdFieldB. So the response schema object we have
type: object
properties:
processed:
type: object
properties:
stdFieldA:
type: string
stdFieldB:
type: string
now that we have the processed subobject defined, can we define a freeform object for the original input, so that this object coming from another source is valid
{
"alpha": "lorem",
"beta": "ipsum"
}
If I don't get any answer to this, my workaround to the problem would be storing the original input as string (convert the original input into JSON string).
type: object without properties describes a free-form object. So the response schema can be:
type: object
properties:
original:
type: object # <----------
processed:
type: object
properties:
stdFieldA:
type: string
stdFieldB:
type: string

Search in parent or any of childs

I have collection of objects that have childs with unknown depth of nesting:
{
name: "foo",
childs: [
{ name: "bar", childs[]},
{
name: "SO",
childs[
{ name: "LINQ2Vodka"},
{ name: "KremlinWinterBearBalalaika"}
]
}
]
}
Is there a way to find any documents where any of child (or the main object itself) has "name" equal to some value? If not then what is the common way to do it?
Thank you!
UPDATE
As a workaround I can store parent and child objects flattenned (i.e. as array) along with the "chain" of ID's that holds their nested structure. Not sure if there's a better way.

Building a dynamic mongo query for meteor

I'm building an app that has clickable 'filters'; I'm creating a list of objects(?) that I want to pass to a mongo 'find', so that I can pull out listings if selected attributes match a certain score.
My data is structured like this (a snippet):
name: 'Entry One',
location: {
type: 'Point',
coordinates: [-5.654182,50.045414]
},
dogs: {
score: '1',
when: 'seasonal',
desc: 'Dogs allowed from October to April'
},
lifeguard: {
score: '1',
when: 'seasonal',
desc: 'A lifeguard hut is manned between April and October',
times: ''
},
cafe: {
score: '1',
name:'Lovely cafe',
open:'seasonal'
}, ...
My search variable is a list of objects (I think?) that I assign to a session variable. If I output this session var ('searchString') via JSON.stringify, it looks like this:
{"cafe":{"score":"1"},"dogs":{"score":"1"}}
I'd like to pass this to my mongo find so that it only lists entries that match these scores on these attributes, but it's returning zero results. Do I need to somehow make this an $and query?
Currently it looks like this:
Beaches.find(searchString);
Unfortunately as soon as I drop searchString into the find, I get zero results even if it's empty {}. (When it's just a find() the entries list fine, so the data itself is ok)
What am I doing wrong? I'm relatively new to mongo/meteor, so I apologise in advance if it's something stupidly obvious!
Don't stringify the query. Flatten the object instead. Example:
Beaches.find({
"cafe.score": 1,
"dogs.score": 1,
});