All files / datamodel/src/operator cross-product.js

98.33% Statements 59/60
96.77% Branches 30/31
100% Functions 10/10
98.33% Lines 59/60

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113                      8x                         12x 12x 12x 12x 12x 12x 12x 12x 12x   12x       12x 48x 48x 7x   48x   12x 41x 41x 14x 7x 7x     27x         12x 32x   32x 72x 72x 72x 72x 72x 308x 308x   72x 276x 236x   276x   72x 72x 72x 27x 27x 179x   27x 4x     23x 23x 23x     45x 8x 8x 8x 80x 40x     40x     8x 8x 8x         12x    
import DataModel from '../datamodel';
import { extend2 } from '../utils';
import { getCommonSchema } from './get-common-schema';
import { rowDiffsetIterator } from './row-diffset-iterator';
import { JOINS } from '../constants';
import { prepareJoinData } from '../helper';
/**
 * Default filter function for crossProduct.
 *
 * @return {boolean} Always returns true.
 */
function defaultFilterFn() { return true; }
 
/**
 * Implementation of cross product operation between two DataModel instances.
 * It internally creates the data and schema for the new DataModel.
 *
 * @param {DataModel} dataModel1 - The left DataModel instance.
 * @param {DataModel} dataModel2 - The right DataModel instance.
 * @param {Function} filterFn - The filter function which is used to filter the tuples.
 * @param {boolean} [replaceCommonSchema=false] - The flag if the common name schema should be there.
 * @return {DataModel} Returns The newly created DataModel instance from the crossProduct operation.
 */
export function crossProduct (dm1, dm2, filterFn, replaceCommonSchema = false, jointype = JOINS.CROSS) {
    const schema = [];
    const data = [];
    const applicableFilterFn = filterFn || defaultFilterFn;
    const dm1FieldStore = dm1.getFieldspace();
    const dm2FieldStore = dm2.getFieldspace();
    const dm1FieldStoreName = dm1FieldStore.name;
    const dm2FieldStoreName = dm2FieldStore.name;
    const name = `${dm1FieldStore.name}.${dm2FieldStore.name}`;
    const commonSchemaList = getCommonSchema(dm1FieldStore, dm2FieldStore);
 
    Iif (dm1FieldStoreName === dm2FieldStoreName) {
        throw new Error('DataModels must have different alias names');
    }
    // Here prepare the schema
    dm1FieldStore.fields.forEach((field) => {
        const tmpSchema = extend2({}, field.schema);
        if (commonSchemaList.indexOf(tmpSchema.name) !== -1 && !replaceCommonSchema) {
            tmpSchema.name = `${dm1FieldStore.name}.${tmpSchema.name}`;
        }
        schema.push(tmpSchema);
    });
    dm2FieldStore.fields.forEach((field) => {
        const tmpSchema = extend2({}, field.schema);
        if (commonSchemaList.indexOf(tmpSchema.name) !== -1) {
            if (!replaceCommonSchema) {
                tmpSchema.name = `${dm2FieldStore.name}.${tmpSchema.name}`;
                schema.push(tmpSchema);
            }
        } else {
            schema.push(tmpSchema);
        }
    });
 
    // Here prepare Data
    rowDiffsetIterator(dm1._rowDiffset, (i) => {
        let rowAdded = false;
        let rowPosition;
        rowDiffsetIterator(dm2._rowDiffset, (ii) => {
            const tuple = [];
            const userArg = {};
            userArg[dm1FieldStoreName] = {};
            userArg[dm2FieldStoreName] = {};
            dm1FieldStore.fields.forEach((field) => {
                tuple.push(field.data[i]);
                userArg[dm1FieldStoreName][field.name] = field.data[i];
            });
            dm2FieldStore.fields.forEach((field) => {
                if (!(commonSchemaList.indexOf(field.schema.name) !== -1 && replaceCommonSchema)) {
                    tuple.push(field.data[ii]);
                }
                userArg[dm2FieldStoreName][field.name] = field.data[ii];
            });
            const dm1Fields = prepareJoinData(userArg[dm1FieldStoreName]);
            const dm2Fields = prepareJoinData(userArg[dm2FieldStoreName]);
            if (applicableFilterFn(dm1Fields, dm2Fields)) {
                const tupleObj = {};
                tuple.forEach((cellVal, iii) => {
                    tupleObj[schema[iii].name] = cellVal;
                });
                if (rowAdded && JOINS.CROSS !== jointype) {
                    data[rowPosition] = tupleObj;
                }
                else {
                    data.push(tupleObj);
                    rowAdded = true;
                    rowPosition = i;
                }
            }
            else if ((jointype === JOINS.LEFTOUTER || jointype === JOINS.RIGHTOUTER) && !rowAdded) {
                const tupleObj = {};
                let len = dm1FieldStore.fields.length - 1;
                tuple.forEach((cellVal, iii) => {
                    if (iii <= len) {
                        tupleObj[schema[iii].name] = cellVal;
                    }
                    else {
                        tupleObj[schema[iii].name] = null;
                    }
                });
                rowAdded = true;
                rowPosition = i;
                data.push(tupleObj);
            }
        });
    });
 
    return new DataModel(data, schema, { name });
}