I have a PostgreSQL table that stores some information in a JSON column, I'd like to take the data from this JSON column and populate a secondary table in the BIRT report.
I've got the data into a JSON object just fine, I can use it, but table #2 won't populate any data even though there is data in the JSON object.
DataSource1 it attached to DataSet1 which is PostgreSQL. DataSource2 is a scripted DS, and it has Dataset2 attached to it with columns defined.
In dataset1 I have the OnFetch function making my JSON array:
vars["invoiceData"] = JSON.parse(row["i_invoice"]);
Then I have in dataset2 (json dataset) I have this for the fetch event setup:
// Get the length of the object
len = vars["invoiceData"].labor.length;
count = 0;// Counter used to step through each item in the JSON object.
// Loop through the JSON object adding it to the scripted data source
if(count < len && len != 0) {
row["hrs"] = vars["invoiceData"].labor[count].hrs;
row["desc"] = vars["invoiceData"].labor[count].desc;
row["rate"] = vars["invoiceData"].labor[count].rate;
row["amount"] = vars["invoiceData"].labor[count].amount;
count++;
return true;
}
return false;
If I echo out the length of my array it has one row in it, but that table never comes back with anything, always empty on my report.
You are mixing initialization code with loop code.
Just think about this fragment:
count = 0;
if (...) {
count++;
return true;
}
return false;
You need to split and modify this code. Basically, for a scripted data set you need three things:
A counter variable (or something similar). This is best declared as a report variable.
Initialization of the counter variable in the data set's open event.
Using the counter variable in the data set's fetch event, eg modifying it and comparing it to the data length.
Your code will return the first data row in an endless loop, because what BIRT does is:
open()
while fetch():
emit the current row
Thus, your code is definitely wrong.
OTOH the behavior that you describe doesn't match what I would expect: A never-ending report. So there are probably other errors in your report. What does the "problems" view in the BIRT designer show?
Related
I am trying to build a query to match two columns and I have tried the following:
obj= obj.filter(e => e.colOne.exactMatch(e.colTwo))
I am not be able to get this working, is there any way to filter by comparing the content of 2 columns?
The filter() method can't dynamically grab the value to filter based on each object, but can be used to filter on a static value.
You can filter a smaller object set (<100K rows) named myUnfilteredObjects of type ObjectType this way:
let myFilteredObjects = new Set<ObjectType>();
for (const unfilteredObj of myUnfilteredObjects) {
if (unfilteredObj.colOne === unfilteredObj.colTwo) {
myFilteredObjects.add(unfilteredObj);
}
}
Edit: updating with a solution for larger-scale object sets:
You can create a new boolean column in your object's underlying dataset that is true if colOne and colTwo match, and false otherwise. Filtering on this new column via the filter() method will then work as you expect.
It is not possible to compare two columns when writing Functions. A recommended strategy here would be to create a new column that captures your equality. For example in your pyspark pipeline, right before you generate the end objects that get indexed:
df.withColumn("colOneEqualsColTwo", F.when(
F.col("colOne") == F.col("colTwo"), True
).otherwise(False)
And then filter on that new column:
obj = obj.filter(e => e.colOneEqualsColTwo.exactMatch(true))
context: azure - ADF brief process description:
Get a list of the fields defined in the first row of a .csv(blobed) file. This is the first step, detect fields
then 2nd step would be a kind of compare with actual columns of an SQL table
3rd one a stored procedure execution to make the alter table task, finishing with a (customized) table containing all fields needed to successfully load the .csv file into the SQl table.
To begin my ADF pipeline, I set up a lookup activity that "querys" the first line of my blobed file, "First row only" flag = ON.As a second pipeline activity, an "Append Variable" task, there I would like to get all .csv fields(first row) retrieved from the lookup activity, as a list.
Here is where a getting the nightmare.
As far I know, with dynamic content I can get an array with all values (w/ format like {"field1_name":"field1_value_1st_row", "field2_name":"field2_value_1st_row", etc })
with something like #activity('Lookup1').output.firstrow.
Or any array element with #activity('Lookup1').output.firstrow.<element_name>,
but I can't figure out how to get a list of all field names (keys?) of the array.
I will appreciate any advice, many thanks!
I would save the part of LookUp Activity because it seems that you are familiar with it.
You could use Azure Function HttpTrigger to get the key list of firstrow JSON object. For example your json object like this as you mentioned in your question:
{"field1_name":"field1_value_1st_row", "field2_name":"field2_value_1st_row"}
Azure Function code:
module.exports = async function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
var array = [];
for(var key in req.body){
array.push(key);
}
context.res = {
body: {"keyValue":array}
};
};
Test Output:
Then use Azure Function Activity to get the output:
#activity('<AzureFunctionActivityName>').keyValue
Use Foreach Activity to loop the keyValue array:
#item()
Still based on the above sample input data,please refer to my sample code:
dct = {"field1_name": "field1_value_1st_row", "field2_name": "field2_value_1st_row"}
list = []
for key in dct.keys():
list.append(key)
print(list)
dicOutput = {"keys": list}
print(dicOutput)
Have you considered doing this in ADF data flow? You would map the incoming fields to a SQL dataset without a target schema. Define a new table name in the dataset definition and then map the incoming fields from your CSV to a new target table schema definition. ADF will write the rows to a new table using that file's schema.
I have a select that I get Json post with http, but I try to sets initially selected index but there is nothing in the list do not select anything. because the json is great.
public AppMainScreen() {
loadLists();
MySelect = new ObjectChoiceField( "Select: ", new Object[0], 3 );
VerticalFieldManager vfm = new VerticalFieldManager(Manager.VERTICAL_SCROLL);
vfm.add(MySelect);
add(vfm);
}
This statement appears wrong to me:
new ObjectChoiceField( "Select: ", new Object[0],3);
The second parameter to this constructor is supposed to be an array of objects whose .toString() method will be used to populate the choices. In this case, you have given it a 0 length array, i.e. no Objects. So there is nothing to choose. And then you have asked it to automatically select the 3rd item, and of course there is no 3rd item.
You should correct the code to actually supply an object array.
One option to make it easy is have your JSON load actually create a String array with one entry per selectable item. Then you use the index selected to identify the chosen item.
I had a similar question before, but this is a few steps beyond that so here we go:
I am trying to access values in a category in Tridion 2011. I am using the Razor TBB and using this code to do it:
#foreach (var keyword in Publication.MetaData.myCategory) {
#: Hello World!
}
I have set up a metadata schema with a field that has an xml name of "myCategory" attached to my publication. If I run this on the publication where myCategory is a Text field, this code works... kind of. It treats EACH character as a separate value of the keyword variable... so if I enter the text "one", what prints is "Hello World! Hello World! Hello World!", and if I just have "o" as the value, it prints "Hello World!".
Bizarre as that is (and I'd like to know why on that too), what I really want the field to be a "Values will be selected from a list" type of field, pointing to my category in Categories and keywords. When I do this, and the value of myCategory changes to the value of the item selected in the dropdown for this type of field instead of direct text entry, the code no longer works and gives this error:
Cannot implicitly convert type 'Tridion.Extensions.Mediators.Razor.Models.KeywordModel' to 'System.Collections.IEnumerable'. An explicit conversion exists (are you missing a cast?)
Please help me.
If you get your output repeated for every character in a string, you are clearly iterating over the characters in the string and not the other range of values that you expected.
I quickly checked the relevant code of the Razor mediator and its ToString method indeed returns the Title of the underlying RepositoryLocalObject.
http://code.google.com/p/razor-mediator-4-tridion/source/browse/trunk/Tridion.Extensions.Mediators.RazorMediator/Tridion.Extensions.Mediators.RazorMediator/Models/AbstractRepositoryLocalObject.cs
http://code.google.com/p/razor-mediator-4-tridion/source/browse/trunk/Tridion.Extensions.Mediators.RazorMediator/Tridion.Extensions.Mediators.RazorMediator/Models/KeywordModel.cs
http://code.google.com/p/razor-mediator-4-tridion/source/browse/trunk/Tridion.Extensions.Mediators.RazorMediator/Tridion.Extensions.Mediators.RazorMediator/Models/DynamicItemFields.cs
The code that handles KeywordFields in in the DynamicItemsFields.cs file:
else if (itemField is KeywordField)
{
KeywordField keywordField = (KeywordField)itemField;
if (keywordField.Definition.MaxOccurs == 1)
if (keywordField.Value == null)
_dictionary[key] = null;
else
_dictionary[key] = new KeywordModel(_engine, keywordField.Value);
else
{
List<KeywordModel> keywords = new List<KeywordModel>();
int i = 0;
foreach (Keyword k in keywordField.Values)
{
var kw = new KeywordModel(_engine, k);
kw.Index = i++;
kw.IsLast = Index == keywordField.Values.Count - 1;
keywords.Add(kw);
}
_dictionary[key] = keywords;
}
}
So it looks like the myCategory property will either be a KeywordModel object (if the KeywordField is single-value) or a List<KeywordModel> (if the KeywordField is marked as multi-value in the Schema). Is your myCategory field single value? Or multi-value?
If it is single-value, what type of output were your expecting? If you were expecting the list of allowed values (instead of the currently selected value), check if you can access it through myCategory.Definition somehow (which should be a regular TOM.NET KeywordFieldDefinition object).
In an C# ASP.Net MVC project, I'm trying to make a List<string> from a LINQ variable.
Now this might be a pretty basic thing, but I just cannot get that to work without using the actual column names for the data in that variable. The thing is that in the interests of trying to make the program as dynamic as possible, I'm leaving it up to a stored procedure to get the data out. There can be any amount of any which way named columns depending on where the data is fetched from. All I care about is taking all of their values into a List<string>, so that I can compare user-input values with them in program.
Pointing to the columns by their names in the code means I'd have to make dozens of overloaded methods that all just basically do the same thing. Below is false non-functioning code. But it should open up the idea of what I mean.
// call for stored procedure
var courses = db.spFetchCourseInformation().ToList();
// if the data fails a check on a single row, it will not pass the check
bool passed = true;
foreach (var i in courses)
{
// each row should be cast into a list of string, which can then be validated
// on a row-by-row basis
List courseRow = new List();
courseRow = courses[i]; // yes, obviously this is wrong syntax
int matches = 0;
foreach (string k in courseRow)
{
if (validator.checkMatch(courseRow[k].ToString()))
{
matches++;
}
}
if (matches == 0)
{
passed = false;
break;
}
}
Now below is an example of how I currently have to do it because I need to use the names for the columns
for (int i = 0; i < courses.Count; i++)
{
int matches = 0;
if (validator.checkMatch(courses[i].Name))
matches++;
if (validator.checkMatch(courses[i].RandomOtherColumn))
matches++;
if (validator.checkMatch(courses[i].RandomThirdColumn))
matches++;
if (validator.checkMatch(courses[i].RandomFourthColumn))
matches++;
/* etc...
* etc...
* you get the point
* and one of these for each and every possible variation from the stored procedure, NOT good practice
* */
Thanks for help!
I'm not 100% sure what problem you are trying to solve (matching user data to a particular record in the DB?), but I'm pretty sure you're going about this in slightly the wrong fashion by putting the data in a List. I
t should be possible to get your user input in an IDictionary with the key being used for the column name, and the object as the input data field.
Then when you get the data from the SP, you can get the data back in a DataReader (a la http://msmvps.com/blogs/deborahk/archive/2009/07/09/dal-access-a-datareader-using-a-stored-procedure.aspx).
DataReaders are indexed on column name, so if you run through the keys in the input data IDictionary, you can check the DataReader to see if it has matching data.
using (SqlDataReader reader = Dac.ExecuteDataReader("CustomerRetrieveAll", null))
{
while (reader.Read())
{
foreach(var key in userInputDictionary.AllKeys)
{
var data = reader[key];
if (data != userInputDictionary[key]) continue;
}
}
}
Still not sure about the problem you are solving but, I hope this helps!
A little creative reflection should do the trick.
var courses = db.spFetchCourseInformation()
var values = courses.SelectMany(c => c.GetType().GetProperties() // gets the properties for your object
.Select(property => property.GetValue(c, null))); // gets the value of each property
List<string> stringValues = new List<string>(
values.Select(v => v == null ? string.Empty : v.ToString()) // some of those values will likely be null
.Distinct()); // remove duplicates