From 282724a04198f1b47b316dac47b56a888a413c0d Mon Sep 17 00:00:00 2001 From: Seba Kerckhof Date: Wed, 12 Sep 2018 20:12:06 +0200 Subject: [PATCH 1/2] Fix minimongo nested array sorting (#10217) Fixes #10192. --- packages/minimongo/common.js | 2 +- packages/minimongo/minimongo_tests_client.js | 45 ++++++++++++++++++++ packages/minimongo/sorter.js | 10 +---- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/packages/minimongo/common.js b/packages/minimongo/common.js index 302514566a..213396301f 100644 --- a/packages/minimongo/common.js +++ b/packages/minimongo/common.js @@ -969,7 +969,7 @@ export function makeLookupFunction(key, options = {}) { const firstPart = parts.length ? parts[0] : ''; const lookupRest = ( parts.length > 1 && - makeLookupFunction(parts.slice(1).join('.')) + makeLookupFunction(parts.slice(1).join('.'), options) ); const omitUnnecessaryFields = result => { diff --git a/packages/minimongo/minimongo_tests_client.js b/packages/minimongo/minimongo_tests_client.js index 4b9f6df3bc..24455b439e 100644 --- a/packages/minimongo/minimongo_tests_client.js +++ b/packages/minimongo/minimongo_tests_client.js @@ -2220,6 +2220,43 @@ Tinytest.add('minimongo - array sort', test => { 'selected'); }); +Tinytest.add('minimongo - nested array sort', test => { + const c = new LocalCollection(); + + // the short fields represent the order it should be when sorting for those keys + // e.g. the cdx_cdy field represents the order when you sort: { 'c.d.x': 1, 'c.d.y': 1 } + c.insert({ ab0x: 0, ab0x_g: 0, g_ab0x: 0, cdx: 0, cdx_cdy: 0, cdy_cdx: 0, n: 0 }); + c.insert({ ab0x: 1, ab0x_g: 2, g_ab0x: 3, cdx: 1, cdx_cdy: 2, cdy_cdx: 4, n: 1 , g: 2, c: { d: [{ y: 2}, { y: 3}] } }); + c.insert({ ab0x: 2, ab0x_g: 1, g_ab0x: 1, cdx: 2, cdx_cdy: 3, cdy_cdx: 5, n: 2 , c: { d: [{ y: 2}] }, g: 0 }); + c.insert({ ab0x: 3, ab0x_g: 3, g_ab0x: 2, cdx: 6, cdx_cdy: 6, cdy_cdx: 8, n: 3 , a: { b: [{ x: 0 }] }, c: { d: [{ x: 1, y: 2}] }, g: 1 }); + c.insert({ ab0x: 4, ab0x_g: 4, g_ab0x: 4, cdx: 3, cdx_cdy: 1, cdy_cdx: 1, n: 4 , a: { b: [{ x: [1, 4] }] }, c: { d: [] }, g: 2 }); + c.insert({ ab0x: 5, ab0x_g: 5, g_ab0x: 5, cdx: 7, cdx_cdy: 7, cdy_cdx: 3, n: 5 , a: { b: [{ x: [2] }, { x: 3 }]}, c: { d: [{x: 2, y: 2}, {x: 3, y: 1}] }, g: 3 }); + c.insert({ ab0x: 6, ab0x_g: 6, g_ab0x: 6, cdx: 8, cdx_cdy: 8, cdy_cdx: 2, n: 6 , a: { b: [{ x: 2.5 }] }, c: { d: [{x: 2, y: 2}, {x: 3}] }, g: 4 }); + c.insert({ ab0x: 7, ab0x_g: 7, g_ab0x: 7, cdx: 4, cdx_cdy: 4, cdy_cdx: 6, n: 7 , a: { b: [{ x: 5 }] }, c: { d: [{ y: 2}, { y: 3}] }, g: 5 }); + c.insert({ ab0x: 8, ab0x_g: 8, g_ab0x: 8, cdx: 5, cdx_cdy: 5, cdy_cdx: 7, n: 8 , a: { b: [{ x: 6 }, { x: 7 }] }, c: { d: [{ y: 2}, { x: 1.5, y: 2}] }, g: 6 }); + + // Test that the the documents in "cursor" contain values with the name + // "field" running from 0 to the max value of that name in the collection. + const testCursorMatchesField = (cursor, field) => { + const fieldValues = []; + c.find().forEach(doc => { + if (hasOwn.call(doc, field)) { + fieldValues.push(doc[field]); + } + }); + test.equal(cursor.fetch().map(doc => doc[field]), + Array.from({ length: Math.max(...fieldValues) + 1 }, (x, i) => i)); + }; + + testCursorMatchesField(c.find({}, { sort: { 'a.b.0.x': 1 } }), 'ab0x'); + testCursorMatchesField(c.find({}, { sort: { 'a.b.0.x': 1, 'g': 1 } }), 'ab0x_g'); + testCursorMatchesField(c.find({}, { sort: { 'g': 1, 'a.b.0.x': 1 } }), 'g_ab0x'); + testCursorMatchesField(c.find({}, { sort: { 'c.d.x': 1 } }), 'cdx'); + testCursorMatchesField(c.find({}, { sort: { 'c.d.x': 1, 'c.d.y': 1 } }), 'cdx_cdy'); + testCursorMatchesField(c.find({}, { sort: { 'c.d.y': 1, 'c.d.x': 1 } }), 'cdy_cdx'); + +}); + Tinytest.add('minimongo - sort keys', test => { const keyListToObject = keyList => { const obj = {}; @@ -2272,6 +2309,14 @@ Tinytest.add('minimongo - sort keys', test => { {a: [1, 2, 3], b: 42}, [[1, 42], [2, 42], [3, 42]]); + testKeys({'a.0.x': 1}, + {a: [{x: 0}]}, + [[0]]); + + testKeys({'a.0.x': 1}, + {a: []}, + [[undefined]]); + // Don't support multiple arrays at the same level. testParallelError({a: 1, b: 1}, {a: [1, 2, 3], b: [42]}); diff --git a/packages/minimongo/sorter.js b/packages/minimongo/sorter.js index 4123b4fbfe..e36a740424 100644 --- a/packages/minimongo/sorter.js +++ b/packages/minimongo/sorter.js @@ -140,9 +140,9 @@ export default class Sorter { let branches = expandArraysInBranches(spec.lookup(doc), true); // If there are no values for a key (eg, key goes to an empty array), - // pretend we found one null value. + // pretend we found one undefined value. if (!branches.length) { - branches = [{value: null}]; + branches = [{ value: void 0 }]; } const element = Object.create(null); @@ -280,12 +280,6 @@ export default class Sorter { } }); - // This could happen if our key filter somehow filters out all the keys even - // though somehow the selector matches. - if (minKey === null) { - throw Error('sort selector found no keys in doc?'); - } - return minKey; } From c42170568af7ce798a4b12ffdc7712f3ebb5a59f Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Wed, 12 Sep 2018 17:43:09 -0400 Subject: [PATCH 2/2] Bump minimonogo patch version to 1.4.5. --- packages/minimongo/package.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/minimongo/package.js b/packages/minimongo/package.js index cabc96089f..047682d2a7 100644 --- a/packages/minimongo/package.js +++ b/packages/minimongo/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Meteor's client-side datastore: a port of MongoDB to Javascript", - version: '1.4.4' + version: '1.4.5' }); Package.onUse(api => {