Use-case(s):
The order of images in an article is determined by the delta of media entities in a directional article => media relation.
I rank my friends by how much I like then/when I meet them chronologically/other arbitrary function, and change my mind or remember new data that needs to be inserted.
Possible solutions:
Allow editing of relations, and let the user manipulate the delta values as weights.
Pro: Relatively easy to do, familiar pattern. Con: Conceptually wrong, bad pratice?
Manipulate the weights in memory, create a new relation, and optionally destroy the existing relation.
Pro: Still moderately easy to do. Adds the ability to retain historical data about old relations. Con: ?
Add a weight/integer field to relations.
Since we only have one value per relation, this would require one relation per related object. One can no longer link like this:
> B
/
A -> C
\
> DBut must instead do
Source | Weight | Target
A -- 1 --> B
A -- 2 --> C
A -- 3 --> D
Pro: Weights become optional. Con: Complexity. Will joining in another table for the weights have performance effects?
It could become a bit problematic to display on a node-edit form, since it's no longer entirely obvious which relations belong together. We could group them by relation type? One possible advantage of this is that something like displaying every other relation per relation type becomes possible.
An example where this could make sense is when an Article can link to Images and Photographers, then one could display every other image/photographer relation.
Fabsor commented on IRC:
an important point about the fields solution
if you use fields you have to load the full entity in the view in order to manipulate thigns
all entities in a view
it also becomes incompatible fairly easily when using pluggable storage, since views simply stops working for fields when you have another storage than db
| Comment | File | Size | Author |
|---|---|---|---|
| #20 | relation-weight-1304196-20.patch | 4.29 KB | jelle_s |
Comments
Comment #0.0
Letharion commentedUpdated issue summary.
Comment #1
naught101 commentedPersonally, I don't see the problem. Your first graph should actually look like this:
which makes it quite clear what a weird model this is. This is implying that the relationship between userA and userB doesn't exist without the userC and D being part of it! Surely if User C is removed, then the a->B relationship still holds?
I would definitely use binary relations, with a weight field on each (a "strength" field, really):
Views should be able to handle the weight on the relation just fine..
Comment #2
Letharion commentedI'm not sure I see a problem any more either, but I still wanted to post it :)
Regardless, would a widget which exposes an int field as a weight have place in the relation module or a sub-module, or should we keep that in a separate project?
Comment #3
naught101 commentedOh, definitely. Exposing fields on relation creation is something that would be very nice to include, if done well. Note that the relation_add block already allows this, but it may need more testing.
Comment #4
honza pobořil commentedIn my experimental module Relation edit widget is sorting feature, which uses proprietal field_weight in a relation entity. We should make some "standardized" way how to save delta of relations.
Comment #5
alanom commentedIn http://drupal.org/node/1262068 * dpi made the good point that in most cases, weights makes more sense by direction and endpoint than by relation entity.
In most use cases, it makes more sense to ask questions along the lines of, "Who is ACME Ltd [node:producer]'s most important Customer [relation:has_b2b_customer], is it Retail Inc [node:distributor] or Supermarket.com [node:distibutor]?" and then also the separate question in the other direction, "Who is Retail Inc's most important Supplier [relation:has_b2b_customer, reverse], is it ACME Ltd or Megacorp?", and it makes less sense to ask, "Which is more important, ACME Ltd has customer Retail Inc, ACME Ltd has customer Supermarket.com, or Megacorp has customer Retail Inc?". Likewise with images and photographers, it probably makes more sense to order each photographer's images, and if somehow an image has two photographers, their order - rather than order all the facts about which photographers have which images.
Of course, that's not reason not to create the ability to do both types of weighting. I'm sure there are use cases where weighting every relation of a certain type makes sense. It's just a relevant thing to consider that changed how I thought about this problem: it started me working on a field widget for creating, editing and sorting relations and which stores relation rids as a field, so you can then directionally sort the relations like you would any field. It's a little way off being ready to share, and it won't be quality drupal standard code for a long time, so please don't wait for me before working on this!
Getting all this to play nicely in Views with relationships etc is a bit of a pain - it'd be great to see basic pluggable API-like support for weighted relation fields and for non-directional relation weights in relation core as something we can all build upon in a standard way.
* is this a duplicate? The discussion here seems to have gone further though.
Comment #6
naught101 commentedYes, marked the other issue as a dup. These arguments really need diagrams for me to be able to make sense of them. Would be interested in seeing your use case as a diagram, alanomaly, because I'm having trouble understanding it.
I'm pretty opposed to having weighted endpoints in relation core, as I don't really see the benefit, as it just makes things more complicated than they already are (binary relations all the way, man!).
On the other hand, relation is supposed to be an API module, so any API stuff that would fit in core that would make this easier to do in contrib would be welcome.
Comment #7
honza pobořil commentedI recomend to add option Sortable to Relation type/bundle. This option creates weight/delta field in this type/bundle. All widgets should use this field for sorting. Field could be type number with special widget (or not).
When it will be in the Relation module, I am ready to use it in my Relation edit widget module.
Comment #8
martin_q#1359244: Ordering for items in 1:n relationship has been closed as a duplicate of this issue. Like #1, I am not convinced by the use case given in this issue, but there certainly are use cases for weighted endpoints of a relation. Playlists of media items is one.
In response to #6, naught101 has a good point that pretty much everything can be done with binary relations. However, if you add a weight to a relation like that, it's ambiguous. I can't quite think of how to do good diagrams for you but here's an attempt to depict the playlist use case:
The order of songs in a playlist is significant. It is an attribute of the song in that playlist, not globally of the song. Now, it certainly is possible to do this relation with binary relations. But when we add in the weight, is it agnostic about the direction of the relation? I don't think so. You might display one of the above playlist nodes for editing and the song nodes can be rearranged, in order to assign new weights for some or all of those relations (if you move song 5 to first position, all 5 relations would need new weights):
But if you display a song node for editing, you might still show all the playlists it appears in, but this time the weights are completely independent of each other. If you change the weight of the song in one of the playlists, its weight in the others should not alter:
So, in this example, the weight is an attribute of the song endpoint for a given relation "playlist contains song", and the direction matters. A hypothetical situation where one might have a weight in the other direction as well is if displaying a given song should result in playlist suggestions. The preferred playlist(s) to suggest might be configurable, and so the order that the playlists for a song are shown in becomes significant too, and is independent of the order of the song in the playlist:
I don't really mind whether I implement this using many 1:1 relations or a smaller number of 1:n relations. Because of what I've just described, using a weight field on the 1:1 relation is insufficient, but the delta value in the field_data_endpoints table might make sense for a 1:n relation (according to the description of 'delta' in the schema), if weights are only needed in one direction.
So the more I think about it, 1:1 route seems more flexible and powerful, but it would require up to two weight fields on each relation. Is the overhead for editing weights (which might require the loading and saving of all the relations attached to one node in order to reassign all weights) acceptable? Or should we store this in a separate table (rather breaking the principle of fields)?
Whatever the solution, the Relation select module needs much expanding to make it possible to select, edit and reorder more than one endpoint, which I would undertake to do myself if I understood Views better. At the moment there is a separate search field for each relation that one adds, and there are draggable icons next to the endpoint once selected, but these don't do anything... which makes me suspect it's not a difficult fix...
Comment #9
naught101 commentedSee also #461826-10: Generalize to entities for a quite interesting use-case.
Comment #10
honza pobořil commentedCould you decide how to do weights? I recommend my solution in #7 :-)
Comment #11
Jorrit commentedPerhaps this issue is of interest to you: #1664502: Sortable relation_select field instances and endpoints
It certainly doesn't cover all use-cases, but it is a first attempt at making relations sortable with Relation Select.
Comment #12
mikran commentedAm I missing something if I think r_index already holds the information of the order of endpoints?
Comment #13
honza pobořil commentedmikran: I am not sure if we can safely update r_index later.
Comment #14
alanom commentedr-index is the direction of the relation, within one relation's endpoints. This is about the weighting between the endpoints of multiple different relations.
The delta of the relation endpoints fields is potentially an option (a bit odd maybe, as the values are between fields on different entities, but workable). However, things go a bit nuts in Views if you try to use data from the Endpoints field - it's duplicate rows galore.
One approach I made work a while ago was based on simply adding fields to relations (e.g. 'weight_from_source', 'weight_from_target'), and creating a multi-value field widget that syncs its own items' deltas with these fields on the appropriate relations.
This approach has two neat advantages:
... and two modest disadvantages:
(if anyone wants a project and doesn't mind machete-ing through some very messy code, I could be up for sharing what I have with someone who was interested in and serious about potentially releasing and maintaining a proper relation weights/UI module)
Comment #15
wimberb commentedJust jumping in here. I had been looking for a method of sorting items in a playlist just like what is described in #8.
My solution was to use the DraggableViews module. It worked perfectly. I was able to drag and drop items into any order I wanted just like you can do with taxonomy terms. But this works right there on the live page.
By using a combination of Views contextual filters and a Viewfield I was able to create just one view but have it display different playlists depending on the page the View was on.
The only real difficulty I had was with the Token for the Viewfield. The normal tokens listed displayed both endpoints of the Relation and thus would not work in the Views contextual filter. After a lot of trial and error I came up with this token that did the trick. [node:relation-is-the-playlist-for-node:0] By appending :0 to the end of the relation token it generated just the first endpoint in the relation which is what I needed to feed into the Contextual Filter.
I hope this helps someone else who is trying to sort endpoints in a list.
Comment #16
ottawadeveloper commentedalanom, it looks like I'm going to need this functionality for one of our projects (we're building an online magazine that needs to link articles and issues, with sorting on the issue side at least, and DraggableViews has been disappointing me lately). If you still have the code, I'd love to see what I done to see if it can help me write a module to address this - I think it makes most sense to build it as an extension module, so that people only get the ordering if they need it, eh?
Comment #17
ottawadeveloper commentedhope this is appropriate. opening the sandbox project: http://drupal.org/sandbox/ottawadeveloper/1909868
Comment #18
naught101 commentedRe: the playlist issue, I think using an n-ary relation is the wrong way to do things. In fact, I think directional n-ary relations are fundamentally a bad idea in every situation. See my last comment on #1259738: n->m relations (mutant squids)
For albums, which are ordered playlists, the relation should be binary and directional: "is in album/compilation", with a "track number" field. The ordering is easy to change: just fix the fields on which ever relations are wrong.
For other playlists, such as "Songs with apostrophes in the title", there is no logical order, so there is no sensible way to create weights. I would just sort by song title, since that's a common convention. For intentionally ordered playlists, you use the same schema as above, with a simple direction binary relation with a "playlist slot" field, and sort on that.
I don't really know how that fits in with the contrib modules mentioned above, but I'm tempted to call this a won't-fix/works-as-designed for relation core.
Comment #19
naught101 commentedSo, a little aside, here is how to easily replicate node_queue storage with relation.
You now have a weighted playlist. This should work fine views, etc, using both a relation (node->node) views relationship and a relation (node->relation) views relationship.
Unfortunately due to the no-data-on-endpoints nature of relation data storage, we can't use field cardinality for weighting.
Ottowadeveloper: ping this issue when that sandbox is populated with some working code, and I'll add a link to the front page of relation.module.
Comment #19.0
naught101 commentedclean up a bit
Comment #20
jelle_sThis patch adds a weight column to the relation table. This way relations can be sorted by contrib modules that wish to do so.
If the weight for A -> B should be different than the weight of B -> A it is essentially a different relation and you should make your relation type directional.
Comment #21
mikran commentedWe have delta and r_index. The delta is not used for anything in relation but this is going to change in d8 (probably). The r_index isn't a thing there but instead delta is used to describe what r_index does now in addition to usual things it does in field api.
This issue is going to be a new feature that won't make it to relation 1.0 that is soon to be released. Instead we should do or atleast decide what to do with endpoint weights in d8 first.
Dynamic Entity Reference provides new edit widgets for drupal 8 version of Relation. There we have drag and drop elements to change the delta(r_index) order of endpoints. So this is about just what issue summary proposes as solution A:
Comment #22
attiks commentedIf I understand correctly the delta is for the ordering of all items of 1 relation. The weight field is for ordering multiple relations.
Comment #23
osopolar@mikran: What do you think about the patch in #20. This would be a clean solution, especially thinking of the changing signification of delta and r_index between D7 and D8. Could this go in, so other modules could build on it? Or do you prefer use delta for that purpose for D7?
Comment #24
mikran commentedI think that using a field is superior solution to order multiple relations. A global column named 'weight' that is then used by contrib to do something that may or may not be compatible with other contrib modules that use the same column to do something else. There will be different scenarios to weigh the relations so the weight can not be global single value column.
Instead it's better to name that weight field by the actual meaning of the order like
track numberormedia weight. If you want to create a customized list ofis track of album-relations then instead of ordering these relations by this magicalweight-property you can instead create a new relationcustomized listand then add all the needed relations to that relation as endpoints and then order those endpoints withr_index/delta.Comment #25
attiks commented#23, #24 We're using the patch from #20 on a production site with 100.000+ relations and it works like a charm. Adding a field to the relation type is possible but it complicates the code and make everything slower.
Comment #26
osopolarI understand @mikran concerns but also would like to have a simple solution for simple/general use-cases. If some relations need some advanced weight handling, then they could implement field based weighting.
My assumption is, that in most cases a simple solution, like the one in #20 would be fine.
The field-based-solution would make contributed modules, which should work for relations in general, like Relation Add module, also more complicated, because there need to be a configuration to define which field should be used for which endpoints weight.
Maybe I am to much focused on my use-case, where I use Relation Add module to add relations and would like that relation module uses the weight set by the tablesort: #1993738: Weight is ignored upon saving a node. To implement that, patch from #20 or something similar needs to be committed - or maybe a won't fix decision on this issue to signalize that there won't be a general implementation for weighted relations.
Comment #27
mikran commentedBut both related issues talk about the order of the endpoints and r_index works just fine for that. Patch #20 and the field-based-solution are both related to the relation itself.
Comment #28
osopolarSorry, I am a bit confused now. Are you talking about D8 or D7 version? You mean that the r_index is or should be used for the order of the endpoints? In D7 the r_index defines if the item is source or target, at least it is used like that in views query – for example to joint only the source endpoint to the node (relation base), so this could not used to sort endpoints.
So in Drupal 7 we could safely use delta for the endpoint order (maybe it is already used somewhere, but at least in the relation add module delta is not (yet) saved to the field_data_endpoints table).
Edit: I took a closer look on the patch in #20, and it's not what I assumed it is. IMHO a weight on the relation itself does not make much sense to me, its like requesting a weight column for the node ore user table. Probably, that could be better done with a field or maybe with something like entityqueues. What I was thinking about is a weight column in the endpoints table, to give each endpoint a wight (i.e. -15 for more important and +15 for less important endpoints). This might also be used in the use-cases where patch #20 was designed for, by just looking on one side of the relation.
The weight column in field_data_endpoints could make sense, at least its function is more clear than a column called r_index or using the delta.
Comment #29
mikran commented