mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2026-09-09 18:02:54 +00:00
[ACS-10514] ADW Search not working for some scenario tocken creation issue (#4891)
* [ACS-10514] improve search term extractrion from query * [ACS-10514] handle other search types, unit tests, docs * [ACS-10515] fix sonar issue * [ACS-10514] format table
This commit is contained in:
@@ -71,24 +71,25 @@ OR ia:whatEvent:"[term]*" OR ia:descriptionEvent:"[term]*" OR lnk:title:"[term]*
|
|||||||
|
|
||||||
### Key facts
|
### Key facts
|
||||||
|
|
||||||
If you have entered more than one word into the search input box, then the search query is constructed automatically using an `AND` operation.
|
1. If you have entered more than one word into the search input box, then the search query is constructed automatically using an `AND` operation.
|
||||||
|
|
||||||
I you have entered more than one word encapsulated in quotation marks, then the search query is constructed treated everything as a single string.
|
2. If you have entered more than one word encapsulated in quotation marks, then the search query is constructed treated everything as a single string.
|
||||||
|
|
||||||
If you have entered more than one word separated by `AND`, then the search query is constructed using an `AND` conjunction.
|
3. If you have entered more than one word separated by `AND`, then the search query is constructed using an `AND` conjunction. Since `AND` is the default operator (see fact 1), the explicit `AND` keywords are removed when the search input value is processed.
|
||||||
|
|
||||||
If you have entered more than one word separated by `OR`, then the search query is constructed using an `OR` disjunction.
|
4. If you have entered more than one word separated by `OR`, then the search query is constructed using an `OR` disjunction. Unlike `AND`, the `OR` operators are preserved when processing the search input value because `OR` is not the default operator.
|
||||||
|
|
||||||
If you have entered an `=` symbol before the search term, then the search query is constructed using exact term matching.
|
5. If you have entered an `=` symbol before the search term, then the search query is constructed using exact term matching. **Note:** Works only with Solr search. For Elastic Search consider using Search Logical Filter.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
| Search Type | Search input | Expected result |
|
| Search Type | Entered search input value | Expected result | Processed search input value |
|
||||||
| --- | --- | --- |
|
| ----------- | -------------------------- | -------------------------------------------------------------------------------- | ---------------------------- |
|
||||||
| Single Term | banana | Nodes that contain the term **banana** in any content |
|
| Single Term | banana | Nodes that contain the term **banana** in any content | banana |
|
||||||
| Conjunction | big yellow banana | Nodes that contain all of the terms **big**, **yellow**, and **banana** |
|
| Conjunction | big yellow banana | Nodes that contain all of the terms **big**, **yellow**, and **banana** | big yellow banana |
|
||||||
| Phrase | "big yellow banana" | Nodes that contain all of the terms **big**, **yellow**, and **banana** |
|
| Phrase | "big yellow banana" | Nodes that contain the exact phrase **big yellow banana** | "big yellow banana" |
|
||||||
| Conjunction | big AND yellow AND banana | Nodes that contain all of the terms **big**, **yellow**, and **banana** |
|
| Conjunction | big AND yellow AND banana | Nodes that contain all of the terms **big**, **yellow**, and **banana** | big yellow banana |
|
||||||
| Disjunction | orange OR banana OR apple | Nodes that contain at least one of the terms **orange**, **banana** or **apple** |
|
| Disjunction | orange OR banana OR apple | Nodes that contain at least one of the terms **orange**, **banana** or **apple** | orange OR banana OR apple |
|
||||||
| Exact term | =orange | Nodes that contain the exact term **orange** in any content |
|
| Exact term | =orange | Nodes that contain the exact term **orange** in any content. | orange |
|
||||||
|
|
||||||
|
**Important note:** Consider using Search Logical Filter when you need to combine multiple search types. Mixing search types directly in the input may result in wrong query format and incorrect results.
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ describe('SearchUtils', () => {
|
|||||||
expect(isOperator(null)).toBeFalse();
|
expect(isOperator(null)).toBeFalse();
|
||||||
expect(isOperator(undefined)).toBeFalse();
|
expect(isOperator(undefined)).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should treat lowercase operators as search terms', () => {
|
||||||
|
expect(isOperator('and')).toBeFalse();
|
||||||
|
expect(isOperator('or')).toBeFalse();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('formatSearchTermByFields', () => {
|
describe('formatSearchTermByFields', () => {
|
||||||
@@ -109,8 +114,8 @@ describe('SearchUtils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should support exact term matching with operators', () => {
|
it('should support exact term matching with operators', () => {
|
||||||
expect(formatSearchTerm('=test1.pdf or =test2.pdf', ['cm:name', 'cm:title'])).toBe(
|
expect(formatSearchTerm('=test1.pdf OR =test2.pdf', ['cm:name', 'cm:title'])).toBe(
|
||||||
`(=cm:name:"test1.pdf" OR =cm:title:"test1.pdf") or (=cm:name:"test2.pdf" OR =cm:title:"test2.pdf")`
|
`(=cm:name:"test1.pdf" OR =cm:title:"test1.pdf") OR (=cm:name:"test2.pdf" OR =cm:title:"test2.pdf")`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -145,9 +150,9 @@ describe('SearchUtils', () => {
|
|||||||
expect(extractSearchedWordFromEncodedQuery(encodeQuery(query))).toBe('test');
|
expect(extractSearchedWordFromEncodedQuery(encodeQuery(query))).toBe('test');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should properly extract search term for custom search', () => {
|
it('should preserve quotes in search term for custom search', () => {
|
||||||
const query = { userQuery: '"test"' };
|
const query = { userQuery: '"test"' };
|
||||||
expect(extractSearchedWordFromEncodedQuery(encodeQuery(query))).toBe('test');
|
expect(extractSearchedWordFromEncodedQuery(encodeQuery(query))).toBe('"test"');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should properly extract search term when userQuery does not contain quotes', () => {
|
it('should properly extract search term when userQuery does not contain quotes', () => {
|
||||||
@@ -164,6 +169,25 @@ describe('SearchUtils', () => {
|
|||||||
const query = { userQuery: 'cm:name:"quoted term" AND TEXT:unquoted' };
|
const query = { userQuery: 'cm:name:"quoted term" AND TEXT:unquoted' };
|
||||||
expect(extractSearchedWordFromEncodedQuery(encodeQuery(query))).toBe('quoted term TEXT:unquoted');
|
expect(extractSearchedWordFromEncodedQuery(encodeQuery(query))).toBe('quoted term TEXT:unquoted');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle complex search query', () => {
|
||||||
|
const query = {
|
||||||
|
userQuery: `((cm:name:"a*" OR cm:title:"a*" OR cm:description:"a*" OR TEXT:"a*" OR TAG:"a*") AND
|
||||||
|
(cm:name:"b*" OR cm:title:"b*" OR cm:description:"b*" OR TEXT:"b*" OR TAG:"b*") OR
|
||||||
|
(cm:name:"c*" OR cm:title:"c*" OR cm:description:"c*" OR TEXT:"c*" OR TAG:"c*"))`
|
||||||
|
};
|
||||||
|
expect(extractSearchedWordFromEncodedQuery(encodeQuery(query))).toBe('a b OR c');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not treat operator as a searched word', () => {
|
||||||
|
const query = { userQuery: 'AND' };
|
||||||
|
expect(extractSearchedWordFromEncodedQuery(encodeQuery(query))).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not unquote when searching for phrase', () => {
|
||||||
|
const query = { userQuery: '"exact phrase search"' };
|
||||||
|
expect(extractSearchedWordFromEncodedQuery(encodeQuery(query))).toBe('"exact phrase search"');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('extractFiltersFromEncodedQuery', () => {
|
describe('extractFiltersFromEncodedQuery', () => {
|
||||||
|
|||||||
@@ -30,10 +30,8 @@
|
|||||||
*/
|
*/
|
||||||
export function isOperator(input: string): boolean {
|
export function isOperator(input: string): boolean {
|
||||||
if (input) {
|
if (input) {
|
||||||
input = input.trim().toUpperCase();
|
|
||||||
|
|
||||||
const operators = ['AND', 'OR'];
|
const operators = ['AND', 'OR'];
|
||||||
return operators.includes(input);
|
return operators.includes(input.trim());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -111,17 +109,54 @@ export function extractUserQueryFromEncodedQuery(encodedQuery: string): string {
|
|||||||
* @returns string
|
* @returns string
|
||||||
*/
|
*/
|
||||||
export function extractSearchedWordFromEncodedQuery(encodedQuery: string): string {
|
export function extractSearchedWordFromEncodedQuery(encodedQuery: string): string {
|
||||||
if (encodedQuery) {
|
if (!encodedQuery) {
|
||||||
const userQuery = extractUserQueryFromEncodedQuery(encodedQuery);
|
return '';
|
||||||
return userQuery !== '' && userQuery !== undefined
|
}
|
||||||
? userQuery
|
|
||||||
.split('AND')
|
const userQuery = extractUserQueryFromEncodedQuery(encodedQuery);
|
||||||
.map((searchCondition) => {
|
if (!userQuery) {
|
||||||
const searchTerm = searchCondition.includes('"') ? searchCondition.split('"')[1] : searchCondition.trim();
|
return '';
|
||||||
return searchTerm?.endsWith('*') && searchTerm !== '*' ? searchTerm.slice(0, -1) : searchTerm;
|
}
|
||||||
})
|
|
||||||
.join(' ')
|
const tokenRegex = /\(([^()]+)\)|\b(AND|OR)\b/g;
|
||||||
: '';
|
const fragments: string[] = [];
|
||||||
|
let match: RegExpExecArray | null;
|
||||||
|
|
||||||
|
while ((match = tokenRegex.exec(userQuery))) {
|
||||||
|
if (match[1]) {
|
||||||
|
fragments.push(extractWordFromQuery(match[1]));
|
||||||
|
} else if (match[2] === 'OR') {
|
||||||
|
fragments.push('OR');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fragments.length === 0) {
|
||||||
|
return userQuery
|
||||||
|
.split(/\bAND\b|\bOR\b/)
|
||||||
|
.map((part) => extractWordFromQuery(part))
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return fragments.join(' ').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the searched word from a part of search query
|
||||||
|
*
|
||||||
|
* @param queryPart encoded query
|
||||||
|
* @returns searched word
|
||||||
|
*/
|
||||||
|
function extractWordFromQuery(queryPart: string): string {
|
||||||
|
const regex = /:"([^"]+)"/;
|
||||||
|
const quoted = regex.exec(queryPart);
|
||||||
|
if (quoted) {
|
||||||
|
return quoted[1].replace(/\*$/, '');
|
||||||
|
}
|
||||||
|
const trimmedPart = queryPart.trim();
|
||||||
|
if (trimmedPart && !isOperator(trimmedPart)) {
|
||||||
|
return trimmedPart;
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user