Google App Engine Python, query from related entities - google-app-engine-python

I have two entity which has one-to-many relationships. Sahip entity may have multiple Calisma.
class Sahip(ndb.Model):
customid=ndb.StringProperty(indexed=True)
adi=ndb.StringProperty(indexed=True)
soyadi=ndb.StringProperty(indexed=True)
ozgecmis=ndb.TextProperty()
tipi=ndb.StringProperty(indexed=True
For this aim I've use KeyProperty
class Calisma(ndb.Model):
sahibi_fk=ndb.KeyProperty(kind=Sahip,indexed=True)
sahibi=ndb.StringProperty(indexed=True) #fk
adi=ndb.StringProperty(indexed=False)
yapimyili=ndb.IntegerProperty(indexed=True)
yapimteknigi=ndb.StringProperty(indexed=True)
en=ndb.IntegerProperty(indexed=True)
boy=ndb.IntegerProperty(indexed=True)
derinlik=ndb.IntegerProperty(indexed=True)
detay=ndb.TextProperty()
I am inserting new entity in this way:
entity=Calisma()
entity.adi=adi
entity.yapimyili=int(yapimyili)
entity.yapimteknigi=yapimteknigi
entity.en=int(en) entity.boy=int(boy)
entity.derinlik=int(derinlik)
entity.detay=detay entity.tip=tip
entity.sahibi=sahibi
entity.sahibi_fk=ndb.Key(Sahip,sahibi)
entity.put()
However, this query is not work:
class agah(webapp2.RequestHandler):
def get(self):
sahip=Sahip.query(Sahip.customid=="suat-atan-1966").get()
calisma=Calisma.query(Calisma.sahibi_fk==sahip.key)
m="***"
for i in calisma:
m=m+i.adi+"<br>"
self.response.write(m)

A query().get() call returns a list of results (possibly empty), not a single result.
In your case:
sahip=Sahip.query(Sahip.customid=="suat-atan-1966").get()
means that sahip is a list, so sahip.key should produce:
AttributeError: 'list' object has no attribute 'key'
The fix could be along these lines:
results = Sahip.query(Sahip.customid=="suat-atan-1966").get()
if results:
sahip = results[0]
calisma=Calisma.query(Calisma.sahibi_fk==sahip.key)

Related

Passing an aggregate select expression to Dynamic Linq's GroupBy

I have simplified the following example from my code and hoping there's no obvious compilation errors because of it. Lets say I have the following entities (not what i actually have, please assume I have no EF or schema issues, this is just for example):
public class Company
{
public string GroupProperty {get;set;}
public virtual ICollection<PricingForm> PricingForms {get;set;}
}
public class PricingForm
{
public decimal Cost {get;set;}
}
And I want to query like so:
IQueryable DynamicGrouping<T>(IQueryable<T> query)
{
Expression<Func<Company, decimal?>> exp = c => c.PricingForms.Sum(fr => fr.Cost);
string selector = "new (it.Key as Key, #0(it) as Value)";
IQueryable grouping = query.GroupBy("it.GroupProperty", "it").Select(selector, exp);
return grouping;
}
I get the following error when calling the groupby/select line:
System.Linq.Dynamic.ParseException: 'Argument list incompatible with lambda expression'
What type is "it" when grouped? I have tried using other expressions that assume it is an IGrouping<string, Company>, or a IQueryable<Company>, same error. I've tried just selecting "Cost" and moving the Sum() aggregate into the selector string (i.e. Sum(#0(it)) as Value) and always seem to get the same error.
I eventually tried something along the lines of:
Expression<Func<IEnumerable<Company>, decimal?>> exp = l => l.SelectMany(c => c.PricingForms).Sum(fr => fr.Cost);
However this one, I get farther but when attempting to iterate through the results I got a different error.
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
So, with this dynamic grouping and injecting my own select expression, what should I assume the datatype of 'it' is? Will this even work?
The type of it is IGrouping<TKey, TElement>, where TKey is dynamic based on the keySelector result type, and TElement is the element type of the input IQueryable. Luckily IGrouping<TKey, TElement> inherits (is a) IEnumerable<TElement>, so as soon as you know the input element type, you can safely base selector on IEnumerable<TElement>.
In other words, the last attempt based on Expression<Func<IEnumerable<Company>, decimal?>> is correct.
The new error you are getting is because #0(it) generates Expression.Invoke call which is not supported by EF. The easiest way to fix that is to use LINQKit Expand method:
Expression<Func<Company, decimal?>> exp = c => c.PricingForms.Sum(fr => fr.Cost);
string selector = "new (it.Key as Key, #0(it) as Value)";
IQueryable grouping = query.GroupBy("it.GroupProperty", "it").Select(selector, exp);
// This would fix the EF invocation expression error
grouping = grouping.Provider.CreateQuery(grouping.Expression.Expand());
return grouping;

GORM low level api finds object (Mongodb record) but its still NULL?

I have code like this in a controller method:
DB db = mongoClient.getDB("twcdb");
DBCollection coll = db.getCollection('countrycodes')
println coll.findOne()
println coll.findOne().class
and I get this output at the console:
[_id:539848b2119918654e7e90c3, Country:Bermuda, Alpha2:BM, Aplha3:BMU, Numeric:60, FIPS:BD, IGA:Model 2]
null
So how can it be that it finds a record but its class is null? Is this because this record isn't modeled by any of my domain classes? It does recognize the record's individual fields as Strings which I just tested but the record overall is classed NULL? How, why?
you should never call class on an object as there are scenarios where this could fail (e.g. getProperty('class') gets called or you are on a "mapish" object, which means that groovy will call get('class') for you -- which is the case for the BasicDBObject (subsubclass of a LinkedHashMap)). always use getClass()

LINQ to Entities dynamic DbSet

Is it possible to specify the DbSet of a LINQ to Entities query at run time?
For example if I have a number of different DbSets that have a common property such as "IsExpired" could I pass the DbSet into the query?
So,
Dim query = From o In db.Products Where o.IsExpired = True
Would look something like,
Dim myDynamicName As String
myDynamicName = "Products"
Dim query = From o In db("myDynamicName") Where o.IsExpired = True
The reason why this is not possible becomes more clear when using fluent syntax:
Dim query = db("myDynamicName").Where(Function(o) o.IsExpired)
The function is a
Function Func(Of In T, Out bool)
and the type of T is infered from the IQueryable Of T that precedes it. (This is possible because Where is an extension method, so the IQueryable is its first input parameter). Thus, the compiler knows that o.IsExpired is a valid expression.
That means that db("myDynamicName") must either be specifically typed, which it isn't, or you must supply the type to the Where method. But that's exactly the thing you're trying to circumvent.

EF1: Filtering derived types of entity class using .OfType<> by passing a string value

I have a situation where I'm trying to filter a LINQ select using a derived sub class.
ctx.BaseEntity.OfType<SubClass>() - this works fine.
However I'd like to do this using a string value instead. I've come across a performance barrier when I have lots (>20) Sub Classes and selecting an Entity without using OfType just isn't an option. I have a generic UI that renders from the base class, so I don't know what Class Type will be returned at compile time.
So what I'd like to do is this:
Perform a projected Select where I
return just the SubClassType from
the database
Perform a second select
using this value as the OfType to
only select the relevant related
entity from the database (No mass
unions generated)
int id = 1;
var classType = (from c in ctx.BaseClass.Include("ClassType")
where c.id == id
select new
{
c.ClassType.TypeName
}).First();
BaseClass caseQuery = ctx.BaseClass.OfType<classType.TypeName>()
.Include("ClassType")
.Include("ChildEntity1")
.Include("ChildEntity2")
.Where(x => x.id== id);
But obviously this won't work because OfType requires a Type and not a string.
Any ideas on how I can achieve this?
Update:
As a side note to the original question, it turns out that the moment you project a query that uses a Navigation Property - it builds the monster SQL too, so I've ended up using a stored procedure to populate my ClassType entity from the BaseClass Id.
So I've just got it to work using eSQL, which I'd never used before. I've posted the code here just in case it helps someone. Has anyone else got a more strongly typed solution they can think of?
BaseClass caseQuery = ctx.BaseClass.CreateQuery<BaseClass>("SELECT VALUE c FROM OFTYPE(Entities.[BaseClass],namespace.[" + classType.TypeName + "]) as c")
.Include("ClassType")
.Include("ChildEntity1")
.Include("ChildEntity2")
.Where(x => x.id== id).FirstOrDefault();
To answer the headline question about calling OfType with a string / runtime type, you can do the following:
// Get the type, assuming the derived type is defined in the same assembly
// as the base class and you have the type name as a string
var typeToFilter = typeof(BaseClass)
.Assembly
.GetType("Namespace." + derivedTypeName);
// The use reflection to get the OfType method and call it directly
MethodInfo ofType = typeof(Queryable).GetMethod("OfType");
MethodInfo ofTypeGeneric = method.MakeGenericMethod(new Type[] { typeToFilter });
var result = (IQueryable<Equipment>)generic.Invoke(null, new object[] { equipment });
Combine this with your stored procedure to get the class name and you (should?) avoid the massive join - I don't have table-per-type implementation to play with so I can't test.

Entity Framework Foreign Key Queries

I have two tables in my entity framework, objects, and parameters which have a foreign key pointing to the object to which they belong. I want to populate a tree with all the attributes of a certain object. So in order to find those I want to do this:
String parentObject = "ParentObjectName";
var getAttributes = (from o in myDB.ATTRIBUTE
where o.PARENT_OBJECT == parentObject
select o);
However when I try to do this I get an error saying it cannot convert from type OBJECT to string, even though in the database this value is stored as a string. I have a workaround where I get an instance of the parentObject, then go through every attribute and check whether it's parent_object == parentObjectInstance, but this is much less efficient than just doing 1 query. Any help would be greatly appreciate, thanks!
Well, PARENT_OBJECT.ToString() can't be called (implicitly or explicitly) in L2E, but if it just returns a property, you can look at that directly:
String parentObject = "ParentObjectName";
var getAttributes = (from o in myDB.ATTRIBUTE
where o.PARENT_OBJECT.NAME == parentObject
select o);
...note the .NAME
Try this:
String parentObject = "ParentObjectName";
var getAttributes = (from o in myDB.ATTRIBUTE
where o.PARENT_OBJECT.ToString() == parentObject
select o);