Skip to content
46 changes: 46 additions & 0 deletions src/ParseQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
diacriticSensitive?: boolean;
}

interface SearchOptions {
index?: string;
}

export interface QueryJSON {
where: WhereClause;
watch?: string;
Expand Down Expand Up @@ -1585,6 +1589,48 @@
return this._addCondition(key, '$text', { $search: fullOptions });
}

/*
* Triggers a MongoDb Atlas Text Search
*
* @param {string} value The string to search
* @param {string[]} path The fields to search
* @param {object} options (Optional)
* @param {string} options.index The index to search
* @returns {Promise} Returns a promise that will be resolved with the results
* of the search
* */

async search(value: string, path: string[], options: SearchOptions = {}): Promise<T[]> {
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.');
}

const controller = CoreManager.getQueryController();
const params = {
$search: {
index: options?.index || 'default',
text: {
path,
query: value,
},
},
};

const searchOptions: { sessionToken?: string; useMasterKey: boolean } = {
useMasterKey: true,
};
const results = await controller.aggregate(this.className, params, searchOptions);
return (
results.results?.map(result =>
ParseObject.fromJSON({ className: this.className, ...result })

Check warning on line 1629 in src/ParseQuery.ts

View check run for this annotation

Codecov / codecov/patch

src/ParseQuery.ts#L1629

Added line #L1629 was not covered by tests
) || []
);
}

/**
* Method to sort the full text search by text score
*
Expand Down
47 changes: 47 additions & 0 deletions src/__tests__/ParseQuery-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2709,6 +2709,53 @@ describe('ParseQuery', () => {
});
});

it('can issue a search query', async () => {
CoreManager.setQueryController({
find() {},
aggregate(className, params, options) {
expect(className).toBe('Item');
expect(params).toEqual({
$search: {
index: 'searchIndex',
text: {
path: ['name'],
query: 'searchTerm',
},
},
});
expect(options.useMasterKey).toEqual(true);
return Promise.resolve({
results: [],
});
},
});
const q = new ParseQuery('Item');
await q.search('searchTerm', ['name'], { index: 'searchIndex' });
});

it('search term is required', async () => {
const q = new ParseQuery('Item');
await expect(q.search()).rejects.toThrow('A search term is required.');
});

it('search term must be a string', async () => {
const q = new ParseQuery('Item');
await expect(q.search(123)).rejects.toThrow('The value being searched for must be a string.');
});

it('search can return an empty array if no results', async () => {
CoreManager.setQueryController({
find() {},
aggregate() {
return Promise.resolve({});
},
});

const q = new ParseQuery('Item');
const results = await q.search('searchTerm', ['name'], { index: 'searchIndex' });
expect(results).toEqual([]);
});

it('aggregate query array pipeline with equalTo', done => {
const pipeline = [{ group: { objectId: '$name' } }];
MockRESTController.request.mockImplementationOnce(() => {
Expand Down
4 changes: 4 additions & 0 deletions types/ParseQuery.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ interface FullTextQueryOptions {
caseSensitive?: boolean;
diacriticSensitive?: boolean;
}
interface SearchOptions {
index?: string;
}
export interface QueryJSON {
where: WhereClause;
watch?: string;
Expand Down Expand Up @@ -634,6 +637,7 @@ declare class ParseQuery<T extends ParseObject = ParseObject> {
* @returns {Parse.Query} Returns the query, so you can chain this call.
*/
fullText<K extends keyof T['attributes'] | keyof BaseAttributes>(key: K, value: string, options?: FullTextQueryOptions): this;
search(value: string, path: string[], options?: SearchOptions): Promise<any[]>;
/**
* Method to sort the full text search by text score
*
Expand Down
Loading