public void GetPostingsTest_AndQueryExist_ReturnsPostings() { IList <Posting> result; //1. Some postings overlap AndQuery andQuery1 = new AndQuery(new List <IQueryComponent> { new TermLiteral("snow"), //0,1,2,4 new TermLiteral("mystery") //1,3,4- }); result = andQuery1.GetPostings(index, processor); result.Should().HaveCount(2, "because 2 postings contain 'snow' and 'mystery'"); Console.Write(andQuery1.ToString() + "\t"); PrintPostingResult(result); //2. All postings overlaps AndQuery andQuery2 = new AndQuery(new List <IQueryComponent> { new TermLiteral("full"), new TermLiteral("of") }); result = andQuery2.GetPostings(index, processor); result.Should().HaveCount(2, "because all posting from 'full' and 'of' are the same"); Console.Write(andQuery2.ToString() + "\t"); PrintPostingResult(result); }
IList <PackedObject> ProcessAndQuery(AndQuery query, ExecutionPlan executionPlan) { if (query.Elements.Count == 1) { return(ProcessSimpleQuery(query.Elements[0], executionPlan)); } var queryExecutionPlan = new QueryExecutionPlan(query.ToString()); // this method can be called in parallel. The only common data is the global execution plan lock (executionPlan) { executionPlan.QueryPlans.Add(queryExecutionPlan); } queryExecutionPlan.StartPlanning(); var indexesToUse = GetIndexesForQuery(query); queryExecutionPlan.EndPlanning(); // this will contain all queries that have can not be resolved by indexes and need to be checked manually var restOfTheQuery = query.Clone(); ISet <PackedObject> result = null; var finalResult = new List <PackedObject>(); if (indexesToUse.Count == 1) // only one index can be used so do not bother with extra logic { queryExecutionPlan.StartIndexUse(); var plan = indexesToUse[0]; queryExecutionPlan.Trace($"single index: {plan.ResolvedQuery.PropertyName}"); result = plan.Index.GetMany(plan.ResolvedQuery.Values, plan.ResolvedQuery.Operator); // this query was resolved by an index so no need to check it manually restOfTheQuery.Elements.Remove(plan.ResolvedQuery); queryExecutionPlan.EndIndexUse(); } else if (indexesToUse.Count > 1) { queryExecutionPlan.StartIndexUse(); foreach (var plan in indexesToUse.OrderBy(p => p.Ranking).Take(2)) // no more than two indexes { if (result == null) { result = plan.Index.GetMany(plan.ResolvedQuery.Values, plan.ResolvedQuery.Operator); queryExecutionPlan.Trace($"first index: {plan.ResolvedQuery.PropertyName} = {plan.Ranking}"); } else { result.IntersectWith(plan.Index.GetMany(plan.ResolvedQuery.Values, plan.ResolvedQuery.Operator)); queryExecutionPlan.Trace($"then index: {plan.ResolvedQuery.PropertyName} = {plan.Ranking} => {result.Count}"); } // do not work too hard if indexes found nothing if (result.Count == 0) { break; } // this query was resolved by an index so no need to check it manually restOfTheQuery.Elements.Remove(plan.ResolvedQuery); } queryExecutionPlan.EndIndexUse(); } else // no index can be used so proceed to full-scan { queryExecutionPlan.FullScan = true; queryExecutionPlan.StartScan(); var res = _dataStore.PrimaryIndex.GetAll().Where(o => restOfTheQuery.Match(o)).ToList(); queryExecutionPlan.EndScan(); return(res); } if (result != null) { if (restOfTheQuery.Elements.Count == 0) // empty query left; fully resolved by indexes { return(result.ToList()); } queryExecutionPlan.StartScan(); foreach (var item in result) { if (restOfTheQuery.Match(item)) { finalResult.Add(item); } } queryExecutionPlan.EndScan(); } return(finalResult); }