Experimental project

This is a sandbox project, which contains experimental code for developer use only.

Original Issue: #1304196: How should we do weighted Relations?

Problem Overview

Relations need weights. There are three primary use cases I have found for this:

  • Uni-Directional Sorting: A parent-child relationship exists between two entities. The parent needs to be able to sort it's children (children meaning the set of all objects within the relation bundle that it is related to). For example, I have playlists of songs. I want to sort my songs within each playlist.
  • Bi-Directional Sorting: A many-many relationship exists between two or more entities. Each entity needs to be able to sort the set of entities they are related to (related meaning the set of all objects within the relation bundle that it is related to). For example, in addition to wanting to sort my songs, I also want to sort the playlists in which the songs appear. For another example (using a large arity relation), I have groups of people for various social groups. I appear in four lists: employees at my work, my immediate family, I'm related by marriage to my wife's family and I'm in the alumni group for my college. I want to sort all the people I am related to directly (or the concatenation of all the groups I belong to).
  • Inner Relation Sorting: An entity is related to two or more other entities (3+ arity). The group of entities as a whole needs to be sorted. For example, in addition to sorting all the people I know above, I also want to provide individual sorting for each group: my employees, my wife's family, my immediate family and my alumni group.

Uni-directional and bi-directional sorting are examples of what I will call "type-wide sorting": within relation A, for a source entity P (let us call it the pivot of the sorting operation), we want to sort the set of target entities R that are related to P (let us call these the "related entities"), using the appropriate rules for transitive properties and direction on the relationships. Type-wide sorting seems to be what most people want to do with sorting: sort the set R of all entities related to the starting object P over relation A. It is also the one that is most sensible to use with Views (we know object C is related to D using relation A, thus we can set P to the source object in Views, and establish the join to get object D, then include the weight related to C-->D on relation A to generate a sort condition).

Inner relation sorting is different though, because it does not involve the entire set of entities over a relation A but only over a relation instance B. Different instances within A will have different weighting sets in this scenario. So, let us use a similar algorithm but with different definitions: within relation instance B, the set R of all entities within it is sorted. When we want to find the order of objects in instance B for entity P, we take the order of set R as set (note that this requires a specific instance be selected) and remove P to obtain the final list and order. This approach seems to be less compatible with Views, unless a specific relation instance was selected. It seems to be more useful when you want to deal with selecting the relation itself and returning all the entities it contains (so, for example, select relation A (or a set of them) and relate it to the Node table based on their presence in relation A, then this column could be used to sort the resulting relationship objects (this would assume you wanted to group by relation). Outside of Views though, this makes a lot of sense when combined with unlimited-arity relations - for example, a friends list relation as an n-arity relation that users create an instance of, then fill with users would need this kind of sorting. However, many of those cases can be implemented using normalized relationships (creating a friend list object, then adding a directional relationship between a friend list and a user), which is often considered to be a better practice in various disciplines like database design. Nevertheless, in case there are uses for it, we will built this mechanism into this module as well, for sake of consistency.

Implementation Plan

  1. Create a new field for type-wide sorting storage. This field will store the relationship type T, the pivot entity P, and a related entity (Rn) for each related entity on P over T. It will store a weight W for that value.
  2. Create a new field or update an existing field for inner-relation sorting (to be examined: using the delta value in the endpoints table to control the sort order for this use case). Within instance B, each entity Rn will have a single weight for that instance.
  3. Add some helper functions to get the weights properly set up, so other modules can properly use them.
  4. Add default weight values for all existing and newly saved instances, so that the weighing algorithms will not disrupt any existing operations. This should include a batch process to apply the default weight to all existing relations (for discussion: default weighting algorithm? Likely should be a neutral algorithm that will not impact sort order).
  5. Create two widgets for entity fields: one that allows you to add a related object (Rn+1) to a pivot object P, with weight W (for use on the related object side) and one that allows you to add related objects (Rn+1) to a pivot object P, with weight W from the pivot object side. The pivot side field should also allow you to alter the order of the objects, using drag-n-drop. Bi-directional relationships should only use the latter field (we must add some restrictions to this).
  6. Create a block for creating or editing a relation (targeted at 3+ arity relations), that allows you to add entities to it and drag-n-drop reorder it. This is mostly intended as a sample of how to implement the concept.
  7. Make sure either the existing widget (for the Relation type, provided by the Relation module) handles the sorting OR provide a new Sortable widget version of it.
  8. Ensure the following Views scenarios are functional:
    • When I select all entities C and relate them by endpoints, I should be able to sort those results R for each C by the weight W as appropriate (ASC or DESC). By applying a single entity filter to this, I can get the results of all results that belong to C sorted by W (so, for example, using exposed filters, we could easily make a playlist selector that shows you sorted songs for any selected playlist).
    • Ideally, there should be some leveraging of inner relation sorting here as well: by selecting all entities that belong to relation instance B, I should be able to have them sorted by weight W for that instance. I am unsure how this works (maybe it would use the entity->relation filter, on a View that selects relations? So I could set up an exposed filter to pick a relation instance, then get a list of all entities associated with it, sorted by their weight within that instance).
  9. Wish me luck!

Project information