Unlock a world of possibilities! Login now and discover the exclusive benefits awaiting you.
Good day everyone,
I am trying to do something I'd consider simple and straight-forward and, yet, I'm failing over and over again. It seems to me I'm missing some basic understanding around Hyper Cubes and how Qlik manages and serves this kind of object.
The ask is simple: to print all the contents in a Table defined by a HyperCube. Below is my .NET/C# code with comments where the problem's been raised:
public int PrintTableFromHypercube(string[] dims, int max_rows) { try { // Defines the Hypercube objects QE.HyperCubeDef hyper_cube = new QE.HyperCubeDef(); List<HyperCubeDimensionDef> dim_defs = new List<HyperCubeDimensionDef>(); List<HyperCubeMeasureDef> measure_defs = new List<HyperCubeMeasureDef>(); dim_defs.Add(new HyperCubeDimensionDef { Def = new HyperCubeDimensionqDef { FieldDefs = dims } }); measure_defs.Add(new HyperCubeMeasureDef { Def = new HyperCubeMeasureqDef { Def = "1" } }); hyper_cube.Dimensions = dim_defs; hyper_cube.Measures = measure_defs; QE.GenericObjectProperties generic_prop = new QE.GenericObjectProperties(); generic_prop.Info = new QE.NxInfo(); generic_prop.Info.Type = "hypercube"; generic_prop.Set<QE.HyperCubeDef>("qHyperCubeDef", hyper_cube); // Creates the Hypercube on the actual Application QE.GenericObject obj = QlikApp.CreateGenericSessionObject(generic_prop); var pager = new QE.NxPage { Top = 0, Left = 0, Width = dims.Length, Height = max_rows }; var qlik_data = obj.GetHyperCubeData("/qHyperCubeDef", new[] { pager }); var hyper_cube_pager = obj.GetHyperCubePager("/qHyperCubeDef"); var current_page = hyper_cube_pager.GetFirstPage(); var data_page = current_page.FirstOrDefault(); // Keeps reading data while the page is not empty. while (data_page.Matrix.Count() > 0) { // Reads each row from the current page of data. foreach (var row in data_page.Matrix) { /**************************************************** Here's where the problem is: No matter how many fields or measures I add, my "row" object will always return a single NxCell. So, it seems
like I cannot traverse those values to print on the screen. *****************************************************/ Debug.WriteLine($"Current row has '{row.Count}' elements"); // Reads each field in the current row for (int i=0; i< dims.Length; i++) { Debug.Write($"Field '{dims[i]}': '{row.ElementAt(i).Text}'\t"); } Debug.Write("\n"); } // Jumps to the next page of data from the pager data_page = hyper_cube_pager.GetNextPage().ToArray()[0]; } return 0; } catch { return ~1; } }
Thanks in advance for your help
That's a lot of code, and I haven't reviewed it in detail, but I see two things in that code that might not be what you want:
var dim_defs = dims.Select(dim => new HyperCubeDimensionDef { Def = new HyperCubeDimensionqDef { FieldDefs = new[] { dim } } }).ToList();
var hyper_cube_pager = obj.GetHyperCubePager("/qHyperCubeDef"); var startPage = new NxPage { Top = 0, Left = 0, Width = hyper_cube_pager.NumberOfColumns, Height = max_rows }; var data_page = hyper_cube_pager.GetData(new []{startPage});;
And finally I would recommend that you use the IteratePages method when you want to traverse the data like you are doing. With that construct you could write your code like this:
foreach (var row in hyper_cube_pager.IteratePages(new[] { startPage }, Pager.Next).SelectMany(pages => pages.First().Matrix)) { Debug.WriteLine($"Current row has '{row.Count}' elements"); ... }
And two references that might be of interest to you:
That's a lot of code, and I haven't reviewed it in detail, but I see two things in that code that might not be what you want:
var dim_defs = dims.Select(dim => new HyperCubeDimensionDef { Def = new HyperCubeDimensionqDef { FieldDefs = new[] { dim } } }).ToList();
var hyper_cube_pager = obj.GetHyperCubePager("/qHyperCubeDef"); var startPage = new NxPage { Top = 0, Left = 0, Width = hyper_cube_pager.NumberOfColumns, Height = max_rows }; var data_page = hyper_cube_pager.GetData(new []{startPage});;
And finally I would recommend that you use the IteratePages method when you want to traverse the data like you are doing. With that construct you could write your code like this:
foreach (var row in hyper_cube_pager.IteratePages(new[] { startPage }, Pager.Next).SelectMany(pages => pages.First().Matrix)) { Debug.WriteLine($"Current row has '{row.Count}' elements"); ... }
And two references that might be of interest to you:
Hi @Øystein_Kolsrud , thank you so very much for your reply.
In short, yes it did work. My fundamental understanding flaw was around the HyperCubeDimensionDef where I did one single dimension with multiple fields instead of adding multiple objects. So, THANK YOU for that.
The second link you forwarded, for the GitHub project, has been really useful. So additional points for that.
One thing I need to comment is around the IteratePages method. For medium sets of data (50K+) and plus, it was taking a lot of time to initialize. I'm guessing the LINQ code asked the server to do the paging before returning the first on. So I fell back to my original approach where I get each page sequentially.
This was a great lesson and I really appreciate your help
Good! I'm glad you were able to make progress!
I'm curious about that IteratePages behavior you saw though. Which .Net SDK version are you using? I know there was a bug in the implementation of that one a couple of releases ago that accidentally made it strict. But it's supposed to be lazy in the latest release.
@Øystein_Kolsrud - I am using QlikSense.NetSDK, version 13.3.1 from nuget.
I just saw the most recent one is 13.9.0, but I won't be able to update this before the next release. When I can, I will update and give the IteratePages another shot.
Thanks again for your help. I see more posts like this rising in the future and I'll start tagging you if that's ok.
OK, that would explain it. It seems the first version where that IteratePages behavior was fixes was in 13.8.0.
Funny you should run into that issue right now. I was in Krakow for the Lambda Days conference just last week and gave a presentation where I actually mentioned that very bug in the context of leveraging functional design principles in an object oriented environment.
It was this one:
http://www.lambdadays.org/lambdadays2019/oystein-kolsrud
They are currently in the process of uploading the presentations, but I'm not sure if mine will make it. There were some technical issues with the recording equipment when I did my talk, and I'm not sure if they were able to fix it in the end.
Nice! It did make it! You'll find it here if you are interested:
The part relating to the bug you saw starts here:
https://youtu.be/Wq32ZjEfZCI?t=1366
With the Qlik Sense specific part here: