How do I put measures in MDX query using Olap4j - olap

Olap4j documentation gives a good example of constructing simple MDX query:
Query myQuery = new Query("SomeArbitraryName", salesCube);
QueryDimension productDim = myQuery.getDimension("Product");
QueryDimension storeDim = myQuery.getDimension("Store");
QueryDimension timeDim = myQuery.getDimension("Time");
myQuery.getAxis(Axis.COLUMNS).addDimension(productDim);
myQuery.getAxis(Axis.ROWS).addDimension(storeDim);
myQuery.getAxis(Axis.FILTER).addDimension(timeDim);
However, there is no info how to put measure into the query.
Is there a way to do this using Olap4j API?

From the point of view of MDX, there is a dimension called Measures, which has no hierarchies, but all the measures as its members. Thus, I would assume you can use this dimension like all the other dimensions and add it to the query.

Related

Order results by number of coincidences in edge properties

I'm working on a recommendation system that recommends other users. The first results should be the most "similar" users to the "searcher" user. Users respond to questions and the amount of questions responded in the same way is the amount of similarity.
The problem is that I don't know how to write the query
So in technical words I need to sort the users by the amount of edges that has specific property values, I tried with this query, I thought it should work but it doesn't work:
let query = g.V().hasLabel('user');
let search = __;
for (const question of searcher.questions) {
search = search.outE('response')
.has('questionId', question.questionId)
.has('answerId', question.answerId)
.aggregate('x')
.cap('x')
}
query = query.order().by(search.unfold().count(), order.asc);
Throws this gremlin internal error:
org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet cannot be cast to org.apache.tinkerpop.gremlin.structure.Vertex
I also tried with multiple .by() for each question, but the result was not ordered by the amount of coincidence.
How can I write this query?
When you cap() an aggregate() it returns a BulkSet which is a Set that has counts for how many times each object exists in that Set. It behaves like a List when you iterate through it by unrolling each object the associated size of the count. So you get your error because the output of cap('x') is a BulkSet but because you are building search in a loop you are basically just calling outE('response') on that BulkSet and that's not valid syntax as has() expects a graph Element such as a Vertex as indicated by the error.
I think you would prefer something more like:
let query = g.V().hasLabel('user').
outE('response');
let search = [];
for (const question of searcher.questions) {
search.push(has('questionId', question.questionId).
has('answerId', question.answerId));
}
query = query.or(...search).
groupCount().
by(outV())
order(local).by(values, asc)
I may not have the javascript syntax exactly right (and I used spread syntax in my or() to just convey the idea quickly of what needs to happen) but basically the idea here is to filter edges that match your question criteria and then use groupCount() to count up those edges.
If you need to count users who have no connection then perhaps you could switch to project() - maybe like:
let query = g.V().hasLabel('user').
project('user','count').
by();
let search = [];
for (const question of searcher.questions) {
search.push(has('questionId', question.questionId).
has('answerId', question.answerId));
}
query = query.by(outE('response').or(...search).count()).
order().by('count', asc);
fwiw, I think you might consider a different schema for your data that might make this recommendation algorithm a bit more graph-like. A thought might be to make the question/answer a vertex (a "qa" label perhaps) and have edges go from the user vertex to the "qa" vertex. Then users directly link to the question/answers they gave. You can easily see by way of edges, a direct relationship, which users gave the same question/answer combination. That change allows the query to flow much more naturally when asking the question, "What users answered questions in the same way user 'A' did?"
g.V().has('person','name','A').
out('responds').
in('responds').
groupCount().
order(local).by(values)
With that change you can see that we can rid ourselves of all those has() filters because they are implicitly implied by the "responds" edges which encode them into the graph data itself.

Aggregations in Marklogic 8 Java

I'm trying to group all the documents based on an element value. Through X-Query, I'm able to get the element value and its corresponding count. But, with Java API I'm not able to do that.
X-Query:
for $name in distinct-values(doc()/document/<element_name>)
return fn:concat("Element Value:",$name,", Count:",fn:count(doc()/document/[element_name eq $name]));
Output:
Element Value:A, Count:100
Element Value:B, Count:200
Java:
QueryManager qryMgr = client.newQueryManager();
StructuredQueryBuilder qb = new StructuredQueryBuilder();
StructuredQueryDefinition querydef = qb.containerQuery(qb.element("<element_name>"), qb.term("A"));
SearchHandle handle = new SearchHandle();
qryMgr.search(querydef, handle);
System.out.println(handle.getTotalResults());
By this method, I'm able to get the document count only for a particular value. Is there any way to get the count of all documents. Kindly Help!
If I understand your use case, you can use a range index to solve this problem, which is - you want to know what all the values are for a particular element, and then how many documents have that value. That's exactly what a range index is for.
Try adding a range index on "element_name" - you can use the ML Admin app for that - go to your database and click on Element Range Indexes.
In XQuery, you can then do something like this:
for $val in cts:element-values(xs:QName("element_name"))
return text{$val, cts:frequency($val)}
With the Java Client, you can do the same by adding a range-based constraint to a search options file, and then the response from SearchManager will have all of the values and frequencies in it that match your query. Check the REST API docs for constructing such a search options file.

Entity Framework - How to return a strongly typed object containing multiple Entities?

I have three tables in SQL Server: Company, Financial and FinancialHistory.
Relationships are:
Company -> Financial (1-1)
Financial -> FinancialHistory (1-Many)
Having generated an Entity Model, I want to return one larger "Entity" that contains ALL the columns from Company and Financial and a select few from FinancialHistory so that I can maintain a sort across multiple GridViews at the front end. Basically I want to avoid returning a dynamic List type which is where i'm at currently, I want a more strongly typed return type.
Is there any feature in Entity Framework 6 that allows me to do this via the Model diagram or do I have to create my own class and use it separately to the Model? An example of the class and instructions would help.
NOTE: I flatten out the FinancialHistory data and create dynamic columns in a DataTable before assigning to GridViews.
Originally this was a Stored Procedure that used PIVOT and generated dynamic columns but I wanted to move it over to EF and use LINQ.
When querying you can project the results into any type. While that type won't be updatable (you need to directly modify entity types) it works very well when querying.
var res = await (from cpy in myDbContent.Companies
// get two newest financial results
let fin = cpy.financials
let finResRecent = fin.History.OrderByDescending(h => h.FinancialYear)
let finResLast = finResRecent.FirstOrDefault()
let finResPrev = finResRecent.Skip(1).FirstOrDefault()
select new {
Company = cpy,
Financials = fin,
LastResults = finResLast,
PreviousResults = finresPrev
}
).ToListAsync();
(Inside the query many operators – like Single – can't be used, but FirstOrDefault can)

How to filter a relational datasource of a widget

How is it possible to filter a relational datasource of a widget?
I want to filter a relational datasource of a widget by a static and/or diynamic parametres?
The short answer is that there is no easy way to do it at this time.
However you can emulate desired behavior with multiple datasources. Let's say we have School (one) and Student (many) models. Given that we can make two queries: one to get the school and other to get students:
app.datasources.Schools.query.filters.Id._equals = 1;
app.datasources.Schools.load();
app.datasources.Students.query.filters.School.Id._equals = 1;
app.datasources.Students.query.filters.Age._greaterThen = 20;
app.datasources.Students.load();
This approach can also gracefully handle paging for related records. You can find complete code sample in Project Tracker template (ViewProject page).

Query Ranges in an X++ Batch Class

Can anyone tell me which method to put a Query Range into for a class which is batchable?
I have a sample batch class, which runs fine. It retrieves all the records in the Sales Table. I know that I need to add a QueryBuildRange object somewhere, then set the value of the range to a particular value (e.g. Sales ID = 00123456), but I'm not sure what method to put it in (main? Run? QueryRun? InitQuery?)
Thanks for your help!
It depends on what you're wanting to do, but in AX 2009 for batch, you can look at InventCountCreate_Base for an example of how Microsoft does it.
Specifically these two methods:
\Classes\InventCountCreate_Base\new
\Classes\InventCountCreate_Base\initQueryRun
Microsoft does it several different ways. You can see an alternative method in WMSShipmentReservationBatch in these two methods:
\Classes\WMSShipmentReservationBatch\main
\Classes\WMSShipmentReservationBatch\buildQueryRun

Categories

Resources