15457 pkg search -p should do more than just wrap the query in <>
authorBrock Pytlik <bpytlik@sun.com>
Mon, 10 May 2010 16:01:41 -0700
changeset 1893 d935808d8050
parent 1892 0bde3350b2d1
child 1894 105fac9ed5cd
15457 pkg search -p should do more than just wrap the query in <>
src/man/pkg.1.txt
src/modules/client/api.py
src/modules/client/query_parser.py
src/modules/query_parser.py
src/modules/server/repository.py
src/tests/cli/t_pkg_search.py
--- a/src/man/pkg.1.txt	Mon May 10 16:08:01 2010 +0100
+++ b/src/man/pkg.1.txt	Mon May 10 16:01:41 2010 -0700
@@ -356,10 +356,10 @@
           search.match_type     Corresponds to the attribute which contained
                                 the string that matched the search query.
 
-          With -p, perform the search and display the packages which contain
-          at least one action which matched the query.  This is equivalent to
-          enclosing the entire query with '<>'.  (For a description of the
-          '<>' operator, please see below.)
+          With -p, display packages which have some actions that match each 
+          query term.  Using this option is equivalent to putting '<>' around 
+          each term  in the query.  (For a description of the '<>' operator, 
+          please see below.)
 
           By default, and with -r, search the repositories corresponding
           to the image's publishers.
--- a/src/modules/client/api.py	Mon May 10 16:08:01 2010 +0100
+++ b/src/modules/client/api.py	Mon May 10 16:01:41 2010 -0700
@@ -21,8 +21,7 @@
 #
 
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 """This module provides the supported, documented interface for clients to
@@ -1799,10 +1798,13 @@
                 ssu = None
                 for i, q in enumerate(query_lst):
                         try:
-                                query = qp.parse(q.encoded_text())
-                                query_rr = qp.parse(q.encoded_text())
+                                query = qp.parse(q.text)
+                                query_rr = qp.parse(q.text)
                                 if query_rr.remove_root(self.__img.root):
                                         query.add_or(query_rr)
+                                if q.return_type == \
+                                    query_p.Query.RETURN_PACKAGES:
+                                        query.propagate_pkg_return()
                         except query_p.BooleanQueryException, e:
                                 raise api_errors.BooleanQueryException(e)
                         except query_p.ParseError, e:
@@ -1945,15 +1947,16 @@
                 qp = query_p.QueryParser(l)
                 for q in query_str_and_args_lst:
                         try:
-                                query = qp.parse(q.encoded_text())
-                                query_rr = qp.parse(q.encoded_text())
+                                query = qp.parse(q.text)
+                                query_rr = qp.parse(q.text)
                                 if query_rr.remove_root(self.__img.root):
                                         query.add_or(query_rr)
-                                        new_qs.append(query_p.Query(str(query),
-                                            q.case_sensitive, q.return_type,
-                                            q.num_to_return, q.start_point))
-                                else:
-                                        new_qs.append(q)
+                                if q.return_type == \
+                                    query_p.Query.RETURN_PACKAGES:
+                                        query.propagate_pkg_return()
+                                new_qs.append(query_p.Query(str(query),
+                                    q.case_sensitive, q.return_type,
+                                    q.num_to_return, q.start_point))
                         except query_p.BooleanQueryException, e:
                                 raise api_errors.BooleanQueryException(e)
                         except query_p.ParseError, e:
@@ -2093,7 +2096,7 @@
                         l.build()
                         qp = query_p.QueryParser(l)
                         try:
-                                query = qp.parse(q.encoded_text())
+                                query = qp.parse(q.text)
                         except query_p.BooleanQueryException, e:
                                 return api_errors.BooleanQueryException(e)
                         except query_p.ParseError, e:
--- a/src/modules/client/query_parser.py	Mon May 10 16:08:01 2010 +0100
+++ b/src/modules/client/query_parser.py	Mon May 10 16:01:41 2010 -0700
@@ -19,8 +19,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 
 import sys
 import threading
@@ -83,6 +82,12 @@
                 else:
                         return "<(a AND b)>"
 
+        def propagate_pkg_return(self):
+                """Makes this node return packages instead of actions.
+                Returns None because no changes need to be made to the tree."""
+                self.return_type = qp.Query.RETURN_PACKAGES
+                return None
+
 
 class OrQuery(qp.OrQuery):
         def remove_root(self, img_dir):
--- a/src/modules/query_parser.py	Mon May 10 16:08:01 2010 +0100
+++ b/src/modules/query_parser.py	Mon May 10 16:01:41 2010 -0700
@@ -21,8 +21,7 @@
 #
 
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 import os
@@ -458,7 +457,7 @@
                 The "start_point" parameter is the number of results to skip
                 before returning results to the querier."""
 
-                self.__text = text
+                self.text = text
                 self.case_sensitive = case_sensitive
                 self.return_type = return_type
                 assert self.return_type == Query.RETURN_PACKAGES or \
@@ -471,18 +470,7 @@
 
                 return "%s_%s_%s_%s_%s" % (self.case_sensitive,
                     self.return_type, self.num_to_return, self.start_point,
-                    self.__text)
-
-        def ver_0(self):
-                """Return the v0 string representation of this query."""
-
-                return self.__text
-
-        def encoded_text(self):
-                if self.return_type == Query.RETURN_PACKAGES:
-                        return "<%s>" % self.__text
-                else:
-                        return self.__text
+                    self.text)
 
         @staticmethod
         def fromstr(s):
@@ -585,6 +573,9 @@
                 self.lc = left_query
                 self.rc = right_query
                 self.return_type = self.lc.return_type
+                self.__check_return_types()
+
+        def __check_return_types(self):
                 if self.lc.return_type != self.rc.return_type:
                         if self.lc.return_type == Query.RETURN_ACTIONS:
                                 raise BooleanQueryException(self.lc, self.rc)
@@ -625,6 +616,22 @@
                 return v > 0 and self.lc.allow_version(v) and \
                     self.rc.allow_version(v)
 
+        def propagate_pkg_return(self):
+                """Makes each child return packages instead of actions.
+
+                If a child returns a value that isn't None, that means a new
+                node in the tree has been created which needs to become the
+                new child for this node."""
+                self.return_type = Query.RETURN_PACKAGES
+                new_lc = self.lc.propagate_pkg_return()
+                if new_lc:
+                        self.lc = new_lc
+                new_rc = self.rc.propagate_pkg_return()
+                if new_rc:
+                        self.rc = new_rc
+                self.__check_return_types()
+                return None
+
 class AndQuery(BooleanQuery):
         """Class representing AND queries in the AST."""
 
@@ -768,6 +775,10 @@
 
                 return v > 0 and self.query.allow_version(v)
 
+        def propagate_pkg_return(self):
+                """Makes this node return packages instead of actions.
+                Returns None because no changes need to be made to the tree."""
+                return None
 
 class PhraseQuery(object):
         """Class representing a phrase search in the AST"""
@@ -788,11 +799,23 @@
                         self.query.add_trailing_wildcard()
                 self._case_sensitive = None
 
+        @property
+        def pkg_name(self):
+                return self.query.pkg_name
+
+        @property
+        def action_type(self):
+                return self.query.action_type
+
+        @property
+        def key(self):
+                return self.query.key
+
         def __repr__(self):
                 return "Phrase Query:'" + self.full_str + "'"
 
         def __str__(self):
-                return "'" + self.full_str + "'"
+                return "%s:'%s'" % (self.query.field_strings(), self.full_str)
 
         def add_field_restrictions(self, *params):
                 self.query.add_field_restrictions(*params)
@@ -847,6 +870,14 @@
                 """Returns whether the query supports a query of version v."""
 
                 return v > 0 and self.query.allow_version(v)
+
+        def propagate_pkg_return(self):
+                """Inserts a conversion to package results into the tree.
+
+                Creates a new node by wrapping a PkgConversion node around
+                itself. It then returns the new node to its parent for
+                insertion into the tree."""
+                return PkgConversion(self)
                 
 class FieldQuery(object):
         """Class representing a structured query in the AST."""
@@ -895,6 +926,14 @@
 
                 return v > 0 and self.query.allow_version(v)
 
+        def propagate_pkg_return(self):
+                """Inserts a conversion to package results into the tree.
+
+                Creates a new node by wrapping a PkgConversion node around
+                itself. It then returns the new node to its parent for
+                insertion into the tree."""
+                return PkgConversion(self)
+
 class TopQuery(object):
         """Class which must be at the top of all valid ASTs, and may only be
         at the top of an AST.  It handles starting N results in, or only
@@ -929,7 +968,8 @@
 
                 if self.query.return_type == Query.RETURN_ACTIONS:
                         return (
-                            (1, Query.RETURN_ACTIONS, (fmri.PkgFmri(pfmri), fv, l))
+                            (1, Query.RETURN_ACTIONS,
+                            (fmri.PkgFmri(pfmri), fv, l))
                             for x, (at, st, pfmri, fv, l)
                             in enumerate(it)
                             if self.__keep(x)
@@ -967,6 +1007,17 @@
                 """Returns whether the query supports a query of version v."""
 
                 return self.query.allow_version(v)
+        
+        def propagate_pkg_return(self):
+                """Makes the child return packages instead of actions.
+
+                If a child returns a value that isn't None, that means a new
+                node in the tree has been created which needs to become the
+                new child for this node."""
+                new_child = self.query.propagate_pkg_return()
+                if new_child:
+                        self.query = new_child
+                return None
 
 class TermQuery(object):
         """Class representing the a single query term in the AST."""
@@ -1028,19 +1079,31 @@
                 return "( TermQuery: " + self._term + " )"
 
         def __str__(self):
+                return "%s:%s" % (self.field_strings(),
+                    self.__wc_to_string(False, self._term))
+
+        def field_strings(self):
                 return ":".join([
                     self.__wc_to_string(wc, v)
                     for wc, v in [(self.pkg_name_wildcard, self.pkg_name),
                         (self.action_type_wildcard, self.action_type),
-                        (self.key_wildcard, self.key), (False, self._term)
+                        (self.key_wildcard, self.key)
                     ]
                 ])
 
+        def propagate_pkg_return(self):
+                """Inserts a conversion to package results into the tree.
+
+                Creates a new node by wrapping a PkgConversion node around
+                itself. It then returns the new node to its parent for
+                insertion into the tree."""
+                return PkgConversion(self)
+
         @staticmethod
         def __wc_to_string(wc, v):
                 if wc:
                         return ""
-                return v
+                return "\\:".join(v.split(":"))
 
         def add_field_restrictions(self, pkg_name, action_type, key):
                 """Add the information needed to restrict the search domain
--- a/src/modules/server/repository.py	Mon May 10 16:08:01 2010 +0100
+++ b/src/modules/server/repository.py	Mon May 10 16:01:41 2010 -0700
@@ -19,8 +19,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 
 import datetime
 import errno
@@ -1288,7 +1287,7 @@
                         l = query_p.QueryLexer()
                         l.build()
                         qp = query_p.QueryParser(l)
-                        query = qp.parse(q.encoded_text())
+                        query = qp.parse(q.text)
                         query.set_info(num_to_return=q.num_to_return,
                             start_point=q.start_point,
                             index_dir=self.index_root,
--- a/src/tests/cli/t_pkg_search.py	Mon May 10 16:08:01 2010 +0100
+++ b/src/tests/cli/t_pkg_search.py	Mon May 10 16:01:41 2010 -0700
@@ -21,8 +21,7 @@
 #
 
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 import testutils
@@ -501,18 +500,18 @@
                 # Testing interaction of -o and -p options
                 self.pkgsend_bulk(durl, self.example_pkg10)
                 self.pkg("search -o action.name -p pkg", exit=2)
-                self.pkg("search -o action.name '<pkg>'", exit=1)
-                self.pkg("search -o action.name '<example_path>'", exit=2)
+                self.pkg("search -o action.name -a '<pkg>'", exit=1)
+                self.pkg("search -o action.name -a '<example_path>'", exit=2)
                 self.pkg("search -o action.key -p pkg", exit=2)
-                self.pkg("search -o action.key '<pkg>'", exit=1)
-                self.pkg("search -o action.key '<example_path>'", exit=2)
+                self.pkg("search -o action.key -a '<pkg>'", exit=1)
+                self.pkg("search -o action.key -a '<example_path>'", exit=2)
                 self.pkg("search -o search.match -p pkg", exit=2)
-                self.pkg("search -o search.match '<pkg>'", exit=1)
-                self.pkg("search -o search.match '<example_path>'", exit=2)
+                self.pkg("search -o search.match -a '<pkg>'", exit=1)
+                self.pkg("search -o search.match -a '<example_path>'", exit=2)
                 self.pkg("search -o search.match_type -p pkg", exit=2)
-                self.pkg("search -o search.match_type '<pkg>'", exit=1)
-                self.pkg("search -o search.match_type '<example_path>'", exit=2)
-                self.pkg("search -o action.foo pkg", exit=2)
+                self.pkg("search -o search.match_type -a '<pkg>'", exit=1)
+                self.pkg("search -o search.match_type -a '<example_path>'", exit=2)
+                self.pkg("search -o action.foo -a pkg", exit=2)
 
         def test_remote(self):
                 """Test remote search."""
@@ -764,18 +763,18 @@
 
                 self.image_create(durl)
                 
-                self.pkg("search 'dup_lines:set:pkg.fmri:'")
+                self.pkg("search -a 'dup_lines:set:pkg.fmri:'")
                 self.assertEqual(len(self.output.splitlines()), 2)
 
-                self.pkg("search -o pkg.shortfmri 'a'")
+                self.pkg("search -a -o pkg.shortfmri 'a'")
                 self.assertEqual(len(self.output.splitlines()), 2)
 
                 self.pkg("install dup_lines")
 
-                self.pkg("search -l 'dup_lines:set:pkg.fmri:'")
+                self.pkg("search -a -l 'dup_lines:set:pkg.fmri:'")
                 self.assertEqual(len(self.output.splitlines()), 2)
 
-                self.pkg("search -l -o pkg.shortfmri,action.key 'a'")
+                self.pkg("search -l -a -o pkg.shortfmri,action.key 'a'")
                 self.assertEqual(len(self.output.splitlines()), 4)
 
 if __name__ == "__main__":