Bug 1091675 - Implement quoting to disable multi-word search in addressbook. r=mkmelin, a=me

This commit is contained in:
Suyash Agarwal 2014-11-15 23:40:00 +02:00
Родитель eb1c1450f8
Коммит 13755795bb
5 изменённых файлов: 178 добавлений и 28 удалений

Просмотреть файл

@ -607,6 +607,74 @@ function GetSelectedDirectory()
}
}
/**
* There is an exact replica of this method in mailnews/.
* We need to remove this duplication with the help of a jsm in mailnews/
*
* Parse the multiword search string to extract individual search terms
* (separated on the basis of spaces) or quoted exact phrases to search
* against multiple fields of the addressbook cards.
*
* @param aSearchString The full search string entered by the user.
*
* @return an array of separated search terms from the full search string.
*/
function getSearchTokens(aSearchString)
{
let searchString = aSearchString.trim();
if (searchString == "")
return searchString;
let quotedTerms = [];
// Split up multiple search words to create a *foo* and *bar* search against
// search fields, using the OR-search template from modelQuery for each word.
// If the search query has quoted terms as "foo bar", extract them as is.
let startIndex;
while ((startIndex = searchString.indexOf('"')) != -1) {
let endIndex = searchString.indexOf('"', startIndex + 1);
if (endIndex == -1)
endIndex = searchString.length;
quotedTerms.push(searchString.substring(startIndex + 1, endIndex));
let query = searchString.substring(0, startIndex);
if (endIndex < searchString.length)
query += searchString.substr(endIndex + 1);
searchString = query.trim();
}
let searchWords = [];
if (searchString.length != 0) {
searchWords = quotedTerms.concat(searchString.split(/\s+/));
} else {
searchWords = quotedTerms;
}
return searchWords;
}
/*
* Given a database model query and a list of search tokens,
* return query URI.
*
* @param aModelQuery database model query
* @param aSearchWords an array of search tokens.
*
* @return query URI.
*/
function generateQueryURI(aModelQuery, aSearchWords)
{
let queryURI = "";
aSearchWords.forEach(searchWord =>
queryURI += aModelQuery.replace(/@V/g, encodeABTermValue(searchWord)));
// queryURI has all the (or(...)) searches, link them up with (and(...)).
queryURI = "?(and" + queryURI + ")";
return queryURI;
}
function onAbClearSearch()
{
var searchInput = document.getElementById("peopleSearchInput");

Просмотреть файл

@ -154,16 +154,11 @@ function onEnterInSearchBar()
var searchURI = GetSelectedDirectory();
var searchInput = document.getElementById("peopleSearchInput");
// Split up multiple search words to create a *foo* and *bar* search against
// search fields, using the OR-search template from gQueryURIFormat for each word.
if (searchInput.value != "") {
let searchWords = searchInput.value.trim().split(/\s+/);
let queryURI = "";
searchWords.forEach(searchWord =>
queryURI += gQueryURIFormat.replace(/@V/g, encodeABTermValue(searchWord)));
// queryURI has all the (or(...)) searches, link them up with (and(...)).
searchURI += "?(and" + queryURI + ")";
// Use helper method to split up search query to multi-word search
// query against multiple fields.
if (searchInput) {
let searchWords = getSearchTokens(searchInput.value);
searchURI += generateQueryURI(gQueryURIFormat, searchWords);
}
SetAbView(searchURI);

Просмотреть файл

@ -519,16 +519,11 @@ function onEnterInSearchBar()
moz-abldapdirectory://nsdirectory.netscape.com:389/ou=People,dc=netscape,dc=com?(or(Department,=,Applications))
*/
var searchInput = document.getElementById("peopleSearchInput");
if (searchInput && searchInput.value != "") {
// Split up multiple search words to create a *foo* and *bar* search against
// search fields, using the OR-search template from gQueryURIFormat for each word.
let searchWords = searchInput.value.trim().split(/\s+/);
let queryURI = "";
searchWords.forEach(searchWord =>
queryURI += gQueryURIFormat.replace(/@V/g, encodeABTermValue(searchWord)));
// queryURI has all the (or(...)) searches, link them up with (and(...)).
searchURI += "?(and" + queryURI + ")";
// Use helper method to split up search query to multi-word search
// query against multiple fields.
if (searchInput) {
let searchWords = getSearchTokens(searchInput.value);
searchURI += generateQueryURI(gQueryURIFormat, searchWords);
}
SetAbView(searchURI);

Просмотреть файл

@ -334,8 +334,9 @@ nsAbAutoCompleteSearch.prototype = {
}
// Array of all the terms from the fullString search query
// (separated on the basis of spaces).
let searchWords = fullString.split(/\s+/);
// (separated on the basis of spaces or exact terms on the
// basis of quotes).
let searchWords = getSearchTokens(fullString);
// Find out about the comment column
try {
@ -371,14 +372,14 @@ nsAbAutoCompleteSearch.prototype = {
// for each word separately so that each result contains all the words
// from the fullstring in the fields of the addressbook card
// (see bug 558931 for explanations).
let searchQuery = "";
let modelQuery = "(or(DisplayName,c,@V)(FirstName,c,@V)(LastName,c,@V)" +
"(NickName,c,@V)(PrimaryEmail,c,@V)(SecondEmail,c,@V)" +
"(and(IsMailList,=,TRUE)(Notes,c,@V)))";
for (let searchWord of searchWords) {
searchQuery += modelQuery.replace(/@V/g, encodeABTermValue(searchWord));
}
searchQuery = "?(and" + searchQuery + ")";
// Use helper method to split up search query to multi-word search
// query against multiple fields.
let searchWords = getSearchTokens(fullString);
let searchQuery = generateQueryURI(modelQuery, searchWords);
// Now do the searching
let allABs = this._abManager.directories;
@ -434,6 +435,75 @@ function encodeABTermValue(aString) {
return encodeURIComponent(aString).replace(/\(/g, "%28").replace(/\)/g, "%29");
}
/**
* This is an exact replica of the method in abCommon.js and needs to
* be merged to remove this duplication.
*
* Parse the multiword search string to extract individual search terms
* (separated on the basis of spaces) or quoted exact phrases to search
* against multiple fields of the addressbook cards.
*
* @param aSearchString The full search string entered by the user.
*
* @return an array of separated search terms from the full search string.
*/
function getSearchTokens(aSearchString)
{
let searchString = aSearchString.trim();
if (searchString == "")
return searchString;
let quotedTerms = [];
// Split up multiple search words to create a *foo* and *bar* search against
// search fields, using the OR-search template from modelQuery for each word.
// If the search query has quoted terms as "foo bar", extract them as is.
let startIndex;
while ((startIndex = searchString.indexOf('"')) != -1) {
let endIndex = searchString.indexOf('"', startIndex + 1);
if (endIndex == -1)
endIndex = searchString.length;
quotedTerms.push(searchString.substring(startIndex + 1, endIndex));
let query = searchString.substring(0, startIndex);
if (endIndex < searchString.length)
query += searchString.substr(endIndex + 1);
searchString = query.trim();
}
let searchWords = [];
if (searchString.length != 0) {
searchWords = quotedTerms.concat(searchString.split(/\s+/));
} else {
searchWords = quotedTerms;
}
return searchWords;
}
/*
* Given a database model query and a list of search tokens,
* return query URI.
*
* @param aModelQuery database model query
* @param aSearchWords an array of search tokens.
*
* @return query URI.
*/
function generateQueryURI(aModelQuery, aSearchWords)
{
let queryURI = "";
aSearchWords.forEach(searchWord =>
queryURI += aModelQuery.replace(/@V/g, encodeABTermValue(searchWord)));
// queryURI has all the (or(...)) searches, link them up with (and(...)).
queryURI = "?(and" + queryURI + ")";
return queryURI;
}
// Module
var components = [nsAbAutoCompleteSearch];

Просмотреть файл

@ -59,6 +59,24 @@ const cards = [
{ // 10
email: "10@example.com", displayName: "däsh l18n",
popularityIndex: 0, value: "däsh l18n <10@example.com>"
},
{ // 11
email: "11@example.com", displayName: "paul mary",
popularityIndex: 0, firstName: "paul", lastName: "mary meyer",
value: "paul mary <11@example.com>"
},
{ // 12
email: "12@example.com", displayName: "paul meyer",
popularityIndex: 0, firstName: "paul", lastName: "mary meyer",
value: "paul meyer <12@example.com>"
},
{ // 13
email: "13@example.com", displayName: "mr robert downey (exp dev)",
popularityIndex: 0, firstName: "robert", lastName: "downey",
value: "mr robert downey (exp dev) <13@example.com>"
}
];
@ -70,7 +88,10 @@ const inputs = [
{ search: "xx", expected: [0, 5] },
{ search: "jan", expected: [1, 3] },
{ search: "sh", expected: [2, 10, 7] },
{ search: "st", expected: [3,8] }
{ search: "st", expected: [3,8] },
{ search: "paul mary", expected: [11, 12] },
{ search: "\"paul mary\"", expected: [11] },
{ search: "\"robert downey\" mr \"exp dev\"", expected: [13] }
];
function acObserver() {}
@ -103,6 +124,7 @@ function run_test()
card.displayName = element.displayName;
card.setProperty("PopularityIndex", element.popularityIndex);
card.firstName = element.firstName;
card.lastName = element.lastName;
ab.addCard(card);
}