/// <summary> /// Adds the content sub query. /// </summary> /// <param name="query">The boolean query.</param> /// <param name="key">The field key.</param> /// <param name="value">The field value.</param> /// <param name="matchVariant">The match variant.</param> /// <param name="condition">The condition.</param> /// <param name="isFirst">if set to <c>true</c> [is first].</param> private void AddContentSubQuery(LuceneSearch.BooleanQuery query, string key, string value, MatchVariant matchVariant, QueryCondition condition) { if (matchVariant == MatchVariant.NotEquals) { query.Add(new LuceneSearch.TermQuery(new Term(key, value)), LuceneSearch.Occur.MUST_NOT); return; } LuceneSearch.Occur occurrence = this.GetOccur(condition); LuceneSearch.TermRangeQuery rangeQuery = this.GetRangeQuery(key, value, matchVariant); if (rangeQuery != null) { query.Add(rangeQuery, occurrence); return; } string[] keywords = value.Split(' '); if (keywords.Length > 1) { LuceneSearch.PhraseQuery phraseQuery = new Lucene.Net.Search.PhraseQuery(); foreach (string keyword in keywords) { phraseQuery.Add(new Term(key, keyword)); } query.Add(phraseQuery, occurrence); } else if (matchVariant == MatchVariant.Like) { query.Add(new LuceneSearch.WildcardQuery(new Term(key, value + "*")), occurrence); } else { query.Add(new LuceneSearch.TermQuery(new Term(key, value)), occurrence); } }
/// <summary> /// Adds the term query. /// </summary> /// <param name="query">The query.</param> /// <param name="subquery">The subquery.</param> /// <param name="condition">The condition.</param> /// <param name="isFirst">if set to <c>true</c> [is first].</param> protected virtual void AddSubQuery(LuceneSearch.BooleanQuery query, SubQuery subquery, QueryCondition condition) { string value = ID.IsID(subquery.Value) ? subquery.Value : subquery.Value.ToLower(); string key = subquery.Key.ToLower(); if (subquery is AttributeQuery) { key = this.MapAttributes(key); } // optimizing search query if (key == Sitecore.Search.BuiltinFields.ID || key == Sitecore.Search.BuiltinFields.Template || key == BuiltinFields.ProductBaseTemplate || key == Sitecore.Search.BuiltinFields.AllTemplates) { this.AddIdQuery(query, key, value, condition); } else { this.AddContentSubQuery(query, key, value, subquery.MatchVariant, condition); } }
/// <summary> /// Adds the simple query. /// </summary> /// <param name="query">The boolean query.</param> /// <param name="key">The field key.</param> /// <param name="value">The field value.</param> /// <param name="condition">The condition.</param> /// <param name="isFirst">if set to <c>true</c> [is first].</param> private void AddIdQuery(LuceneSearch.BooleanQuery query, string key, string value, QueryCondition condition) { value = this.GetItemPath(new ID(value)); LuceneSearch.Occur occurrence = this.GetOccur(condition); query.Add(new LuceneSearch.TermQuery(new Term(key, value)), occurrence); }
private void CreateQueryWhitelists (ICollection search_subset_uris, LNS.IndexSearcher primary_searcher, LNS.IndexSearcher secondary_searcher, LNS.BooleanQuery primary_prohibited_part_query, LNS.BooleanQuery secondary_prohibited_part_query, out LuceneBitArray primary_whitelist, out LuceneBitArray secondary_whitelist) { primary_whitelist = null; secondary_whitelist = null; if (search_subset_uris != null && search_subset_uris.Count > 0) { primary_whitelist = new LuceneBitArray (primary_searcher); if (secondary_searcher != null) secondary_whitelist = new LuceneBitArray (secondary_searcher); foreach (Uri uri in search_subset_uris) { primary_whitelist.AddUri (uri); if (secondary_whitelist != null) secondary_whitelist.AddUri (uri); } primary_whitelist.FlushUris (); if (secondary_whitelist != null) secondary_whitelist.FlushUris (); } // Build blacklists from our prohibited parts. LuceneBitArray primary_blacklist = null; LuceneBitArray secondary_blacklist = null; if (primary_prohibited_part_query != null) { primary_blacklist = new LuceneBitArray (primary_searcher, primary_prohibited_part_query); if (secondary_searcher != null) { secondary_blacklist = new LuceneBitArray (secondary_searcher); if (secondary_prohibited_part_query != null) secondary_blacklist.Or (secondary_prohibited_part_query); primary_blacklist.Join (secondary_blacklist); } } // Combine our whitelist and blacklist into just a whitelist. if (primary_blacklist != null) { if (primary_whitelist == null) { primary_blacklist.Not (); primary_whitelist = primary_blacklist; } else { primary_whitelist.AndNot (primary_blacklist); } } if (secondary_blacklist != null) { if (secondary_whitelist == null) { secondary_blacklist.Not (); secondary_whitelist = secondary_blacklist; } else { secondary_whitelist.AndNot (secondary_blacklist); } } }
/// <summary> /// Builds the query. /// </summary> /// <param name="query">The result query.</param> /// <param name="node">The query node.</param> protected virtual void BuildQuery(LuceneSearch.BooleanQuery query, QueryNode node) { Query subQuery = node.Element as Query; if (subQuery != null && !subQuery.IsEmpty()) { LuceneSearch.BooleanQuery booleanQuery = new LuceneSearch.BooleanQuery(); if (!string.IsNullOrEmpty(subQuery.SearchRoot)) { this.AddSearchRoot(booleanQuery, subQuery.SearchRoot); } this.BuildQuery(booleanQuery, subQuery.FirstNode); LuceneSearch.Occur occurance = LuceneSearch.Occur.MUST; if (node.IsFirst) { if (!node.IsLast && (node.NextNode.Element is Condition)) { occurance = this.GetOccur(((Condition)node.NextNode.Element).QueryCondition); } } else { if (!node.IsFirst && (node.PreviousNode.Element is Condition)) { occurance = this.GetOccur(((Condition)node.PreviousNode.Element).QueryCondition); } } query.Add(booleanQuery, occurance); } else if (node.Element is AttributeQuery || node.Element is FieldQuery) { QueryCondition condition = QueryCondition.And; if (node.IsFirst) { if (!node.IsLast && (node.NextNode.Element is Condition)) { condition = ((Condition)node.NextNode.Element).QueryCondition; } } else { if (!node.IsFirst && (node.PreviousNode.Element is Condition)) { condition = ((Condition)node.PreviousNode.Element).QueryCondition; } } this.AddSubQuery(query, node.Element as SubQuery, condition); } if (!node.IsLast) { this.BuildQuery(query, node.NextNode); } this.resultQuery = query; }
static protected LNS.Query UriQuery (string field_name, ICollection uri_list, LNS.Query extra_requirement) { if (uri_list.Count == 0) return null; int max_clauses; max_clauses = LNS.BooleanQuery.GetMaxClauseCount (); int N; N = 1 + (uri_list.Count - 1) / max_clauses; LNS.BooleanQuery top_query; top_query = new LNS.BooleanQuery (); int cursor = 0; if (extra_requirement != null) { top_query.Add (extra_requirement, LNS.BooleanClause.Occur.MUST); ++cursor; } ArrayList bottom_queries = null; if (N > 1) { bottom_queries = new ArrayList (); for (int i = 0; i < N; ++i) { LNS.BooleanQuery bq; bq = new LNS.BooleanQuery (); bottom_queries.Add (bq); top_query.Add (bq, LNS.BooleanClause.Occur.SHOULD); } } foreach (Uri uri in uri_list) { LNS.Query subquery; subquery = UriQuery (field_name, uri); LNS.BooleanQuery target; if (N == 1) target = top_query; else { target = (LNS.BooleanQuery) bottom_queries [cursor]; ++cursor; if (cursor >= N) cursor = 0; } target.Add (subquery, LNS.BooleanClause.Occur.SHOULD); } return top_query; }
private void Explain (LNS.Query query) { int j = 0; while (j < collector.Array.Count) { int i; i = collector.Array.GetNextTrueIndex (j); if (i >= collector.Array.Count) break; j = i + 1; Document doc = searcher.Doc (i); LNS.Explanation exp = searcher.Explain (query, i); Log.Debug ("Query: [{0}]", query); Log.Debug ("Matching URI: {0}", doc.Get ("Uri")); Log.Debug ("Explanation: {0}", exp); } }
// Any whitelists that are passed in must be fully joined, or // query results will be incorrect. private static BetterBitArray DoRequiredQueries_TwoIndex (LNS.IndexSearcher primary_searcher, LNS.IndexSearcher secondary_searcher, ArrayList primary_queries, ArrayList secondary_queries, BetterBitArray primary_whitelist, BetterBitArray secondary_whitelist) { ArrayList match_info_list; match_info_list = new ArrayList (); // First, do all of the low-level queries // and store them in our MatchInfo for (int i = 0; i < primary_queries.Count; ++i) { LNS.Query pq, sq; pq = primary_queries [i] as LNS.Query; sq = secondary_queries [i] as LNS.Query; LuceneBitArray p_matches = null, s_matches = null; p_matches = new LuceneBitArray (primary_searcher); if (pq != null) { p_matches.Or (pq); if (primary_whitelist != null) p_matches.And (primary_whitelist); } s_matches = new LuceneBitArray (secondary_searcher); if (sq != null) { s_matches.Or (sq); if (secondary_whitelist != null) s_matches.And (secondary_whitelist); } MatchInfo info; info = new MatchInfo (); info.PrimaryMatches = p_matches; info.SecondaryMatches = s_matches; info.RestrictBy (null); // a hack to initialize the UpperBound match_info_list.Add (info); } // We want to be smart about the order we do this in, // to minimize the expense of the Join. while (match_info_list.Count > 1) { // linear scan to find the minimum int index_min = 0; for (int i = 1; i < match_info_list.Count; ++i) if (((MatchInfo) match_info_list [i]).CompareTo ((MatchInfo) match_info_list [index_min]) < 0) index_min = i; MatchInfo smallest; smallest = match_info_list [index_min] as MatchInfo; match_info_list.RemoveAt (index_min); // We can short-circuit if our smallest set of // matches is empty. if (smallest.UpperBound == 0) return smallest.PrimaryMatches; // this must be an empty array. smallest.Join (); foreach (MatchInfo info in match_info_list) info.RestrictBy (smallest); } // For the final pair, we don't need to do a full join: // mapping the secondary onto the primary is sufficient MatchInfo last; last = match_info_list [0] as MatchInfo; last.SecondaryMatches.ProjectOnto (last.PrimaryMatches); return last.PrimaryMatches; }
//////////////////////////////////////////////////////////////// // Returns the lists of terms in the query private ArrayList AssembleQuery (Query query, QueryPartHook query_part_hook, HitFilter hit_filter, out ArrayList primary_required_part_queries, out ArrayList secondary_required_part_queries, out LNS.BooleanQuery primary_prohibited_part_query, out LNS.BooleanQuery secondary_prohibited_part_query, out AndHitFilter all_hit_filters) { primary_required_part_queries = null; secondary_required_part_queries = null; primary_prohibited_part_query = null; secondary_prohibited_part_query = null; all_hit_filters = new AndHitFilter (); if (hit_filter != null) all_hit_filters.Add (hit_filter); ArrayList term_list = new ArrayList (); foreach (QueryPart part in query.Parts) { LNS.Query primary_part_query; LNS.Query secondary_part_query; HitFilter part_hit_filter; QueryPartToQuery (part, false, // we want both primary and secondary queries part.Logic == QueryPartLogic.Required ? term_list : null, query_part_hook, out primary_part_query, out secondary_part_query, out part_hit_filter); if (primary_part_query == null) continue; switch (part.Logic) { case QueryPartLogic.Required: if (primary_required_part_queries == null) { primary_required_part_queries = new ArrayList (); secondary_required_part_queries = new ArrayList (); } primary_required_part_queries.Add (primary_part_query); secondary_required_part_queries.Add (secondary_part_query); if (part_hit_filter != null) all_hit_filters.Add (part_hit_filter); break; case QueryPartLogic.Prohibited: if (primary_prohibited_part_query == null) primary_prohibited_part_query = new LNS.BooleanQuery (); primary_prohibited_part_query.Add (primary_part_query, LNS.BooleanClause.Occur.SHOULD); if (secondary_part_query != null) { if (secondary_prohibited_part_query == null) secondary_prohibited_part_query = new LNS.BooleanQuery (); secondary_prohibited_part_query.Add (secondary_part_query, LNS.BooleanClause.Occur.SHOULD); } if (part_hit_filter != null) { NotHitFilter nhf; nhf = new NotHitFilter (part_hit_filter); all_hit_filters.Add (new HitFilter (nhf.HitFilter)); } break; } } return term_list; }
public LuceneBitArray And (LNS.Query query) { UseScratch (); searcher.Search (query, null, collector); if (Debug) Explain (query); this.And (scratch); return this; }
//////////////////////////////////////////////////////////////// // // Special logic for handling our set of required queries // // This is the easy case: we just combine all of the queries // into one big BooleanQuery. private static BetterBitArray DoRequiredQueries (LNS.IndexSearcher primary_searcher, ArrayList primary_queries, BetterBitArray primary_whitelist) { LNS.BooleanQuery combined_query; combined_query = new LNS.BooleanQuery (); foreach (LNS.Query query in primary_queries) combined_query.Add (query, LNS.BooleanClause.Occur.MUST); LuceneBitArray matches; matches = new LuceneBitArray (primary_searcher, combined_query); if (primary_whitelist != null) matches.And (primary_whitelist); return matches; }
public LuceneBitArray Search (LNS.Query query) { this.SetAll (false); this.Or (query); return this; }
public LuceneBitArray (LNS.IndexSearcher searcher, LNS.Query query) : this (searcher) { this.Or (query); }
public LuceneBitArray (LNS.IndexSearcher searcher) : base (searcher.MaxDoc ()) { this.searcher = searcher; this.collector = new BitArrayHitCollector (); this.scratch = null; }
/// <summary> /// Adds the serch root. /// </summary> /// <param name="query">The query.</param> /// <param name="searchRoot">The search root.</param> protected virtual void AddSearchRoot(LuceneSearch.BooleanQuery query, string searchRoot) { if (!string.IsNullOrEmpty(searchRoot)) { if (ID.IsID(searchRoot)) { searchRoot = this.GetItemPath(new ID(searchRoot)); } else { Item rootItem = this.Database.SelectSingleItem(searchRoot); if (rootItem != null) { searchRoot = this.GetItemPath(rootItem.ID); } } query.Add(new LuceneSearch.TermQuery(new Term(Sitecore.Search.BuiltinFields.Path, searchRoot)), LuceneSearch.Occur.MUST); } }
// Returns true if there are docs to search and creates the readers and searchers // in that case. Otherwise, returns false. private bool BuildSearchers (out IndexReader primary_reader, out LNS.IndexSearcher primary_searcher, out IndexReader secondary_reader, out LNS.IndexSearcher secondary_searcher) { primary_searcher = null; secondary_reader = null; secondary_searcher = null; primary_reader = LuceneCommon.GetReader (PrimaryStore); if (primary_reader.NumDocs() == 0) { ReleaseReader (primary_reader); primary_reader = null; return false; } primary_searcher = new LNS.IndexSearcher (primary_reader); if (SecondaryStore != null) { secondary_reader = LuceneCommon.GetReader (SecondaryStore); if (secondary_reader.NumDocs () == 0) { ReleaseReader (secondary_reader); secondary_reader = null; } } if (secondary_reader != null) secondary_searcher = new LNS.IndexSearcher (secondary_reader); return true; }
// search_subset_uris is a list of Uris that this search should be // limited to. static protected void QueryPartToQuery (QueryPart abstract_part, bool only_build_primary_query, ArrayList term_list, QueryPartHook query_part_hook, out LNS.Query primary_query, out LNS.Query secondary_query, out HitFilter hit_filter) { primary_query = null; secondary_query = null; // By default, we assume that our lucene queries will return exactly the // matching set of objects. We need to set the hit filter if further // refinement of the search results is required. (As in the case of // date range queries, for example.) We essentially have to do this // to make OR queries work correctly. hit_filter = true_hit_filter; // The exception is when dealing with a prohibited part. Just return // null for the hit filter in that case. This works since // prohibited parts are not allowed inside of OR queries. if (abstract_part.Logic == QueryPartLogic.Prohibited) hit_filter = null; if (abstract_part == null) return; // Run the backend hook first. // This gives a chance to modify create new queries based on // backend specific properties if (query_part_hook != null) abstract_part = query_part_hook (abstract_part); if (abstract_part == null) return; if (abstract_part is QueryPart_Text) { QueryPart_Text part = (QueryPart_Text) abstract_part; if (! (part.SearchFullText || part.SearchTextProperties)) return; LNS.BooleanQuery p_query = new LNS.BooleanQuery (); LNS.BooleanQuery s_query = new LNS.BooleanQuery (); bool added_subquery = false; if (part.SearchFullText) { LNS.Query subquery; subquery = StringToQuery ("Text", part.Text, term_list); if (subquery != null) { p_query.Add (subquery, LNS.BooleanClause.Occur.SHOULD); added_subquery = true; } // FIXME: HotText is ignored for now! // subquery = StringToQuery ("HotText", part.Text); // if (subquery != null) { // p_query.Add (subquery, LNS.BooleanClause.Occur.SHOULD); // added_subquery = true; // } } if (part.SearchTextProperties) { LNS.Query subquery; subquery = StringToQuery ("PropertyText", part.Text, term_list); if (subquery != null) { p_query.Add (subquery, LNS.BooleanClause.Occur.SHOULD); // Properties can live in either index if (! only_build_primary_query) s_query.Add (subquery.Clone () as LNS.Query, LNS.BooleanClause.Occur.SHOULD); added_subquery = true; } // The "added_subquery" check is to handle the situation where // a part of the text is a stop word. Normally, a search for // "hello world" would break down into this query: // // (Text:hello OR PropertyText:hello OR PropertyKeyword:hello) // AND (Text:world OR PropertText:world OR PropertyKeyword:world) // // This fails with stop words, though. Let's assume that "world" // is a stop word. You would end up with: // // (Text:hello OR PropertyText:hello OR PropertyKeyword:hello) // AND (PropertyKeyword:world) // // Which is not what we want. We'd want to match documents that // had only "hello" without also having a keyword "world". In // this case, don't create the PropertyKeyword part of the query, // since it would be included in the larger set if it weren't // required anyway. if (added_subquery) { Term term; term = new Term ("PropertyKeyword", part.Text.ToLower ()); // make sure text is lowercased // FIXME: terms are already added in term_list. But they may have been tokenized // The term here is non-tokenized version. Should this be added to term_list ? // term_list is used to calculate scores if (term_list != null) term_list.Add (term); subquery = new LNS.TermQuery (term); p_query.Add (subquery, LNS.BooleanClause.Occur.SHOULD); // Properties can live in either index if (! only_build_primary_query) s_query.Add (subquery.Clone () as LNS.Query, LNS.BooleanClause.Occur.SHOULD); } else { // Reset these so we return a null query p_query = null; s_query = null; } } primary_query = p_query; if (! only_build_primary_query) secondary_query = s_query; return; } if (abstract_part is QueryPart_Wildcard) { QueryPart_Wildcard part = (QueryPart_Wildcard) abstract_part; LNS.BooleanQuery p_query = new LNS.BooleanQuery (); LNS.BooleanQuery s_query = new LNS.BooleanQuery (); Term term; LNS.Query subquery; // Lower case the terms for searching string query_string_lower = part.QueryString.ToLower (); // Search text content if (! part.PropertyOnly) { term = new Term ("Text", query_string_lower); subquery = new LNS.WildcardQuery (term); p_query.Add (subquery, LNS.BooleanClause.Occur.SHOULD); term_list.Add (term); } // Search text properties term = new Term ("PropertyText", query_string_lower); subquery = new LNS.WildcardQuery (term); p_query.Add (subquery, LNS.BooleanClause.Occur.SHOULD); // Properties can live in either index if (! only_build_primary_query) s_query.Add (subquery.Clone () as LNS.Query, LNS.BooleanClause.Occur.SHOULD); term_list.Add (term); if (! part.PropertyOnly) { // Search property keywords term = new Term ("PropertyKeyword", query_string_lower); term_list.Add (term); subquery = new LNS.WildcardQuery (term); p_query.Add (subquery, LNS.BooleanClause.Occur.SHOULD); // Properties can live in either index if (! only_build_primary_query) s_query.Add (subquery.Clone () as LNS.Query, LNS.BooleanClause.Occur.SHOULD); } primary_query = p_query; if (! only_build_primary_query) secondary_query = s_query; return; } if (abstract_part is QueryPart_DateRange) { QueryPart_DateRange part = (QueryPart_DateRange) abstract_part; // FIXME: We don't handle prohibited queries with sub-date // accuracy. For example, if we say we prohibit matches // between 5 May 2007 at 2 PM and 8 May at 5 AM, we'll // miss any matches that happen between midnight and 2 PM // on 5 May 2007 and between midnight and 5 AM on 8 May. primary_query = GetDateRangeQuery (part, out hit_filter); // Date properties can live in either index if (! only_build_primary_query && primary_query != null) secondary_query = primary_query.Clone () as LNS.Query; return; } if (abstract_part is QueryPart_Or) { QueryPart_Or part = (QueryPart_Or) abstract_part; // Assemble a new BooleanQuery combining all of the sub-parts. LNS.BooleanQuery p_query; p_query = new LNS.BooleanQuery (); LNS.BooleanQuery s_query = null; if (! only_build_primary_query) s_query = new LNS.BooleanQuery (); primary_query = p_query; secondary_query = s_query; OrHitFilter or_hit_filter = null; foreach (QueryPart sub_part in part.SubParts) { LNS.Query p_subq, s_subq; HitFilter sub_hit_filter; // FIXME: This is (and must be) ignored // FIXME: Any subpart in an OR which has a hit filter won't work // correctly, because we can't tell which part of an OR we matched // against to filter correctly. This affects date range queries. QueryPartToQuery (sub_part, only_build_primary_query, term_list, query_part_hook, out p_subq, out s_subq, out sub_hit_filter); if (p_subq != null) p_query.Add (p_subq, LNS.BooleanClause.Occur.SHOULD); if (s_subq != null) s_query.Add (s_subq, LNS.BooleanClause.Occur.SHOULD); if (sub_hit_filter != null) { if (or_hit_filter == null) or_hit_filter = new OrHitFilter (); or_hit_filter.Add (sub_hit_filter); } } if (or_hit_filter != null) hit_filter = new HitFilter (or_hit_filter.HitFilter); return; } if (abstract_part is QueryPart_Uri) { QueryPart_Uri part = (QueryPart_Uri) abstract_part; // Do a term query on the Uri field. // This is probably less efficient that using a TermEnum; // but this is required for the query API where the uri query // can be part of a prohibited query or a boolean or query. Term term; term = new Term ("Uri", UriFu.UriToEscapedString (part.Uri)); if (term_list != null) term_list.Add (term); primary_query = new LNS.TermQuery (term); // Query only the primary index return; } if (abstract_part is QueryPart_Property) { QueryPart_Property part = (QueryPart_Property) abstract_part; string field_name; if (part.Key == QueryPart_Property.AllProperties) field_name = TypeToWildcardField (part.Type); else field_name = PropertyToFieldName (part.Type, part.Key); // Details of the conversion here depends on BeagrepAnalyzer::TokenStream if (part.Type == PropertyType.Text) primary_query = StringToQuery (field_name, part.Value, term_list); else { Term term; // FIXME: Handle date queries for other date fields if (part.Type == PropertyType.Internal || field_name.StartsWith ("prop:k:" + Property.PrivateNamespace)) term = new Term (field_name, part.Value); else term = new Term (field_name, part.Value.ToLower ()); if (term_list != null) term_list.Add (term); primary_query = new LNS.TermQuery (term); } // Properties can live in either index if (! only_build_primary_query && primary_query != null) secondary_query = primary_query.Clone () as LNS.Query; return; } throw new Exception ("Unhandled QueryPart type! " + abstract_part.ToString ()); }
private void CloseSearchers (IndexReader primary_reader, LNS.IndexSearcher primary_searcher, IndexReader secondary_reader, LNS.IndexSearcher secondary_searcher) { primary_searcher.Close (); if (secondary_searcher != null) secondary_searcher.Close (); ReleaseReader (primary_reader); if (secondary_reader != null) ReleaseReader (secondary_reader); }
static public void ReleaseSearcher (LNS.IndexSearcher searcher) { IndexReader reader = searcher.GetIndexReader (); searcher.Close (); ReleaseReader (reader); }
public LuceneBitArray Or (LNS.Query query) { collector.Array = this; searcher.Search (query, null, collector); if (Debug) Explain (query); return this; }