Skip to content

Commit 87ac280

Browse files
rueckstiesspzrq
authored andcommitted
COMPASS-287: distinct numeric types (#584)
* COMPASS-287 upgrade old language-model to pegjs * fix bar selection rounding bug for negative, very large numbers and 0. * don’t promote values. * allow dotted field names and fix type switching bug * remove debug statements. * handle number ranges correctly for unpromoted bson types * fix query building for negative long values.
1 parent 7772941 commit 87ac280

File tree

16 files changed

+171
-145
lines changed

16 files changed

+171
-145
lines changed

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
"d3-timer": "^1.0.3",
9999
"debug": "mongodb-js/debug#v2.2.3",
100100
"debug-menu": "^0.3.0",
101-
"detect-coordinates": "^0.1.0",
101+
"detect-coordinates": "^0.2.0",
102102
"electron-squirrel-startup": "^1.0.0",
103103
"font-awesome": "https://github.com/FortAwesome/Font-Awesome/archive/v4.4.0.tar.gz",
104104
"get-object-path": "azer/get-object-path#74eb42de0cfd02c14ffdd18552f295aba723d394",
@@ -125,15 +125,15 @@
125125
"mongodb": "^2.2.8",
126126
"mongodb-collection-model": "^0.3.1",
127127
"mongodb-connection-model": "^6.3.1",
128-
"mongodb-data-service": "^2.1.0",
128+
"mongodb-data-service": "^2.1.1",
129129
"mongodb-database-model": "^0.1.2",
130130
"mongodb-explain-plan-model": "^0.2.2",
131-
"mongodb-extended-json": "^1.7.0",
131+
"mongodb-extended-json": "^1.8.0",
132132
"mongodb-instance-model": "^3.3.0",
133133
"mongodb-js-metrics": "^1.5.2",
134-
"mongodb-language-model": "^0.3.3",
134+
"mongodb-language-model": "^1.0.2",
135135
"mongodb-ns": "^1.0.3",
136-
"mongodb-schema": "^5.0.0",
136+
"mongodb-schema": "^6.0.2",
137137
"ms": "^0.7.1",
138138
"ncp": "^2.0.0",
139139
"numeral": "^1.5.3",

src/app/home/collection.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ var MongoDBCollectionView = View.extend({
213213
this.loadIndexesAction();
214214
this.fetchExplainPlanAction();
215215
});
216-
Action.filterChanged(app.queryOptions.query.serialize());
216+
Action.filterChanged(app.queryOptions.query);
217217
this.switchView(this.activeView);
218218
},
219219
onCollectionFetched: function(model) {

src/app/models/editable-query.js

Lines changed: 0 additions & 72 deletions
This file was deleted.

src/app/models/query-options.js

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
var ms = require('ms');
22
var Model = require('ampersand-model');
33
var EJSON = require('mongodb-extended-json');
4-
var Query = require('mongodb-language-model').Query;
54
// var debug = require('debug')('mongodb-compass:models:query-options');
65

76
var DEFAULT_QUERY = {};
@@ -12,12 +11,6 @@ var DEFAULT_SIZE = 1000;
1211
var DEFAULT_SKIP = 0;
1312
var DEFAULT_MAX_TIME_MS = ms('10 seconds');
1413

15-
var getDefaultQuery = function() {
16-
return new Query(DEFAULT_QUERY, {
17-
parse: true
18-
});
19-
};
20-
2114
/**
2215
* Options for reading a collection of documents from MongoDB.
2316
*/
@@ -26,7 +19,7 @@ module.exports = Model.extend({
2619
query: {
2720
type: 'state',
2821
default: function() {
29-
return getDefaultQuery();
22+
return DEFAULT_QUERY;
3023
}
3124
},
3225
sort: {
@@ -53,18 +46,18 @@ module.exports = Model.extend({
5346
deps: ['query'],
5447
cache: false,
5548
fn: function() {
56-
return EJSON.stringify(this.query.serialize());
49+
return EJSON.stringify(this.query);
5750
}
5851
}
5952
},
6053
serialize: function() {
6154
var res = Model.prototype.serialize.call(this);
62-
res.query = this.query.serialize();
55+
res.query = this.query;
6356
return res;
6457
},
6558
reset: function() {
6659
this.set({
67-
query: getDefaultQuery(),
60+
query: DEFAULT_QUERY,
6861
sort: DEFAULT_SORT,
6962
size: DEFAULT_SIZE,
7063
skip: DEFAULT_SKIP,

src/internal-packages/crud/lib/store/load-more-documents-store.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const LoadMoreDocumentsStore = Reflux.createStore({
2727
* @param {Integer} skip - The number of documents to skip.
2828
*/
2929
loadMoreDocuments: function(skip) {
30-
const filter = app.queryOptions.query.serialize();
30+
const filter = app.queryOptions.query;
3131
const options = { skip: skip, limit: 20, sort: [[ '_id', 1 ]], readPreference: READ };
3232
app.dataService.find(NamespaceStore.ns, filter, options, (error, documents) => {
3333
if (!error) {

src/internal-packages/indexes/lib/component/create-index-text-field.jsx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const React = require('react');
22
const Action = require('../action/index-actions');
3-
const Query = require('mongodb-language-model').Query;
3+
const accepts = require('mongodb-language-model').accepts;
44
const EJSON = require('mongodb-extended-json');
55
const _ = require('lodash');
66

@@ -69,20 +69,17 @@ class CreateIndexTextField extends React.Component {
6969
* @returns {Object|Boolean} false if invalid, otherwise the query
7070
*/
7171
_validateQueryString(queryString) {
72-
let parsed;
7372
try {
74-
// is it valid eJSON?
7573
const cleaned = this._cleanQueryString(queryString);
76-
parsed = EJSON.parse(cleaned);
77-
// is it a valid parsable Query according to the language?
78-
/* eslint no-unused-vars: 0 */
79-
const query = new Query(parsed, {
80-
parse: true
81-
});
74+
// is it valid eJSON?
75+
const parsed = EJSON.parse(cleaned);
76+
// can it be serialized to JSON?
77+
const stringified = JSON.stringify(parsed);
78+
// is it a valid MongoDB query according to the language?
79+
return accepts(stringified);
8280
} catch (e) {
8381
return false;
8482
}
85-
return parsed;
8683
}
8784

8885
/**
@@ -107,7 +104,7 @@ class CreateIndexTextField extends React.Component {
107104
if (this.props.units) inputClassName += ' inline-option-field';
108105
if (this.props.option === 'partialFilterExpression') {
109106
inputClassName += ' partial-filter-input';
110-
const valid = Boolean(this._validateQueryString(this.state.value));
107+
const valid = this._validateQueryString(this.state.value);
111108
if (!valid) groupClassName += ' has-error';
112109
}
113110
return (

src/internal-packages/query/lib/store/index.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const NamespaceStore = require('hadron-reflux-store').NamespaceStore;
44
const StateMixin = require('reflux-state-mixin');
55
const QueryAction = require('../action');
66
const EJSON = require('mongodb-extended-json');
7-
const Query = require('mongodb-language-model').Query;
7+
const accepts = require('mongodb-language-model').accepts;
88
const _ = require('lodash');
99
const hasDistinctValue = require('../util').hasDistinctValue;
1010
const filterChanged = require('hadron-action').filterChanged;
@@ -115,23 +115,18 @@ const QueryStore = Reflux.createStore({
115115
* validates whether a string is a valid query.
116116
*
117117
* @param {Object} queryString a string to validate
118-
* @return {Object|Boolean} false if invalid, otherwise the query
118+
* @returns {Object|Boolean} false if invalid, otherwise the query
119119
*/
120120
_validateQueryString(queryString) {
121-
let parsed;
122121
try {
123-
// is it valid eJSON?
124122
const cleaned = this._cleanQueryString(queryString);
125-
parsed = EJSON.parse(cleaned);
126-
// is it a valid parsable Query according to the language?
127-
/* eslint no-unused-vars: 0 */
128-
const query = new Query(parsed, {
129-
parse: true
130-
});
123+
// is it valid eJSON?
124+
const parsed = EJSON.parse(cleaned);
125+
// is it a valid MongoDB query according to the language?
126+
return accepts(cleaned) ? parsed : false;
131127
} catch (e) {
132128
return false;
133129
}
134-
return parsed;
135130
},
136131

137132
_validateFeatureFlag(queryString) {

src/internal-packages/query/lib/util/index.js

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
const _ = require('lodash');
2-
// const debug = require('debug')('mongodb-compass:schema:test');
2+
// const debug = require('debug')('mongodb-compass:query:utils');
33

44
function bsonEqual(value, other) {
55
const bsontype = _.get(value, '_bsontype', undefined);
66
if (bsontype === 'ObjectID') {
77
return value.equals(other);
88
}
9+
if (_.includes(['Decimal128', 'Long'], bsontype)) {
10+
return value.toString() === other.toString();
11+
}
12+
if (_.includes(['Int32', 'Double'], bsontype)) {
13+
return value.value === other.value;
14+
}
915
// for all others, use native comparisons
1016
return undefined;
1117
}
@@ -27,7 +33,9 @@ function hasDistinctValue(field, value) {
2733
if (_.has(field, '$in')) {
2834
// check if $in array contains the value
2935
const inArray = field.$in;
30-
return (_.contains(inArray, value));
36+
return (_.some(inArray, (other) => {
37+
return _.isEqual(value, other, bsonEqual);
38+
}));
3139
}
3240
}
3341
// it is not a $in operator, check value directly
@@ -111,8 +119,10 @@ function inValueRange(field, d) {
111119
}
112120
const dx = _.get(d, 'dx', null);
113121

114-
// extract bound(s)
115-
const bounds = dx === null ? [d.value] : _.uniq([d.value, d.value + dx]);
122+
// extract bound(s): if a dx value is set, create a 2-element array of
123+
// upper and lower bound. otherwise create a single value array of the
124+
// original bson type (if available) or the extracted value.
125+
const bounds = dx ? _.uniq([d.value, d.value + dx]) : [d.bson || d.value];
116126

117127
/*
118128
* Logic to determine if the query covers the value (or value range)
@@ -129,10 +139,10 @@ function inValueRange(field, d) {
129139
*/
130140
const results = _.map(bounds, function(bound, i) {
131141
// adjust the upper bound slightly as it represents an exclusive bound
132-
// getting this right would require a lot more code to check for all 4
133-
// edge cases.
142+
// getting this perfectly right would require a lot more code to check for
143+
// all 4 edge cases.
134144
if (i > 0) {
135-
bound *= 0.999999;
145+
bound -= (0.00001 * Math.abs(bound)) + 0.00001;
136146
}
137147
return _.every(_.map(conditions, function(cond) {
138148
return cond(bound);

src/internal-packages/query/test/index.test.js

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/internal-packages/schema/lib/component/minichart.jsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ const D3Component = require('./d3component');
88
const vizFns = require('../d3');
99
const Actions = require('../action');
1010

11-
const STRING = 'String';
12-
const NUMBER = 'Number';
13-
const DECIMAL_128 = 'Decimal128';
11+
const { STRING, DECIMAL_128, DOUBLE, LONG, INT_32 } = require('../helpers');
1412

1513
const Minichart = React.createClass({
1614

@@ -72,17 +70,20 @@ const Minichart = React.createClass({
7270
},
7371

7472
minichartFactory() {
75-
/* eslint camelcase: 0 */
76-
const typeName = this.props.type.name;
73+
// cast all numeric types to Number minichart
74+
const typeName = _.includes([ DECIMAL_128, DOUBLE, INT_32 ],
75+
this.props.type.name) ? 'Number' : this.props.type.name;
76+
7777
const fieldName = this.props.fieldName;
7878
const queryClause = this.state.query[fieldName];
79-
const has_duplicates = this.props.type.has_duplicates;
79+
const hasDuplicates = this.props.type.has_duplicates;
8080
const fn = vizFns[typeName.toLowerCase()];
8181
const width = this.state.containerWidth;
8282

83-
if (_.includes([ STRING, NUMBER, DECIMAL_128 ], typeName) && !has_duplicates) {
83+
if (_.includes([ STRING, LONG ], typeName) && !hasDuplicates) {
8484
return (
8585
<UniqueMinichart
86+
key={typeName}
8687
fieldName={fieldName}
8788
query={queryClause}
8889
type={this.props.type}

0 commit comments

Comments
 (0)