public ResultSet doMatch(
      String queryNumber, MatchingQueryTerms queryTerms, final FatResultSet fat)
      throws IOException {
    final int numResults = fat.getResultSize();
    final FeaturedQueryResultSet rtr = new FeaturedQueryResultSet(fat);
    if (fat.getResultSize() == 0) {
      rtr.scores = new double[0];
      rtr.docids = new int[0];
      rtr.occurrences = new short[0];
      return rtr;
    }

    if (sampleFeature) rtr.putFeatureScores("SAMPLE", fat.getScores());

    // for each WMODEL feature
    for (int fid = 0; fid < wModels.length; fid++) {
      final ResultSet thinChild = wModels[fid].doMatch(queryNumber, queryTerms, fat);
      rtr.putFeatureScores(wModelNames[fid], thinChild.getScores());
    }

    // for each QI features
    if (qiFeatures.length > 0) {
      WritablePosting[][] postings = fat.getPostings();
      int[] docids = fat.getDocids();
      for (int fid = 0; fid < qiFeatures.length; fid++) {
        WeightingModel wm = qiFeatures[fid];
        double[] scores = new double[numResults];
        for (int di = 0; di < numResults; di++) {
          WritablePosting p = FatUtils.firstPosting(postings[di]);
          if (p == null) {
            p = new BlockFieldPostingImpl(docids[di], 0, new int[0], new int[4]); // hack
            ((FieldPosting) p).setFieldLengths(new int[4]);
          }
          scores[di] = wm.score(p);
        }
        rtr.putFeatureScores(qiFeatureNames[fid], scores);
      }
    }

    // for each DSM feature
    if (dsms.length > 0) {
      TIntIntHashMap docidMap = new TIntIntHashMap(numResults);
      int position = 0;
      for (int docid : fat.getDocids()) {
        docidMap.put(docid, position++);
      }
      final Index fatIndex = FatUtils.makeIndex(fat);
      for (int fid = 0; fid < dsms.length; fid++) {
        final double[] scores = new double[numResults];
        final int[] docids = new int[numResults];
        final short[] occurrences = new short[numResults];
        System.arraycopy(fat.getDocids(), 0, docids, 0, numResults);
        System.arraycopy(fat.getOccurrences(), 0, occurrences, 0, numResults);

        // Sort by docid so that term postings we have a recoverable score ordering
        MultiSort.ascendingHeapSort(docids, scores, occurrences, docids.length);

        final ResultSet thinChild = new QueryResultSet(docids, scores, occurrences);
        final MatchingQueryTerms mqtLocal = new MatchingQueryTerms(queryNumber);
        mqtLocal.setDefaultTermWeightingModel(queryTerms.defaultWeightingModel);
        int ti = 0;
        for (String t : fat.getQueryTerms()) {
          mqtLocal.setTermProperty(t, fat.getKeyFrequencies()[ti]);
          mqtLocal.setTermProperty(t, fat.getEntryStatistics()[ti]);
          ti++;
        }
        // apply the dsm on the temporary resultset
        dsms[fid].modifyScores(fatIndex, mqtLocal, thinChild);

        // map scores back into original ordering
        double[] scoresFinal = new double[numResults];
        for (int i = 0; i < numResults; i++) {
          scoresFinal[docidMap.get(docids[i])] = scores[i];
        }
        // add the feature, regardless of whether it has scores or not
        rtr.putFeatureScores(dsmNames[fid], scoresFinal);
      }
    }
    final String[] labels = new String[rtr.getResultSize()];
    Arrays.fill(labels, "-1");
    rtr.setLabels(labels);

    if (fat.hasMetaItems("docno")) {
      rtr.addMetaItems("docno", fat.getMetaItems("docno"));
    }
    if (fat.hasMetaItems("label")) rtr.setLabels(fat.getMetaItems("label"));

    return rtr;
  }