Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handled collation param for case insensitive indexes #263

Merged
merged 6 commits into from
Oct 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,34 @@ Now in your query, you can specify a value for your discriminatorKey:

and Feathers will automatically swap in the correct model and execute the query it instead of its parent model.

## Collation Support

This adapter includes support for [collation and case insensitive indexes available in MongoDB v3.4](https://docs.mongodb.com/manual/release-notes/3.4/#collation-and-case-insensitive-indexes). Collation parameters may be passed using the special `collation` parameter to the `find()`, `remove()` and `patch()` methods.

### Example: Patch records with case-insensitive alphabetical ordering

The example below would patch all student records with grades of `'c'` or `'C'` and above (a natural language ordering). Without collations this would not be as simple, since the comparison `{ $gt: 'c' }` would not include uppercase grades of `'C'` because the code point of `'C'` is less than that of `'c'`.

```js
const patch = { shouldStudyMore: true };
const query = { grade: { $gte: 'c' } };
const collation = { locale: 'en', strength: 1 };
students.patch(null, patch, { query, collation }).then( ... );
```

### Example: Find records with a case-insensitive search

Similar to the above example, this would find students with a grade of `'c'` or greater, in a case-insensitive manner.

```js
const query = { grade: { $gte: 'c' } };
const collation = { locale: 'en', strength: 1 };
students.find({ query, collation }).then( ... );
```

For more information on MongoDB's collation feature, visit the [collation reference page](https://docs.mongodb.com/manual/reference/collation/).


## License

[MIT](LICENSE)
Expand Down
13 changes: 13 additions & 0 deletions lib/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ class Service {
q.sort(filters.$sort);
}

// Handle collation
if (params.collation) {
q.collation(params.collation);
}

// Handle $limit
if (typeof filters.$limit !== 'undefined') {
q.limit(filters.$limit);
Expand Down Expand Up @@ -232,6 +237,10 @@ class Service {
const query = Object.assign({}, filterQuery(params.query || {}).query);
const mapIds = page => page.data.map(current => current[this.id]);

if (params.collation) {
query.collation = params.collation;
}

// By default we will just query for the one id. For multi patch
// we create a list of the ids of all items that will be changed
// to re-query them after the update
Expand Down Expand Up @@ -303,6 +312,10 @@ class Service {
remove (id, params) {
const query = Object.assign({}, filterQuery(params.query || {}).query);

if (params.collation) {
query.collation = params.collation;
}

if (id !== null) {
query[this.id] = id;
}
Expand Down
2 changes: 1 addition & 1 deletion test/error-handler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ describe('Feathers Mongoose Error Handler', () => {
});

it('wraps a VersionError as a BadRequest', done => {
let e = new mongoose.Error.VersionError({ _id: 'testing' });
let e = new mongoose.Error.VersionError({ _id: 'testing' }, null, []);
errorHandler(e).catch(error => {
expect(error).to.be.an.instanceof(errors.BadRequest);
done();
Expand Down
92 changes: 92 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,98 @@ describe('Feathers Mongoose Service', () => {
});
});

describe('Special collation param', () => {
function indexOfName (results, name) {
let index;
results.every(function (person, i) {
if (person.name === name) {
index = i;
return false;
}
return true;
});
return index;
}

beforeEach(() => {
return people.remove(null, {}).then(() => {
return people.create([
{ name: 'AAA' },
{ name: 'aaa' },
{ name: 'ccc' }
]);
});
});

it('sorts with default behavior without collation param', () => {
return people.find({ query: { $sort: { name: -1 } } }).then(r => {
expect(indexOfName(r, 'aaa')).to.be.below(indexOfName(r, 'AAA'));
});
});

it('sorts using collation param if present', () => {
return people
.find({
query: { $sort: { name: -1 } },
collation: { locale: 'en', strength: 1 }
})
.then(r => {
expect(indexOfName(r, 'AAA')).to.be.below(indexOfName(r, 'aaa'));
});
});

it('removes with default behavior without collation param', () => {
return people
.remove(null, { query: { name: { $gt: 'AAA' } } })
.then(() => {
return people.find().then(r => {
expect(r).to.have.lengthOf(1);
expect(r[0].name).to.equal('AAA');
});
});
});

it('removes using collation param if present', () => {
return people
.remove(null, {
query: { name: { $gt: 'AAA' } },
collation: { locale: 'en', strength: 1 }
})
.then(() => {
return people.find().then(r => {
expect(r).to.have.lengthOf(3);
});
});
});

it('updates with default behavior without collation param', () => {
const query = { name: { $gt: 'AAA' } };

return people.patch(null, { age: 99 }, { query }).then(r => {
expect(r).to.have.lengthOf(2);
r.forEach(person => {
expect(person.age).to.equal(99);
});
});
});

it('updates using collation param if present', () => {
return people
.patch(
null,
{ age: 110 },
{
query: { name: { $gt: 'AAA' } },
collation: { locale: 'en', strength: 1 }
}
)
.then(r => {
expect(r).to.have.lengthOf(1);
expect(r[0].name).to.equal('ccc');
});
});
});

describe('Common functionality', () => {
beforeEach(() => {
// FIXME (EK): This is shit. We should be loading fixtures
Expand Down