(#6664 / #8011 / #6879) - fix nested $or inside $and
diff --git a/packages/node_modules/pouchdb-selector-core/src/utils.js b/packages/node_modules/pouchdb-selector-core/src/utils.js
index adfa56d..cdfcbc9 100644
--- a/packages/node_modules/pouchdb-selector-core/src/utils.js
+++ b/packages/node_modules/pouchdb-selector-core/src/utils.js
@@ -69,6 +69,7 @@
   // $and: [{$gt: 'a'}, {$gt: 'b'}], then it's collapsed into
   // just {$gt: 'b'}
   var res = {};
+  var first = {$or: true, $nor: true};
 
   selectors.forEach(function (selector) {
     Object.keys(selector).forEach(function (field) {
@@ -78,11 +79,32 @@
       }
 
       if (isCombinationalField(field)) {
+        // or, nor
         if (matcher instanceof Array) {
-          res[field] = matcher.map(function (m) {
-            return mergeAndedSelectors([m]);
+          if (first[field]) {
+            first[field] = false;
+            res[field] = matcher;
+            return;
+          }
+
+          var entries = [];
+          res[field].forEach(function (existing) {
+            Object.keys(matcher).forEach(function (key) {
+              var m = matcher[key];
+              var longest = Math.max(Object.keys(existing).length, Object.keys(m).length);
+              var merged = mergeAndedSelectors([existing, m]);
+              if (Object.keys(merged).length <= longest) {
+                // we have a situation like: (a :{$eq :1} || ...) && (a {$eq: 2} || ...)
+                // merging would produce a $eq 2 when actually we shouldn't ever match against these merged conditions
+                // merged should always contain more values to be valid
+                return;
+              }
+              entries.push(merged);
+            });
           });
+          res[field] = entries;
         } else {
+          // not
           res[field] = mergeAndedSelectors([matcher]);
         }
       } else {
diff --git a/tests/find/test-suite-1/test.or.js b/tests/find/test-suite-1/test.or.js
index 735360f..c23cacb 100644
--- a/tests/find/test-suite-1/test.or.js
+++ b/tests/find/test-suite-1/test.or.js
@@ -121,6 +121,282 @@
                 ]);
             });
         });
+        describe("nested $or inside $and", function () {
+            it('equal length $ors', function () {
+                var db = context.db;
+                var index = {
+                    "index": {
+                        "fields": ["field.a"]
+                    }
+                };
 
+                var selector = {
+                    $and: [
+                        {
+                            $or: [
+                                {a: 1},
+                                {b: 2}
+                            ]
+                        },
+                        {
+                            $or: [
+                                {a: 3},
+                                {b: 4}
+                            ]
+                        }
+                    ]
+                };
+                return db.createIndex(index).then(function () {
+                    return db.bulkDocs([
+                        {_id: '1', a: 1, b: 2},
+                        {_id: '2', a: 1, b: 4},
+                        {_id: '3', a: 3, b: 2},
+                        {_id: '4', a: 3, b: 4},
+                    ]);
+                }).then(function () {
+                    return db.find({
+                        selector,
+                        fields: ["_id"]
+                    }).then(function (resp) {
+                        resp.docs.should.deep.equal([{_id: '2'}, {_id: '3'}]);
+                    });
+                }).then(function () {
+                    if (db.adapter === "local") {
+                        return db.explain({
+                            selector,
+                            fields: ["_id"]
+                        }).then(function (resp) {
+                            resp.selector.should.deep.equal({
+                            "$or": [
+                                    {"a": {"$eq": 1}, "b": {"$eq": 4}},
+                                    {"a": {"$eq": 3}, "b": {"$eq": 2}}
+                                ]
+                            });
+                        });
+                    }
+                });
+            });
+            it('first $or length less than second', function () {
+                var db = context.db;
+                var index = {
+                    "index": {
+                        "fields": ["field.a"]
+                    }
+                };
+
+                var selector = {
+                    $and: [
+                        {
+                            $or: [
+                                {c: 2},
+                            ]
+                        },
+                        {
+                            $or: [
+                                {a: 1},
+                                {b: 1},
+                            ]
+                        },
+                    ]
+                };
+                return db.createIndex(index).then(function () {
+                    return db.bulkDocs([
+                        {_id: '1', a: 1, b: 2, c: 2},
+                        {_id: '2', a: 2, b: 2, c: 2},
+                        {_id: '3', a: 2, b: 1, c: 2},
+                        {_id: '4', a: 2, b: 2, c: 2},
+                    ]);
+                }).then(function () {
+                    return db.find({
+                        selector,
+                        fields: ["_id"]
+                    }).then(function (resp) {
+                        resp.docs.should.deep.equal([{_id: '1'}, {_id: '3'}]);
+                    });
+                }).then(function () {
+                    if (db.adapter === "local") {
+                        return db.explain({
+                            selector,
+                            fields: ["_id"]
+                        }).then(function (resp) {
+                            console.log(resp.selector);
+                            resp.selector.should.deep.equal({
+                                "$or": [
+                                    {"a": {"$eq": 1}, "c": {"$eq": 2}},
+                                    {"b": {"$eq": 1}, "c": {"$eq": 2}}
+                                ]
+                            });
+                        });
+                    }
+                });
+            });
+            it('second $or length less than first', function () {
+                var db = context.db;
+                var index = {
+                    "index": {
+                        "fields": ["field.a"]
+                    }
+                };
+
+                var selector = {
+                    $and: [
+                        {
+                            $or: [
+                                {a: 1},
+                                {b: 1},
+                            ]
+                        },
+                        {
+                            $or: [
+                                {c: 2},
+                            ]
+                        },
+                    ]
+                };
+                return db.createIndex(index).then(function () {
+                    return db.bulkDocs([
+                        {_id: '1', a: 1, b: 2, c: 2},
+                        {_id: '2', a: 2, b: 2, c: 2},
+                        {_id: '3', a: 2, b: 1, c: 2},
+                        {_id: '4', a: 2, b: 2, c: 2},
+                    ]);
+                }).then(function () {
+                    return db.find({
+                        selector,
+                        fields: ["_id"]
+                    }).then(function (resp) {
+                        resp.docs.should.deep.equal([{_id: '1'}, {_id: '3'}]);
+                    });
+                }).then(function () {
+                    if (db.adapter === "local") {
+                        return db.explain({
+                            selector,
+                            fields: ["_id"]
+                        }).then(function (resp) {
+                            resp.selector.should.deep.equal({
+                                "$or": [
+                                    {"a": {"$eq": 1}, "c": {"$eq": 2}},
+                                    {"b": {"$eq": 1}, "c": {"$eq": 2}}
+                                ]
+                            });
+                        });
+                    }
+                });
+            });
+            it('should do complex queries', function () {
+                var db = context.db;
+                var index = {
+                    "index": {
+                        "fields": ["field.a"]
+                    }
+                };
+
+                var selector = {
+                    $or: [
+                        {
+                            $and: [
+                            {
+                                $or: [
+                                    {
+                                        $or: [
+                                            {
+                                                $and: [
+                                                    {due: {important: true}},
+                                                    {tags: {$all: ["home"]}},
+                                                ]
+                                            },
+                                            // implicit and
+                                            {due: {date: "soon", important:false}},
+                                        ]
+                                    },
+                                    {due: {date: "tomorrow"}},
+                                ]
+                            },
+                            {
+                                $or: [
+                                    {assigned: "me"},
+                                    {assigned: "other1"},
+                                ]
+                            },
+                            {include: true}
+                                ]
+                        }
+                    ]
+                };
+                return db.createIndex(index).then(function () {
+                    return db.bulkDocs([
+                        {_id: '1', include:true, due: {repeating: true, date: "friday"}, tags: ["home"], assigned: "other2"},
+                        {_id: '2', include:true, due: {repeating: true, date: "friday", important: true}, tags: ["home"], assigned: "other1"},
+                        {_id: '3', include:true, due: {repeating: false, date: "soon", important: false}, tags: ["home"], assigned: "me"},
+                        {_id: '4', include:true, due: {repeating: false, date: "tuesday"}, tags: ["home"], assigned: "me"},
+                        {_id: '5', include:true, due: {repeating: true, date: "friday", important: true}, tags: ["work"], assigned: "me"},
+                        {_id: '6', include:true, due: {repeating: false, date: "tomorrow"}, tags: ["health"], assigned: "me"},
+                        {_id: '7', include:false, due: {repeating: false, date: "tomorrow"}, tags: ["health"], assigned: "me"},
+                    ]);
+                }).then(function () {
+                    return db.find({
+                        selector,
+                        fields: ["_id"]
+                    }).then(function (resp) {
+                        resp.docs.should.deep.equal([{_id: '2'}, {_id: '3'}, {_id: '6'}]);
+                    });
+                }).then(function () {
+                    if (db.adapter === "local") {
+                        return db.explain({
+                            selector,
+                            fields: ["_id"]
+                        }).then(function (resp) {
+                            console.log(JSON.stringify(resp.selector, null, "\t"));
+
+                            resp.selector.should.deep.equal({
+                                $or: [
+                                    {
+                                        $or: [
+                                            {
+                                                due: {important: true},
+                                                tags: {$all: ["home"]},
+                                            },
+                                            {
+                                                due: {
+                                                    date: "soon",
+                                                    important: false
+                                                }
+                                            },
+                                        ],
+                                        assigned: {$eq: "me"},
+                                    },
+                                    {
+                                        $or: [
+                                            {
+                                                due: {important: true},
+                                                tags: {$all: ["home"]},
+                                            },
+                                            {
+                                                due: {
+                                                    date: "soon",
+                                                    important: false
+                                                }
+                                            },
+                                        ],
+                                        assigned: {$eq: "other1"},
+                                    },
+                                    {
+                                        due: {date: "tomorrow"},
+                                        assigned: {$eq: "me"},
+                                    },
+                                    {
+                                        due: {date: "tomorrow"},
+                                        assigned: {$eq: "other1"},
+                                    },
+                                ],
+                                include: {
+                                    $eq: true,
+                                },
+                            });
+                        });
+                    }
+                });
+            });
+        });
     });
 });