public static void ReadEntriesForFields( IndexSearcherHolder.IndexSearcherHoldingState state, HashSet <string> fieldsToRead, HashSet <int> docIds, Func <Term, double> convert, Action <Term, double, int> onTermFound) { var reader = state.IndexSearcher.IndexReader; var readFromCache = new Dictionary <string, HashSet <int> >(); state.Lock.EnterReadLock(); try { foreach (var field in fieldsToRead) { var read = new HashSet <int>(); readFromCache[field] = read; foreach (var docId in docIds) { foreach (var val in state.GetFromCache(field, docId)) { read.Add(docId); double converted; if (val.Val == null) { val.Val = converted = convert(val.Term); } else { converted = val.Val.Value; } onTermFound(val.Term, converted, docId); } } } } finally { state.Lock.ExitReadLock(); } foreach (var kvp in readFromCache) { if (kvp.Value.Count == docIds.Count) { fieldsToRead.Remove(kvp.Key); // already read all of it } } if (fieldsToRead.Count == 0) { return; } state.Lock.EnterWriteLock(); try { using (var termDocs = reader.TermDocs()) { foreach (var field in fieldsToRead) { var read = readFromCache[field]; var shouldReset = new HashSet <Tuple <string, int> >(); using (var termEnum = reader.Terms(new Term(field))) { do { if (termEnum.Term == null || field != termEnum.Term.Field) { break; } if (LowPrecisionNumber(termEnum.Term)) { continue; } var totalDocCountIncludedDeletes = termEnum.DocFreq(); termDocs.Seek(termEnum.Term); while (termDocs.Next() && totalDocCountIncludedDeletes > 0) { totalDocCountIncludedDeletes -= 1; if (read.Contains(termDocs.Doc)) { break; } if (reader.IsDeleted(termDocs.Doc)) { break; } if (docIds.Contains(termDocs.Doc) == false) { break; } if (shouldReset.Add(Tuple.Create(field, termDocs.Doc))) { state.ResetInCache(field, termDocs.Doc); } var d = convert(termEnum.Term); state.SetInCache(field, termDocs.Doc, termEnum.Term, d); onTermFound(termEnum.Term, d, termDocs.Doc); } } while (termEnum.Next()); } } } } finally { state.Lock.ExitWriteLock(); } }