[JAVA-3888] BsonDocument.equals should consider field order Created: 13/Nov/20  Updated: 30/Mar/22

Status: Backlog
Project: Java Driver
Component/s: BSON
Affects Version/s: None
Fix Version/s: None

Type: Improvement Priority: Major - P3
Reporter: Kai Orend Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified


 Description   

When comparing two BsonDocuments using the BsonDocuments.equals() method the order of the fields does not seem to be considered.

Currently {a:1, b:1}.equals({b:1,a:1})  evaluates to true for BsonDocuments. For documents the expected behavior would be that documents are only considered to be same if the field order is the same. 



 Comments   
Comment by Ross Lawley [ 23/Nov/20 ]

An alternative proposal could be to add a BsonDocument.strict() method to configure strict checking mode and check the flag in the equals() method.

That way recursive equality checks for sub documents etc can be handled easily and users could configure the setting globally via a custom BsonDocumentCodec.

Comment by Jeffrey Yemin [ 23/Nov/20 ]

I think I see now why we did it this way: it's because Document and BsonDocument implement the Map interface and as such they must obey the contract defined in the Map.equals documentation, which states:

Compares the specified object with this map for equality. Returns true if the given object is also a map and the two maps represent the same mappings. More formally, two maps m1 and m2 represent the same mappings if m1.entrySet().equals(m2.entrySet()).
This ensures that the equals method works properly across different implementations of the Map interface.

So I'm convinced now that this is not a bug and should not be changed.

We can consider adding an orderedEquals method to both classes to handle this particular use case.

Comment by Jeffrey Yemin [ 19/Nov/20 ]

I tested both Document and BasicDBObject, and they both work the same as BsonDocument: they ignore key order.  So we have been consistent.

Comment by Jeffrey Yemin [ 16/Nov/20 ]

I think it's clear that the current behavior is incorrect and could be considered a bug. The concern is that changing the behavior of BsonDocument#equals to take field order into consideration could break existing applications. And there is no way to warn people relying on the current behavior with deprecation warnings. It would only be release notes.

Maybe what we should do is introduce two new methods, one which takes order into account and one which does not. And then update the documentation for equals to say that the behavior is "undefined" and may change in a future release.

Comment by Peter Williamson [ 16/Nov/20 ]

My 2c's worth: There's certainly a use case for a method that doesn't take order into account but given that order can be important equals should consider order. Perhaps contentsEquals could perform the current functionality.

Comment by Kai Orend [ 16/Nov/20 ]

Hi ross.lawley,

 

on the server it depends. In you example of a find query the filter is using an implicit and. However in cases where you filter on a whole document the server would use a binary comparison on the BSON representation:

 

 

db.test.insertOne({ d : {a:1, b:2}})
 
db.test.count({ d : {b:2, b:1}}) 
0
 

 

However the context where I was hitting this issue was when comparing index definitions where the order is definitely important. And the BsonDocument equals should probably behave the same way as the one from the Document class.  

Kai

Comment by Ross Lawley [ 16/Nov/20 ]

Hi kai.orend,

What are your reasons for wanting to test insertion order? Just to note the server considers such documents as equal:

MongoDB Enterprise > db.test.insert({b: 1, a: 1})
WriteResult({ "nInserted" : 1 })
MongoDB Enterprise > db.test.find({a: 1, b: 1})
{ "_id" : ObjectId("5fb2484503eb536ee8a70ceb"), "b" : 1, "a" : 1 }

From the documented comparison / sort order for bson objects is:

MongoDB’s comparison of BSON objects uses the following order:

  1. Recursively compare key-value pairs in the order that they appear within the BSON object.
  2. Compare the key field names.
  3. If the key field names are equal, compare the field values.
  4. If the field values are equal, compare the next key/value pair (return to step 1). An object without further pairs is less than an object with further pairs.

So even the server doesn't appear to follow the ordering of the fields from within the Bson document.

Ross

Generated at Thu Feb 08 09:00:41 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.