Multiple Outer Join Condition LLBLGen - orm

I have the following LLBLGen code that retrieves articles by Category. Essentially it is selecting from the article table where the articles are not marked for deletion and joining on the ArticleTopicCategory table to retrieve specific categories (where category = 'string')
ArticleCollection articles = new ArticleCollection();
IPredicateExpression articlesFilter = new PredicateExpression();
articlesFilter.Add(ArticleFields.IsFlaggedForDeletion != true);
PrefetchPath prefetchTopic = new PrefetchPath(EntityType.ArticleEntity);
prefetchTopic.Add(ArticleEntity.PrefetchPathTopic);
prefetchTopic.Add(ArticleEntity.PrefetchPathArticleTopicCategories).SubPath.Add(ArticleTopicCategoryEntity.PrefetchPathTopicCategory);
articles.GetMulti(articlesFilter, prefetchTopic);
I have added another table named SuppressedArticle which is a 1 to many and contains Id, OrganizationId, and ArticleId. The theory is that since articles are syndicated to multiple websites, if "Website A" did not want to publish "Article A" they could suppress it, i.e insert a record into the SuppressedArticle table.
On the article admin screen, i'd like to add a link button to suppress/unsuppress the article, by adding a left join with the two conditions like:
left join SuppressedArticle on (Article.Id = SuppressedArticle.articleId and SuppressedArticle.organizationId='CC177558-85CC-45CC-B4E6-805BDD1EECCC')
I tried adding the multiple join like so, but I cast/conversion error:
"Cannot implicitly convert type 'SD.LLBLGen.Pro.ORMSupportClasses.FieldCompareValuePredicate' to 'SD.LLBLGen.Pro.ORMSupportClasses.IPredicateExpression'. An explicit conversion exists (are you missing a cast?)"
IRelationCollection relations = new RelationCollection();
relations.Add(ArticleEntity.Relations.SuppressedArticleEntityUsingArticleId, JoinHint.Left).CustomFilter = new FieldCompareValuePredicate(SuppressedArticleFields.OrganizationId, ComparisonOperator.Equal, this.CurrentIdentity.OrganizationId);
Any help would be greatly appreciated!

CustomFilter is of type IPredicateExpression, you create a predicate (of type IPredicate) and assign it to that property, which of course doesn't work :)
do:
IRelationCollection relations = new RelationCollection();
relations.Add(ArticleEntity.Relations.SuppressedArticleEntityUsingArticleId, JoinHint.Left)
.CustomFilter = new PredicateExpression(SuppressedArticleFields.OrganizationId == this.CurrentIdentity.OrganizationId);

Related

Selecting related model: Left join, prefetch_related or select_related?

Considering I have the following relationships:
class House(Model):
name = ...
class User(Model):
"""The standard auth model"""
pass
class Alert(Model):
user = ForeignKey(User)
house = ForeignKey(House)
somevalue = IntegerField()
Meta:
unique_together = (('user', 'property'),)
In one query, I would like to get the list of houses, and whether the current user has any alert for any of them.
In SQL I would do it like this:
SELECT *
FROM house h
LEFT JOIN alert a
ON h.id = a.house_id
WHERE a.user_id = ?
OR a.user_id IS NULL
And I've found that I could use prefetch_related to achieve something like this:
p = Prefetch('alert_set', queryset=Alert.objects.filter(user=self.request.user), to_attr='user_alert')
houses = House.objects.order_by('name').prefetch_related(p)
The above example works, but houses.user_alert is a list, not an Alert object. I only have one alert per user per house, so what is the best way for me to get this information?
select_related didn't seem to work. Oh, and surely I know I can manage this in multiple queries, but I'd really want to have it done in one, and the 'Django way'.
Thanks in advance!
The solution is clearer if you start with the multiple query approach, and then try to optimise it. To get the user_alerts for every house, you could do the following:
houses = House.objects.order_by('name')
for house in houses:
user_alerts = house.alert_set.filter(user=self.request.user)
The user_alerts queryset will cause an extra query for every house in the queryset. You can avoid this with prefetch_related.
alerts_queryset = Alert.objects.filter(user=self.request.user)
houses = House.objects.order_by('name').prefetch_related(
Prefetch('alert_set', queryset=alerts_queryset, to_attrs='user_alerts'),
)
for house in houses:
user_alerts = house.user_alerts
This will take two queries, one for houses and one for the alerts. I don't think you require select related here to fetch the user, since you already have access to the user with self.request.user. If you want you could add select_related to the alerts_queryset:
alerts_queryset = Alert.objects.filter(user=self.request.user).select_related('user')
In your case, user_alerts will be an empty list or a list with one item, because of your unique_together constraint. If you can't handle the list, you could loop through the queryset once, and set house.user_alert:
for house in houses:
house.user_alert = house.user_alerts[0] if house.user_alerts else None

Magento: Get Collection of Order Items for a product collection filtered by an attribute

I'm working on developing a category roll-up report for a Magento (1.6) store.
To that end, I want to get an Order Item collection for a subset of products - those product whose unique category id (that's a Magento product attribute that I created) match a particular value.
I can get the relevant result set by basing the collection on catalog/product.
$collection = Mage::getModel('catalog/product')
->getCollection()
->addAttributeToFilter('unique_category_id', '75')
->joinTable('sales/order_item', 'product_id=entity_id', array('price'=>'price','qty_ordered' => 'qty_ordered'));
Magento doesn't like it, since there are duplicate entries for the same product id.
How do I craft the code to get this result set based on Order Items? Joining in the product collection filtered by an attribute is eluding me. This code isn't doing the trick, since it assumes that attribute is on the Order Item, and not the Product.
$collection = Mage::getModel('sales/order_item')
->getCollection()
->join('catalog/product', 'entity_id=product_id')
->addAttributeToFilter('unique_category_id', '75');
Any help is appreciated.
The only way to make cross entity selects work cleanly and efficiently is by building the SQL with the collections select object.
$attributeCode = 'unique_category_id';
$alias = $attributeCode.'_table';
$attribute = Mage::getSingleton('eav/config')
->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attributeCode);
$collection = Mage::getResourceModel('sales/order_item_collection');
$select = $collection->getSelect()->join(
array($alias => $attribute->getBackendTable()),
"main_table.product_id = $alias.entity_id AND $alias.attribute_id={$attribute->getId()}",
array($attributeCode => 'value')
)
->where("$alias.value=?", 75);
This works quite well for me. I tend to skip going the full way of joining the eav_entity_type table, then eav_attribute, then the value table etc for performance reasons. Since the attribute_id is entity specific, that is all that is needed.
Depending on the scope of your attribute you might need to add in the store id, too.

NHibernate: how to retrieve an entity that "has" all entities with a certain predicate in Criteria

I have an Article with a Set of Category.
How can I query, using the criteria interface, for all Articles that contain all Categories with a certain Id?
This is not an "in", I need exclusively those who have all necessary categories - and others. Partial matches should not come in there.
Currently my code is failing with this desperate attempt:
var c = session.CreateCriteria<Article>("a");
if (categoryKeys.HasItems())
{
c.CreateAlias("a.Categories", "c");
foreach (var key in categoryKeys)
c.Add(Restrictions.Eq("c", key)); //bogus, I know!
}
Use the "IN" restriction, but supplement to ensure that the number of category matches is equal to the count of all the categories you're looking for to make sure that all the categories are matched and not just a subset.
For an example of what I mean, you might want to take a look at this page, especially the "Intersection" query under the "Toxi solution" heading. Replace "bookmarks" with "articles" and "tags" with "categories" to map that back to your specific problem. Here's the SQL that they show there:
SELECT b.*
FROM tagmap bt, bookmark b, tag t
WHERE bt.tag_id = t.tag_id
AND (t.name IN ('bookmark', 'webservice', 'semweb'))
AND b.id = bt.bookmark_id
GROUP BY b.id
HAVING COUNT( b.id )=3
I believe you can also represent this using a subquery that may be easier to represent with the Criteria API
SELECT Article.Id
FROM Article
INNER JOIN (
SELECT ArticleId, count(*) AS MatchingCategories
FROM ArticleCategoryMap
WHERE CategoryId IN (<list of category ids>)
GROUP BY ArticleId
) subquery ON subquery.ArticleId = EntityTable.Id
WHERE subquery.MatchingCategories = <number of category ids in list>
I'm not 100% sure, but I think query by example may be what you want.
Assuming that Article to Category is a one-to-many relationship and that the Category has a many-to-one property called Article here is a VERY dirty way of doing this (I am really not proud of this but it works)
List<long> catkeys = new List<long>() { 4, 5, 6, 7 };
if (catkeys.Count == 0)
return;
var cr = Session.CreateCriteria<Article>("article")
.CreateCriteria("Categories", "cat0")
.Add(Restrictions.Eq("cat0.Id", catkeys[0]));
if (catkeys.Count > 1)
{
for (int i = 1; i < catkeys.Count; i++)
{
cr = cr.CreateCriteria("Article", "a" + i)
.CreateCriteria("Categories", "cat" + i)
.Add(Restrictions.Eq("cat" + i + ".Id", catkeys[i]));
}
}
var results = cr.List<Article>();
What it does is to re-join the relationship over and over again guaranteeing you the AND between category Ids. It should be very slow query especially if the list of Ids gets big.
I am offering this solution as NOT a recommended way but at least you can have something working while looking for a proper one.

Can I get all the fields in an item (in Sitecore)?

I'm trying to write a sql query to get all the fields in a given item in Sitecore.
To say I am stuck is putting it mildly.
I'm guessing I have to do some self joining on the fields table, but I'm getting myself in knots.
Anyone have any ideas?
In none of the cases you should ever try to query the Sitecore database yourself. The database changes over time and this would break your code. Rather, use the Item.Fields. This is a collection which contains all the necessary fields. If you want to make sure that all the fields are loaded(really loaded, not lazy loaded), than you can use Item.Fields.ReadAll().
Edit: Also, keep in mind that querying doesn't allow you to construct an Item, so you miss the behavior of default values and do not use the intelligent Sitecore caching at all.
Try to call Sitecore.Context.Item.Fields.ReadAll() before looking up a field.
First Attempt, but does not return all fields
SELECT I2.Name FROM
Items AS I
JOIN UnversionedFields AS UF ON I.ID = UF.ItemId
JOIN VersionedFields AS V ON I.ID = V.ItemId
JOIN SharedFields AS S ON I.ID = S.ItemId
JOIN Items AS I2 ON I2.ID = UF.FieldId OR I2.ID=V.FieldId OR I2.ID = S.FieldId
WHERE I.ID = '110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9'
GROUP BY I2.Name
By calling item.Fields you get the item fields that you have designated in your templates as well as the Sitecore standard fields that exist on all items. Use the code below if you only want the fields that you have defined in your templates. Of course, this assumes your field names do not start with "__"
// Get Fields directly from the Item
List<string> fieldNames = new List<string>();
item.Fields.ReadAll();
FieldCollection fieldCollection = item.Fields;
foreach (Field field in fieldCollection)
{
//Use the following check if you do not want
//the Sitecore Standard Fields
if (!field.Name.StartsWith("__"))
{
fieldNames.Add(field.Name);
}
}

Linqtosql - joined rows

In Linqtosql how do I show items from multiple rows in a single field.
eg I have a 3 table setup for tagging(entity, tag, entitytag) all linked via foreign keys.
For each entity I would like to return the name in one field and then all relevant tags in 2nd field.
eg Item1, tag1; tag2; tag3
Item2, tag4, tag5....
VB statements preferred.
Thanks
Geoff
Okay, not sure if this is the most efficient way but it works.
Dim dc As New DataContext
Dim query = From i In dc.Items _
Let tags = (From t In dc.ItemTags _
Where t.ItemID = i.ID _
Select t.Tag.Name).ToArray _
Select i.ItemName, Tags = String.Join(" | ", tags)
With this answer I am assuming you have your tables setup similar to the following, names are not great, just for illustration:
AnEntity: Id, Name
ATag: Id, TagName
EntityTag: EntityId (FK to AnEntity.Id), TagId (FK to ATag.Id)
You might try this:
var entityTags = from ent in theEntities
from enttags in ent.EntityTags
group enttags by enttags.AnEntity into entityGroup
select new { TheEntity = entityGroup.Key, TheTags =
from t in entityGroup
select t.ATag.TagName };
I have not been able to actually test this, I'll give it a shot this afternoon and edit it if need be. What is happening here is a SelectMany. The 'from ent in dc.AnEntities' gets all of the entity records, then the next 'from enttags in ent.EntityTags' gets all the entity tag records for each entity. The group by does pretty much that, groups the EntityTag records by AnEntity. Put them in an anonymous type and you are good to go.
EDITED:
Okay, changed the code above, it works now. Before you would get a list of the EntityTag objects, now you get the Entiy object and a list of strings (tags) for that entity.