Specifically, this ensures that with sort specifier {'a.x': 1, 'a.y': 1},
{a: [{x: 0, y: 4}]} sorts before {a: [{x: 0, y: 5}, {x: 1, y: 3}]} by
ensuring that the latter only has sort keys [0,5] and [1,3], and not
[0,3].
It does this by ensuring that the different fields used to generate a
sort key are only matched up if they use the same "path" (series of
array indices used during lookup). This is a little stronger than what
MongoDB does: MongoDB lets you do {'a.x': 1, 'a.y.z': 1} where 'a' and
'a.y' are both arrays, and our new rule requires all fields to have
either the exact same set of arrays or to use no arrays at all.
(MongoDB does still have some constraints; eg, with sort key {a: 1,
b:1}, a and b cannot both be arrays. Basically, there may be at most
one "outermost" array across all the fields; and so on recursively, sort
of.
The previously failing test passes now.
This rewrites the sorter to generate "keys" for each doc and find the
lexicographically minimum "key", rather than to look at each sort key
field separately.
This commit doesn't actually change the semantics (and the failing test
still fails) because the key generator actually still computes the full
cartesian product of all keys, rather than only using fields that are
next to each other in arrays.
This 'x' thing is a bit of a hack. The idea is that there are two
consumers of arrayIndices. One is '$', which only looks at the first
index. The other is (soon) sort ken generators, which cares if indices
are explicit or implicit, but doesn't really need to "parse"
arrayIndices; it just will use them to associate branches from different
lookup functions with the matching arrayIndices. 'x' is a simple way to
accomplish this, though I could have made the values of arrayIndices be
objects too.
Some tools like `ag` don't work well with absolute paths but work well with
relative ones. Considering that this project-wise '.gitignore' stays in root, it
will be OK to change the paths to relative.
This matches what the SockJS server uses; now we only need to understand
and fix bugs in the implementation of one websocket npm module.
Some notes:
- I actually trust that it's possible to close a connection before it
successfully connects, which allows me to simplify the code a
lot (since there shouldn't be multiple connections active per
ClientStream). I put in some assertions to make sure this is the
case, though. (Note that this module also has a simpler model,
where there's a single object representing the client connection,
not a "client" object that spawns "connections".)
- We now print connect errors as well as post-connect errors. (This
required adding a flag to keep tests quiet since it makes an
expected-to-fail-to-connect connection.) We need a better approach
to stream error handling, though.
- We used to have a test to make sure that a certain not-user-visible
callback is called within a Fiber; structuring the code such that
this test is still possible would lead to the code being less
consistent and harder to read, so I dropped the test.
- Fix a few bugs where we weren't using Meteor.setTimeout.
Previously, we could make a connection, do some method calls, and then
10 seconds later the connection happens to be dropped and the connection
timer fires, which not only throws an unexpected error into the future,
but also resolves the future twice. I think ServiceConnection is just
supposed to time out if you don't hear anything from the server within
10 seconds, so it now no longer times out if you hear things from the
server but then happen to be not connected when 10 seconds has elapsed.
If we set it up before `subscribeAndWait` returns, then we'll end up
with two subscriptions; we don't have the log-reader sub yet, so we
can't stop it when `onReconnect` runs the first time, so we end up with
a redundant subscription. This means that if a real reconnect happens
later, we'll stop the sub that we set up inside `onReconnect`, but not
the initial sub, so we've leaked a sub and end up with duplicate
messages after reconnect.