While looking for ways to add an ordered to-many relationship to my Core Data model, with the least possible amount of changes to the model, I noticed an option of the to-many relationship that says ordered (see screenshot below). Wow, sounds great, but what does it do?
My SQLite store is not complaining when I check or uncheck it, and my app still compiles and runs fine too. I was thinking maybe the lightweight migration takes care of the change, but from the looks of it, all my custom NSManagedObject subclasses work without the need for modification too, so what's going on?
To summarize the questions:
Should that ordered flag change the to-many relationship's data type from NSSet to NSArray?
Or is it just that the order in which the set is modified, will persist on sequential reads and writes?
Or am I wrong with my assumptions and is it something else entirely?
Is there an Apple doc page where this feature is described?
Many thanks!
Ordered relationships allow you to assign an arbitrary ordering to related objects. You can think of this as ordering colors from your most to least favorite, rather than sorting by date, title, etc.
Before this feature was added ordering was implemented by creating a position attribute, then manually updating the position indexes for items whenever the user reordered them. If you have a large number of items, using the built in ordering can be more expensive than implementing this manually, as described above.
Related
I have a table that maintains rows of products that are for sale (tbl_products) using PostgreSQL 9.1. There are also several other tables that maintain ratings on the items, comments, etc. We're using JPA/Hibernate for ORM in a Seam application, and have the appropriate entities wired up properly. In an effort to provide better listings of these items, I've created a SQL VIEW (v_product_summary) that aggregates some of the basic product data (name, description, price, etc.) with data from the other tables (number of comments, average rating, etc.). This provides a nice concise view of the data, and I've created a corresponding JPA entity object that provides read-only access to the view data.
Everything is working fine with respect to running JPQL queries on either the Product object (tbl_products) or the ProductSummary (v_product_summary) objects. However, we'd like to provide a richer search experience using Hibernate Search and Lucene. The issue we're running into, though, is how do we query the ProductSummary objects using Hibernate Search? They're not indexed upon creation, because they're never really "created". They're obtained as read-only objects from the v_product_summary VIEW. An index entry is only created on Product when it's persisted to the database, and not for ProductSummary since it's never persisted.
Our thought is that we should be able to:
Persist our Product object to the database
Immediately query the corresponding ProductSummary object using the product's ID
Manually update the Hibernate Search index for the ProductSummary object
Is this possible? Is this even a good idea? I can see there will be a performance impact since we're executing a query for the ProductSummary object every time a new Product is persisted. However, products are not added to the database at a high volume, so I don't think this will be a huge issue.
We'd really like to find a better, more efficient way of accomplishing this. Can anyone provide any tips or recommendations? If we do go the route of updating the search index manually, is that even doable? Can anyone provide a resource explaining how we can add a single ProductSummary to the index?
Any help you can provide is GREATLY appreciated.
If I understand the question correctly, you're trying to do the normal thing of persisting an object and indexing it at that point, but you're dealing with 2 separate objects.
I find myself doing kludgey things in Hibernate all the time, it feels like it almost demands it of you. Yes, there'd be a performance impact, and as you say, it is probably not a big deal, so it might be worth profiling.
A part of me remembers there's a way you can refresh the object upon write, and wonders if there's a way you can wrap the Product and the ProductSummary and tweak the mapping so that you read part and write part of it (waves hands on syntax and mapping). Or create a Hibernate-facing object with readonly fields that can be split and merged into your two objects. I don't know if your design allows Hibernate-only objects, it's a common idiom in my system.
Either way could be useful if you had a lot of objects in this situation, if this is the only object you're searching in this way, your 3 steps look much clearer.
As for the syntax for adding an object manually, I think you're looking for something like this, after your fetch:
FullTextSession textSession = Search.getFullTextSession(session);
textSession.index(myProductSummary);
Was that all you wanted?
Since you are using postgresql, you could insert to the view and use a rule to redirect the insert to the appropriate table.
A postgresql rule is a way to change the query just before it gets executed. I used it in an application which needed a change in schema but required the old queries to still work for a little while.
You can check out the documentation about rules on insert queries on the postgresql site
Since you'll be inserting and updating to the view, hibernate search will work as usual.
EDIT
An easier strategy. You could insert and update ProductSummary when doing so on Product and tell PostgreSQL to ignore the inserts, updates and deletes on the view.
On the database side"
create RULE dontinsert AS ON insert to v_product_summary do instead nothing
create RULE dontupdate AS ON update to v_product_summary do instead nothing
create RULE dontdelete AS ON delete to v_product_summary do instead nothing
But I guess you will need to hack a little, since the jdbc call executeUpdate will return 0, and hibernate will probably freak.
Technically I think this would be possible, but I think your entire efficiency dilemma might be better solved using something like memcached, therefore making performance less of an issue, and perhaps increasing code maintainability depending on how you currently have it implemented at statement level. By updating the search index manually, do you mean the database index? That is not recommended, and I'm not sure if it's even doable. Why not index them on creation?
When using an ORM, is it breaking some kind of good practice to have a model class with a few non-persistent properties, which are only used for calculations, and then can be safely dropped?
Let's say we have a Product. This Product has list of possible Options. An Option may have a price impact on the Product. We also have a set of Rules, which say that when one Option is selected, then the price of another Option changes.
When we add a Product to an Order, along with a selection of Options, we first need to recalculate the price of all the Options based on the rules affecting each selected Option. Then we can calculate the final price of the Product with all its selected Options.
In this example, the Option could have a calculatedPrice property, which would only have meaning within the context of the selected Options, and could be safely dropped after the Product has been added to the Order.
Is there a more correct way to think about this problem, or is that ok?
Yes, it is perfectly fine to have #Transient properties.
Some people may consider it wrong and insist on having a separate class that is almost the same as the entity, but having the additional fields, but that is unnecessary code duplication. Your approach is what I'd do.
The other approach, which is used in a large and ghastly e-commerce system i work with, is to have a parallel structure of transient objects containing the computed information. So, in parallel to the Order, there is an OrderPrice. For each Item in the order, there is an ItemPrice. If an Item has a set of Options, then the ItemPrice will have a set of OptionPrices. The Order's ShippingOption also has a ShippingPrice, and so on. Pricing is then handled by another parallel structure of price calculators - you give an Order to an OrderPriceCalculator, and it gives you back an OrderPrice. In doing so, it will send each Item to the ItemPriceCalculator, which will send each Option to the OptionPriceCalculator, and so on.
The price objects can refer to the order objects, but not vice versa. Our system does actually persist the prices, but separately from the orders.
The advantage of this is that it separates the concerns of describing the contents of an order, describing the price of an order, and calculating the price of an order.
The disadvantage is that you have a huge number of classes, and the information you need is, inevitably, never in the objects you have to hand.
The disadvantage probably outweighs the advantage.
I have 2 entities. entity A will hold many entity Bs and order will matter.
if i check the little box that says 'indexed' in xcode, how do i go about using that index, if i even can? ( i know that i CAN use it in some way: http://cocoawithlove.com/2008/03/testing-core-data-with-very-big.html but i am not so spiffy with Obj-c yet.)
I have seen this Indexed Relationships in Core Data , but it seems broken and too much over my head to fix myself.
Index doesn't mean what you think it means. In this context, "indexed" means like the index of a book. It add a lookup table so the database can find individual records quickly. If you need to be able to sort the records into a specific order, use NSSortDescriptor with the NSFetchRequest. If the existing properties are not what you want to sort on, you'll need to add another property.
I'm designing a Core Data app which needs to keep dated records.
Imagine a user works with the program for a while and some records are stored. The user then changes important attributes and relationships (which transcend individual records), and continues using the program with this new configuration. Imagine now that the user looks back to the first records. I'd like to somehow store the old configuration so that, when the user is looking at any given record, the changed data (which, I repeat, transcends individual records) looks as it did when the record was written. (Note that the types of changes that need to be recorded are very infrequent.)
Storing a copy of the entire object graph, filed by date, each time a change is made, is obviously terrible (though it would provide the behavior I seek, if my description above was incomprehensible).
I considered storing only the changed objects each time, and deriving the up-to-date graph for any given record by collating all previous time periods. (If this were the case, how would I keep track of the lack of an object in a to-many relationship, I wondered? And would I be able to write something for NSManagedObject in general, or would I need to add this feature to each relevant entity?)
Is there a simpler or better way to do this sort of date-relative data storage?
Since you need to store the old relationships as well as individual objects, you really don't have a choice but to save a snapshot of the old object graph in some fashion.
There are several options for doing so:
(1) If the changes are rare and the graph small, then saving a snapshot of the entire object graph might be the simplest and most robust solution. Why add complexity for a seldom used feature?
(2) Use fetched relationships for all relationships. The fetch predicate will fetch on date stamp. The disadvantage of this is complexity. You have to configure a fetch for every relationship and nothing works automatically. Neither can the context enforce graph integrity as easily.
(3) Linked stack: Each entity has a previous and a next relationship that points to the previous and next version of itself. When an attribute/relationship is changed, a new object and new objects for its relation targets are pushed on their respective link stacks. This would clone a big chunk of the graph every time you made a change. Finding a previous graph would be a matter of walking the previous relationship until the proper date was found.
(4) Entities for relationships: Say you have two entities A and B. Normally, you would just have a relationship such as A<-->>B. However, you need to persist old relationships so you create an intermediate entity whose sole task is to model and track the changing relationship between A and B. So something like:
AlinkB {
dateStamp:Date;
a<-->>A.theBs;
b<<-->B.theA;
}
To find a previous relationship, you would search the relationships for the right date range. You'll need a custom subclass for each so you automatically manage the adding and transversing of the link entities.
I'm just starting out with using Core Data on the iPhone SDK and I'm looking into saving an ordered list, something like an array. However, relationships in Core Data are expressed as Sets when retrieved. This makes it difficult to save the order in which the objects are positioned.
A good example would be data items in table view when re-ordering of items are allowed. An easy solution would be to include an index property on the managed object.
Consider the following hierarchy:
Document <-Many-to-many-> DataItem
Different Document instances could link to the same DataItem, and each Document might reference one or more DataItem(s). Hence, having an index property in DataItem would lead to less reusability of that instance, i.e. you can only save the index for one instance of Document.
Any ideas of how I can present the hierarchy neatly ordered in a table view but still keep each DataItem instance reusable? Thanks!
As of OS X Lion (10.7) this is now much more straight forward. Cocoa now has support for a new NSOrderedSet class which is compatible with Core Data. This functionality is also available in iOS though requires iOS 5.0 or later. This means that this can't be used if you want your app to be backwards compatible with earlier versions of iOS or OS X.
All you need to do to gain ordering is open the Core Data model editor, select a to-many relationship and check the "ordered" check-box.
You could do it with another entity, like this:
alt text http://gallery.me.com/davedelong/100084/Screenshot-20on-202009-07-04-20at-2010-34-56-20AM/web.jpg?ver=12467253090001
A Document could find its actual dataItems by using something like this:
NSSet * documentDataItems = [[document orderedDataItems] valueForKey:#"dataItem"];
Likewise, a DataItem could find all its documents by doing the same:
NSSet * dataItemDocuments = [[dataItem orderedPositions] valueForKey:#"document"];
A good solution is to keep a separate data structure in Document to map DataItems to a position in the table view. Besides allowing the same DataItem to exist in multiple positions, if you need to add a DataItem to multiple Documents this solution will also work.
Back when I was looking at different strategies of keeping Core Data objects ordered I found a blog post that explains how to do this in great detail, including sample code too.