Skip to content

Commit

Permalink
fix: Rework view filter query to take default values into account
Browse files Browse the repository at this point in the history
When gathering rows that are visible in views we need to take rows into
account that have no corresponding entry in the columns_TYPE tables. For
those we can match the filter in PHP and include row ids from a left
join where there is no entry.

Taking an example of a yes/no selection:

If the default value of a column doesn't match we end up using the same
query as before for collecting row ids

...
	`id` IN (SELECT `row_id` FROM `oc_tables_row_cells_selection` WHERE (`column_id` = 7) AND (`value` LIKE '"false"'))
...

If the default value matches, we need to include those that do not have
an entry

...
	`id` IN (SELECT `row_id` FROM `oc_tables_row_cells_selection` WHERE (`column_id` = 7) AND (`value` LIKE '"false"'))
OR
	`id` IN (select sl.id from oc_tables_row_sleeves sl LEFT JOIN oc_tables_row_cells_selection se ON sl.id = se.row_id AND se.column_id = 7 WHERE se.id IS NULL)
...

Signed-off-by: Julius Härtl <[email protected]>
  • Loading branch information
juliusknorr committed Mar 5, 2024
1 parent 67948ab commit 83238bb
Showing 1 changed file with 74 additions and 15 deletions.
89 changes: 74 additions & 15 deletions lib/Db/Row2Mapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -309,46 +309,86 @@ private function getFilterExpression(IQueryBuilder $qb, Column $column, string $
$paramType = $this->getColumnDbParamType($column);
$value = $this->formatValue($column, $value, 'in');

// We try to match the requested value against the default before building the query
// so we know if we shall include rows that have no entry in the column_TYPE tables upfront
$includeDefault = false;
$defaultValue = $this->getFormattedDefaultValue($column);

$qb2 = $this->db->getQueryBuilder();
$qb2->select('row_id');
$qb2->where($qb->expr()->eq('column_id', $qb->createNamedParameter($column->getId()), IQueryBuilder::PARAM_INT));
$qb2->from('tables_row_cells_' . $column->getType());
$qb2->selectAlias('sl.id', 'row_id')
->from('tables_row_sleeves', 'sl')
->leftJoin('sl', 'tables_row_cells_' . $column->getType(), 'v', $qb->expr()->andX(
$qb->expr()->eq('sl.id', 'v.row_id'),
$qb->expr()->eq('v.column_id', $qb->createNamedParameter($column->getId(), IQueryBuilder::PARAM_INT)),
));
$qb2->where(
$qb->expr()->eq('sl.table_id', $qb->createNamedParameter($column->getTableId(), IQueryBuilder::PARAM_INT))
);

switch ($operator) {
case 'begins-with':
return $qb2->andWhere($qb->expr()->like('value', $qb->createNamedParameter('%'.$this->db->escapeLikeParameter($value), $paramType)));
$includeDefault = str_starts_with($defaultValue, $value);
$filterExpression = $qb->expr()->like('value', $qb->createNamedParameter($this->db->escapeLikeParameter($value) . '%', $paramType));
break;
case 'ends-with':
return $qb2->andWhere($qb->expr()->like('value', $qb->createNamedParameter($this->db->escapeLikeParameter($value).'%', $paramType)));
$includeDefault = str_ends_with($defaultValue, $value);
$filterExpression = $qb->expr()->like('value', $qb->createNamedParameter('%' . $this->db->escapeLikeParameter($value), $paramType));
break;
case 'contains':
$includeDefault = str_contains($defaultValue, $value);
if ($column->getType() === 'selection' && $column->getSubtype() === 'multi') {
$value = str_replace(['"', '\''], '', $value);
return $qb2->andWhere($qb2->expr()->orX(
$filterExpression = $qb2->expr()->orX(
$qb->expr()->like('value', $qb->createNamedParameter('['.$this->db->escapeLikeParameter($value).']')),
$qb->expr()->like('value', $qb->createNamedParameter('['.$this->db->escapeLikeParameter($value).',%')),
$qb->expr()->like('value', $qb->createNamedParameter('%,'.$this->db->escapeLikeParameter($value).']%')),
$qb->expr()->like('value', $qb->createNamedParameter('%,'.$this->db->escapeLikeParameter($value).',%'))
));
);
break;
}
return $qb2->andWhere($qb->expr()->like('value', $qb->createNamedParameter('%'.$this->db->escapeLikeParameter($value).'%', $paramType)));
$filterExpression = $qb->expr()->like('value', $qb->createNamedParameter('%'.$this->db->escapeLikeParameter($value).'%', $paramType));
break;
case 'is-equal':
$includeDefault = $defaultValue === $value;
if ($column->getType() === 'selection' && $column->getSubtype() === 'multi') {
$value = str_replace(['"', '\''], '', $value);
return $qb2->andWhere($qb->expr()->eq('value', $qb->createNamedParameter('['.$this->db->escapeLikeParameter($value).']', $paramType)));
$filterExpression = $qb->expr()->eq('value', $qb->createNamedParameter('['.$this->db->escapeLikeParameter($value).']', $paramType));
break;
}
return $qb2->andWhere($qb->expr()->eq('value', $qb->createNamedParameter($value, $paramType)));
$filterExpression = $qb->expr()->eq('value', $qb->createNamedParameter($value, $paramType));
break;
case 'is-greater-than':
return $qb2->andWhere($qb->expr()->gt('value', $qb->createNamedParameter($value, $paramType)));
$includeDefault = $column->getNumberDefault() > (float)$value;
$filterExpression = $qb->expr()->gt('value', $qb->createNamedParameter($value, $paramType));
break;
case 'is-greater-than-or-equal':
return $qb2->andWhere($qb->expr()->gte('value', $qb->createNamedParameter($value, $paramType)));
$includeDefault = $column->getNumberDefault() >= (float)$value;
$filterExpression = $qb->expr()->gte('value', $qb->createNamedParameter($value, $paramType));
break;
case 'is-lower-than':
return $qb2->andWhere($qb->expr()->lt('value', $qb->createNamedParameter($value, $paramType)));
$includeDefault = $column->getNumberDefault() < (float)$value;
$filterExpression = $qb->expr()->lt('value', $qb->createNamedParameter($value, $paramType));
break;
case 'is-lower-than-or-equal':
return $qb2->andWhere($qb->expr()->lte('value', $qb->createNamedParameter($value, $paramType)));
$includeDefault = $column->getNumberDefault() <= (float)$value;
$filterExpression = $qb->expr()->lte('value', $qb->createNamedParameter($value, $paramType));
break;
case 'is-empty':
return $qb2->andWhere($qb->expr()->isNull('value'));
$includeDefault = empty($defaultValue);
$filterExpression = $qb->expr()->isNull('value');
break;
default:
throw new InternalError('Operator '.$operator.' is not supported.');
}

return $qb2->andWhere(
$qb->expr()->orX(
...array_values(array_filter([
$filterExpression,
$includeDefault ? $qb->expr()->isNull('value') : null
])),
),
);
}

/**
Expand Down Expand Up @@ -792,4 +832,23 @@ public function countRowsForView(View $view, string $userId, array $columns): in
return count($rowIds);
}

private function getFormattedDefaultValue(Column $column) {
$defaultValue = null;
switch ($column->getType()) {
case Column::TYPE_SELECTION:
$defaultValue = $this->formatValue($column, $column->getSelectionDefault(), 'in');
break;
case Column::TYPE_DATETIME:
$defaultValue = $this->formatValue($column, $column->getDatetimeDefault(), 'in');
break;
case Column::TYPE_NUMBER:
$defaultValue = $this->formatValue($column, $column->getNumberDefault(), 'in');
break;
case Column::TYPE_TEXT:
$defaultValue = $this->formatValue($column, $column->getTextDefault(), 'in');
break;
}
return $defaultValue;
}

}

0 comments on commit 83238bb

Please sign in to comment.