public IList <LuceneResult> Search(Query query, SortField sortField, int limit, int offset, float threshold, LuceneProfiler profiler, Plug authPlug) { return(_disposalLock.ExecuteWithReadLock(() => { using (profiler.ProfileQueryInternals()) { EnsureInstanceNotDisposed(); var searcher = GetSearcher(); int numHits; if (authPlug == null) { numHits = Math.Min(searcher.MaxDoc(), limit == int.MaxValue ? int.MaxValue : limit + offset); } else { var setSizeGuessLong = ((long)offset + limit) * 5; setSizeGuessLong = Math.Max(1000, setSizeGuessLong); var setSizeGuess = setSizeGuessLong.ToInt(); numHits = Math.Min(searcher.MaxDoc(), setSizeGuess); } var collector = TopFieldCollector.create( sortField == null ? new Sort() : new Sort(sortField), numHits, false, // fillFields, not required true, // trackDocScores, required true, // trackMaxScore, related to trackDocScores sortField == null // should docs be in docId order? ); searcher.Search(query, collector); var docs = collector.TopDocs(); var maxscore = docs.GetMaxScore(); // Note: cheap way to avoid div/zero if (maxscore == 0) { maxscore = 1; } var items = from hit in docs.scoreDocs let score = hit.score / maxscore where score >= threshold select new LuceneResult(searcher.Doc(hit.doc), score); IList <LuceneResult> resultSet; if (authPlug == null) { if (offset > 0 || limit != int.MaxValue) { items = items.Skip(offset).Take(limit); } resultSet = items.ToList(); } else { resultSet = LuceneResultFilter.Filter(authPlug, items, offset, limit, new Result <IList <LuceneResult> >()).Wait(); } return resultSet; } })); }
//--- Class Methods --- public static Result<IList<LuceneResult>> Filter(Plug authPlug, IEnumerable<LuceneResult> items, int offset, int limit, Result<IList<LuceneResult>> result) { var builder = new LuceneResultFilter(authPlug, 10000, 100); return Coroutine.Invoke(builder.Filter, items, offset, limit, result); }
public void Limit_smaller_than_item_set_forces_authorization_in_chunks_as_candidates_nears_limit() { var items = new List<LuceneResult>(); for(var i = 1; i <= 200; i++) { items.Add(Result(i)); } // first chunk should ask for 50 ids and we'll filter 20 from that set MockPlug.Setup(_authUri) .Verb("POST") .WithMessage(m => { var t = m.ToText(); var match = t.EqualsInvariant(items.Select(x => x.PageId.Value).Take(50).ToCommaDelimitedString()); _log.DebugFormat("first chunk match? {0} => {1}", match, t); return match; }) .Returns(DreamMessage.Ok(MimeType.TEXT, S(10,39).ToCommaDelimitedString())) .ExpectCalls(Times.Once()); // second chunk should ask for 30 and we'll filter 5 from that set // which gives it a total larger than our limit, i.e. it won't try for a third chunk MockPlug.Setup(_authUri) .Verb("POST") .WithMessage(m => { var t = m.ToText(); var match = t.EqualsInvariant(items.Select(x => x.PageId.Value).Skip(50).Take(30).ToCommaDelimitedString()); _log.DebugFormat("second chunk match? {0} => {1}", match, t); return match; }) .Returns(DreamMessage.Ok(MimeType.TEXT, S(51,55).ToCommaDelimitedString())) .ExpectCalls(Times.Once()); var builder = new LuceneResultFilter(_authPlug, 10000, 20); var set = Coroutine.Invoke(builder.Filter, items, 0, 30, new Result<IList<LuceneResult>>()).Wait(); Assert.AreEqual(30, set.Count); // we expect a sequence of 1-9,40-50,56-... and take the first 30 var expected = S(1, 9).Union(S(40, 50)).Union(S(56, 100)).Take(30); Assert.AreEqual( expected.ToCommaDelimitedString(), set.Select(x => x.PageId.Value).ToCommaDelimitedString() ); MockPlug.VerifyAll(1.Seconds()); }
public void Item_sets_larger_than_maxAuthItems_are_authorized_in_chunks() { var items = new List<LuceneResult>(); for(var i = 1; i <= 50; i++) { items.Add(Result(i)); } MockPlug.Setup(_authUri) .Verb("POST") .WithMessage(m => { var t = m.ToText(); var match = t.EqualsInvariant(items.Select(x => x.PageId.Value).Take(30).ToCommaDelimitedString()); _log.DebugFormat("first chunk match? {0} => {1}", match, t); return match; }) .Returns(DreamMessage.Ok(MimeType.TEXT, "")) .ExpectCalls(Times.Once()); MockPlug.Setup(_authUri) .Verb("POST") .WithMessage(m => { var t = m.ToText(); var match = t.EqualsInvariant(items.Select(x => x.PageId.Value).Skip(30).ToCommaDelimitedString()); _log.DebugFormat("second chunk match? {0} => {1}", match, t); return match; }) .Returns(DreamMessage.Ok(MimeType.TEXT, "")) .ExpectCalls(Times.Once()); var builder = new LuceneResultFilter(_authPlug, 30, 1000); var set = Coroutine.Invoke(builder.Filter, items, 0, int.MaxValue, new Result<IList<LuceneResult>>()).Wait(); Assert.AreEqual(items.Count, set.Count); Assert.AreEqual( items.Select(x => x.PageId.Value).ToCommaDelimitedString(), set.Select(x => x.PageId.Value).ToCommaDelimitedString() ); MockPlug.VerifyAll(1.Seconds()); }
//--- Class Methods --- public static Result <IList <LuceneResult> > Filter(Plug authPlug, IEnumerable <LuceneResult> items, int offset, int limit, Result <IList <LuceneResult> > result) { var builder = new LuceneResultFilter(authPlug, 10000, 100); return(Coroutine.Invoke(builder.Filter, items, offset, limit, result)); }