Support negated searches on staging.

Behind a feature flag you can now do searches like this:

    -pm-with:othello@example.com is:private

The "-" in front of "pm-with" tells us to exclude messages
with Othello from our search.  We support "-" in front of
all operators, although the behavior for "-search:" and
and "-near:" doesn't really change in this commit.

Note that the filtering out of "negated" predicates only
happens on the client side in this commit.  On the server
side we ignore negated predicates and send back a superset
of the results.

(imported from commit 6cdeaf32f2d493fbbb838630f0da3da880b1ca18)
This commit is contained in:
Steve Howell
2014-02-11 15:36:59 -05:00
parent 41e3a89398
commit 2fb7c0059d
6 changed files with 32 additions and 5 deletions

View File

@@ -47,6 +47,7 @@ exports.local_echo = page_params.staging || is_customer4 || _.contains(['custome
exports.cleanup_before_reload = page_params.staging;
exports.show_digest_email_setting = page_params.staging;
exports.negated_search = page_params.staging;
exports.fade_at_stream_granularity = page_params.staging;

View File

@@ -202,6 +202,7 @@ function decodeOperand(encoded, operator) {
Filter.parse = function (str) {
var operators = [];
var search_term = [];
var negated;
var operator;
var operand;
var term;
@@ -219,9 +220,16 @@ Filter.parse = function (str) {
} else {
// Looks like an operator.
// FIXME: Should we skip unknown operator names here?
negated = false;
operator = parts.shift();
if (feature_flags.negated_search) {
if (operator[0] === '-') {
negated = true;
operator = operator.slice(1);
}
}
operand = decodeOperand(parts.join(':'), operator);
term = {operator: operator, operand: operand};
term = {negated: negated, operator: operator, operand: operand};
operators.push(term);
}
});
@@ -252,7 +260,8 @@ Filter.unparse = function (operators) {
// a colon are glued together to form a search term.
return elem.operand;
} else {
return elem.operator + ':' + encodeOperand(elem.operand.toString());
var sign = elem.negated ? '-' : '';
return sign + elem.operator + ':' + encodeOperand(elem.operand.toString());
}
});
return parts.join(' ');

View File

@@ -71,7 +71,8 @@ exports.operators_to_hash = function (operators) {
operand = elem[1];
}
hash += '/' + hashchange.encodeHashComponent(operator)
var sign = elem.negated ? '-' : '';
hash += '/' + sign + hashchange.encodeHashComponent(operator)
+ '/' + hashchange.encodeHashComponent(operand);
});
}
@@ -95,7 +96,12 @@ function parse_narrow(hash) {
try {
var operator = decodeHashComponent(hash[i]);
var operand = decodeHashComponent(hash[i+1] || '');
operators.push({operator: operator, operand: operand});
var negated = false;
if (operator[0] === '-') {
negated = true;
operator = operator.slice(1);
}
operators.push({negated: negated, operator: operator, operand: operand});
} catch (err) {
return undefined;
}

View File

@@ -8,6 +8,8 @@ set_global('page_params', {
domain: 'zulip.com'
});
set_global('feature_flags', {});
var Filter = require('js/filter.js');
var _ = global._;

View File

@@ -16,6 +16,7 @@ add_dependencies({
var search = require('js/search_suggestion.js');
set_global('feature_flags', {});
set_global('page_params', {
email: 'bob@zulip.com'
});

View File

@@ -95,6 +95,10 @@ class NarrowBuilder(object):
# Python __magic__ stuff.
operator = term['operator']
operand = term['operand']
if term.get('negated', False):
return query
method_name = 'by_' + operator.replace('-', '_')
method = getattr(self, method_name, None)
if method is None:
@@ -312,7 +316,11 @@ def narrow_parameter(json):
raise JsonableError(error)
# whitelist the fields we care about for now
return dict(operator=elem['operator'], operand=elem['operand'])
return dict(
operator=elem['operator'],
operand=elem['operand'],
negated=elem.get('negated', False),
)
raise ValueError("element is not a dictionary")