mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 05:28:59 +01:00 
			
		
		
		
	default search should look also into attribute names and values, #980
This commit is contained in:
		
							parent
							
								
									32020d78b5
								
							
						
					
					
						commit
						87a1e98fa2
					
				@ -12,7 +12,9 @@ const VIRTUAL_ATTRIBUTES = [
 | 
				
			|||||||
    "type",
 | 
					    "type",
 | 
				
			||||||
    "mime",
 | 
					    "mime",
 | 
				
			||||||
    "text",
 | 
					    "text",
 | 
				
			||||||
    "parentCount"
 | 
					    "parentCount",
 | 
				
			||||||
 | 
					    "attributeName",
 | 
				
			||||||
 | 
					    "attributeValue"
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = function(filters, selectedColumns = 'notes.*') {
 | 
					module.exports = function(filters, selectedColumns = 'notes.*') {
 | 
				
			||||||
@ -33,11 +35,29 @@ module.exports = function(filters, selectedColumns = 'notes.*') {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            // forcing to use particular index since SQLite query planner would often choose something pretty bad
 | 
					            // forcing to use particular index since SQLite query planner would often choose something pretty bad
 | 
				
			||||||
            joins[alias] = `LEFT JOIN attributes AS ${alias} INDEXED BY IDX_attributes_noteId_index `
 | 
					            joins[alias] = `LEFT JOIN attributes AS ${alias} INDEXED BY IDX_attributes_noteId_index `
 | 
				
			||||||
                + `ON ${alias}.noteId = notes.noteId `
 | 
					                + `ON ${alias}.noteId = notes.noteId AND ${alias}.isDeleted = 0`
 | 
				
			||||||
                + `AND ${alias}.name = '${property}' AND ${alias}.isDeleted = 0`;
 | 
					                + `AND ${alias}.name = '${property}' `;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            accessor = `${alias}.value`;
 | 
					            accessor = `${alias}.value`;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        else if (['attributeType', 'attributeName', 'attributeValue'].includes(property)) {
 | 
				
			||||||
 | 
					            const alias = "attr_filter";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!(alias in joins)) {
 | 
				
			||||||
 | 
					                joins[alias] = `LEFT JOIN attributes AS ${alias} INDEXED BY IDX_attributes_noteId_index `
 | 
				
			||||||
 | 
					                    + `ON ${alias}.noteId = notes.noteId AND ${alias}.isDeleted = 0`;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (property === 'attributeType') {
 | 
				
			||||||
 | 
					                accessor = `${alias}.type`
 | 
				
			||||||
 | 
					            } else if (property === 'attributeName') {
 | 
				
			||||||
 | 
					                accessor = `${alias}.name`
 | 
				
			||||||
 | 
					            } else if (property === 'attributeValue') {
 | 
				
			||||||
 | 
					                accessor = `${alias}.value`
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                throw new Error(`Unrecognized property ${property}`);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        else if (property === 'content') {
 | 
					        else if (property === 'content') {
 | 
				
			||||||
            const alias = "note_contents";
 | 
					            const alias = "note_contents";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -73,79 +93,85 @@ module.exports = function(filters, selectedColumns = 'notes.*') {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let where = '1';
 | 
					 | 
				
			||||||
    const params = [];
 | 
					    const params = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const filter of filters) {
 | 
					    function parseWhereFilters(filters) {
 | 
				
			||||||
        if (['isarchived', 'in', 'orderby', 'limit'].includes(filter.name.toLowerCase())) {
 | 
					        let whereStmt = '';
 | 
				
			||||||
            continue; // these are not real filters
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        where += " " + filter.relation + " ";
 | 
					        for (const filter of filters) {
 | 
				
			||||||
 | 
					            if (['isarchived', 'in', 'orderby', 'limit'].includes(filter.name.toLowerCase())) {
 | 
				
			||||||
 | 
					                continue; // these are not real filters
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const accessor = getAccessor(filter.name);
 | 
					            if (whereStmt) {
 | 
				
			||||||
 | 
					                whereStmt += " " + filter.relation + " ";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (filter.operator === 'exists') {
 | 
					            if (filter.children) {
 | 
				
			||||||
            where += `${accessor} IS NOT NULL`;
 | 
					                whereStmt += "(" + parseWhereFilters(filter.children) + ")";
 | 
				
			||||||
        }
 | 
					                continue;
 | 
				
			||||||
        else if (filter.operator === 'not-exists') {
 | 
					            }
 | 
				
			||||||
            where += `${accessor} IS NULL`;
 | 
					
 | 
				
			||||||
        }
 | 
					            const accessor = getAccessor(filter.name);
 | 
				
			||||||
        else if (filter.operator === '=' || filter.operator === '!=') {
 | 
					
 | 
				
			||||||
            where += `${accessor} ${filter.operator} ?`;
 | 
					            if (filter.operator === 'exists') {
 | 
				
			||||||
            params.push(filter.value);
 | 
					                whereStmt += `${accessor} IS NOT NULL`;
 | 
				
			||||||
        }
 | 
					            } else if (filter.operator === 'not-exists') {
 | 
				
			||||||
        else if (filter.operator === '*=' || filter.operator === '!*=') {
 | 
					                whereStmt += `${accessor} IS NULL`;
 | 
				
			||||||
            where += `${accessor}`
 | 
					            } else if (filter.operator === '=' || filter.operator === '!=') {
 | 
				
			||||||
 | 
					                whereStmt += `${accessor} ${filter.operator} ?`;
 | 
				
			||||||
 | 
					                params.push(filter.value);
 | 
				
			||||||
 | 
					            } else if (filter.operator === '*=' || filter.operator === '!*=') {
 | 
				
			||||||
 | 
					                whereStmt += `${accessor}`
 | 
				
			||||||
                    + (filter.operator.includes('!') ? ' NOT' : '')
 | 
					                    + (filter.operator.includes('!') ? ' NOT' : '')
 | 
				
			||||||
                    + ` LIKE ` + utils.prepareSqlForLike('%', filter.value, '');
 | 
					                    + ` LIKE ` + utils.prepareSqlForLike('%', filter.value, '');
 | 
				
			||||||
        }
 | 
					            } else if (filter.operator === '=*' || filter.operator === '!=*') {
 | 
				
			||||||
        else if (filter.operator === '=*' || filter.operator === '!=*') {
 | 
					                whereStmt += `${accessor}`
 | 
				
			||||||
            where += `${accessor}`
 | 
					 | 
				
			||||||
                    + (filter.operator.includes('!') ? ' NOT' : '')
 | 
					                    + (filter.operator.includes('!') ? ' NOT' : '')
 | 
				
			||||||
                    + ` LIKE ` + utils.prepareSqlForLike('', filter.value, '%');
 | 
					                    + ` LIKE ` + utils.prepareSqlForLike('', filter.value, '%');
 | 
				
			||||||
        }
 | 
					            } else if (filter.operator === '*=*' || filter.operator === '!*=*') {
 | 
				
			||||||
        else if (filter.operator === '*=*' || filter.operator === '!*=*') {
 | 
					                const columns = filter.name === 'text' ? [getAccessor("title"), getAccessor("content")] : [accessor];
 | 
				
			||||||
            const columns = filter.name === 'text' ? [getAccessor("title"), getAccessor("content")] : [accessor];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let condition = "(" + columns.map(column =>
 | 
					                let condition = "(" + columns.map(column =>
 | 
				
			||||||
                `${column}` + ` LIKE ` + utils.prepareSqlForLike('%', filter.value, '%'))
 | 
					                    `${column}` + ` LIKE ` + utils.prepareSqlForLike('%', filter.value, '%'))
 | 
				
			||||||
                .join(" OR ") + ")";
 | 
					                    .join(" OR ") + ")";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (filter.operator.includes('!')) {
 | 
					                if (filter.operator.includes('!')) {
 | 
				
			||||||
                condition = "NOT(" + condition + ")";
 | 
					                    condition = "NOT(" + condition + ")";
 | 
				
			||||||
            }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (['text', 'title', 'content'].includes(filter.name)) {
 | 
					                if (['text', 'title', 'content'].includes(filter.name)) {
 | 
				
			||||||
                // for title/content search does not make sense to search for protected notes
 | 
					                    // for title/content search does not make sense to search for protected notes
 | 
				
			||||||
                condition = `(${condition} AND notes.isProtected = 0)`;
 | 
					                    condition = `(${condition} AND notes.isProtected = 0)`;
 | 
				
			||||||
            }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            where += condition;
 | 
					                whereStmt += condition;
 | 
				
			||||||
        }
 | 
					            } else if ([">", ">=", "<", "<="].includes(filter.operator)) {
 | 
				
			||||||
        else if ([">", ">=", "<", "<="].includes(filter.operator)) {
 | 
					                let floatParam;
 | 
				
			||||||
            let floatParam;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // from https://stackoverflow.com/questions/12643009/regular-expression-for-floating-point-numbers
 | 
					                // from https://stackoverflow.com/questions/12643009/regular-expression-for-floating-point-numbers
 | 
				
			||||||
            if (/^[+-]?([0-9]*[.])?[0-9]+$/.test(filter.value)) {
 | 
					                if (/^[+-]?([0-9]*[.])?[0-9]+$/.test(filter.value)) {
 | 
				
			||||||
                floatParam = parseFloat(filter.value);
 | 
					                    floatParam = parseFloat(filter.value);
 | 
				
			||||||
            }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (floatParam === undefined || isNaN(floatParam)) {
 | 
					                if (floatParam === undefined || isNaN(floatParam)) {
 | 
				
			||||||
                // if the value can't be parsed as float then we assume that string comparison should be used instead of numeric
 | 
					                    // if the value can't be parsed as float then we assume that string comparison should be used instead of numeric
 | 
				
			||||||
                where += `${accessor} ${filter.operator} ?`;
 | 
					                    whereStmt += `${accessor} ${filter.operator} ?`;
 | 
				
			||||||
                params.push(filter.value);
 | 
					                    params.push(filter.value);
 | 
				
			||||||
            }
 | 
					                } else {
 | 
				
			||||||
            else {
 | 
					                    whereStmt += `CAST(${accessor} AS DECIMAL) ${filter.operator} ?`;
 | 
				
			||||||
                where += `CAST(${accessor} AS DECIMAL) ${filter.operator} ?`;
 | 
					                    params.push(floatParam);
 | 
				
			||||||
                params.push(floatParam);
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                throw new Error("Unknown operator " + filter.operator);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else {
 | 
					
 | 
				
			||||||
            throw new Error("Unknown operator " + filter.operator);
 | 
					        return whereStmt;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const where = parseWhereFilters(filters);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (orderBy.length === 0) {
 | 
					    if (orderBy.length === 0) {
 | 
				
			||||||
        // if no ordering is given then order at least by note title
 | 
					        // if no ordering is given then order at least by note title
 | 
				
			||||||
        orderBy.push("notes.title");
 | 
					        orderBy.push("notes.title");
 | 
				
			||||||
 | 
				
			|||||||
@ -60,6 +60,20 @@ module.exports = function (searchText) {
 | 
				
			|||||||
                operator: '*=*',
 | 
					                operator: '*=*',
 | 
				
			||||||
                value: searchText
 | 
					                value: searchText
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            filters.push({
 | 
				
			||||||
 | 
					                relation: 'or',
 | 
				
			||||||
 | 
					                name: 'attributeName',
 | 
				
			||||||
 | 
					                operator: '*=*',
 | 
				
			||||||
 | 
					                value: searchText
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            filters.push({
 | 
				
			||||||
 | 
					                relation: 'or',
 | 
				
			||||||
 | 
					                name: 'attributeValue',
 | 
				
			||||||
 | 
					                operator: '*=*',
 | 
				
			||||||
 | 
					                value: searchText
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else {
 | 
					        else {
 | 
				
			||||||
            const tokens = searchText.split(/\s+/);
 | 
					            const tokens = searchText.split(/\s+/);
 | 
				
			||||||
@ -67,9 +81,27 @@ module.exports = function (searchText) {
 | 
				
			|||||||
            for (const token of tokens) {
 | 
					            for (const token of tokens) {
 | 
				
			||||||
                filters.push({
 | 
					                filters.push({
 | 
				
			||||||
                    relation: 'and',
 | 
					                    relation: 'and',
 | 
				
			||||||
                    name: 'text',
 | 
					                    name: 'sub',
 | 
				
			||||||
                    operator: '*=*',
 | 
					                    children: [
 | 
				
			||||||
                    value: token
 | 
					                        {
 | 
				
			||||||
 | 
					                            relation: 'or',
 | 
				
			||||||
 | 
					                            name: 'text',
 | 
				
			||||||
 | 
					                            operator: '*=*',
 | 
				
			||||||
 | 
					                            value: token
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            relation: 'or',
 | 
				
			||||||
 | 
					                            name: 'attributeName',
 | 
				
			||||||
 | 
					                            operator: '*=*',
 | 
				
			||||||
 | 
					                            value: token
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            relation: 'or',
 | 
				
			||||||
 | 
					                            name: 'attributeValue',
 | 
				
			||||||
 | 
					                            operator: '*=*',
 | 
				
			||||||
 | 
					                            value: token
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user