/// <summary> /// Each of the query words must be found in at least one field ∀w ∃f /// </summary> public SearchResult IsMatch(BaseNode node) { SearchResult result = null; if (node == null) { return(null); } if (NodeIndex > -1) { if (node is TimedNode timedNode && timedNode.Index == NodeIndex) { result = new SearchResult(node); var prefix = "Node id: "; result.AddMatch(prefix + NodeIndex.ToString(), NodeIndex.ToString()); return(result); } } var searchFields = PopulateSearchFields(node); if (TypeKeyword != null) { // zeroth field is always the type if (string.Equals(TypeKeyword, searchFields.array[0], StringComparison.OrdinalIgnoreCase) || // special case for types derived from Task, $task should still work (TypeKeyword == "task" && searchFields.count > 1 && searchFields.array[1] == "Task")) { // this node is of the type that we need, search other fields if (result == null) { result = new SearchResult(node, IncludeDuration, IncludeStart, IncludeEnd); } result.AddMatchByNodeType(); } else { return(null); } } bool nameMatched = false; bool valueMatched = false; for (int i = 0; i < Words.Count; i++) { bool anyFieldMatched = false; var term = Words[i]; var word = term.Word; for (int j = 0; j < searchFields.count; j++) { var field = searchFields.array[j]; //if (!stringCache.Contains(field)) //{ //} if (!term.IsMatch(field, MatchesInStrings[i])) { continue; } if (result == null) { result = new SearchResult(node, IncludeDuration, IncludeStart, IncludeEnd); } // if matched on the type of the node (always field 0), special case it if (j == 0) { result.AddMatchByNodeType(); } else { string fullText = field; var nameValueNode = node as NameValueNode; // NameValueNode is a special case: have to check in which field to search if (nameValueNode != null && (nameToSearch != default || valueToSearch != default)) { if (j == 1 && term == nameToSearch) { result.AddMatch(fullText, word, addAtBeginning: true); nameMatched = true; anyFieldMatched = true; break; } if (j == 2 && term == valueToSearch) { result.AddMatch(fullText, word); valueMatched = true; anyFieldMatched = true; break; } } else { result.AddMatch(fullText, word); anyFieldMatched = true; break; } } } if (!anyFieldMatched) { return(null); } } if (result == null) { return(null); } if (nameToSearch != default && valueToSearch != default && (!nameMatched || !valueMatched)) { return(null); } bool showResult = IncludeMatchers.Count == 0; foreach (NodeQueryMatcher matcher in IncludeMatchers) { if (!showResult) { showResult = IsUnder(matcher, result); } } if (!showResult) { return(null); } foreach (NodeQueryMatcher matcher in ExcludeMatchers) { if (IsUnder(matcher, result)) { return(null); } } return(result); }