#2892·LiteDB

BsonDocument.CompareTo is not antisymmetric: documents with different keys compare greater in both directions

Author: JKamskerCreated Sep 16, 2026Updated Sep 16, 2026
Labelsbugbreaking-changeseverity: highpriority: P2

BsonDocument.CompareTo walks only the left-hand document's keys and looks each one up in the right-hand document, treating a missing key as Null. When the two documents have different key sets, each side finds a non-null value on its own side and Null on the other, so both a.CompareTo(b) and b.CompareTo(a) return 1. IComparable requires sign(a.CompareTo(b)) == -sign(b.CompareTo(a)).

Verified on dev (828d760f). The comparison code is unchanged by #2755; the randomised comparison test added there had to exclude documents because of this.

Reproduction

csharp
var a = new BsonDocument { ["x"] = 1 };
var b = new BsonDocument { ["y"] = 1 };
Console.WriteLine($"{a.CompareTo(b)} {b.CompareTo(a)}");   // 1 1

var c = new BsonDocument { ["x"] = 1, ["y"] = 0 };
var d = new BsonDocument { ["y"] = 1, ["x"] = 0 };
Console.WriteLine($"{c.CompareTo(d)} {d.CompareTo(c)}");   // 1 1  (same keys, different order)

var list = new List<BsonValue> { b, a, new BsonDocument { ["z"] = 1 } };
list.Sort();                                              // {"z":1} {"x":1} {"y":1}

The second case shows it is not only about disjoint keys: the result depends on which document's key order is walked first.

Impact

  • Equals is unaffected: a 0 result requires every left key to match and the counts to be equal, which is symmetric. Hash codes therefore stay consistent.
  • Ordering is affected: sorting a list of documents, ORDER BY on a document-valued field, and B-tree index placement for document keys depend on comparison order and on which operand is on the left. Results can differ between runs or between insert orders, and List<T>.Sort may produce an order that is not a total order.

Cause

LiteDB/Document/BsonDocument.cs: the loop iterates this.Keys and compares this[key] with otherDoc[key], then compares key counts. Keys present only in other are never visited.

Possible fix

Compare a canonical view of both documents, for example the ordinal-ignore-case sorted key sequence: walk the union of keys in sorted order, compare key names first, then values, then fall back to count. That keeps Equals semantics (same keys and equal values) while making the ordering a total order. Any change to document ordering must consider existing indexes on document-valued keys, since their on-disk order was produced by the current comparison.