Skip to content

Conversation

@TamimEhsan
Copy link

Setup

Let's consider a case where the massageSelector fails to render some branches

[
    {_id: '1', a: 1, b: 2},
    {_id: '2', a: 1, b: 4},
    {_id: '3', a: 3, b: 2},
    {_id: '4', a: 3, b: 4},
    {_id: '5', a: 5, b: 6}
 ]

with query

query = {
    fields: ["_id"],
    selector: {
      $and: [
        {
            $or: [
                {a: 1},
                {b: {$gte:2}}
            ]
        },
        {
            $or: [
                {a: 3},
                {b: {$gte:4}}
            ]
        }
    ]
    }
  };

We expect that the doc with _id : 5 to be returned. But the actual return doesn't include that.

Possible problem

the two selector {b: {$gte:2}} and {b: {$gte:4}} gets merged into one {b: {$gte:4}}. However, due to the logic implemented at the below segment, we lose this merged selector.
https://github.com/pouchdb/pouchdb/blob/f2c665a2a885437b9cea80dda62c02a93a137c1e/packages/node_modules/pouchdb-selector-core/src/utils.js#L96-L101

We can come up with more of similar cases where we lose the selector. So, removing it might be a good approach. However, then we actually fail to cover the case it was actually meant to handle ie {b: {$eq:2}} and {b: {$eq:4}}. Which should not be merged into {b: {$eq:4}} due to the current implementation of mergeEq function
https://github.com/pouchdb/pouchdb/blob/f2c665a2a885437b9cea80dda62c02a93a137c1e/packages/node_modules/pouchdb-selector-core/src/utils.js#L212-L221

Proposed Solution

I propose a simple fix to this. At first we can remove code segment if (Object.keys(merged).length <= longest) { return; } . Then for conflicting $eq conditions, we add a $ne to the condition, thus causing the selector to be false by default. Although this is a bit hacky, but it will make the logic a bit easier. Later, we can filter the selector based on this contradiction instead of length.

function mergeEq(value, fieldMatchers) {
  // these all have less specificity than the $eq
  // TODO: check for user errors here
  delete fieldMatchers.$gt;
  delete fieldMatchers.$gte;
  delete fieldMatchers.$lt;
  delete fieldMatchers.$lte;
  delete fieldMatchers.$ne;
+  if ( '$eq' in fieldMatchers && fieldMatchers.$eq !== value ) {
+   mergeNe(value, fieldMatchers);
+ }
  fieldMatchers.$eq = value;
}

Alternative Solution

Alternatively, we can also introduce a new field like $falacy or something, which will be set to true when such contradictions arise. Then like before we can filter the selector based on that.

Testing

I have tested the change against some edge cases. So, far it seems to be working. And it passes all the tests too for TYPE=find PLUGINS=pouchdb-find COUCH_HOST='http://admin:password@localhost:5984' npm test

Image

Note 1

Although $eq has more specificity than the others, but deleting the other operators might not be the best choice here. Having an operator like $gte : 10 and merging it with $eq : 5 should keep both, as the correct merged selector is an always false selector. But deleting $gte will cause it to change its behavior.

…slectors

fixes merging logic of selectors in mergeAndedSelectors function which previously removed some valid selectors during merge as discuessed in issue apache#9065.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant