Skip to content

Commit 88b20c2

Browse files
authored
Reduce the usage of lodash (round 2) (#145)
* Removed lodash from index.js * Removed lodash from stats.js * Removed lodash from stream.js Only function I could not re-write was defaults-deep. I moved the function to a utils folder, so we can remove it in the near future * Removed lodash from tests * fixed linting * Updated lodash 🚀 I could not completly remove lodash, need help with 2 function calls: - stream.js --> _.get --> _.defaultsDeep * Attempt to fix travic ci failing build * Updated mocha - istanbul to modern API * Moving travis "back" to use npm ci Maybe this can solve the CI issue? * Fix CI error by using "normal" install * removed and re-installed node_modules * upgraded mocha * updating out-dated dependencies * Removed zuul It was depreceated and not maintained anymore: https://github.com/defunctzombie/zuul#readme * Fix pre-commit to match setup in compas This shoul help align the style used in: https://github.com/mongodb-js/compass * updated github workflow to look like compass * updated mognodb-js-precommit to latest * very small change from ssh --> https * fixed failing tests from upgrading BSON
1 parent 33e0f51 commit 88b20c2

26 files changed

+6352
-17630
lines changed

Diff for: .eslintrc

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"parserOptions": {
3-
"ecmaVersion": 2017
3+
"ecmaVersion": 2019
44
},
55
"env": {
66
"mocha": true,
@@ -18,6 +18,6 @@
1818
},
1919
"extends": [
2020
"mongodb-js/node",
21-
"mongodb-js/browser",
21+
"mongodb-js/browser"
2222
]
2323
}

Diff for: .github/workflows/unit-tests.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ jobs:
1616
uses: actions/setup-node@v2
1717
with:
1818
node-version: ${{ matrix.node-version }}
19-
- uses: bahmutov/npm-install@v1
19+
- run: npm ci
20+
- run: npm run check
2021
- run: npm test
2122
- run: npm run coverage

Diff for: .gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ dist/
3333
# Test output
3434
.nyc_output
3535
# Mac OS files on folder create/delete
36-
.DS_Store
36+
.DS_Store

Diff for: .zuul.yml

-12
This file was deleted.

Diff for: bin/mongodb-schema

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ var bar = new ProgressBar('analyzing [:bar] :percent :etas ', {
141141
clear: true
142142
});
143143

144-
mongodb.connect(uri, function(err, conn) {
144+
mongodb.connect(uri, {useUnifiedTopology: true}, function(err, conn) {
145145
if (err) {
146146
console.error('Failed to connect to MongoDB: ', err);
147147
process.exit(1);

Diff for: lib/index.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
var stream = require('./stream');
22
var es = require('event-stream');
3-
var _ = require('lodash');
43

54
// var debug = require('debug')('mongodb-schema:wrapper');
65

@@ -20,7 +19,7 @@ var _ = require('lodash');
2019
module.exports = function(docs, options, callback) {
2120
const promise = new Promise((resolve, reject) => {
2221
// shift parameters if no options are specified
23-
if (_.isUndefined(options) || (_.isFunction(options) && _.isUndefined(callback))) {
22+
if (typeof options === 'undefined' || (typeof options === 'function' && typeof callback === 'undefined')) {
2423
callback = options;
2524
options = {};
2625
}
@@ -33,7 +32,7 @@ module.exports = function(docs, options, callback) {
3332
} else if (docs.pipe && typeof docs.pipe === 'function') {
3433
src = docs;
3534
// Arrays
36-
} else if (_.isArray(docs)) {
35+
} else if (Array.isArray(docs)) {
3736
src = es.readArray(docs);
3837
} else {
3938
reject(new Error(

Diff for: lib/stats.js

+24-20
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
var _ = require('lodash');
21
// var debug = require('debug')('mongodb-schema:stats');
32

43
var widthRecursive = function(schema) {
@@ -8,17 +7,21 @@ var widthRecursive = function(schema) {
87
}
98
if (schema.fields !== undefined) {
109
width += schema.fields.length;
11-
width += _.sum(schema.fields.map(function(field) {
12-
var doc = _.find(field.types, 'name', 'Document');
10+
11+
width += schema.fields.map(field => {
12+
var doc = field.types.find(v => v.name === 'Document');
1313
return widthRecursive(doc);
14-
}));
15-
width += _.sum(schema.fields.map(function(field) {
16-
var arr = _.find(field.types, 'name', 'Array');
14+
}).reduce((p, c) => p + c || 0, 0);
15+
16+
17+
width += schema.fields.map(field => {
18+
var arr = field.types.find(v => v.name === 'Array');
1719
if (arr) {
18-
var doc = _.find(arr.types, 'name', 'Document');
20+
var doc = arr.types.find(v => v.name === 'Document');
1921
return widthRecursive(doc);
2022
}
21-
}));
23+
})
24+
.reduce((p, c) => p + c || 0, 0);
2225
}
2326
return width;
2427
};
@@ -30,18 +33,19 @@ var depthRecursive = function(schema) {
3033
var maxChildDepth = 0;
3134
if (schema.fields !== undefined && schema.fields.length > 0) {
3235
maxChildDepth = 1 + Math.max(
33-
_.max(schema.fields.map(function(field) {
34-
var doc = _.find(field.types, 'name', 'Document');
36+
Math.max(...schema.fields.map(field => {
37+
var doc = field.types.find(v => v.name === 'Document');
3538
return depthRecursive(doc);
3639
})),
37-
_.max(schema.fields.map(function(field) {
38-
var arr = _.find(field.types, 'name', 'Array');
40+
Math.max(...schema.fields.map(field => {
41+
var arr = field.types.find(v => v.name === 'Array');
3942
if (arr) {
40-
var doc = _.find(arr.types, 'name', 'Document');
43+
var doc = arr.types.find(v => v.name === 'Document');
4144
return depthRecursive(doc);
4245
}
4346
return 0;
44-
})));
47+
}))
48+
);
4549
}
4650
return maxChildDepth;
4751
};
@@ -55,21 +59,21 @@ var branchingFactors = function(schema) {
5559
if (schema.fields !== undefined && schema.fields.length > 0) {
5660
branchArray.push(schema.fields.length);
5761
res = schema.fields.map(function(field) {
58-
var doc = _.find(field.types, 'name', 'Document');
62+
var doc = field.types.find(v => v.name === 'Document');
5963
return branchingFactors(doc);
6064
});
61-
branchArray.push.apply(branchArray, _.flatten(res, true));
65+
branchArray.push(...res.flat(Infinity));
6266
res = schema.fields.map(function(field) {
63-
var arr = _.find(field.types, 'name', 'Array');
67+
var arr = field.types.find(v => v.name === 'Array');
6468
if (arr) {
65-
var doc = _.find(arr.types, 'name', 'Document');
69+
var doc = arr.types.find(v => v.name === 'Document');
6670
return branchingFactors(doc);
6771
}
6872
return [];
6973
});
70-
branchArray.push.apply(branchArray, _.flatten(res, true));
74+
branchArray.push(...res.flat(Infinity));
7175
}
72-
return _.sortBy(branchArray).reverse();
76+
return branchArray.sort().reverse();
7377
};
7478

7579
module.exports = {

Diff for: lib/stream.js

+50-53
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
var es = require('event-stream');
2-
var _ = require('lodash');
32
var Reservoir = require('reservoir');
3+
var _ = require('lodash');
44

55
// var debug = require('debug')('mongodb-schema:stream');
66

@@ -13,14 +13,14 @@ var Reservoir = require('reservoir');
1313
*/
1414
var extractStringValueFromBSON = function(value) {
1515
if (value && value._bsontype) {
16-
if (_.includes([ 'Decimal128', 'Long' ], value._bsontype)) {
16+
if (['Decimal128', 'Long'].includes(value._bsontype)) {
1717
return value.toString();
1818
}
19-
if (_.includes([ 'Double', 'Int32' ], value._bsontype)) {
19+
if ([ 'Double', 'Int32' ].includes(value._bsontype)) {
2020
return String(value.value);
2121
}
2222
}
23-
if (_.isString(value)) {
23+
if (typeof value === 'string') {
2424
return value;
2525
}
2626
return String(value);
@@ -68,7 +68,7 @@ var finalizeSchema = function(schema, parent, tag) {
6868
finalizeSchema(schema.fields, schema, 'fields');
6969
}
7070
if (tag === 'fields') {
71-
_.each(schema, function(field) {
71+
Object.values(schema).forEach((field) => {
7272
// create `Undefined` pseudo-type
7373
var missing = parent.count - field.count;
7474
if (missing > 0) {
@@ -79,25 +79,27 @@ var finalizeSchema = function(schema, parent, tag) {
7979
count: missing
8080
};
8181
}
82-
field.total_count = _.sum(field.types, 'count');
82+
field.total_count = Object.values(field.types)
83+
.map(v => v.count)
84+
.reduce((p, c) => p + c, 0);
8385

8486
// recursively finalize types
8587
finalizeSchema(field.types, field, 'types');
86-
field.type = _.pluck(field.types, 'name');
88+
field.type = field.types.map(v => v.name);
8789
if (field.type.length === 1) {
8890
field.type = field.type[0];
8991
}
9092
// a field has duplicates when any of its types have duplicates
91-
field.has_duplicates = _.any(field.types, 'has_duplicates');
93+
field.has_duplicates = !!field.types.find(v => v.has_duplicates);
9294
// compute probability
9395
field.probability = field.count / parent.count;
9496
});
9597
// turn object into array
96-
parent.fields = _.values(parent.fields).sort(fieldComparator);
98+
parent.fields = Object.values(parent.fields).sort(fieldComparator);
9799
}
98100
if (tag === 'types') {
99-
_.each(schema, function(type) {
100-
type.total_count = _.sum(type.lengths);
101+
Object.values(schema).forEach(type => {
102+
type.total_count = (type.lengths || []).reduce((p, c) => p + c || 0, 0);
101103
// debug('recursively calling schema.fields');
102104
finalizeSchema(type.fields, type, 'fields');
103105
// debug('recursively calling schema.types');
@@ -110,7 +112,7 @@ var finalizeSchema = function(schema, parent, tag) {
110112
type.unique = type.count === 0 ? 0 : 1;
111113
type.has_duplicates = type.count > 1;
112114
} else if (type.values) {
113-
type.unique = _.uniq(type.values, false, extractStringValueFromBSON).length;
115+
type.unique = new Set(type.values.map(extractStringValueFromBSON)).size;
114116
type.has_duplicates = type.unique !== type.values.length;
115117
}
116118
// compute `average_length` for array types
@@ -119,7 +121,7 @@ var finalizeSchema = function(schema, parent, tag) {
119121
}
120122
// recursively finalize fields and types
121123
});
122-
parent.types = _.sortByOrder(_.values(parent.types), 'probability', 'desc');
124+
parent.types = Object.values(parent.types).sort((a, b) => b.probability - a.probability);
123125
}
124126
return schema;
125127
};
@@ -146,31 +148,25 @@ module.exports = function parse(options) {
146148
/* eslint no-sync: 0 */
147149

148150
// set default options
149-
options = _.defaults({}, options, {
150-
semanticTypes: false,
151-
storeValues: true
152-
});
151+
options = { semanticTypes: false, storeValues: true, ...options};
153152

154153
var semanticTypes = require('./semantic-types');
155154

156-
if (_.isObject(options.semanticTypes)) {
155+
if (typeof options.semanticTypes === 'object') {
157156
// enable existing types that evaluate to true
158-
var enabledTypes = _(options.semanticTypes)
159-
.pick(function(val) {
160-
return _.isBoolean(val) && val;
161-
})
162-
.keys()
163-
.map(function(val) {
164-
return val.toLowerCase();
165-
})
166-
.value();
167-
semanticTypes = _.pick(semanticTypes, function(val, key) {
168-
return _.includes(enabledTypes, key.toLowerCase());
169-
});
170-
// merge with custom types that are functions
171-
semanticTypes = _.assign(semanticTypes,
172-
_.pick(options.semanticTypes, _.isFunction)
173-
);
157+
var enabledTypes = Object.entries(options.semanticTypes)
158+
.filter(([, v]) => typeof v === 'boolean' && v)
159+
.map(([k]) => k.toLowerCase());
160+
161+
semanticTypes = {...
162+
Object.entries(semanticTypes)
163+
.filter(([k]) => enabledTypes.includes(k.toLowerCase()))
164+
.reduce((p, [k, v]) => ({...p, [k]: v}), {}),
165+
};
166+
167+
Object.entries(options.semanticTypes)
168+
.filter(([, v]) => typeof v === 'function')
169+
.forEach(([k, v]) => {semanticTypes[k] = v;});
174170
}
175171

176172
var rootSchema = {
@@ -205,9 +201,13 @@ module.exports = function parse(options) {
205201

206202
var getSemanticType = function(value, path) {
207203
// pass value to semantic type detectors, return first match or undefined
208-
return _.findKey(semanticTypes, function(fn) {
209-
return fn(value, path);
210-
});
204+
205+
const returnValue = Object.entries(semanticTypes)
206+
.filter(([, v]) => {
207+
return v(value, path);
208+
})
209+
.map(([k]) => k)[0];
210+
return returnValue;
211211
};
212212

213213
/**
@@ -236,13 +236,13 @@ module.exports = function parse(options) {
236236
* @param {Object} schema the updated schema object
237237
*/
238238

239+
239240
var addToType = function(path, value, schema) {
240241
var bsonType = getBSONType(value);
241242
// if semantic type detection is enabled, the type is the semantic type
242243
// or the original bson type if no semantic type was detected. If disabled,
243244
// it is always the bson type.
244-
var typeName = (options.semanticTypes) ?
245-
getSemanticType(value, path) || bsonType : bsonType;
245+
var typeName = (options.semanticTypes) ? getSemanticType(value, path) || bsonType : bsonType;
246246
var type = schema[typeName] = _.get(schema, typeName, {
247247
name: typeName,
248248
bsonType: bsonType,
@@ -252,24 +252,22 @@ module.exports = function parse(options) {
252252
type.count++;
253253
// recurse into arrays by calling `addToType` for each element
254254
if (typeName === 'Array') {
255-
type.types = _.get(type, 'types', {});
256-
type.lengths = _.get(type, 'lengths', []);
255+
type.types = type.types || {};
256+
type.lengths = type.lengths || [];
257257
type.lengths.push(value.length);
258-
_.each(value, function(v) {
259-
addToType(path, v, type.types);
260-
});
258+
value.forEach(v => addToType(path, v, type.types));
261259

262260
// recurse into nested documents by calling `addToField` for all sub-fields
263261
} else if (typeName === 'Document') {
264262
type.fields = _.get(type, 'fields', {});
265-
_.forOwn(value, function(v, k) {
266-
addToField(path + '.' + k, v, type.fields);
267-
});
263+
Object.entries(value).forEach(([k, v]) => addToField(path + '.' + k, v, type.fields));
268264

269265
// if the `storeValues` option is enabled, store some example values
270266
} else if (options.storeValues) {
271-
type.values = _.get(type, 'values', bsonType === 'String' ?
272-
new Reservoir(100) : new Reservoir(10000));
267+
var defaultValue = bsonType === 'String' ?
268+
new Reservoir(100) : new Reservoir(10000);
269+
type.values = type.values || defaultValue;
270+
273271
addToValue(type, value);
274272
}
275273
};
@@ -284,8 +282,9 @@ module.exports = function parse(options) {
284282
addToField = function(path, value, schema) {
285283
var defaults = {};
286284

285+
var pathSplitOnDot = path.split('.');
287286
defaults[path] = {
288-
name: _.last(path.split('.')),
287+
name: pathSplitOnDot[pathSplitOnDot.length - 1],
289288
path: path,
290289
count: 0,
291290
types: {}
@@ -306,9 +305,7 @@ module.exports = function parse(options) {
306305
}
307306

308307
var parser = es.through(function write(obj) {
309-
_.each(_.keys(obj), function(key) {
310-
addToField(key, obj[key], rootSchema.fields);
311-
});
308+
Object.keys(obj).forEach(key => addToField(key, obj[key], rootSchema.fields));
312309
rootSchema.count += 1;
313310
this.emit('progress', obj);
314311
}, function end() {

0 commit comments

Comments
 (0)