diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /container-search/src/main/java/com/yahoo/search/searchchain/testutil/DocumentSourceSearcher.java |
Publish
Diffstat (limited to 'container-search/src/main/java/com/yahoo/search/searchchain/testutil/DocumentSourceSearcher.java')
-rw-r--r-- | container-search/src/main/java/com/yahoo/search/searchchain/testutil/DocumentSourceSearcher.java | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/searchchain/testutil/DocumentSourceSearcher.java b/container-search/src/main/java/com/yahoo/search/searchchain/testutil/DocumentSourceSearcher.java new file mode 100644 index 00000000000..a5b9c58f084 --- /dev/null +++ b/container-search/src/main/java/com/yahoo/search/searchchain/testutil/DocumentSourceSearcher.java @@ -0,0 +1,190 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.search.searchchain.testutil; + + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.Map; +import java.util.HashMap; +import java.util.List; + +import com.yahoo.net.URI; +import com.yahoo.search.Query; +import com.yahoo.search.Result; +import com.yahoo.search.Searcher; +import com.yahoo.search.result.Hit; +import com.yahoo.search.searchchain.Execution; + +/** + * <p>Implements a document source. You pass in a query and a Result + * set. When this Searcher is called with that query it will return + * that result set.</p> + * + * <p>This supports multi-phase search.</p> + * + * <p>To avoid having to add type information for the fields, a quck hack is used to + * support testing of attribute prefetching. + * Any field in the configured hits which has a name starting by attribute + * will be returned when attribute prefetch filling is requested.</p> + * + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +public class DocumentSourceSearcher extends Searcher { + + // using null as name in the API would just be a horrid headache + public static final String DEFAULT_SUMMARY_CLASS = "default"; + + // TODO: update tests to explicitly set hits, so that the default results can be removed entirely. + private Result defaultFilledResult; + + private Map<Query, Result> completelyFilledResults = new HashMap<>(); + private Map<Query, Result> unFilledResults = new HashMap<>(); + private Map<String, Set<String>> summaryClasses = new HashMap<>(); + + private int queryCount; + + public DocumentSourceSearcher() { + addDefaultResults(); + } + + /** + * Adds a result which can be searched for and filled. + * Summary fields starting by "a" are attributes, others are not. + * + * @return true when replacing an existing <query, result> pair. + */ + public boolean addResult(Query query, Result fullResult) { + Result emptyResult = new Result(query.clone()); + emptyResult.setTotalHitCount(fullResult.getTotalHitCount()); + for (Hit fullHit : fullResult.hits().asList()) { + Hit emptyHit = fullHit.clone(); + emptyHit.clearFields(); + emptyHit.setFillable(); + emptyHit.setRelevance(fullHit.getRelevance()); + + emptyResult.hits().add(emptyHit); + } + unFilledResults.put(getQueryKeyClone(query), emptyResult); + + if (completelyFilledResults.put(getQueryKeyClone(query), fullResult.clone()) != null) { + // TODO: throw exception if the key exists from before, change the method to void + return true; + } + return false; + } + + public void addSummaryClass(String name, Set<String> fields) { + summaryClasses.put(name,fields); + } + + public void addSummaryClassByCopy(String name, Collection<String> fields) { + addSummaryClass(name, new HashSet<>(fields)); + } + + private void addDefaultResults() { + Query q = new Query("?query=default"); + Result r = new Result(q); + // These four used to assign collapseId 1,2,3,4 - re-add that if needed + r.hits().add(new Hit("http://default-1.html", 0)); + r.hits().add(new Hit("http://default-2.html", 0)); + r.hits().add(new Hit("http://default-3.html", 0)); + r.hits().add(new Hit("http://default-4.html", 0)); + defaultFilledResult = r; + addResult(q, r); + } + + public @Override Result search(Query query, Execution execution) { + queryCount++; + Result r; + r = unFilledResults.get(getQueryKeyClone(query)); + if (r == null) { + r = defaultFilledResult.clone(); + } else { + r = r.clone(); + } + + r.setQuery(query); + r.hits().trim(query.getOffset(), query.getHits()); + return r; + } + + /** + * Returns a query clone which has offset and hits set to null. This is used by access to + * the maps using the query as key to achieve lookup independent of offset/hits value + */ + private Query getQueryKeyClone(Query query) { + Query key=query.clone(); + key.setWindow(0,0); + return key; + } + + public @Override void fill(Result result, String summaryClass, Execution execution) { + Result filledResult; + filledResult = completelyFilledResults.get(getQueryKeyClone(result.getQuery())); + + if (filledResult == null) { + filledResult = defaultFilledResult; + } + fillHits(filledResult,result,summaryClass); + } + + private void fillHits(Result filledHits, Result hitsToFill, String summaryClass) { + Set<String> fieldsToFill = summaryClasses.get(summaryClass); + + if (fieldsToFill == null ) { + fieldsToFill = summaryClasses.get(DEFAULT_SUMMARY_CLASS); + } + + for (Hit hitToFill : hitsToFill.hits()) { + Hit filledHit = getMatchingFilledHit(hitToFill.getId(), filledHits); + + if (filledHit != null) { + if (fieldsToFill != null) { + copyFieldValuesThatExist(filledHit,hitToFill,fieldsToFill); + } else { + // TODO: remove this block and update fieldsToFill above to throw an exception if no appropriate summary class is found + for (Map.Entry<String,Object> propertyEntry : filledHit.fields().entrySet()) { + hitToFill.setField(propertyEntry.getKey(), + propertyEntry.getValue()); + } + } + hitToFill.setFilled(summaryClass == null ? DEFAULT_SUMMARY_CLASS : summaryClass); + } + } + hitsToFill.analyzeHits(); + } + + private Hit getMatchingFilledHit(URI hitToFillId, Result filledHits) { + Hit filledHit = null; + + for ( Hit filledHitCandidate : filledHits.hits()) { + if ( hitToFillId == filledHitCandidate.getId() ) { + filledHit = filledHitCandidate; + break; + } + } + return filledHit; + } + + private void copyFieldValuesThatExist(Hit filledHit, Hit hitToFill, Set<String> fieldsToFill) { + for (String fieldToFill : fieldsToFill ) { + if ( filledHit.getField(fieldToFill) != null ) { + hitToFill.setField(fieldToFill, filledHit.getField(fieldToFill)); + } + } + } + + /** + * Returns the number of queries made to this searcher since the last + * reset. For testing - not reliable if multiple threads makes + * queries simultaneously + */ + public int getQueryCount() { + return queryCount; + } + + public void resetQueryCount() { + queryCount=0; + } +} |