I have the following entities:
class Book():
team = db.StringProperty()
class Entry():
role = db.StringProperty()
I create two Books:
book1 = Book(team='Plants').put()
book2 = Book(team='Zombies').put()
I create three Entrys:
entry1 = Entry(parent=book1, role='Peashooter').put()
entry2 = Entry(parent=book2, role='Gargantuar').put()
entry3 = Entry(parent=book2, role='Flag Zombie').put()
I can make a query for the 'Plants' Book:
query = Entry.all().ancestor(book1).fetch(100)
I'd like to make a query for several books. I can only think of list concatenation:
list1 = Entry.all().ancestor(book1).fetch(100)
list2 = Entry.all().ancestor(book2).fetch(100)
query = list1 + list2
Is there a more elegant way to do this other than list concatenation?
If you want to combine queries for several different ancestors (that don't share a common ancestor), the only way to do it is with separate queries, as you demonstrate. This is no different than if you want to query for several distinct values in a field.
Related
I'm trying to combine 3 separate SOQL queries into one, but still end up with 3 separate lists for ease of use and readability later.
List<Object__c> objectList = [SELECT Name, Id, Parent_Object__r.Name, Parent_Object__r.Id,
(SELECT Name, Id FROM Child_Objects__r)
FROM Object__c];
I know I can get a list of child objects thus:
List<Child_Object__c> childObjectList = new List<Child_Object__c>();
for(Object__c object : objectList){
childObjectList.addAll(object.Child_Objects__r);
}
How would I go about adding the Parent_Object__c records to their own list?
I'm assuming a map could be used to deal with duplicates, but how do I get this Parent_Object__c data into that map?
You are basically there.
All lookup fields are available in your example as object.Parent_Object__r. Use a Set to natively avoid duplicates. No deduping required on your part!
Set<Parent_Object__c> parentObjectSet = new Set<Parent_Object__c>();
List<Child_Object__c> childObjectList = new List<Child_Object__c>();
for(Object__c object : objectList){
childObjectList.addAll(object.Child_Objects__r);
parentObjectSet.add(object.Parent_Object__r);
}
Edit:
As per #eyescream (trust him!) you are indeed better off with a map to avoid duplicates.
So the above code would just be slightly different:
Map<Id, Parent_Object__c> parentObjectMap = new Map<Id, Parent_Object__c>();
List<Child_Object__c> childObjectList = new List<Child_Object__c>();
for(Object__c object : objectList){
childObjectList.addAll(object.Child_Objects__r);
if (object.Parent_Object__r != null) {
parentObjectMap.put(object.Parent_Object__r.Id, object.Parent_Object__r);
}
}
I've added a to-many relationship called listItems between my entities Person and ListItem in my data model, and then successfully added objects to a given Person using
let personSelectedListItems = person.mutableSetValue(forKey: "listItems")
personSelectedListItems.addObjects(from: selectedListItems)
print("personSelectedListItems after addObjects contains \(personSelectedListItems)")
Where selectedListItems is an array of objects of type ListItem. The print statement gives:
personSelectedListItems after addObjects contains Relationship 'listItems' on managed object (0x6080000d2050) <PersonMO: 0x6080000d2050> (entity: Person; id: 0xd000000000040002 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/Person/p1> ; data: {
age = Ky;
firstName = Uykyu;
image = <89504e47 0d0a1a0a 0000000d 49484452 000002cc 00000333 08020000 003d00d3 35000000 01735247 4200aece 1ce90000 001c>;
isVisited = 0;
lastName = Kuyuy;
listItems = (
"0xd000000005140000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p325>",
"0xd000000005680000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p346>",
"0xd0000000023c0000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p143>"
);
locationCity = Uyk;
locationState = You;
notes = "";
phoneNumber = "";
score = nil;
}) with objects {(
<ListItemMO: 0x6000000b6c20> (entity: ListItem; id: 0xd000000005140000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p325> ; data: {
category = Meta;
isSelected = 1;
listItem = "Similar values";
listItemStatus = 1;
listItemWeight = 1;
people = (
"0xd000000000040002 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/Person/p1>"
);
}),
<ListItemMO: 0x6000000af900> (entity: ListItem; id: 0xd000000000080000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p2> ; data: {
category = Appearance;
isSelected = 1;
listItem = Attractive;
listItemStatus = 3;
listItemWeight = 1;
people = (
"0xd000000000040002 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/Person/p1>"
);
}),
<ListItemMO: 0x6000000b27e0> (entity: ListItem; id: 0xd0000000023c0000 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/ListItem/p143> ; data: {
category = Behavior;
isSelected = 1;
listItem = "Good grammar";
listItemStatus = 1;
listItemWeight = 1;
people = (
"0xd000000000040002 <x-coredata://1350DE85-4F65-462A-9C36-1EEE3D5298CD/Person/p1>"
);
})
)}
This is good, because I want to have these objects created for each Person, but it's bad because it's an unordered set that I'm seemingly not able to access. I'd like for it to be a mutable array so I can access each object at an index, but when I do
let personSelectedListItems = person.mutableArrayValue(forKey: "listItems")
personSelectedListItems.addObjects(from: selectedListItems)
print("personSelectedListItems after addObjects contains \(personSelectedListItems)")
I get this error message:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'NSManagedObjects of entity 'Person' do not support -mutableArrayValueForKey: for the property 'listItems''
Which is puzzling, since I don't know why mutableSetValueForKey: was was supported but the array version wasn't. I'd really like to be able to access the objects through the relationship, ideally through dot notation. What is the best way to go about this?
Core Data to-many relationships are always represented by sets. Part of the support for this is that NSManagedObject implements mutableSetValue(forKey:), which is one of the documented ways to access the relationship. Since to-many relationships are sets, NSManagedObject does not support mutableArrayValue(forKey:), as that error message indicates.
If you want an ordered relationship, mark it as ordered in the data model editor, and then you can use NSOrderedSet. It's still not an array but it does have ordering.
Both #Wain and #TomHarrington pointed me in the right direction on this, but here's what the answer to my question was (after some searching and tinkering):
First and foremost, I needed to mark the to-many relationship as 'ordered' in the Data Model Inspector of my data model file. The second thing was to change
let personSelectedListItems = person.mutableSetValue(forKey: "listItems")
personSelectedListItems.addObjects(from: selectedListItems)
print("personSelectedListItems after addObjects contains \(personSelectedListItems)")
to
let personSelectedListItems = person.mutableOrderedSetValue(forKey: "listItems")
personSelectedListItems.addObjects(from: selectedListItems)
print("personSelectedListItems after addObjects contains \(personSelectedListItems)")
so that mutableOrderedSetValue would reflect the ordered nature of the relationship. After that, I had a returned set that was in order. Hurray! But now how do I access and change those individual objects through the relationship? That brings me to the third thing - I had to downcast the set to my class (called ListItemMO). To grab the first object in the ordered set and then change a property through the relationship, it looks like this:
if let listItemSet = personVar.listItems?.object(at: 0) as? ListItemMO {
listItemSet.listItemStatus = 0
}
main table:
class example(models.Model):
name = models.CharField('Item Name', max_length=200)
color = models.ManyToManyField(Color)
category = models.ForeignKey(Category)
image = models.ImageField('Item Image',upload_to="example/images/")
Category table:
class Category(models.Model):
catname = models.CharField('Category Name',max_length=100)
How can i query it while filtering the example table according to the category.
This didn't work out:
def list(request, cat):
c = example.object.filter(category = cat)
What should i change to make this filtering work ?
See Django's documentation on related lookups:
def list(request, cat):
c = example.objects.filter(category__catname=cat)
I'm trying to figure out how to execute a custom query with Castle ActiveRecord.
I was able to run simple query that returns my entity, but what I really need is the query like that below (with custom field set):
select count(1) as cnt, data from workstationevent where serverdatetime >= :minDate and serverdatetime < :maxDate and userId = 1 group by data having count(1) > :threshold
Thanks!
In this case what you want is HqlBasedQuery. Your query will be a projection, so what you'll get back will be an ArrayList of tuples containing the results (the content of each element of the ArrayList will depend on the query, but for more than one value will be object[]).
HqlBasedQuery query = new HqlBasedQuery(typeof(WorkStationEvent),
"select count(1) as cnt, data from workstationevent where
serverdatetime >= :minDate and serverdatetime < :maxDate
and userId = 1 group by data having count(1) > :threshold");
var results =
(ArrayList)ActiveRecordMediator.ExecuteQuery(query);
foreach(object[] tuple in results)
{
int count = (int)tuple[0]; // = cnt
string data = (string)tuple[1]; // = data (assuming this is a string)
// do something here with these results
}
You can create an anonymous type to hold the results in a more meaningful fashion. For example:
var results = from summary in
(ArrayList)ActiveRecordMediator.ExecuteQuery(query)
select new {
Count = (int)summary[0], Data = (string)summary[1]
};
Now results will contain a collection of anonymous types with properties Count and Data. Or indeed you could create your own summary type and populate it out this way too.
ActiveRecord also has the ProjectionQuery which does much the same thing but can only return actual mapped properties rather than aggregates or functions as you can with HQL.
Be aware though, if you're using ActiveRecord 1.0.3 (RC3) as I was, this will result in a runtime InvalidCastException. ActiveRecordMediator.ExecuteQuery returns an ArrayList and not a generic ICollection. So in order to make it work, just change this line:
var results = (ICollection<object[]>) ActiveRecordMediator.ExecuteQuery(query);
to
var results = (ArrayList) ActiveRecordMediator.ExecuteQuery(query);
and it should work.
Also note that using count(1) in your hql statement will make the query return an ArrayList of String instead of an ArrayList of object[] (which is what you get when using count(*).)
Just thought I'd point this out for the sake of having it all documented in one place.
I have a self referencing Categories table. Each Category has a CategoryID, ParentCategoryID, CategoryName, etc. And each category can have any number of sub categories, and each of those sub categories can have any number of sub categories, and so and and so forth. So basically the tree can be X levels deep.
Then Products are associated to leaf (sub) Categories. Is there a way to get all the Products for any given Category (which would be all the products associated to all its leaf descendants) using LINQ to SQL?
This feels like a recursive problem. Is it better to used a Stored Procedure instead?
I don't think linq-to-sql has a good answer to this problem. Since you are using sql server 2005 you can use CTEs to do hierarchical queries. Either a stored procedure or an inline query (using DataContext.ExecuteQuery) will do the trick.
Well here is a terrible rushed implementation using LINQ.
Don't use this :-)
public IQueryable GetCategories(Category parent)
{
var cats = (parent.Categories);
foreach (Category c in cats )
{
cats = cats .Concat(GetCategories(c));
}
return a;
}
The performant approach is to create an insert/modify/delete trigger which maintains an entirely different table which contains node-ancestor pairs for all ancestors of all nodes. This way, the lookup is O(N).
To use it for getting all products belonging to a node and all of its descendants, you can just select all category nodes which have your target node as an ancestor. After this, you simply select any products belonging to any of these categories.
The way I handle this is by using some extension methods (filters). I've written up some sample code from a project I have implemented this on. Look specifically at the lines where I'm populating a ParentPartner object and a SubPartners List.
public IQueryable<Partner> GetPartners()
{
return from p in db.Partners
select new Partner
{
PartnerId = p.PartnerId,
CompanyName = p.CompanyName,
Address1 = p.Address1,
Address2 = p.Address2,
Website = p.Website,
City = p.City,
State = p.State,
County = p.County,
Country = p.Country,
Zip = p.Zip,
ParentPartner = GetPartners().WithPartnerId(p.ParentPartnerId).ToList().SingleOrDefault(),
SubPartners = GetPartners().WithParentPartnerId(p.PartnerId).ToList()
};
}
public static IQueryable<Partner> WithPartnerId(this IQueryable<Partner> qry, int? partnerId)
{
return from t in qry
where t.PartnerId == partnerId
select t;
}
public static IQueryable<Partner> WithParentPartnerId(this IQueryable<Partner> qry, int? parentPartnerId)
{
return from p in qry
where p.ParentPartner.PartnerId == parentPartnerId
select p;
}