Skip to content

Commit 7ef1b0a

Browse files
authored
Merge pull request #541 from dplewis/full-text-search
Support for Full Text Search Query
2 parents ee33c48 + 3cd3f7c commit 7ef1b0a

File tree

6 files changed

+157
-22
lines changed

6 files changed

+157
-22
lines changed

integration/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"dependencies": {
44
"express": "^4.13.4",
55
"mocha": "^2.4.5",
6-
"parse-server": "^2.7.0"
6+
"parse-server": "^2.7.1"
77
},
88
"scripts": {
99
"test": "mocha --reporter dot -t 5000"

integration/test/ParseQueryTest.js

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ describe('Parse Query', () => {
578578
assert.equal(results[2].get('string'), 'd');
579579
assert.equal(results[3].get('number'), 1);
580580
assert.equal(results[3].get('string'), 'b');
581-
581+
582582
let query = new Parse.Query(TestObject);
583583
query.equalTo('doubleDescending', true);
584584
query.descending('number, string');
@@ -608,7 +608,7 @@ describe('Parse Query', () => {
608608
assert.equal(results[2].get('string'), 'd');
609609
assert.equal(results[3].get('number'), 1);
610610
assert.equal(results[3].get('string'), 'b');
611-
611+
612612
let query = new Parse.Query(TestObject);
613613
query.equalTo('doubleDescending', true);
614614
query.descending('number', 'string');
@@ -623,7 +623,7 @@ describe('Parse Query', () => {
623623
assert.equal(results[2].get('string'), 'd');
624624
assert.equal(results[3].get('number'), 1);
625625
assert.equal(results[3].get('string'), 'b');
626-
626+
627627
done();
628628
});
629629
});
@@ -760,7 +760,7 @@ describe('Parse Query', () => {
760760
assert.equal(results.length, 2);
761761
assert.equal(results[0].id, objects[0].id);
762762
assert.equal(results[1].id, objects[1].id);
763-
763+
764764
let query = new Parse.Query('TestObject');
765765
query.equalTo('timed2', true);
766766
query.greaterThan('createdAt', objects[2].createdAt);
@@ -1210,7 +1210,7 @@ describe('Parse Query', () => {
12101210
}).then((results) => {
12111211
assert.equal(results.length, 1);
12121212
assert.equal(results[0].get('name'), 'Bob');
1213-
1213+
12141214
let query = new Parse.Query(Restaurant);
12151215
query.greaterThan('rating', 4);
12161216
let mainQuery = new Parse.Query(Person);
@@ -1426,4 +1426,61 @@ describe('Parse Query', () => {
14261426
done();
14271427
});
14281428
});
1429+
1430+
it('full text search', (done) => {
1431+
const subjects = [
1432+
'coffee',
1433+
'Coffee Shopping',
1434+
'Baking a cake',
1435+
'baking',
1436+
'Café Con Leche',
1437+
'Сырники',
1438+
'coffee and cream',
1439+
'Cafe con Leche',
1440+
];
1441+
const objects = [];
1442+
for (const i in subjects) {
1443+
const obj = new TestObject({ subject: subjects[i] });
1444+
objects.push(obj);
1445+
}
1446+
Parse.Object.saveAll(objects).then(() => {
1447+
const q = new Parse.Query(TestObject);
1448+
q.fullText('subject', 'coffee');
1449+
return q.find();
1450+
}).then((results) => {
1451+
assert.equal(results.length, 3);
1452+
done();
1453+
});
1454+
});
1455+
1456+
it('full text search sort', (done) => {
1457+
const subjects = [
1458+
'coffee',
1459+
'Coffee Shopping',
1460+
'Baking a cake',
1461+
'baking',
1462+
'Café Con Leche',
1463+
'Сырники',
1464+
'coffee and cream',
1465+
'Cafe con Leche',
1466+
];
1467+
const objects = [];
1468+
for (const i in subjects) {
1469+
const obj = new TestObject({ comment: subjects[i] });
1470+
objects.push(obj);
1471+
}
1472+
Parse.Object.saveAll(objects).then(() => {
1473+
const q = new Parse.Query(TestObject);
1474+
q.fullText('comment', 'coffee');
1475+
q.ascending('$score');
1476+
q.select('$score');
1477+
return q.find();
1478+
}).then((results) => {
1479+
assert.equal(results.length, 3);
1480+
assert.equal(results[0].get('score'), 1);
1481+
assert.equal(results[1].get('score'), 0.75);
1482+
assert.equal(results[2].get('score'), 0.75);
1483+
done();
1484+
});
1485+
});
14291486
});

src/ParsePolygon.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import ParseGeoPoint from './ParseGeoPoint';
1717
* new Polygon([[0,0],[0,1],[1,1],[1,0]])
1818
* new Polygon([GeoPoint, GeoPoint, GeoPoint])
1919
* </pre>
20-
*
20+
*
2121
* <p>Represents a coordinates that may be associated
2222
* with a key in a ParseObject or used as a reference point for geo queries.
2323
* This allows proximity-based queries on the key.</p>
@@ -56,7 +56,7 @@ class ParsePolygon {
5656
}
5757

5858
/**
59-
* Returns a JSON representation of the GeoPoint, suitable for Parse.
59+
* Returns a JSON representation of the Polygon, suitable for Parse.
6060
* @return {Object}
6161
*/
6262
toJSON(): { __type: string; coordinates: Array;} {
@@ -89,9 +89,9 @@ class ParsePolygon {
8989
}
9090

9191
/**
92-
*
93-
* @param {Parse.GeoPoint} point
94-
* @returns {Boolean} wether the points is contained into the polygon
92+
*
93+
* @param {Parse.GeoPoint} point
94+
* @returns {Boolean} Returns if the point is contained in the polygon
9595
*/
9696
containsPoint(point: ParseGeoPoint): boolean {
9797
let minX = this._coordinates[0][0];

src/ParseQuery.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,40 @@ class ParseQuery {
954954
return this._addCondition(key, '$regex', quote(value));
955955
}
956956

957+
/**
958+
* Adds a constraint for finding string values that contain a provided
959+
* string. This may be slow for large datasets. Requires Parse-Server > 2.5.0
960+
*
961+
* In order to sort you must use select and ascending ($score is required)
962+
* <pre>
963+
* query.fullText('term');
964+
* query.ascending('$score');
965+
* query.select('$score');
966+
* </pre>
967+
*
968+
* To retrieve the weight / rank
969+
* <pre>
970+
* object->get('score');
971+
* </pre>
972+
*
973+
* @param {String} key The key that the string to match is stored in.
974+
* @param {String} value The string to search
975+
* @return {Parse.Query} Returns the query, so you can chain this call.
976+
*/
977+
fullText(key: string, value: string): ParseQuery {
978+
if (!key) {
979+
throw new Error('A key is required.');
980+
}
981+
if (!value) {
982+
throw new Error('A search term is required');
983+
}
984+
if (typeof value !== 'string') {
985+
throw new Error('The value being searched for must be a string.');
986+
}
987+
988+
return this._addCondition(key, '$text', { $search: { $term: value } });
989+
}
990+
957991
/**
958992
* Adds a constraint for finding string values that start with a provided
959993
* string. This query will use the backend index, so it will be fast even

src/__tests__/ParseQuery-test.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1864,4 +1864,59 @@ describe('ParseQuery', () => {
18641864
});
18651865
});
18661866

1867+
it('full text search', () => {
1868+
const query = new ParseQuery('Item');
1869+
query.fullText('size', 'small');
1870+
1871+
expect(query.toJSON()).toEqual({
1872+
where: {
1873+
size: {
1874+
$text: {
1875+
$search: {
1876+
$term: "small"
1877+
}
1878+
}
1879+
}
1880+
}
1881+
});
1882+
});
1883+
1884+
it('full text search sort', () => {
1885+
const query = new ParseQuery('Item');
1886+
query.fullText('size', 'medium');
1887+
query.ascending('$score');
1888+
query.select('$score');
1889+
1890+
expect(query.toJSON()).toEqual({
1891+
where: {
1892+
size: {
1893+
$text: {
1894+
$search: {
1895+
$term: "medium",
1896+
}
1897+
}
1898+
}
1899+
},
1900+
keys : "$score",
1901+
order : "$score"
1902+
});
1903+
});
1904+
1905+
it('full text search key required', (done) => {
1906+
const query = new ParseQuery('Item');
1907+
expect(() => query.fullText()).toThrow('A key is required.');
1908+
done();
1909+
});
1910+
1911+
it('full text search value required', (done) => {
1912+
const query = new ParseQuery('Item');
1913+
expect(() => query.fullText('key')).toThrow('A search term is required');
1914+
done();
1915+
});
1916+
1917+
it('full text search value must be string', (done) => {
1918+
const query = new ParseQuery('Item');
1919+
expect(() => query.fullText('key', [])).toThrow('The value being searched for must be a string.');
1920+
done();
1921+
});
18671922
});

src/__tests__/ParseSchema-test.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,6 @@ var ParseSchema = require('../ParseSchema').default;
1313
var ParsePromise = require('../ParsePromise').default;
1414
var CoreManager = require('../CoreManager');
1515

16-
function generateSaveMock(prefix) {
17-
return function(name, payload) {
18-
return ParsePromise.as({
19-
name: name,
20-
url: prefix + name
21-
});
22-
};
23-
}
24-
2516
var defaultController = CoreManager.getSchemaController();
2617

2718
describe('ParseSchema', () => {
@@ -388,11 +379,9 @@ describe('SchemaController', () => {
388379
beforeEach(() => {
389380
CoreManager.setSchemaController(defaultController);
390381
var request = function(method, path, data, options) {
391-
var name = path.substr(path.indexOf('/') + 1);
392382
return ParsePromise.as([]);
393383
};
394384
var ajax = function(method, path, data, headers) {
395-
var name = path.substr(path.indexOf('/') + 1);
396385
return ParsePromise.as([]);
397386
};
398387
CoreManager.setRESTController({ request: request, ajax: ajax });

0 commit comments

Comments
 (0)