[Zope-CVS] CVS: Products/ZCTextIndex/tests - testZCTextIndex.py:1.10
Tim Peters
tim.one@comcast.net
Thu, 16 May 2002 02:29:23 -0400
Update of /cvs-repository/Products/ZCTextIndex/tests
In directory cvs.zope.org:/tmp/cvs-serv17369/tests
Modified Files:
testZCTextIndex.py
Log Message:
Added some rather good white-box and black-box tests for Okapi
internals.
=== Products/ZCTextIndex/tests/testZCTextIndex.py 1.9 => 1.10 ===
# an example set of documents in queries in Managing
# Gigabytes, pp. 180-188. This test peeks into many internals of the
- # cosine index.
+ # cosine indexer.
def testRanking(self):
self.words = ["cold", "days", "eat", "hot", "lot", "nine", "old",
@@ -136,7 +136,119 @@
eq(d[doc], score)
class OkapiIndexTests(ZCIndexTestsBase, testIndex.OkapiIndexTest):
- pass
+
+ # A white-box test.
+ def testAbsoluteScores(self):
+ from Products.ZCTextIndex.OkapiIndex import inverse_doc_frequency
+
+ docs = ["one",
+ "one two",
+ "one two three"]
+ for i in range(len(docs)):
+ self.zc_index.index_object(i + 1, Indexable(docs[i]))
+ self.assertEqual(self.index._totaldoclen, 6)
+ # So the mean doc length is 2. We use that later.
+
+ r, num = self.zc_index.query("one")
+ self.assertEqual(num, 3)
+ self.assertEqual(len(r), 3)
+
+ # Because our Okapi's B parameter is > 0, and "one" only appears
+ # once in each doc, the verbosity hypothesis favors shorter docs.
+ self.assertEqual([doc for doc, score in r], [1, 2, 3])
+
+ # The way the Okapi math works, a word that appears exactly once in
+ # an average (length) doc gets tf score 1. Our second doc has
+ # an average length, so its score should by 1 (tf) times the
+ # inverse doc frequency of "one". But "one" appears in every
+ # doc, so its IDF is log(1 + 3/3) = log(2).
+ self.assertEqual(r[1][1], scaled_int(inverse_doc_frequency(3, 3)))
+
+ # Similarly for "two".
+ r, num = self.zc_index.query("two")
+ self.assertEqual(num, 2)
+ self.assertEqual(len(r), 2)
+ self.assertEqual([doc for doc, score in r], [2, 3])
+ self.assertEqual(r[0][1], scaled_int(inverse_doc_frequency(2, 3)))
+
+ # And "three", except that doesn't appear in an average-size doc, so
+ # the math is much more involved.
+ r, num = self.zc_index.query("three")
+ self.assertEqual(num, 1)
+ self.assertEqual(len(r), 1)
+ self.assertEqual([doc for doc, score in r], [3])
+ idf = inverse_doc_frequency(1, 3)
+ meandoclen = 2.0
+ lengthweight = 1.0 - OkapiIndex.B + OkapiIndex.B * 3 / meandoclen
+ tf = (1.0 + OkapiIndex.K1) / (1.0 + OkapiIndex.K1 * lengthweight)
+ self.assertEqual(r[0][1], scaled_int(tf * idf))
+
+ # More of a black-box test, but based on insight into how Okapi is trying
+ # to think.
+ def testRelativeScores(self):
+ # Create 9 10-word docs.
+ # All contain one instance of "one".
+ # Doc #i contains i instances of "two" and 9-i of "xyz".
+ for i in range(1, 10):
+ doc = "one " + "two " * i + "xyz " * (9 - i)
+ self.zc_index.index_object(i, Indexable(doc))
+
+ r, num = self.zc_index.query("one two")
+ self.assertEqual(num, 9)
+ self.assertEqual(len(r), 9)
+ # The more twos in a doc, the better the score should be.
+ self.assertEqual([doc for doc, score in r], range(9, 0, -1))
+
+ # Search for "two" alone shouldn't make any difference to relative
+ # results.
+ r, num = self.zc_index.query("two")
+ self.assertEqual(num, 9)
+ self.assertEqual(len(r), 9)
+ self.assertEqual([doc for doc, score in r], range(9, 0, -1))
+
+ # Searching for xyz should skip doc 9, and favor the lower-numbered
+ # docs (they have more instances of xyz).
+ r, num = self.zc_index.query("xyz")
+ self.assertEqual(num, 8)
+ self.assertEqual(len(r), 8)
+ self.assertEqual([doc for doc, score in r], range(1, 9))
+
+ # And relative results shouldn't change if we add "one".
+ r, num = self.zc_index.query("xyz one")
+ self.assertEqual(num, 8)
+ self.assertEqual(len(r), 8)
+ self.assertEqual([doc for doc, score in r], range(1, 9))
+
+ # But if we search for all the words, it's much muddier. The boost
+ # in going from i instances to i+1 of a given word is smaller than
+ # the boost in going from i-1 to i, so the winner will be the one
+ # that balances the # of twos and xyzs best. But the test is nasty
+ # that way: doc 4 has 4 two and 5 xyz, while doc 5 has the reverse.
+ # However, xyz is missing from doc 9, so xyz has a larger idf than
+ # two has. Since all the doc lengths are the same, doc lengths don't
+ # matter. So doc 4 should win, and doc 5 should come in second.
+ # The loser will be the most unbalanced, but is that doc 1 (1 two 8
+ # xyz) or doc 8 (8 two 1 xyz)? Again xyz has a higher idf, so doc 1
+ # is more valuable, and doc 8 is the loser.
+ r, num = self.zc_index.query("xyz one two")
+ self.assertEqual(num, 8)
+ self.assertEqual(len(r), 8)
+ self.assertEqual(r[0][0], 4) # winner
+ self.assertEqual(r[1][0], 5) # runner up
+ self.assertEqual(r[-1][0], 8) # loser
+ self.assertEqual(r[-2][0], 1) # penultimate loser
+
+ # And nothing about the relative results in the last test should
+ # change if we leave "one" out of the search (it appears in all
+ # docs, so it's a wash).
+ r, num = self.zc_index.query("two xyz")
+ self.assertEqual(num, 8)
+ self.assertEqual(len(r), 8)
+ self.assertEqual(r[0][0], 4) # winner
+ self.assertEqual(r[1][0], 5) # runner up
+ self.assertEqual(r[-1][0], 8) # loser
+ self.assertEqual(r[-2][0], 1) # penultimate loser
+
############################################################################
# Subclasses of QueryTestsBase must set a class variable IndexFactory to