зеркало из https://github.com/mozilla/gecko-dev.git
Bug 384228 - Implement Search-In-Folder(s) back-end. r=dietrich.
This commit is contained in:
Родитель
b312070289
Коммит
6c8d54ddfb
|
@ -12,15 +12,17 @@
|
||||||
* for the specific language governing rights and limitations under the
|
* for the specific language governing rights and limitations under the
|
||||||
* License.
|
* License.
|
||||||
*
|
*
|
||||||
* The Original Code is Mozilla History System
|
* The Original Code is Mozilla History System.
|
||||||
*
|
*
|
||||||
* The Initial Developer of the Original Code is
|
* The Initial Developer of the Original Code is Google Inc.
|
||||||
* Google Inc.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2005
|
* Portions created by the Initial Developer are Copyright (C) 2005
|
||||||
* the Initial Developer. All Rights Reserved.
|
* the Initial Developer. All Rights Reserved.
|
||||||
*
|
*
|
||||||
* Contributor(s):
|
* Contributor(s):
|
||||||
* Brett Wilson <brettw@gmail.com> (original author)
|
* Brett Wilson <brettw@gmail.com> (original author)
|
||||||
|
* Dietrich Ayala <dietrich@mozilla.com>
|
||||||
|
* Seth Spitzer <sspitzer@mozilla.com>
|
||||||
|
* Asaf Romano <mano@mozilla.com>
|
||||||
*
|
*
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
@ -180,7 +182,8 @@ static PRBool IsNumericHostName(const nsCString& aHost);
|
||||||
static PRInt64 GetSimpleBookmarksQueryFolder(
|
static PRInt64 GetSimpleBookmarksQueryFolder(
|
||||||
const nsCOMArray<nsNavHistoryQuery>& aQueries,
|
const nsCOMArray<nsNavHistoryQuery>& aQueries,
|
||||||
nsNavHistoryQueryOptions* aOptions);
|
nsNavHistoryQueryOptions* aOptions);
|
||||||
static void ParseSearchQuery(const nsString& aQuery, nsStringArray* aTerms);
|
static void ParseSearchTermsFromQueries(const nsCOMArray<nsNavHistoryQuery>& aQueries,
|
||||||
|
nsTArray<nsStringArray*>* aTerms);
|
||||||
|
|
||||||
inline void ReverseString(const nsString& aInput, nsAString& aReversed)
|
inline void ReverseString(const nsString& aInput, nsAString& aReversed)
|
||||||
{
|
{
|
||||||
|
@ -1552,8 +1555,10 @@ nsNavHistory::EvaluateQueryForNode(const nsCOMArray<nsNavHistoryQuery>& aQueries
|
||||||
// an array.
|
// an array.
|
||||||
nsCOMArray<nsNavHistoryResultNode> inputSet;
|
nsCOMArray<nsNavHistoryResultNode> inputSet;
|
||||||
inputSet.AppendObject(aNode);
|
inputSet.AppendObject(aNode);
|
||||||
|
nsCOMArray<nsNavHistoryQuery> queries;
|
||||||
|
queries.AppendObject(query);
|
||||||
nsCOMArray<nsNavHistoryResultNode> filteredSet;
|
nsCOMArray<nsNavHistoryResultNode> filteredSet;
|
||||||
nsresult rv = FilterResultSet(nsnull, inputSet, &filteredSet, query->SearchTerms());
|
nsresult rv = FilterResultSet(nsnull, inputSet, &filteredSet, queries);
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
continue;
|
continue;
|
||||||
if (! filteredSet.Count())
|
if (! filteredSet.Count())
|
||||||
|
@ -2384,6 +2389,17 @@ nsNavHistory::GetQueryResults(nsNavHistoryQueryResultNode *aResultNode,
|
||||||
|
|
||||||
// bind parameters
|
// bind parameters
|
||||||
PRInt32 numParameters = 0;
|
PRInt32 numParameters = 0;
|
||||||
|
|
||||||
|
// optimize the case where we just want a list with no grouping: this
|
||||||
|
// directly fills in the results and we avoid a copy of the whole list
|
||||||
|
PRBool resultAsList = PR_TRUE;
|
||||||
|
PRUint32 groupCount;
|
||||||
|
const PRUint16 *groupings = aOptions->GroupingMode(&groupCount);
|
||||||
|
|
||||||
|
if (groupCount != 0 || aOptions->ExcludeQueries()) {
|
||||||
|
resultAsList = PR_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
PRInt32 i;
|
PRInt32 i;
|
||||||
for (i = 0; i < aQueries.Count(); i ++) {
|
for (i = 0; i < aQueries.Count(); i ++) {
|
||||||
PRInt32 clauseParameters = 0;
|
PRInt32 clauseParameters = 0;
|
||||||
|
@ -2391,18 +2407,20 @@ nsNavHistory::GetQueryResults(nsNavHistoryQueryResultNode *aResultNode,
|
||||||
aQueries[i], aOptions, &clauseParameters);
|
aQueries[i], aOptions, &clauseParameters);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
numParameters += clauseParameters;
|
numParameters += clauseParameters;
|
||||||
|
if (resultAsList) {
|
||||||
|
if (aQueries[i]->Folders().Length() != 0) {
|
||||||
|
resultAsList = PR_FALSE;
|
||||||
|
} else {
|
||||||
|
PRBool hasSearchTerms;
|
||||||
|
rv = aQueries[i]->GetHasSearchTerms(&hasSearchTerms);
|
||||||
|
if (hasSearchTerms)
|
||||||
|
resultAsList = PR_FALSE;
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PRUint32 groupCount;
|
if (resultAsList) {
|
||||||
const PRUint16 *groupings = aOptions->GroupingMode(&groupCount);
|
|
||||||
|
|
||||||
PRBool hasSearchTerms;
|
|
||||||
rv = aQueries[0]->GetHasSearchTerms(&hasSearchTerms);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
if (groupCount == 0 && ! hasSearchTerms) {
|
|
||||||
// optimize the case where we just want a list with no grouping: this
|
|
||||||
// directly fills in the results and we avoid a copy of the whole list
|
|
||||||
rv = ResultsAsList(statement, aOptions, aResults);
|
rv = ResultsAsList(statement, aOptions, aResults);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2411,22 +2429,13 @@ nsNavHistory::GetQueryResults(nsNavHistoryQueryResultNode *aResultNode,
|
||||||
rv = ResultsAsList(statement, aOptions, &toplevel);
|
rv = ResultsAsList(statement, aOptions, &toplevel);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
if (hasSearchTerms) {
|
if (groupCount == 0) {
|
||||||
// keyword search
|
FilterResultSet(aResultNode, toplevel, aResults, aQueries);
|
||||||
if (groupCount == 0) {
|
|
||||||
// keyword search with no grouping: can filter directly into the result
|
|
||||||
FilterResultSet(aResultNode, toplevel, aResults, aQueries[0]->SearchTerms());
|
|
||||||
} else {
|
|
||||||
// keyword searching with grouping: need intermediate filtered results
|
|
||||||
nsCOMArray<nsNavHistoryResultNode> filteredResults;
|
|
||||||
FilterResultSet(aResultNode, toplevel, &filteredResults, aQueries[0]->SearchTerms());
|
|
||||||
rv = RecursiveGroup(aResultNode, filteredResults, groupings, groupCount,
|
|
||||||
aResults);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// group unfiltered results
|
nsCOMArray<nsNavHistoryResultNode> filteredResults;
|
||||||
rv = RecursiveGroup(aResultNode, toplevel, groupings, groupCount, aResults);
|
FilterResultSet(aResultNode, toplevel, &filteredResults, aQueries);
|
||||||
|
rv = RecursiveGroup(aResultNode, filteredResults, groupings, groupCount,
|
||||||
|
aResults);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3603,19 +3612,8 @@ nsNavHistory::QueryToSelectClause(nsNavHistoryQuery* aQuery, // const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (aOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS) {
|
if (aOptions->QueryType() != nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS &&
|
||||||
// Folders only have an affect on bookmark queries
|
aQuery->OnlyBookmarked()) {
|
||||||
// XXX: add multiple folders support
|
|
||||||
if (aQuery->Folders().Length() == 1) {
|
|
||||||
if (!aClause->IsEmpty())
|
|
||||||
*aClause += NS_LITERAL_CSTRING(" AND ");
|
|
||||||
|
|
||||||
nsCAutoString paramString;
|
|
||||||
parameterString(aStartParameter + *aParamCount, paramString);
|
|
||||||
(*aParamCount) ++;
|
|
||||||
*aClause += NS_LITERAL_CSTRING(" b.parent = ") + paramString;
|
|
||||||
}
|
|
||||||
} else if (aQuery->OnlyBookmarked()) {
|
|
||||||
// only bookmarked, has no affect on bookmarks-only queries
|
// only bookmarked, has no affect on bookmarks-only queries
|
||||||
if (!aClause->IsEmpty())
|
if (!aClause->IsEmpty())
|
||||||
*aClause += NS_LITERAL_CSTRING(" AND ");
|
*aClause += NS_LITERAL_CSTRING(" AND ");
|
||||||
|
@ -3754,18 +3752,6 @@ nsNavHistory::BindQueryClauseParameters(mozIStorageStatement* statement,
|
||||||
(*aParamCount) ++;
|
(*aParamCount) ++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// folder
|
|
||||||
if (aOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS) {
|
|
||||||
// XXX: add multiple folders support
|
|
||||||
if (aQuery->Folders().Length() == 1) {
|
|
||||||
rv = statement->BindInt64Parameter(aStartParameter + *aParamCount,
|
|
||||||
aQuery->Folders()[0]);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
(*aParamCount) ++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// onlyBookmarked: nothing to bind
|
|
||||||
|
|
||||||
// domain (see GetReversedHostname for more info on reversed host names)
|
// domain (see GetReversedHostname for more info on reversed host names)
|
||||||
if (NS_SUCCEEDED(aQuery->GetHasDomain(&hasIt)) && hasIt) {
|
if (NS_SUCCEEDED(aQuery->GetHasDomain(&hasIt)) && hasIt) {
|
||||||
nsString revDomain;
|
nsString revDomain;
|
||||||
|
@ -4143,104 +4129,140 @@ nsNavHistory::URIHasTag(nsIURI* aURI, const nsAString& aTag)
|
||||||
//
|
//
|
||||||
// This does some post-query-execution filtering:
|
// This does some post-query-execution filtering:
|
||||||
// - searching on title & url
|
// - searching on title & url
|
||||||
|
// - parent folder (recursively)
|
||||||
// - excludeQueries
|
// - excludeQueries
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsNavHistory::FilterResultSet(nsNavHistoryQueryResultNode* aParentNode,
|
nsNavHistory::FilterResultSet(nsNavHistoryQueryResultNode* aQueryNode,
|
||||||
const nsCOMArray<nsNavHistoryResultNode>& aSet,
|
const nsCOMArray<nsNavHistoryResultNode>& aSet,
|
||||||
nsCOMArray<nsNavHistoryResultNode>* aFiltered,
|
nsCOMArray<nsNavHistoryResultNode>* aFiltered,
|
||||||
const nsString& aSearch)
|
const nsCOMArray<nsNavHistoryQuery>& aQueries)
|
||||||
{
|
{
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
nsStringArray terms;
|
|
||||||
ParseSearchQuery(aSearch, &terms);
|
// get the bookmarks service
|
||||||
|
nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
|
||||||
|
NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
// parse the search terms
|
||||||
|
nsTArray<nsStringArray*> terms;
|
||||||
|
ParseSearchTermsFromQueries(aQueries, &terms);
|
||||||
|
|
||||||
|
PRUint32 queryIndex;
|
||||||
|
|
||||||
|
// The includeFolders array for each query is initialized with its
|
||||||
|
// query's folders array. We add sub-folders as we check items.
|
||||||
|
nsTArray< nsTArray<PRInt64>* > includeFolders;
|
||||||
|
nsTArray< nsTArray<PRInt64>* > excludeFolders;
|
||||||
|
for (queryIndex = 0;
|
||||||
|
queryIndex < aQueries.Count(); queryIndex++) {
|
||||||
|
includeFolders.AppendElement(new nsTArray<PRInt64>(aQueries[queryIndex]->Folders()));
|
||||||
|
excludeFolders.AppendElement(new nsTArray<PRInt64>());
|
||||||
|
}
|
||||||
|
|
||||||
// filter against query options
|
// filter against query options
|
||||||
// XXX only excludeQueries is supported at the moment
|
// XXX only excludeQueries is supported at the moment
|
||||||
PRBool excludeQueries = PR_FALSE;
|
PRBool excludeQueries = PR_FALSE;
|
||||||
if (aParentNode) {
|
if (aQueryNode) {
|
||||||
rv = aParentNode->mOptions->GetExcludeQueries(&excludeQueries);
|
rv = aQueryNode->mOptions->GetExcludeQueries(&excludeQueries);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCStringArray searchAnnotations;
|
|
||||||
/*
|
|
||||||
if (mAnnotationService) {
|
|
||||||
searchAnnotations.AppendCString(NS_LITERAL_CSTRING("qwer"));
|
|
||||||
searchAnnotations.AppendCString(NS_LITERAL_CSTRING("asdf"));
|
|
||||||
searchAnnotations.AppendCString(NS_LITERAL_CSTRING("zxcv"));
|
|
||||||
//mAnnotationService->GetSearchableAnnotations();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
for (PRInt32 nodeIndex = 0; nodeIndex < aSet.Count(); nodeIndex ++) {
|
for (PRInt32 nodeIndex = 0; nodeIndex < aSet.Count(); nodeIndex ++) {
|
||||||
if (aParentNode && aParentNode->mItemId != -1) {
|
|
||||||
if (aParentNode->mItemId == aSet[nodeIndex]->mItemId) {
|
|
||||||
continue; // filter out bookmark nodes that are the same as the parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (excludeQueries && IsQueryURI(aSet[nodeIndex]->mURI))
|
if (excludeQueries && IsQueryURI(aSet[nodeIndex]->mURI))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
PRBool allTermsFound = PR_TRUE;
|
PRInt64 parentId = -1;
|
||||||
|
if (aSet[nodeIndex]->mItemId != -1) {
|
||||||
nsStringArray curAnnotations;
|
if (aQueryNode->mItemId == aSet[nodeIndex]->mItemId)
|
||||||
/*
|
continue;
|
||||||
if (searchAnnotations.Count()) {
|
rv = bookmarks->GetFolderIdForItem(aSet[nodeIndex]->mItemId, &parentId);
|
||||||
// come up with a list of all annotation *values* we need to search
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
for (PRInt32 annotIndex = 0; annotIndex < searchAnnotations.Count(); annotIndex ++) {
|
|
||||||
nsString annot;
|
|
||||||
if (NS_SUCCEEDED(mAnnotationService->GetAnnotationString(
|
|
||||||
aSet[nodeIndex]->mURI,
|
|
||||||
*searchAnnotations[annotIndex],
|
|
||||||
annot)))
|
|
||||||
curAnnotations.AppendString(annot);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
if (terms.Count() == 0) {
|
// Append the node if it matches one of the queries
|
||||||
allTermsFound = PR_TRUE;
|
PRBool appendNode = PR_FALSE;
|
||||||
} else {
|
for (queryIndex = 0;
|
||||||
|
queryIndex < aQueries.Count() && !appendNode; queryIndex++) {
|
||||||
|
// parent folder
|
||||||
|
if (includeFolders[queryIndex]->Length() != 0) {
|
||||||
|
// filter out simple history nodes from bookmark queries
|
||||||
|
if (aSet[nodeIndex]->mItemId == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
for (PRInt32 termIndex = 0; termIndex < terms.Count(); termIndex ++) {
|
// filter out the node of which their parent is in the exclude-folders
|
||||||
|
// cache
|
||||||
|
if (excludeFolders[queryIndex]->IndexOf(parentId) != -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (includeFolders[queryIndex]->IndexOf(parentId) == -1) {
|
||||||
|
// check ancestors
|
||||||
|
PRInt64 ancestor = parentId, lastAncestor;
|
||||||
|
PRBool belongs = PR_FALSE;
|
||||||
|
|
||||||
|
while (!belongs) {
|
||||||
|
// Avoid using |ancestor| itself if GetFolderIdForItem failed.
|
||||||
|
lastAncestor = ancestor;
|
||||||
|
|
||||||
|
// GetFolderIdForItems throws when called for the places-root
|
||||||
|
if (NS_FAILED(bookmarks->GetFolderIdForItem(ancestor,&ancestor))) {
|
||||||
|
break;
|
||||||
|
} else if (includeFolders[queryIndex]->IndexOf(ancestor) != -1) {
|
||||||
|
belongs = PR_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (belongs) {
|
||||||
|
includeFolders[queryIndex]->AppendElement(lastAncestor);
|
||||||
|
} else {
|
||||||
|
excludeFolders[queryIndex]->AppendElement(lastAncestor);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// search terms
|
||||||
|
// XXXmano/dietrich: when bug 331487 is fixed, bookmark queries can group
|
||||||
|
// by folder or not regardless of specified folders or search terms.
|
||||||
|
PRBool allTermsFound = PR_TRUE;
|
||||||
|
for (PRInt32 termIndex = 0; termIndex < terms[queryIndex]->Count() &&
|
||||||
|
allTermsFound; termIndex ++) {
|
||||||
|
// search terms should match title, url or tags
|
||||||
PRBool termFound = PR_FALSE;
|
PRBool termFound = PR_FALSE;
|
||||||
// title and URL
|
// title and URL
|
||||||
if (CaseInsensitiveFindInReadable(*terms[termIndex],
|
if (CaseInsensitiveFindInReadable(*terms[queryIndex]->StringAt(termIndex),
|
||||||
NS_ConvertUTF8toUTF16(aSet[nodeIndex]->mTitle)) ||
|
NS_ConvertUTF8toUTF16(aSet[nodeIndex]->mTitle)) ||
|
||||||
(aSet[nodeIndex]->IsURI() &&
|
(aSet[nodeIndex]->IsURI() &&
|
||||||
CaseInsensitiveFindInReadable(*terms[termIndex],
|
CaseInsensitiveFindInReadable(*terms[queryIndex]->StringAt(termIndex),
|
||||||
NS_ConvertUTF8toUTF16(aSet[nodeIndex]->mURI))))
|
NS_ConvertUTF8toUTF16(aSet[nodeIndex]->mURI))))
|
||||||
termFound = PR_TRUE;
|
termFound = PR_TRUE;
|
||||||
|
|
||||||
// searchable annotations
|
// tags
|
||||||
/*if (! termFound) {
|
|
||||||
for (PRInt32 annotIndex = 0; annotIndex < curAnnotations.Count(); annotIndex ++) {
|
|
||||||
if (CaseInsensitiveFindInReadable(*terms[termIndex],
|
|
||||||
*curAnnotations[annotIndex]))
|
|
||||||
termFound = PR_TRUE;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// search tags
|
|
||||||
if (!termFound) {
|
if (!termFound) {
|
||||||
nsCOMPtr<nsIURI> itemURI;
|
nsCOMPtr<nsIURI> itemURI;
|
||||||
rv = NS_NewURI(getter_AddRefs(itemURI), aSet[nodeIndex]->mURI);
|
rv = NS_NewURI(getter_AddRefs(itemURI), aSet[nodeIndex]->mURI);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
termFound = URIHasTag(itemURI, *terms[termIndex]);
|
termFound = URIHasTag(itemURI, *terms[queryIndex]->StringAt(termIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!termFound) {
|
if (!termFound)
|
||||||
allTermsFound = PR_FALSE;
|
allTermsFound = PR_FALSE;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
if (!allTermsFound)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (allTermsFound)
|
appendNode = PR_TRUE;
|
||||||
|
}
|
||||||
|
if (appendNode)
|
||||||
aFiltered->AppendObject(aSet[nodeIndex]);
|
aFiltered->AppendObject(aSet[nodeIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// de-allocate the matrixes
|
||||||
|
for (PRUint32 i=0; i < aQueries.Count(); i++) {
|
||||||
|
delete terms[i];
|
||||||
|
delete includeFolders[i];
|
||||||
|
delete excludeFolders[i];
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4920,9 +4942,13 @@ GetSimpleBookmarksQueryFolder(const nsCOMArray<nsNavHistoryQuery>& aQueries,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ParseSearchQuery
|
// ParseSearchTermsFromQueries
|
||||||
//
|
//
|
||||||
// This just breaks the query up into words. We don't do anything fancy,
|
// Construct a matrix of search terms from the given queries array.
|
||||||
|
// All of the query objects are ORed together. Within a query, all the terms
|
||||||
|
// are ANDed together. See nsINavHistory.idl.
|
||||||
|
//
|
||||||
|
// This just breaks the quer up into words. We don't do anything fancy,
|
||||||
// not even quoting. We do, however, strip quotes, because people might
|
// not even quoting. We do, however, strip quotes, because people might
|
||||||
// try to input quotes expecting them to do something and get no results
|
// try to input quotes expecting them to do something and get no results
|
||||||
// back.
|
// back.
|
||||||
|
@ -4932,26 +4958,38 @@ inline PRBool isQueryWhitespace(PRUnichar ch)
|
||||||
return ch == ' ';
|
return ch == ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParseSearchQuery(const nsString& aQuery, nsStringArray* aTerms)
|
void ParseSearchTermsFromQueries(const nsCOMArray<nsNavHistoryQuery>& aQueries,
|
||||||
|
nsTArray<nsStringArray*>* aTerms)
|
||||||
{
|
{
|
||||||
PRInt32 lastBegin = -1;
|
PRInt32 lastBegin = -1;
|
||||||
for (PRUint32 i = 0; i < aQuery.Length(); i ++) {
|
for (PRUint32 i=0; i < aQueries.Count(); i++) {
|
||||||
if (isQueryWhitespace(aQuery[i]) || aQuery[i] == '"') {
|
nsStringArray *queryTerms = new nsStringArray();
|
||||||
if (lastBegin >= 0) {
|
PRBool hasSearchTerms;
|
||||||
// found the end of a word
|
if (NS_SUCCEEDED(aQueries[i]->GetHasSearchTerms(&hasSearchTerms)) &&
|
||||||
aTerms->AppendString(Substring(aQuery, lastBegin, i - lastBegin));
|
hasSearchTerms) {
|
||||||
lastBegin = -1;
|
const nsString& searchTerms = aQueries[i]->SearchTerms();
|
||||||
}
|
for (PRUint32 j = 0; j < searchTerms.Length(); j++) {
|
||||||
} else {
|
if (isQueryWhitespace(searchTerms[j]) ||
|
||||||
if (lastBegin < 0) {
|
searchTerms[j] == '"') {
|
||||||
// found the beginning of a word
|
if (lastBegin >= 0) {
|
||||||
lastBegin = i;
|
// found the end of a word
|
||||||
|
queryTerms->AppendString(Substring(searchTerms, lastBegin,
|
||||||
|
j - lastBegin));
|
||||||
|
lastBegin = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (lastBegin < 0) {
|
||||||
|
// found the beginning of a word
|
||||||
|
lastBegin = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// last word
|
||||||
|
if (lastBegin >= 0)
|
||||||
|
queryTerms->AppendString(Substring(searchTerms, lastBegin));
|
||||||
}
|
}
|
||||||
|
aTerms->AppendElement(queryTerms);
|
||||||
}
|
}
|
||||||
// last word
|
|
||||||
if (lastBegin >= 0)
|
|
||||||
aTerms->AppendString(Substring(aQuery, lastBegin));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -516,7 +516,7 @@ protected:
|
||||||
nsresult FilterResultSet(nsNavHistoryQueryResultNode *aParentNode,
|
nsresult FilterResultSet(nsNavHistoryQueryResultNode *aParentNode,
|
||||||
const nsCOMArray<nsNavHistoryResultNode>& aSet,
|
const nsCOMArray<nsNavHistoryResultNode>& aSet,
|
||||||
nsCOMArray<nsNavHistoryResultNode>* aFiltered,
|
nsCOMArray<nsNavHistoryResultNode>* aFiltered,
|
||||||
const nsString& aSearch);
|
const nsCOMArray<nsNavHistoryQuery>& aQueries);
|
||||||
|
|
||||||
// observers
|
// observers
|
||||||
nsMaybeWeakPtrArray<nsINavHistoryObserver> mObservers;
|
nsMaybeWeakPtrArray<nsINavHistoryObserver> mObservers;
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is mozilla.org code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is Google Inc.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2005
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Seth Spitzer <sspitzer@mozilla.com>
|
||||||
|
* Asaf Romano <mano@mozilla.com>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
// Get bookmark service
|
||||||
|
try {
|
||||||
|
var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
|
||||||
|
} catch(ex) {
|
||||||
|
do_throw("Could not get nav-bookmarks-service\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get history service
|
||||||
|
try {
|
||||||
|
var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].getService(Ci.nsINavHistoryService);
|
||||||
|
} catch(ex) {
|
||||||
|
do_throw("Could not get history service\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// get bookmarks root id
|
||||||
|
var root = bmsvc.bookmarksRoot;
|
||||||
|
|
||||||
|
// main
|
||||||
|
function run_test() {
|
||||||
|
// test querying for bookmarks in multiple folders
|
||||||
|
var testFolder1 = bmsvc.createFolder(root, "bug 384228 test folder 1",
|
||||||
|
bmsvc.DEFAULT_INDEX);
|
||||||
|
var testFolder2 = bmsvc.createFolder(root, "bug 384228 test folder 2",
|
||||||
|
bmsvc.DEFAULT_INDEX);
|
||||||
|
var testFolder3 = bmsvc.createFolder(root, "bug 384228 test folder 3",
|
||||||
|
bmsvc.DEFAULT_INDEX);
|
||||||
|
|
||||||
|
var b1 = bmsvc.insertBookmark(testFolder1, uri("http://foo.tld/"),
|
||||||
|
bmsvc.DEFAULT_INDEX, "title b1 (folder 1)");
|
||||||
|
var b2 = bmsvc.insertBookmark(testFolder1, uri("http://foo.tld/"),
|
||||||
|
bmsvc.DEFAULT_INDEX, "title b2 (folder 1)");
|
||||||
|
var b3 = bmsvc.insertBookmark(testFolder2, uri("http://foo.tld/"),
|
||||||
|
bmsvc.DEFAULT_INDEX, "title b3 (folder 2)");
|
||||||
|
var b4 = bmsvc.insertBookmark(testFolder3, uri("http://foo.tld/"),
|
||||||
|
bmsvc.DEFAULT_INDEX, "title b4 (folder 3)");
|
||||||
|
// also test recursive search
|
||||||
|
var testFolder1_1 = bmsvc.createFolder(testFolder1, "bug 384228 test folder 1.1",
|
||||||
|
bmsvc.DEFAULT_INDEX);
|
||||||
|
var b5 = bmsvc.insertBookmark(testFolder1_1, uri("http://a1.com/"),
|
||||||
|
bmsvc.DEFAULT_INDEX, "title b5 (folder 1.1)");
|
||||||
|
var options = histsvc.getNewQueryOptions();
|
||||||
|
var query = histsvc.getNewQuery();
|
||||||
|
query.searchTerms = "title";
|
||||||
|
options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS;
|
||||||
|
query.setFolders([testFolder1, testFolder2], 2);
|
||||||
|
|
||||||
|
var result = histsvc.executeQuery(query, options);
|
||||||
|
var rootNode = result.root;
|
||||||
|
rootNode.containerOpen = true;
|
||||||
|
|
||||||
|
// should not match item from folder 3
|
||||||
|
do_check_eq(rootNode.childCount, 4);
|
||||||
|
|
||||||
|
do_check_eq(rootNode.getChild(0).itemId, b1);
|
||||||
|
do_check_eq(rootNode.getChild(1).itemId, b2);
|
||||||
|
do_check_eq(rootNode.getChild(2).itemId, b3);
|
||||||
|
do_check_eq(rootNode.getChild(3).itemId, b5);
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is bug 395593 unit test code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is Mozilla Corporation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Asaf Romano <mano@mozilla.com> (Original Author)
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
// Get bookmark service
|
||||||
|
try {
|
||||||
|
var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
|
||||||
|
} catch(ex) {
|
||||||
|
do_throw("Could not get nav-bookmarks-service\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get history service
|
||||||
|
try {
|
||||||
|
var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].getService(Ci.nsINavHistoryService);
|
||||||
|
} catch(ex) {
|
||||||
|
do_throw("Could not get history service\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// get bookmarks root id
|
||||||
|
var root = bmsvc.bookmarksRoot;
|
||||||
|
|
||||||
|
// main
|
||||||
|
function run_test() {
|
||||||
|
/**
|
||||||
|
* All of the query objects are ORed together. Within a query, all the terms
|
||||||
|
* are ANDed together. See nsINavHistory.idl.
|
||||||
|
*/
|
||||||
|
var id1 = bmsvc.insertBookmark(root, uri("http://foo.tld"),
|
||||||
|
bmsvc.DEFAULT_INDEX, "123 0");
|
||||||
|
var id2 = bmsvc.insertBookmark(root, uri("http://foo.tld"),
|
||||||
|
bmsvc.DEFAULT_INDEX, "456");
|
||||||
|
var id3 = bmsvc.insertBookmark(root, uri("http://foo.tld"),
|
||||||
|
bmsvc.DEFAULT_INDEX, "123 456");
|
||||||
|
var id4 = bmsvc.insertBookmark(root, uri("http://foo.tld"),
|
||||||
|
bmsvc.DEFAULT_INDEX, "789 456");
|
||||||
|
|
||||||
|
var queries = [];
|
||||||
|
queries.push(histsvc.getNewQuery());
|
||||||
|
queries[0].searchTerms = "123";
|
||||||
|
queries.push(histsvc.getNewQuery());
|
||||||
|
queries[1].searchTerms = "789";
|
||||||
|
|
||||||
|
var options = histsvc.getNewQueryOptions();
|
||||||
|
options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS;
|
||||||
|
|
||||||
|
var result = histsvc.executeQueries(queries, queries.length, options);
|
||||||
|
var root = result.root;
|
||||||
|
root.containerOpen = true;
|
||||||
|
do_check_eq(root.childCount, 3);
|
||||||
|
do_check_eq(root.getChild(0).itemId, id1);
|
||||||
|
do_check_eq(root.getChild(1).itemId, id3);
|
||||||
|
do_check_eq(root.getChild(2).itemId, id4);
|
||||||
|
|
||||||
|
queries[0].searchTerms = "123";
|
||||||
|
queries[1].searchTerms = "456";
|
||||||
|
result = histsvc.executeQueries(queries, queries.length, options);
|
||||||
|
root = result.root;
|
||||||
|
root.containerOpen = true;
|
||||||
|
do_check_eq(root.childCount, 4);
|
||||||
|
do_check_eq(root.getChild(0).itemId, id1);
|
||||||
|
do_check_eq(root.getChild(1).itemId, id2);
|
||||||
|
do_check_eq(root.getChild(2).itemId, id3);
|
||||||
|
do_check_eq(root.getChild(3).itemId, id4);
|
||||||
|
|
||||||
|
queries[0].searchTerms = "00";
|
||||||
|
queries[1].searchTerms = "789";
|
||||||
|
result = histsvc.executeQueries(queries, queries.length, options);
|
||||||
|
root = result.root;
|
||||||
|
root.containerOpen = true;
|
||||||
|
do_check_eq(root.childCount, 1);
|
||||||
|
do_check_eq(root.getChild(0).itemId, id4);
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче