webCOMAND

COMAND API Collection Queries

Queries performed on a Collection follow the process below.

  1. Limit - If this collection is already associated with a query (from a previous call to the limit() query method), the query limit is reconciled and merged with the 'limit' option, if one is provided.
  2. Fields - All field references in the SELECT, WHERE, GROUP BY and ORDER BY clauses are collected into an associative array named $dot_notation_fields. The associative array keys are the dot notation field (ie. "Type.Fields.Identifier") and the values are an associative array. The value associative array looks like array('dot_notation'=>TRUE). If 'dot_notation' is TRUE, the field is in dot notation (requires "joins"), not just a field that can be retreived without a join, or directly from the object with $object->field (query field may be a query "AS" alias to a field that was joined but can now be selected directly).
  3. Expand - If any list fields were found in the previous step, the collection of objects is expanded to an array of objects where each object may be listed multiple times, one for each combination of content records in all referenced list fields. This is similar to the expansion that happens when relational tables with one-to-many relationships are joined. It is critical for the next step to work properly with list fields. Each expanded_object has a reference to a single object and an associative array of references to a single object for one iteration through that objects colleciton fields. A new top-level object in the main array is added for every combination of referenced collection fields. The expansion process follows (starts in Collection class get() method).
    (collection_iterator)
    $dot_notation_fields = [ 'field1'=>TRUE, 'field2'=>TRUE, 'field2.field3'=>TRUE, 'field4'=>TRUE ];
    $expanded_objects = [ { index: 0, object: ?, field_objects: ['field1'=>?] } ]
    $collections[0] $collections[1] $collections[2] $collections[3]
    (collection_field)
    $field_array = field1
    $collection =
    1 ← $index = 0
    2  
    3 ← $count = 3
    (collection_field)
    $field_array = field2
    $collection =
    1 ← $index = 0
    2  
    3  
    4  
    5 ← $count = 5
    (collection_field)
    $field_array = field2.field3
    $collection =
    1 ← $index = 0
    2 ← $count = 2
    (collection_field)
    $field_array = field4
    $collection =
    1 ← $index = 0
    2  
    3  
    4 ← $count = 4
    $all_objects = $this->Objects;
    if( count($dot_notation_fields) > 0 ) {
    	$all_objects = array();
    	foreach( $this->Objects as $object ) {
    
    		// create first expanded_object for this object
    		$walker = new expanded_object( $object );
    		$all_objects[] &= $walker;
    
    		// expand each combination of fields to a new expanded_object
    		foreach( $dot_notation_fields as $dot_notation_field ) {
    			foreach( $dot_notation_field->value() as $field_array ) {
    				if( isset($walker->object->$field_array) && $walker->object->$field_array instance of \comand\repository\collection ) {
    
    					// if we don't already know about this field, start tracking it in the current expanded_object
    					if( isset($walker->fields[$field_array]) ) {
    
    						// this should probably be the start of the recursive call (to keep track of the index)
    
    					// if we do already know about this field, create a new expanded_object to continue track the next object in the collection
    					} else {
    						// create new expanded_object
    						$next_walker = $walker; // clone $walker
    						$all_objects[] &= $next_walker;
    
    						$walker->fields[$field_array]['index']++;
    					}
    
    					// recursive call
    					$walker->
    					$walker = $walker->object->$field_array[0];
    					$all_objects[] = ?;
    			}
    		}
    	}
    }
    • Create new empty array of expanded_object objects.
    • For each object in the collection, iterate through all combinations of its referenced collection fields at their full depth, adding an expanded_object to the main array for each combination. This is done by looping through each item in $dot_notation_fields and then each field within those. Each time it needs to clone the entire expanded_object.
  4. Process Where - .
  5. Process Group By - .
  6. Process Order By - Sort the resulting array with usort, once for each order by expression.
  7. Reduce - The resulting object array is reduced back to a collection of objects, merging array elements with the same top-level object, and performing any aggregate functions on its values as they are merged.

Expand

The temporarily expanded collection is stored in memory using the expanded_object class.

expanded_object

  • object - A reference to the COMAND object in the collection.
  • field_objects - An associative array where key is field identifier and value is an array of expanded_objects.

The following example illustrates how a collection would be expanded in memory for list field comparisons and aggregation.

Original Collection

[
    {
        ID: 1, Name: 'Amy',
        Phones: [ { Description: 'Home', Number: '111-111-1111' },
                  { Description: 'Work', Number: '222-222-2222' } ]
    },
    {
        ID: 2, Name: 'Sue',
        Phones: [ { Description: 'Home', Number: '333-333-3333' },
                  { Description: 'Work', Number: '444-444-4444' } ]
    },
    {
        ID: 3, Name: 'Jim',
        Phones: [ { Description: 'Mobile', Number: '555-555-5555' } ]
    },
    {
        ID: 4, Name: 'Joe',
        Phones: null
    }
]

Expanded

[
    [
        {
            ID: 1, Name: 'Amy',
            Phones: { Description: 'Home', Number: '111-111-1111' }
        },
        {
            ID: 1, Name: 'Amy',
            Phones: { Description: 'Work', Number: '222-222-2222' }
        }
    ],
    [
        {
            ID: 2, Name: 'Sue',
            Phones: { Description: 'Home', Number: '333-333-3333' }
        },
        {
            ID: 2, Name: 'Sue',
            Phones: { Description: 'Work', Number: '444-444-4444' }
        }
    ],
    [
        {
            ID: 3, Name: 'Jim',
            Phones: { Description: 'Mobile', Number: '555-555-5555' }
        }
    ],
    [
        {
            ID: 4, Name: 'Joe',
            Phones: null
        }
    ]
]

Reduce