mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-25 09:46:07 +08:00
668c2935c9
Summary: Previously we waited to build the local ThreadSearch index until we were done with the initial mail sync. This is undesirable because inboxes can be very large, making local sync useless for a number of hours after a user adds an account. This diff removes that restriction. This diff also adds a new rudimentary new grammar (along with accompanying lexer and parser) for local thread search queries. This grammar is then translated into the appropriate SQL queries on the ThreadSearch index table. More advanced features (e.g. in:category, date ranges, etc) can be added easily in the future by augmenting this simple search query language. Test Plan: Run locally, new unit tests Reviewers: juan, evan, khamidou Reviewed By: khamidou Differential Revision: https://phab.nylas.com/D3614
133 lines
4.7 KiB
JavaScript
133 lines
4.7 KiB
JavaScript
import {
|
|
ThreadQueryAST,
|
|
} from 'nylas-exports';
|
|
import {parseSearchQuery} from '../lib/search-query-parser'
|
|
|
|
const {
|
|
SearchQueryToken,
|
|
OrQueryExpression,
|
|
AndQueryExpression,
|
|
FromQueryExpression,
|
|
ToQueryExpression,
|
|
SubjectQueryExpression,
|
|
GenericQueryExpression,
|
|
TextQueryExpression,
|
|
UnreadStatusQueryExpression,
|
|
StarredStatusQueryExpression,
|
|
} = ThreadQueryAST;
|
|
|
|
const token = (text) => { return new SearchQueryToken(text); }
|
|
const and = (e1, e2) => { return new AndQueryExpression(e1, e2); }
|
|
const or = (e1, e2) => { return new OrQueryExpression(e1, e2); }
|
|
const from = (text) => { return new FromQueryExpression(text); }
|
|
const to = (text) => { return new ToQueryExpression(text); }
|
|
const subject = (text) => { return new SubjectQueryExpression(text); }
|
|
const generic = (text) => { return new GenericQueryExpression(text); }
|
|
const text = (tok) => { return new TextQueryExpression(tok); }
|
|
const unread = (status) => { return new UnreadStatusQueryExpression(status); }
|
|
const starred = (status) => { return new StarredStatusQueryExpression(status); }
|
|
|
|
|
|
fdescribe('parseSearchQuery', () => {
|
|
it('correctly parses simple queries', () => {
|
|
expect(parseSearchQuery('blah').equals(
|
|
generic(text(token('blah')))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('"foo bar"').equals(
|
|
generic(text(token('foo bar')))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('to:blah').equals(
|
|
to(text(token('blah')))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('from:blah').equals(
|
|
from(text(token('blah')))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('subject:blah').equals(
|
|
subject(text(token('blah')))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('to:mhahnenb@gmail.com').equals(
|
|
to(text(token('mhahnenb@gmail.com')))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('to:"mhahnenb@gmail.com"').equals(
|
|
to(text(token('mhahnenb@gmail.com')))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('to:"Mark mhahnenb@gmail.com"').equals(
|
|
to(text(token('Mark mhahnenb@gmail.com')))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('is:unread').equals(
|
|
unread(true)
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('is:read').equals(
|
|
unread(false)
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('is:starred').equals(
|
|
starred(true)
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('is:unstarred').equals(
|
|
starred(false)
|
|
)).toBe(true)
|
|
});
|
|
|
|
it('correctly parses reserved words as normal text in certain places', () => {
|
|
expect(parseSearchQuery('to:blah').equals(
|
|
to(text(token('blah')))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('to:to').equals(
|
|
to(text(token('to')))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('to:subject').equals(
|
|
to(text(token('subject')))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('to:from').equals(
|
|
to(text(token('from')))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('to:unread').equals(
|
|
to(text(token('unread')))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('to:starred').equals(
|
|
to(text(token('starred')))
|
|
)).toBe(true)
|
|
});
|
|
|
|
it('correctly parses compound queries', () => {
|
|
expect(parseSearchQuery('foo bar').equals(
|
|
and(generic(text(token('foo'))), generic(text(token('bar'))))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('foo AND bar').equals(
|
|
and(generic(text(token('foo'))), generic(text(token('bar'))))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('foo OR bar').equals(
|
|
or(generic(text(token('foo'))), generic(text(token('bar'))))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('to:foo OR bar').equals(
|
|
or(to(text(token('foo'))), generic(text(token('bar'))))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('foo OR to:bar').equals(
|
|
or(generic(text(token('foo'))), to(text(token('bar'))))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('foo bar baz').equals(
|
|
and(generic(text(token('foo'))),
|
|
and(generic(text(token('bar'))), generic(text(token('baz')))))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('foo AND bar AND baz').equals(
|
|
and(generic(text(token('foo'))),
|
|
and(generic(text(token('bar'))), generic(text(token('baz')))))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('foo OR bar AND baz').equals(
|
|
and(
|
|
or(generic(text(token('foo'))), generic(text(token('bar')))),
|
|
generic(text(token('baz'))))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('foo OR bar OR baz').equals(
|
|
or(generic(text(token('foo'))),
|
|
or(generic(text(token('bar'))), generic(text(token('baz')))))
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('foo is:unread').equals(
|
|
and(generic(text(token('foo'))), unread(true)),
|
|
)).toBe(true)
|
|
expect(parseSearchQuery('is:unread foo').equals(
|
|
and(unread(true), generic(text(token('foo'))))
|
|
)).toBe(true)
|
|
});
|
|
});
|