Skip to content

Commit 37d178c

Browse files
committed
fix(Indexes): Removed specific indexes and generate clean field names
Helpers `sort` and `filter` may create not valid typename with dot. Also removed `text`, `2d` and others from creating sorting/filtering fields. Cause they have specific query syntax and should be added by user manually.
1 parent ab3e46f commit 37d178c

File tree

4 files changed

+115
-13
lines changed

4 files changed

+115
-13
lines changed

src/resolvers/helpers/filter.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ export function getIndexedFieldNames(model: MongooseModelT): string[] {
142142
const fieldNames = [];
143143
indexes.forEach((indexData) => {
144144
const keys = Object.keys(indexData);
145-
fieldNames.push(keys[0]);
145+
const clearedName = keys[0].replace(/[^_a-zA-Z0-9]/i, '__');
146+
fieldNames.push(clearedName);
146147
});
147148

148149
return fieldNames;

src/resolvers/helpers/sort.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export function getSortTypeFromModel(
5454
const sortEnumValues = {};
5555
indexes.forEach((indexData) => {
5656
const keys = Object.keys(indexData);
57-
let name = keys.join('__').toUpperCase().replace('.', '__');
57+
let name = keys.join('__').toUpperCase().replace(/[^_a-zA-Z0-9]/i, '__');
5858
if (indexData[keys[0]] === 1) {
5959
name = `${name}_ASC`;
6060
} else if (indexData[keys[0]] === -1) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { expect } from 'chai';
2+
import mongoose from 'mongoose';
3+
import getIndexesFromModel from '../getIndexesFromModel';
4+
5+
const AgentSchema = new mongoose.Schema(
6+
{
7+
subDoc: {
8+
field1: String,
9+
field2: {
10+
field21: String,
11+
field22: String,
12+
},
13+
},
14+
15+
name: {
16+
type: String,
17+
description: 'Person name',
18+
},
19+
20+
age: {
21+
type: Number,
22+
description: 'Full years',
23+
},
24+
25+
gender: {
26+
type: String,
27+
enum: ['male', 'female', 'ladyboy'],
28+
},
29+
30+
skills: {
31+
type: [String],
32+
default: [],
33+
description: 'List of skills',
34+
},
35+
36+
relocation: {
37+
type: Boolean,
38+
description: 'Does candidate relocate to another region',
39+
},
40+
}
41+
);
42+
43+
AgentSchema.set('autoIndex', false);
44+
AgentSchema.index({ name: 1, age: -1 });
45+
AgentSchema.index({ 'subDoc.field2': 1 });
46+
AgentSchema.index({ name: 'text', skills: 'text' });
47+
48+
const AgentModel = mongoose.model('Agent', AgentSchema);
49+
50+
describe('getIndexesFromModel', () => {
51+
it('should get regular indexes and extract compound idx by default', () => {
52+
const idx = getIndexesFromModel(AgentModel);
53+
expect(idx).to.deep.have.all.members([
54+
{ _id: 1 },
55+
{ name: 1 },
56+
{ name: 1, age: -1 },
57+
{ 'subDoc.field2': 1 },
58+
]);
59+
});
60+
61+
it('should not extract compound indexes', () => {
62+
const idx = getIndexesFromModel(AgentModel, { extractCompound: false });
63+
expect(idx).to.deep.have.all.members([
64+
{ _id: 1 },
65+
{ name: 1, age: -1 },
66+
{ 'subDoc.field2': 1 },
67+
]);
68+
});
69+
70+
it('it should return specialIndexes indexes', () => {
71+
const idx = getIndexesFromModel(AgentModel, { skipSpecificIndexes : false });
72+
expect(idx).to.deep.have.all.members([
73+
{ _id: 1 },
74+
{ name: 1 },
75+
{ name: 1, age: -1 },
76+
{ 'subDoc.field2': 1 },
77+
{ name: 'text' },
78+
{ name: 'text', skills: 'text' },
79+
]);
80+
});
81+
});

src/utils/getIndexesFromModel.js

+31-11
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ import type {
77

88
export type getIndexesFromModelOpts = {
99
extractCompound?: boolean, // true by default
10+
skipSpecificIndeces?: boolean, // eg text, 2d, 2dsphere (true by default)
11+
}
12+
13+
function isSpecificIndex(idx) {
14+
let hasSpecialIndex = false;
15+
Object.keys(idx).forEach((k) => {
16+
if (typeof idx[k] !== 'number'
17+
&& typeof idx[k] !== 'boolean') {
18+
hasSpecialIndex = true;
19+
}
20+
});
21+
return hasSpecialIndex;
1022
}
1123

1224
/**
@@ -17,7 +29,12 @@ export default function getIndexesFromModel(
1729
mongooseModel: MongooseModelT,
1830
opts: getIndexesFromModelOpts = {}
1931
): ObjectMap[] {
20-
const extractCompound = opts.extractCompound || true;
32+
const extractCompound = opts.extractCompound === undefined
33+
? true
34+
: Boolean(opts.extractCompound);
35+
const skipSpecificIndexes = opts.skipSpecificIndexes === undefined
36+
? true
37+
: Boolean(opts.skipSpecificIndexes);
2138

2239
const indexedFields = [];
2340

@@ -33,20 +50,23 @@ export default function getIndexesFromModel(
3350
}
3451
});
3552

36-
// scan compound indexes [MONGOOSE SCHEMA LEVEL INDEXES]
53+
// scan compound and special indexes [MONGOOSE SCHEMA LEVEL INDEXES]
3754
if (Array.isArray(mongooseModel.schema._indexes)) {
3855
mongooseModel.schema._indexes.forEach((idxData) => {
3956
const partialIndexes = {};
4057
const idxFields = idxData[0];
41-
if (!extractCompound) {
42-
indexedFields.push(idxFields);
43-
} else {
44-
// extract partial indexes from compound index
45-
// { name: 1, age: 1, salary: 1} -> [{name:1}, {name:1, age:1}, {name:1, age:1, salary:1}]
46-
Object.keys(idxFields).forEach((fieldName) => {
47-
partialIndexes[fieldName] = idxFields[fieldName];
48-
indexedFields.push(Object.assign({}, partialIndexes));
49-
});
58+
59+
if (!skipSpecificIndexes || !isSpecificIndex(idxFields)) {
60+
if (!extractCompound) {
61+
indexedFields.push(idxFields);
62+
} else {
63+
// extract partial indexes from compound index
64+
// { name: 1, age: 1, salary: 1} -> [{name:1}, {name:1, age:1}, {name:1, age:1, salary:1}]
65+
Object.keys(idxFields).forEach((fieldName) => {
66+
partialIndexes[fieldName] = idxFields[fieldName];
67+
indexedFields.push(Object.assign({}, partialIndexes));
68+
});
69+
}
5070
}
5171
});
5272
}

0 commit comments

Comments
 (0)