Commit 993856e5 authored by Guido van Rossum's avatar Guido van Rossum

Move IQueryParseTree to a separate file, to conform to style

guidelines.  Added some conformance tests.
parent 58331c88
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Query Parser Tree Interface."""
import Interface
class IQueryParseTree(Interface.Base):
"""Interface for parse trees returned by parseQuery()."""
def nodeType():
"""Return the node type.
This is one of 'AND', 'OR', 'NOT', 'ATOM', 'PHRASE' or 'GLOB'.
"""
def getValue():
"""Return a node-type specific value.
For node type: Return:
'AND' a list of parse trees
'OR' a list of parse trees
'NOT' a parse tree
'ATOM' a string (representing a single search term)
'PHRASE' a string (representing a search phrase)
'GLOB' a string (representing a pattern, e.g. "foo*")
"""
def terms():
"""Return a list of all terms in this node, excluding NOT subtrees."""
def executeQuery(index):
"""Execute the query represented by this node against the index.
The index argument must implement the IIndex interface.
Return an IIBucket or IIBTree mapping document ids to scores
(higher scores mean better results).
May raise ParseTree.QueryError.
"""
...@@ -51,38 +51,3 @@ class IQueryParser(Interface.Base): ...@@ -51,38 +51,3 @@ class IQueryParser(Interface.Base):
May raise ParseTree.ParseError. May raise ParseTree.ParseError.
""" """
class IQueryParseTree(Interface.Base):
"""Interface for parse trees returned by parseQuery()."""
def nodeType():
"""Return the node type.
This is one of 'AND', 'OR', 'NOT', 'ATOM', 'PHRASE' or 'GLOB'.
"""
def getValue():
"""Return a node-type specific value.
For node type: Return:
'AND' a list of parse trees
'OR' a list of parse trees
'NOT' a parse tree
'ATOM' a string (representing a single search term)
'PHRASE' a string (representing a search phrase)
'GLOB' a string (representing a pattern, e.g. "foo*")
"""
def terms():
"""Return a list of all terms in this node, excluding NOT subtrees."""
def executeQuery(index):
"""Execute the query represented by this node against the index.
The index argument must implement the IIndex interface.
Return an IIBucket or IIBTree mapping document ids to scores
(higher scores mean better results).
May raise ParseTree.QueryError.
"""
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
from BTrees.IIBTree import difference from BTrees.IIBTree import difference
from Products.ZCTextIndex.IQueryParseTree import IQueryParseTree
from Products.ZCTextIndex.SetOps import mass_weightedIntersection, \ from Products.ZCTextIndex.SetOps import mass_weightedIntersection, \
mass_weightedUnion mass_weightedUnion
...@@ -27,6 +28,8 @@ class ParseError(Exception): ...@@ -27,6 +28,8 @@ class ParseError(Exception):
class ParseTreeNode: class ParseTreeNode:
__implements__ = IQueryParseTree
_nodeType = None _nodeType = None
def __init__(self, value): def __init__(self, value):
......
...@@ -57,7 +57,8 @@ Summarizing the default operator rules: ...@@ -57,7 +57,8 @@ Summarizing the default operator rules:
import re import re
import ParseTree # relative import from Products.ZCTextIndex.IQueryParser import IQueryParser
from Products.ZCTextIndex import ParseTree
# Create unique symbols for token types. # Create unique symbols for token types.
_AND = intern("AND") _AND = intern("AND")
...@@ -94,6 +95,8 @@ _tokenizer_regex = re.compile(r""" ...@@ -94,6 +95,8 @@ _tokenizer_regex = re.compile(r"""
class QueryParser: class QueryParser:
__implements__ = IQueryParser
# This class is not thread-safe; # This class is not thread-safe;
# each thread should have its own instance # each thread should have its own instance
......
...@@ -14,13 +14,31 @@ ...@@ -14,13 +14,31 @@
from unittest import TestCase, TestSuite, main, makeSuite from unittest import TestCase, TestSuite, main, makeSuite
from Products.ZCTextIndex.QueryParser import QueryParser from Interface import verify_class_implementation
from Products.ZCTextIndex.IQueryParser import IQueryParser
from Products.ZCTextIndex.IQueryParseTree import IQueryParseTree
from Products.ZCTextIndex.QueryParser import QueryParser
from Products.ZCTextIndex.ParseTree import ParseError, ParseTreeNode from Products.ZCTextIndex.ParseTree import ParseError, ParseTreeNode
from Products.ZCTextIndex.ParseTree import OrNode, AndNode, NotNode from Products.ZCTextIndex.ParseTree import OrNode, AndNode, NotNode
from Products.ZCTextIndex.ParseTree import AtomNode, PhraseNode, GlobNode from Products.ZCTextIndex.ParseTree import AtomNode, PhraseNode, GlobNode
from Products.ZCTextIndex.Lexicon import Lexicon, Splitter from Products.ZCTextIndex.Lexicon import Lexicon, Splitter
class TestInterfaces(TestCase):
def testInterfaces(self):
verify_class_implementation(IQueryParser, QueryParser)
verify_class_implementation(IQueryParseTree, ParseTreeNode)
verify_class_implementation(IQueryParseTree, OrNode)
verify_class_implementation(IQueryParseTree, AndNode)
verify_class_implementation(IQueryParseTree, NotNode)
verify_class_implementation(IQueryParseTree, AtomNode)
verify_class_implementation(IQueryParseTree, PhraseNode)
verify_class_implementation(IQueryParseTree, GlobNode)
class TestQueryParserBase(TestCase): class TestQueryParserBase(TestCase):
def setUp(self): def setUp(self):
...@@ -67,6 +85,7 @@ class TestQueryParserBase(TestCase): ...@@ -67,6 +85,7 @@ class TestQueryParserBase(TestCase):
for i in range(len(list1)): for i in range(len(list1)):
self.compareParseTrees(list1[i], list2[i], msg) self.compareParseTrees(list1[i], list2[i], msg)
class TestQueryParser(TestQueryParserBase): class TestQueryParser(TestQueryParserBase):
def test001(self): def test001(self):
...@@ -216,6 +235,7 @@ class TestQueryParser(TestQueryParserBase): ...@@ -216,6 +235,7 @@ class TestQueryParser(TestQueryParserBase):
def test122(self): def test122(self):
self.failure("foo AND -bar") self.failure("foo AND -bar")
class StopWordTestQueryParser(TestQueryParserBase): class StopWordTestQueryParser(TestQueryParserBase):
def setUp(self): def setUp(self):
...@@ -259,6 +279,7 @@ class StopWordTestQueryParser(TestQueryParserBase): ...@@ -259,6 +279,7 @@ class StopWordTestQueryParser(TestQueryParserBase):
def test306(self): def test306(self):
self.failure('stop AND NOT foo') self.failure('stop AND NOT foo')
class FakeStopWordRemover: class FakeStopWordRemover:
def process(self, list): def process(self, list):
...@@ -268,7 +289,9 @@ class FakeStopWordRemover: ...@@ -268,7 +289,9 @@ class FakeStopWordRemover:
def test_suite(): def test_suite():
return TestSuite((makeSuite(TestQueryParser), return TestSuite((makeSuite(TestQueryParser),
makeSuite(StopWordTestQueryParser), makeSuite(StopWordTestQueryParser),
makeSuite(TestInterfaces),
)) ))
if __name__=="__main__": if __name__=="__main__":
main(defaultTest='test_suite') main(defaultTest='test_suite')
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment