I'm new to graphQL and Hasura. I'm trying(in Hasura) to let me users provide custom aggregation (ideally in the form of a normal graphQL query) and have then each item the results compared against the aggreation.
Here's a example. Assume I have this schema:
USERTABLE:
userID
Name
Age
City
Country
Gender
HairColor
INCOMETABLE:
userID
Income
I created a relationship in hasura and I can query the data but my users want to do custom scoring of users' income level. For example, one user may want to query the data broken down by country and gender.
For the first example the result maybe:
{Country : Canada
{ gender : female
{ userID: 1,
Name: Nancy Smith,..
#data below is on aggregated results
rank: 1
%fromAverage: 35%
}...
Where I'm struggling is the data showing the users info relative to the aggregated data.
for Rank, I get the order by sorting but I'm not sure how to display the relative ranking and for the %fromAverage, I'm not sure how to do it at all.
Is there a way to do this in Hasura? I suspected that actions might be able to do this but I'm not sure.
You can use track a Postgres view. Your view would have as many fields as you'd like calculated in SQL and tracked as a separate "table" on your graphql api.
I am giving examples below based on a simplification where you have just table called contacts with just a single field called: id which is an auto-integer. I am just adding the id of the current contact to the avg(id) (a useless endeavor to be sure; just to illustrate...). Obviously you can customize the logic to your liking.
A simple implementation of a view would look like this (make sure to hit 'track this' in hasura:
CREATE OR REPLACE VIEW contact_with_custom AS
SELECT id, (SELECT AVG(ID) FROM contacts) + id as custom FROM contacts;
See Extend with views
Another option is to use a computed field. This is just a postgres function that takes a row as an argument and returns some data and it just adds a new field to your existing 'table' in the Graphql API that is the return value of said function. (you don't 'track this' function; once created in the SQL section of Hasura, you add it as a 'computed field' under 'Modify' for the relevant table) Important to note that this option does not allow you to filter by this computed function, whereas in a view, all fields are filterable.
In the same schema mentioned above, a function for a computed field would look like this:
CREATE OR REPLACE FUNCTION custom(contact contacts)
RETURNS Numeric AS $$
SELECT (SELECT AVG(ID) from contacts ) + contact.id
$$ LANGUAGE sql STABLE;
Then you select this function for your computed field, naming it whatever you'd like...
See Computed fields
I have a secondary index on an optional column:
class Sessions extends CassandraTable[ConcreteSessions, Session] {
object matchId extends LongColumn(this) with PartitionKey[Long]
object userId extends OptionalLongColumn(this) with Index[Option[Long]]
...
}
However, the indexedToQueryColumn implicit conversion is not available for optional columns, so this does not compile:
def getByUserId(userId: Long): Future[Seq[Session]] = {
select.where(_.userId eqs userId).fetch()
}
Neither does this:
select.where(_.userId eqs Some(userId)).fetch()
Or changing the type of the index:
object userId extends OptionalLongColumn(this) with Index[Long]
Is there a way to perform such a query using phantom?
I know that I could denormalize, but it would involve some very messy housekeeping and triple our (substantial) data size. The query usually returns only a handful of results, so I'd be willing to use a secondary index in this case.
Short answer: You could not use optional fields in order to query things in phantom.
Long detailed answer:
But, if you really want to work with secondary optional columns, you should declare your entity field as Option but your phantom representation should not be an option in order to query.
object userId extends LongColumn(this) with Index[Long]
In the fromRow(r: Row) you can create your object like this:
Sessions(matchId(r), Some(userId(r)))
Then in the service part you could do the following:
.value(_.userId, t.userId.getOrElse(0))
You also have a better way to do that. You could duplicate the table, making a new kind of query like sessions_by_user_id where in this table your user_id would be the primary key and the match_id the clustering key.
Since user_id is optional, you would end with a table that contains only valid user ids, which is easy and fast to lookup.
Cassandra relies on queries, so use it in your favor.
Take a look up on my github project that helps you get up with multiple queries in the same table.
https://github.com/iamthiago/cassandra-phantom
how can I build a table of "orders" containing "IdOrder", "Description" and "User"?... the "User" field is a reference to the table "Users", which has "IdUser" and "Name". I'm using repositories.
I have this repository:
Repository<Orders> ordersRepo = new OrderRepo<Orders>(unitOfWork.Session);
to return all Orders to View, I just do:
return View(ordersRepo.All());
But this will result in something like:
IdOrder:1 -- Description: SomeTest -- User: UserProxy123ih12i3123ih12i3uh123
-
When the expected result was:
IdOrder:1 -- Description: SomeTest -- User: Thiago.
PS: I don't know why it returns this "UserProxy123ih12i3123ih12i3uh123". In Db there is a valid value.
The View:
It is showed in a foreach (var item in Model).
#item.Description
#item.User //--> If it is #item.User.Name doesn't work.
What I have to do to put the Name on this list? May I have to do a query using LINQ - NHibernate?
Tks.
What type of ORM are you using? You mention "repositories" but does that mean LinqToSql, Entity Framework, NHibernate, or other?
It looks like you are getting an error because the User field is not loaded as part of the original query. This is likely done to reduce the size of the result set by excluding the related fields from the original query for Orders.
There are a couple of options to work around this:
Set up the repository (or context, depending on the ORM) to include the User property in the result set.
Explicitly load the User property before you access it. Note that this would be an additional round-trip to the database and should not be done in a loop.
In cases where you know that you need the User information it would make sense to ensure that this data in returned from the original query. If you are using LinqToSql take a look at the DataLoadOptions type. You can use this type to specify which relationships you want to retrieve with the query:
var options = new DataLoadOptions();
options.LoadWith<Orders>(o => o.User);
DataContext context = ...;
context.LoadOptions = options;
var query = from o in context.Orders
select o;
There should be similar methods to achive the same thing whatever ORM you are using.
In NHibernate you can do the following:
using (ISession session = SessionFactory.OpenSession())
{
var orders = session.Get<Order>(someId);
NHibernateUtil.Initialize(orders.User);
}
This will result in only two database trips (regardless of the number of orders returned). More information on this can be found here.
In asp.net MVC the foreign key doesn't work the way you are using it. I believe you have to set the user to a variable like this:
User user = #item.User;
Or you have to load the reference sometimes. I don't know why this is but in my experience if I put this line before doing something with a foreign key it works
#item.UserReference.load();
Maybe when you access item.User.Name the session is already closed so NHib cannot load appropriate user from the DB.
You can create some model and initialize it with proper values at the controller. Also you can disable lazy loading for Orders.User in your mapping.
But maybe it is an other problem. What do you have when accessing "#item.User.Name" from your View?
I'm attempting to use LINQ to insert a record into a child table and I'm
receiving a "Specified cast is not valid" error that has something to do w/
the keys involved. The stack trace is:
Message: Specified cast is not valid.
Type: System.InvalidCastException
Source: System.Data.Linq TargetSite:
Boolean
TryCreateKeyFromValues(System.Object[],
V ByRef) HelpLink: null Stack: at
System.Data.Linq.IdentityManager.StandardIdentityManager.SingleKeyManager2.TryCreateKeyFromValues(Object[]
values, V& v) at
System.Data.Linq.IdentityManager.StandardIdentityManager.IdentityCache2.Find(Object[]
keyValues) at
System.Data.Linq.IdentityManager.StandardIdentityManager.Find(MetaType
type, Object[] keyValues) at
System.Data.Linq.CommonDataServices.GetCachedObject(MetaType
type, Object[] keyValues) at
System.Data.Linq.ChangeProcessor.GetOtherItem(MetaAssociation
assoc, Object instance) at
System.Data.Linq.ChangeProcessor.BuildEdgeMaps()
at
System.Data.Linq.ChangeProcessor.SubmitChanges(ConflictMode
failureMode) at
System.Data.Linq.DataContext.SubmitChanges(ConflictMode
failureMode) at
System.Data.Linq.DataContext.SubmitChanges()
(.....)
This error is being thrown on the following code:
ResponseDataContext db = new ResponseDataContext(m_ConnectionString);
CodebookVersion codebookVersion = db.CodebookVersions.Single(cv => cv.VersionTag == m_CodebookVersionTag);
ResponseCode rc = new ResponseCode()
{
SurveyQuestionName = "Q11",
Code = 3,
Description = "Yet another code"
};
codebookVersion.ResponseCodes.Add(rc);
db.SubmitChanges(); //exception gets thrown here
The tables in question have a FK relationship between the two of them.
The parent table's column is called 'id', is the PK, and is of type: INT NOT NULL IDENTITY
The child table's column is called 'responseCodeTableId' and is of type: INT NOT NULL.
codebookVersion (parent class) maps to table tblResponseCodeTable
responseCode (childClass) maps to table tblResponseCode
If I execute SQL directly, it works. e.g.
INSERT INTO tblResponseCode
(responseCodeTableId, surveyQuestionName, code, description)
VALUES (13683, 'Q11', 3, 'Yet another code')
Updates to the same class work properly. e.g.
codebookVersion.ResponseCodes[0].Description = "BlahBlahBlah";
db.SubmitChanges(); //no exception - change is committed to db
I've examined the variable, rc, after the .Add() operation and it does, indeed, receive the proper responseCodeTableId, just as I would expect since I'm adding it to that collection.
tblResponseCodeTable's full definition:
COLUMN_NAME TYPE_NAME
id int identity
responseCodeTableId int
surveyQuestionName nvarchar
code smallint
description nvarchar
dtCreate smalldatetime
dtCreate has a default value of GetDate().
The only other bit of useful information that I can think of is that no SQL
is ever tried against the database, so LINQ is blowing up before it ever
tries (hence the error not being a SqlException). I've profiled and verified
that no attempt is made to execute any statements on the database.
I've read around and seen the problem when you have a relationship to a non PK field, but that doesn't fit my case.
Can anyone shed any light on this situation for me? What incredibly obvious thing am I missing here?
Many thanks.
Paul Prewett
Post up the schema of the parent table.
if you look here, some other people have had your problem.
http://forums.microsoft.com/msdn/ShowPost.aspx?PostID=3493504&SiteID=1
It appears that Linq2SQL has trouble mapping some foreign keys to some primary keys. One guy had a resolution, but I think you are already mapping to an IDENTITY column.
Since the database isn't being called I think you have to look at the mappings linq to sql is using. What does the Association look like? There should be an Association on both the parent and child classes.
Take a look at the linq to sql Association between the two classes. The Association should have a ThisKey property. The cast that is failing is trying to cast the value of the property that ThisKey points to, I think.
As far as I can tell there can be a problem when there is more than one key and the type of the first key does not match the type that ThisKey points too. I'm not sure how linq would determine what the first key is.
From the looks of it you only have one key and one foreign key so that shouldn't be the problem, but the designer, if you are using it, has been known to get creative.
I'm pretty much guessing, but this looks like something I've seen before.
Is this an example of this bug? If so, try running your code in .NET 4.0 now that the beta is out.
If, like me, you aren't ready to start using the beta, you may be able to work around the problem. The issue seems to be that LINQ does not properly support relationships defined on non-primary key fields. However, the term "primary key" does not refer to the primary key defined on the SQL table, but the primary key defined in the LINQ designer.
If you dragged your tables into the designer, then Visual Studio automatically inspects the primary key defined in the database and marks the corresponding class field(s) as "primary keys". However, these do not need to correspond to each other. You can remove the key Visual Studio chose for you, and pick another field (or group of fields). Of course, you need to make sure this is logical (you should have a unique constraint in the database on the field/fields you choose).
So I had 2 tables/classes related to eachother using an alternative key. The parent table had 2 keys: a surrogate primary key defined as an int, and an alternative natural key defined as a string. In the LINQ designer, I had defined the association using the alternative key, and I experienced the InvalidCastException whenever trying to update that association field on the child object.
To work around this, I went into the LINQ designer, selected the int, and then changed the Primary Key property from True to False. Then I chose the string, and set it's Primary Key property to True. Recompiled, retested, and the InvalidCastException is gone.
Looking at your screen shot it looks like you may be able to fix your issue by changing the LINQ primary key on ResponseCode from ResponseCode.ID to ResponseCode.ResponseCodeTableID
ResponseCode rc = new ResponseCode()
{
SurveyQuestionName = "Q11",
Code = 3,
Description = "Yet another code"
};
and:
INSERT INTO tblResponseCode
(responseCodeTableId, surveyQuestionName, code, description)
VALUES (13683, 'Q11', 3, 'Yet another code')
Are not the same, you are not passing in the foreign key reference. Now, I'm huge n00b at LINQ2SQL, but I'd wager that LINQ2SQL is not smart enough to do that for you, and it expects it as the first parameter of the anonymous dictionary, and is trying to cast a string to an integer.
Just some ideas.
This block:
codebookVersion.ResponseCodes.Add(rc);
db.SubmitChanges(); //exception gets thrown here
Can you try InsertOnSubmit instead of Add? i.e.
codebookVersion.ResponseCodes.InsertOnSubmit(rc);
I think Add is not meant to be used to insert records if my memory serves me right. InsertOnSubmit is the one to use.
To try and narrow down the culprit.
Have you tried replacing the anonymous dictionary with something like:
ResponseCode rc = new ResponseCode();
rc.SurveyQuestName = "Q11";
rc.Code = 3;
rc.Description = "Yet Another Code";
I've yet to really work with .NET 3.5 yet (day job is still all 2.0), so I'm wondering if there is an issue with passing the data using the anonymous dictionary (The cases don't match the SQL Columns for one).
Yea, I've read that and other posts, but it always seems to involve someone linking up to a field that simply has a unique contraint. Or in this guy's case (which does sound exactly like mine), he didn't get a solution.
Here's the parent table:
tblResponseTable definition (which maps to CodebookVersion)
COLUMN_NAME TYPE_NAME
id int identity
versionTag nvarchar
responseVersionTag nvarchar
versionTag does have a unique contraint on it, but that's not represented anywhere that I can see in the LINQ-to-SQL stuff - and since nothing ever goes to the database... still stuck.
Mike, I hear you. But no matter where I look, everything looks correct. I've checked and rechecked that the ResponseTableId is an int and that Id is an int. They're defined as such in the designer and when I go look at the generated code, everything again appears to be in order.
I've examined the associations. Here they are:
[Table(Name="dbo.tblResponseCode")]
public partial class ResponseCode : ...
...
[Association(Name="CodebookVersion_tblResponseCode", Storage="_CodebookVersion", ThisKey="ResponseCodeTableId", OtherKey="Id", IsForeignKey=true)]
public CodebookVersion CodebookVersion
{
...
}
[Table(Name="dbo.tblResponseCodeTable")]
public partial class CodebookVersion : ...
...
[Association(Name="CodebookVersion_tblResponseCode", Storage="_ResponseCodes", ThisKey="Id", OtherKey="ResponseCodeTableId")]
public EntitySet<ResponseCode> ResponseCodes
{
...
}
And a screenshot of the association in case that will help:
Any further thoughts?
ResponseCode rc = new ResponseCode()
{
CodebookVersion = codebookVersion,
SurveyQuestionName = "Q11",
Code = 3,
Description = "Yet another code"
};
db.ResponseCodes.InsertOnSubmit(rc);
db.SubmitChanges();
You may want to check to see that any fields in your database tables which are set by the db server when inserting a new record have that reflected in the Linq to SQL diagram. If you select a field on the Linq to SQL diagram and view its properties you will see a field called "Auto Generated Value" which if set to true will ensure all new records take on the default value specified in the database.
LINQ to SQL has been deprecated, FYI - http://blogs.msdn.com/adonet/archive/2008/10/29/update-on-linq-to-sql-and-linq-to-entities-roadmap.aspx.
I ran into a very similar problem. I'll link you over to my wordy post: http://forums.asp.net/p/1223080/2763049.aspx
And I'll also offer a solution, just a guess...
ResponseDataContext db = new ResponseDataContext(m_ConnectionString);
CodebookVersion codebookVersion = db.CodebookVersions.Single(cv => cv.VersionTag == m_CodebookVersionTag);
ResponseCode rc = new ResponseCode()
{
ResponseCodeTableId = codebookVersion.Id,
SurveyQuestionName = "Q11",
Code = 3,
Description = "Yet another code"
};
db.ResponseCodes.InsertOnSubmit(rc);
db.SubmitChanges();
Somewhere in your object graph there is a conversion error, the underlying data model (or the Linq To SQL model) has changed. This is typically something like NVARCHAR(1) -> CHAR when it should be STRING, or something similar.
This error is not fun to hunt down, hopefully your object model is small.
We had a similar problem, caused by using non-integer keys. Details and hotfix number are here: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=351358
Is there any reason something like this would not work?
This is the logic I have used many times to update a record in a table with LINQ:
DataClasses1DataContext db = new DataClasses1DataContext();
User updateUser = db.Users.Single(e => e.user == user);
updateUser.InUse = !updateUser.InUse;
db.Log = new System.IO.StreamWriter(#"c:\temp\linq.log") { AutoFlush = true };
db.SubmitChanges();
(updateUser.InUse is a bit field)
For some reason it isn't working. When I check the linq.log it is completely blank.
Could there be a problem with my .dbml? Other tables seem to work fine but I've compared properties in the .dbml and they all match.
It's as if the db.SubmitChanges(); does not detect any updates being required.
The table could not be updated properly because it had no primary key. (Actually it had the column but the constraint was not copied when I did a SELECT INTO my dev table). The DataContext class requires a primary key for updates.
Is the InUse property a "normal" one as far as LINQ is concerned? (e.g. it's not autogenerated or anything funky like that?)
Alternatively, I don't suppose it's a Nullable<bool> is it, with a current value of null? If so, your update line isn't actually doing anything - for nullable Booleans, !null = null.