spring-data-mongodb icon indicating copy to clipboard operation
spring-data-mongodb copied to clipboard

Fix same hashCodes an different queries with include and exclude

Open 13wjdgk opened this issue 1 year ago • 3 comments

Closes #4032

There was a bug where the hashCodes of the exclude and include queries were identical.

The issue was caused by the hashCode calculation in HashMap, where the XOR operation between the hashCode of the key and the hashCode of the value (0 or 1) resulted in duplicate hashCodes.

image

Since Exclude and Include are differentiated by 0 and 1 and are put into the hashMap, they were vulnerable to returning duplicate hashCodes.

To resolve this, the fix involved combining the key and value of the HashMap into a single String and calculating the hashCode based on that.

  • [x] You have read the Spring Data contribution guidelines.
  • [x] You use the code formatters provided here and have them applied to your changes. Don’t submit any formatting related changes.
  • [x] You submit test cases (unit or integration tests) that back your changes.
  • [x] You added yourself as author in the headers of the classes you touched. Amend the date range in the Apache license header if needed. For new types, add the license header (copy from another file and set the current year only).

13wjdgk avatar Nov 10 '24 04:11 13wjdgk

@13wjdgk Please sign the Contributor License Agreement!

Click here to manually synchronize the status of this Pull Request.

See the FAQ for frequently asked questions.

pivotal-cla avatar Nov 10 '24 04:11 pivotal-cla

Instead of toString(), I also considered the following solution.

	int result = nullSafeHashCodeForCriteria(criteria);
	
	public static int nullSafeHashCodeForCriteria(@Nullable Object obj) {
		if (obj == null) {
			return 0;
		} else if(obj instanceof Map<?,?>){
			int result = 0;
			for(Entry<?, ?> entry : ((Map<?, ?>)obj).entrySet()){
				if(entry.getValue() instanceof Integer){
					Integer value = (Integer)entry.getValue();
					int valueHashCode;
					if(value == 0){
						valueHashCode = FieldProjection.EXCLUDE.hashCode();
					}
					else if(value == 1){
						valueHashCode = FieldProjection.INCLUDE.hashCode();
					}else{
						valueHashCode = value.hashCode();
					}
					result += entry.getKey().hashCode() ^ valueHashCode;
				}else {
					result += entry.hashCode();
				}
			}
			return result;
		}

		return obj.hashCode();

	}

If you have a better idea, please let me know in the comments!

13wjdgk avatar Nov 10 '24 04:11 13wjdgk

@13wjdgk Thank you for signing the Contributor License Agreement!

pivotal-cla avatar Nov 10 '24 04:11 pivotal-cla