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

Support for Full Text Search Query #541

Merged
merged 3 commits into from
Jan 15, 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
2 changes: 1 addition & 1 deletion integration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"dependencies": {
"express": "^4.13.4",
"mocha": "^2.4.5",
"parse-server": "^2.7.0"
"parse-server": "^2.7.1"
},
"scripts": {
"test": "mocha --reporter dot -t 5000"
Expand Down
67 changes: 62 additions & 5 deletions integration/test/ParseQueryTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ describe('Parse Query', () => {
assert.equal(results[2].get('string'), 'd');
assert.equal(results[3].get('number'), 1);
assert.equal(results[3].get('string'), 'b');

let query = new Parse.Query(TestObject);
query.equalTo('doubleDescending', true);
query.descending('number, string');
Expand Down Expand Up @@ -608,7 +608,7 @@ describe('Parse Query', () => {
assert.equal(results[2].get('string'), 'd');
assert.equal(results[3].get('number'), 1);
assert.equal(results[3].get('string'), 'b');

let query = new Parse.Query(TestObject);
query.equalTo('doubleDescending', true);
query.descending('number', 'string');
Expand All @@ -623,7 +623,7 @@ describe('Parse Query', () => {
assert.equal(results[2].get('string'), 'd');
assert.equal(results[3].get('number'), 1);
assert.equal(results[3].get('string'), 'b');

done();
});
});
Expand Down Expand Up @@ -760,7 +760,7 @@ describe('Parse Query', () => {
assert.equal(results.length, 2);
assert.equal(results[0].id, objects[0].id);
assert.equal(results[1].id, objects[1].id);

let query = new Parse.Query('TestObject');
query.equalTo('timed2', true);
query.greaterThan('createdAt', objects[2].createdAt);
Expand Down Expand Up @@ -1210,7 +1210,7 @@ describe('Parse Query', () => {
}).then((results) => {
assert.equal(results.length, 1);
assert.equal(results[0].get('name'), 'Bob');

let query = new Parse.Query(Restaurant);
query.greaterThan('rating', 4);
let mainQuery = new Parse.Query(Person);
Expand Down Expand Up @@ -1426,4 +1426,61 @@ describe('Parse Query', () => {
done();
});
});

it('full text search', (done) => {
const subjects = [
'coffee',
'Coffee Shopping',
'Baking a cake',
'baking',
'Café Con Leche',
'Сырники',
'coffee and cream',
'Cafe con Leche',
];
const objects = [];
for (const i in subjects) {
const obj = new TestObject({ subject: subjects[i] });
objects.push(obj);
}
Parse.Object.saveAll(objects).then(() => {
const q = new Parse.Query(TestObject);
q.fullText('subject', 'coffee');
return q.find();
}).then((results) => {
assert.equal(results.length, 3);
done();
});
});

it('full text search sort', (done) => {
const subjects = [
'coffee',
'Coffee Shopping',
'Baking a cake',
'baking',
'Café Con Leche',
'Сырники',
'coffee and cream',
'Cafe con Leche',
];
const objects = [];
for (const i in subjects) {
const obj = new TestObject({ comment: subjects[i] });
objects.push(obj);
}
Parse.Object.saveAll(objects).then(() => {
const q = new Parse.Query(TestObject);
q.fullText('comment', 'coffee');
q.ascending('$score');
q.select('$score');
return q.find();
}).then((results) => {
assert.equal(results.length, 3);
assert.equal(results[0].get('score'), 1);
assert.equal(results[1].get('score'), 0.75);
assert.equal(results[2].get('score'), 0.75);
done();
});
});
});
10 changes: 5 additions & 5 deletions src/ParsePolygon.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import ParseGeoPoint from './ParseGeoPoint';
* new Polygon([[0,0],[0,1],[1,1],[1,0]])
* new Polygon([GeoPoint, GeoPoint, GeoPoint])
* </pre>
*
*
* <p>Represents a coordinates that may be associated
* with a key in a ParseObject or used as a reference point for geo queries.
* This allows proximity-based queries on the key.</p>
Expand Down Expand Up @@ -56,7 +56,7 @@ class ParsePolygon {
}

/**
* Returns a JSON representation of the GeoPoint, suitable for Parse.
* Returns a JSON representation of the Polygon, suitable for Parse.
* @return {Object}
*/
toJSON(): { __type: string; coordinates: Array;} {
Expand Down Expand Up @@ -89,9 +89,9 @@ class ParsePolygon {
}

/**
*
* @param {Parse.GeoPoint} point
* @returns {Boolean} wether the points is contained into the polygon
*
* @param {Parse.GeoPoint} point
* @returns {Boolean} Returns if the point is contained in the polygon
*/
containsPoint(point: ParseGeoPoint): boolean {
let minX = this._coordinates[0][0];
Expand Down
34 changes: 34 additions & 0 deletions src/ParseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,40 @@ class ParseQuery {
return this._addCondition(key, '$regex', quote(value));
}

/**
* Adds a constraint for finding string values that contain a provided
* string. This may be slow for large datasets. Requires Parse-Server > 2.5.0
*
* In order to sort you must use select and ascending ($score is required)
* <pre>
* query.fullText('term');
* query.ascending('$score');
* query.select('$score');
* </pre>
*
* To retrieve the weight / rank
* <pre>
* object->get('score');
* </pre>
*
* @param {String} key The key that the string to match is stored in.
* @param {String} value The string to search
* @return {Parse.Query} Returns the query, so you can chain this call.
*/
fullText(key: string, value: string): ParseQuery {
if (!key) {
throw new Error('A key is required.');
}
if (!value) {
throw new Error('A search term is required');
}
if (typeof value !== 'string') {
throw new Error('The value being searched for must be a string.');
}

return this._addCondition(key, '$text', { $search: { $term: value } });
}

/**
* Adds a constraint for finding string values that start with a provided
* string. This query will use the backend index, so it will be fast even
Expand Down
55 changes: 55 additions & 0 deletions src/__tests__/ParseQuery-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1864,4 +1864,59 @@ describe('ParseQuery', () => {
});
});

it('full text search', () => {
const query = new ParseQuery('Item');
query.fullText('size', 'small');

expect(query.toJSON()).toEqual({
where: {
size: {
$text: {
$search: {
$term: "small"
}
}
}
}
});
});

it('full text search sort', () => {
const query = new ParseQuery('Item');
query.fullText('size', 'medium');
query.ascending('$score');
query.select('$score');

expect(query.toJSON()).toEqual({
where: {
size: {
$text: {
$search: {
$term: "medium",
}
}
}
},
keys : "$score",
order : "$score"
});
});

it('full text search key required', (done) => {
const query = new ParseQuery('Item');
expect(() => query.fullText()).toThrow('A key is required.');
done();
});

it('full text search value required', (done) => {
const query = new ParseQuery('Item');
expect(() => query.fullText('key')).toThrow('A search term is required');
done();
});

it('full text search value must be string', (done) => {
const query = new ParseQuery('Item');
expect(() => query.fullText('key', [])).toThrow('The value being searched for must be a string.');
done();
});
});
11 changes: 0 additions & 11 deletions src/__tests__/ParseSchema-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,6 @@ var ParseSchema = require('../ParseSchema').default;
var ParsePromise = require('../ParsePromise').default;
var CoreManager = require('../CoreManager');

function generateSaveMock(prefix) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused?

Copy link
Member Author

@dplewis dplewis Jan 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I copied the jest test from another file when I added schema support. Forgot I left those in 😅

return function(name, payload) {
return ParsePromise.as({
name: name,
url: prefix + name
});
};
}

var defaultController = CoreManager.getSchemaController();

describe('ParseSchema', () => {
Expand Down Expand Up @@ -388,11 +379,9 @@ describe('SchemaController', () => {
beforeEach(() => {
CoreManager.setSchemaController(defaultController);
var request = function(method, path, data, options) {
var name = path.substr(path.indexOf('/') + 1);
return ParsePromise.as([]);
};
var ajax = function(method, path, data, headers) {
var name = path.substr(path.indexOf('/') + 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused here as well?

return ParsePromise.as([]);
};
CoreManager.setRESTController({ request: request, ajax: ajax });
Expand Down