ChiliProject is not maintained anymore. Please be advised that there will be no more updates.

We do not recommend that you setup new ChiliProject instances and we urge all existing users to migrate their data to a maintained system, e.g. Redmine. We will provide a migration script later. In the meantime, you can use the instructions by Christian Daehn.

Wrong filters for int and float custom fields (Bug #498)


Added by Felix Schäfer at 2011-06-30 12:20 pm. Updated at 2011-07-08 07:41 pm.


Status:Closed Start date:2011-06-30
Priority:Normal Due date:
Assignee:- % Done:

0%

Category:Custom Fields
Target version:2.1.0
Remote issue URL:http://www.redmine.org/issues/6180 Affected version:master

Description

int and float custom fields get the same filters as strings (default for custom fields in "unknown formats") instead as the ones for "integer custom fields".


Related issues

duplicated by Bug #387: Filter for integer custom field has options for searching... Duplicate 2011-05-09

Associated revisions

Revision 7c4abeb8
Added by Felix Schäfer at 2011-07-08 09:39 pm

Correct filters for int,float custom fields. #498

History

Updated by Felix Schäfer at 2011-06-30 12:21 pm

I have a fix for this, will post it shortly.

Updated by Felix Schäfer at 2011-07-04 02:04 pm

Ok, so this was a little longer in the making than I had thought because of how custom fields work (wouldn't it have been easier to have a text field for "character"-based custom fields and a float/numeric field for numeric custom fields?…). Anyway, here's my proposed patch:

 1diff --git a/app/models/query.rb b/app/models/query.rb
 2index 1772d68..f7abb19 100644
 3--- a/app/models/query.rb
 4+++ b/app/models/query.rb
 5@@ -586,9 +586,17 @@ class Query < ActiveRecord::Base
 6       sql = "#{db_table}.#{db_field} IS NOT NULL" 
 7       sql << " AND #{db_table}.#{db_field} <> ''" if is_custom_filter
 8     when ">=" 
 9-      sql = "#{db_table}.#{db_field} >= #{value.first.to_i}" 
10+      if is_custom_filter
11+        sql = "#{db_table}.#{db_field} != '' AND CAST(#{db_table}.#{db_field} AS decimal(60,3)) >= #{value.first.to_f}" 
12+      else
13+        sql = "#{db_table}.#{db_field} >= #{value.first.to_f}" 
14+      end
15     when "<=" 
16-      sql = "#{db_table}.#{db_field} <= #{value.first.to_i}" 
17+      if is_custom_filter
18+        sql = "#{db_table}.#{db_field} != '' AND CAST(#{db_table}.#{db_field} AS decimal(60,3)) <= #{value.first.to_f}" 
19+      else
20+        sql = "#{db_table}.#{db_field} <= #{value.first.to_f}" 
21+      end
22     when "o" 
23       sql = "#{IssueStatus.table_name}.is_closed=#{connection.quoted_false}" if field == "status_id" 
24     when "c" 
25@@ -628,6 +636,8 @@ class Query < ActiveRecord::Base
26
27     custom_fields.select(&:is_filter?).each do |field|
28       case field.field_format
29+      when "int", "float" 
30+        options = { :type => :integer, :order => 20 }
31       when "text" 
32         options = { :type => :text, :order => 20 }
33       when "list"

While we're at it, I'd like to change the :integer filter type to :numeric to make it clear it doesn't work only for integers.

  • Target version set to 2.1.0
  • Assignee deleted (Felix Schäfer)
  • Status changed from Open to Ready for review

Updated by Holger Just at 2011-07-04 02:34 pm

Hmmm, this results in a full table scan as the casts can not use any index. This might be an issue if you have a couple of custom values. Also I'd use a scale of a least 4 digits in the decimal to accomodate common currency precision. Using this, I think this is a nice workaround

That said, I think a much cleaner variant would be to provide more columns on the custom fields to be used for different data types. Those columns should by correctly typed (and indexed) in the database. Thus, there could be the following columns:

  • :text for actual strings and serialized complex values
  • :integer for well,integers
  • :currency which could be a decimal(60,4) in the database
  • *:float@, well floating point stuff. This one might not be necessary as the decimal could be sufficient

Additionally, a type field would select the actually value column to be used. There would only be one column used, the others would be force to contain null.

The slight storage overhead shouldn't really matter compared to the performance opportunitues of actually using indexes.

Updated by Felix Schäfer at 2011-07-04 10:20 pm

Holger Just wrote:

Hmmm, this results in a full table scan as the casts can not use any index. This might be an issue if you have a couple of custom values.

The current custom field structure doesn't allow anything better, more on that below.

Also I'd use a scale of a least 4 digits in the decimal to accomodate common currency precision. Using this, I think this is a nice workaround

Well, I just copied the precision from source:/app/models/custom_field.rb#L116, we can bump both to DECIMAL(60,4) or even DECIMAL(65,10) though, I don't think the performance will be much different.

That said, I think a much cleaner variant would be to provide more columns on the custom fields to be used for different data types. Those columns should by correctly typed (and indexed) in the database. Thus, there could be the following columns:

  • :text for actual strings and serialized complex values
  • :integer for well,integers
  • :currency which could be a decimal(60,4) in the database
  • *:float@, well floating point stuff. This one might not be necessary as the decimal could be sufficient

Additionally, a type field would select the actually value column to be used. There would only be one column used, the others would be force to contain null.

The slight storage overhead shouldn't really matter compared to the performance opportunitues of actually using indexes.

Regarding that, I really (really really) dislike that the types are hardcoded and just differentiated by a bunch of case statements. I've spent a few minutes pondering this, and I think the nicest solution would be to have sort of a CustomField namespace and have say CustomField::String, CustomField::Text, CustomField::Integer, CustomField::Date, CustomField::Users and so on, and each could provide/decide how to store values (maybe CustomValue::Text, CustomValue::Numeric, CustomValue::Association (for stuff referencing core objects and/or (multiselect) lists)), how to sort them, how to sort objects using them, and so on. This would also have the advantage of every field type being able to provide it's own form for the admin section instead of relying on (unextensible) js, that you wouldn't need to declare custom field types in lib/redmine.rb as is done now (just scan for CustomField:: types), and all that combined would make adding new types even from a plugin a breeze.

The biggest snag I've hit though is that that would need some sort of doubly polymorphic many-to-many relation: Customizable many-to-many CustomField, where Customizable can be anything and sees custom fields as "CustomizableCustomField" (e.g. IssueCustomField), and CustomField being a CustomField::String or CustomField::Users and so on. The backend shouldn't be too much of a problem, but I'm not sure how good rails will play with that.

Anyway, already much too much discussion about that for the scope of this ticket, I'll open a forum thread tomorrow to discuss all this, I'm also pretty sure there's a nicer way that what I have in mind currently, but I guess my ruby-foo isn't good enough for it yet :-)

Updated by Felix Schäfer at 2011-07-05 09:21 am

Also see the custom field rework proposal for more info.

Updated by Felix Schäfer at 2011-07-08 07:41 pm

Committed the proposed patch with DECIMAL(60,3) changed to DECIMAL(60,4), this will at least get us working filters until we have a better solution.

  • Status changed from Ready for review to Closed

Also available in: Atom PDF