Imported Upstream version 14.45.0
[platform/upstream/libzypp.git] / zypp / PoolQuery.cc
index e018b8e..4e286b2 100644 (file)
 */
 #include <iostream>
 #include <sstream>
-#include <list>
-#include <vector>
-#include <algorithm>
-#include <fnmatch.h>
-
-#include "zypp/base/Logger.h"
-#include "zypp/base/PtrTypes.h"
-#include "zypp/base/DefaultIntegral.h"
-#include "zypp/base/Regex.h"
+
+#include "zypp/base/Gettext.h"
+#include "zypp/base/LogTools.h"
 #include "zypp/base/Algorithm.h"
-#include "zypp/base/UserRequestException.h"
+#include "zypp/base/String.h"
 #include "zypp/repo/RepoException.h"
+#include "zypp/RelCompare.h"
 
 #include "zypp/sat/Pool.h"
 #include "zypp/sat/Solvable.h"
-#include "zypp/sat/SolvAttr.h"
-#include "zypp/sat/detail/PoolImpl.h"
-
-extern "C"
-{
-#include "satsolver/repo.h"
-}
+#include "zypp/base/StrMatcher.h"
 
 #include "zypp/PoolQuery.h"
 
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "PoolQuery"
+
 using namespace std;
 using namespace zypp::sat;
 
@@ -44,169 +36,493 @@ namespace zypp
 { /////////////////////////////////////////////////////////////////
 
   ///////////////////////////////////////////////////////////////////
+  namespace
+  { /////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+    // some Helpers and Predicates
+    /////////////////////////////////////////////////////////////////
+
+    bool isDependencyAttribute( sat::SolvAttr attr_r )
+    {
+      static sat::SolvAttr deps[] = {
+        SolvAttr::provides,
+        SolvAttr::requires,
+        SolvAttr::recommends,
+        SolvAttr::obsoletes,
+        SolvAttr::conflicts,
+        SolvAttr::suggests,
+        SolvAttr::supplements,
+        SolvAttr::enhances,
+      };
+      for_( it, arrayBegin(deps), arrayEnd(deps) )
+        if ( *it == attr_r )
+          return true;
+      return false;
+    }
+
+    /** Whether the current capabilities edition range ovelaps and/or its solvables arch matches.
+     * Query asserts \a iter_r points to a capability and we
+     * have to check the range only.
+     */
+    struct EditionRangePredicate
+    {
+      EditionRangePredicate( const Rel & op, const Edition & edition )
+        : _range( op, edition )
+        , _arch( Arch_empty )
+      {}
+      EditionRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
+        : _range( op, edition )
+        , _arch( arch )
+      {}
+
+      bool operator()( sat::LookupAttr::iterator iter_r )
+      {
+       if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
+         return false;
+
+        CapDetail cap( iter_r.id() );
+        if ( ! cap.isSimple() )
+          return false;
+        if ( cap.isNamed() ) // no range to match
+          return true;
+        return overlaps( Edition::MatchRange( cap.op(), cap.ed() ), _range );
+      }
+
+      std::string serialize() const
+      {
+        std::string ret( "EditionRange" );
+        str::appendEscaped( ret, _range.op.asString() );
+        str::appendEscaped( ret, _range.value.asString() );
+        str::appendEscaped( ret, _arch.asString() );
+        return ret;
+      }
+
+      Edition::MatchRange _range;
+      Arch                _arch;
+   };
+
+    /** Whether the current Solvables edition is within a given range and/or its arch matches. */
+    struct SolvableRangePredicate
+    {
+      SolvableRangePredicate( const Rel & op, const Edition & edition )
+        : _range( op, edition )
+        , _arch( Arch_empty )
+      {}
+
+      SolvableRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
+        : _range( op, edition )
+        , _arch( arch )
+      {}
+
+      bool operator()( sat::LookupAttr::iterator iter_r )
+      {
+       if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
+         return false;
+       return overlaps( Edition::MatchRange( Rel::EQ, iter_r.inSolvable().edition() ), _range );
+      }
+
+      std::string serialize() const
+      {
+        std::string ret( "SolvableRange" );
+        str::appendEscaped( ret, _range.op.asString() );
+        str::appendEscaped( ret, _range.value.asString() );
+        str::appendEscaped( ret, _arch.asString() );
+        return ret;
+      }
+
+      Edition::MatchRange _range;
+      Arch                _arch;
+    };
+
+    /** Whether the current capability matches a given one.
+     * Query asserts \a iter_r points to a capability and we
+     * have to check the match only.
+     */
+    struct CapabilityMatchPredicate
+    {
+      CapabilityMatchPredicate( Capability cap_r )
+        : _cap( cap_r )
+      {}
+
+      bool operator()( sat::LookupAttr::iterator iter_r ) const
+      {
+        return _cap.matches( iter_r.asType<Capability>() ) == CapMatch::yes;
+      }
+
+      std::string serialize() const
+      {
+        std::string ret( "CapabilityMatch" );
+        str::appendEscaped( ret, _cap.asString() );
+        return ret;
+      }
+
+      Capability _cap;
+    };
+
+    /////////////////////////////////////////////////////////////////
+    //
+    /////////////////////////////////////////////////////////////////
+    /** Match data per attribtue.
+     *
+     * This includes the attribute itself, an optional \ref StrMatcher
+     * to restrict the query to certain string values, and an optional
+     * boolean \ref Predicate that may apply further restrictions that can
+     * not be expressed by the \ref strMatcher.
+     *
+     * Example for such a \ref predicate would be an additional edition range
+     * check whan looking for dependencies. The \ref strMatcher would
+     * find potential matches by looking at the dependencies name, the
+     * predicate will then check the edition ranges.
+     *
+     * As the \ref predicate takes an iterator pointing to the current
+     * match, it's also suitable for sub-structure (flexarray) inspection
+     * (\see \ref sat::LookupAttr::iterator::solvAttrSubEntry).
+     *
+     * \note: \see \ref addPredicate for further constraints.
+     */
+    struct AttrMatchData
+    {
+      typedef function<bool(sat::LookupAttr::iterator)> Predicate;
+
+      static bool always( sat::LookupAttr::iterator ) { return true; }
+      static bool never( sat::LookupAttr::iterator ) { return false; }
+
+      AttrMatchData()
+      {}
+
+      AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r )
+        : attr( attr_r )
+        , strMatcher( strMatcher_r )
+      {}
+
+      AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r,
+                     const Predicate & predicate_r, const std::string & predicateStr_r )
+        : attr( attr_r )
+        , strMatcher( strMatcher_r )
+        , predicate( predicate_r )
+        , predicateStr( predicateStr_r )
+      {}
+
+      /** A usable Predicate must provide a string serialization.
+       * As there is no \c operator== for \ref Predicate, we compare it's
+       * string representation instead. If you add new predicated, check the
+       * deserialization code in \ref deserialize.
+       */
+      template<class _Predicate>
+      void addPredicate( const _Predicate & predicate_r )
+      {
+        predicate    = predicate_r;
+        predicateStr = predicate_r.serialize();
+      }
+
+      /** Dumb serialization.
+       * \code
+       *   AttrMatchData ATTRIBUTE SEARCHSTRING [C|X] SERIALIZED_PREDICATE
+       * \endcode
+      */
+      std::string serialize() const
+      {
+        std::string ret( "AttrMatchData" );
+        str::appendEscaped( ret, attr.asString() );
+        str::appendEscaped( ret, strMatcher.searchstring() );
+        // TODO: Actually the flag should be serialized too, but for PoolQuery
+        // it's by now sufficient to differ between mode OTHER and others,
+        // i.e. whether to compile or not compile.
+        str::appendEscaped( ret, strMatcher.flags().mode() == Match::OTHER ? "C" : "X" );
+        str::appendEscaped( ret, predicateStr );
+        return ret;
+      }
+
+       /** Dumb restore from serialized string.
+        * \throw Exception on parse error.
+        */
+      static AttrMatchData deserialize( const std::string & str_r )
+      {
+        std::vector<std::string> words;
+        str::splitEscaped( str_r, std::back_inserter(words) );
+        if ( words.empty() || words[0] != "AttrMatchData" )
+          ZYPP_THROW( Exception( str::Str() << "Expecting AttrMatchData: " << str_r ) );
+        if ( words.size() != 5 )
+          ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+
+        AttrMatchData ret;
+        ret.attr = sat::SolvAttr( words[1] );
+        ret.strMatcher = StrMatcher( words[2] );
+        if ( words[3] == "C" )
+          ret.strMatcher.setFlags( Match::OTHER );
+        ret.predicateStr = words[4];
+
+        // now the predicate
+        words.clear();
+        str::splitEscaped( ret.predicateStr, std::back_inserter(words) );
+        if ( ! words.empty() )
+        {
+          if ( words[0] == "EditionRange" )
+          {
+           switch( words.size() )
+           {
+             case 3:
+               ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]) );
+               break;
+             case 4:
+               ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
+               break;
+             default:
+               ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+               break;
+           }
+          }
+          else if ( words[0] == "SolvableRange" )
+          {
+           switch( words.size() )
+           {
+             case 3:
+               ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]) );
+               break;
+             case 4:
+               ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
+               break;
+             default:
+               ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+               break;
+           }
+          }
+          else if ( words[0] == "CapabilityMatch" )
+          {
+            if ( words.size() != 2 )
+              ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+            ret.predicate = CapabilityMatchPredicate( Capability(words[1]) );
+          }
+          else
+            ZYPP_THROW( Exception( str::Str() << "Unknown predicate: " << str_r ) );
+        }
+        return ret;
+     }
+
+      sat::SolvAttr    attr;
+      StrMatcher strMatcher;
+      Predicate        predicate;
+      std::string      predicateStr;
+    };
+
+    /** \relates AttrMatchData */
+    inline std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
+    {
+      str << obj.attr << ": " << obj.strMatcher;
+      if ( obj.predicate )
+        str << " +(" << obj.predicateStr << ")";
+      return str;
+    }
+
+    /** \relates AttrMatchData */
+    inline bool operator==( const AttrMatchData & lhs, const AttrMatchData & rhs )
+    {
+      return ( lhs.attr == rhs.attr
+               && lhs.strMatcher == rhs.strMatcher
+               && lhs.predicateStr == rhs.predicateStr );
+    }
+
+    /** \relates AttrMatchData */
+    inline bool operator!=( const AttrMatchData & lhs, const AttrMatchData & rhs )
+    { return !( lhs == rhs ); }
+
+    /** \relates AttrMatchData Arbitrary order for std::container. */
+    inline bool operator<( const AttrMatchData & lhs, const AttrMatchData & rhs )
+    {
+      if ( lhs.attr != rhs.attr )
+        return (  lhs.attr < rhs.attr );
+      if ( lhs.strMatcher != rhs.strMatcher )
+        return (  lhs.strMatcher < rhs.strMatcher );
+      if ( lhs.predicateStr != rhs.predicateStr )
+        return (  lhs.predicateStr < rhs.predicateStr );
+      return false;
+    }
+
+    typedef std::list<AttrMatchData> AttrMatchList;
+
+
+  } /////////////////////////////////////////////////////////////////
+  // namespace
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
   //
   //  CLASS NAME : PoolQuery::Impl
   //
+  /** */
   class PoolQuery::Impl
   {
   public:
     Impl()
-      : _flags( SEARCH_ALL_REPOS | SEARCH_NOCASE | SEARCH_SUBSTRING )
-      , _status_flags(ALL)
-      , _match_word(false) 
+      : _flags( Match::SUBSTRING | Match::NOCASE | Match::SKIP_KIND )
+      , _match_word(false)
       , _require_all(false)
-      , _compiled(false)
+      , _status_flags(ALL)
     {}
 
     ~Impl()
     {}
 
   public:
-/*
-    static int repo_search_cb(void *cbdata, ::Solvable *s, ::Repodata *data, ::Repokey *key, ::KeyValue *kv)
-    {
-      PoolQuery *me = (PoolQuery*) cbdata;
-
-      bool r = false;
-
-      sat::Solvable solvable(s - sat::Pool::instance().get()->solvables);
-
-      // now filter by kind here (we cant do it before)
-      if ( ! me->_pimpl->_kinds.empty() )
-      {
-        // the user wants to filter by kind.
-        if ( find( me->_pimpl->_kinds.begin(),
-                   me->_pimpl->_kinds.end(),
-                   solvable.kind() )
-             == me->_pimpl->_kinds.end() )
-        {
-          // we did not find the kind in the list
-          // so this is not a result.
-          return SEARCH_NEXT_SOLVABLE;
-        }
-      }
-
-      if (me->_pimpl->_fnc)
-        r = me->_pimpl->_fnc( solvable );//makeResObject(solvable) );
-      
-      if (!r)
-        return SEARCH_STOP;
-      return SEARCH_NEXT_SOLVABLE;
-    }
-  */  
-    ResultIterator begin();
-    ResultIterator end();
-    
+    /** String representation */
     string asString() const;
-    
-  private:
-    void compile();
-    string createRegex(StrContainer & container);
 
-  public:
+    /** \name Raw query options. */
+    //@{
     /** Raw search strings. */
     StrContainer _strings;
-    /** Regex-compiled search strings. */
-    string _rcstrings;
     /** Raw attributes */
-    AttrMap _attrs;
-    /** Regex-compiled attributes */
-    CompiledAttrMap _rcattrs;
+    AttrRawStrMap _attrs;
+    /** Uncompiled attributes with predicate. */
+    std::set<AttrMatchData> _uncompiledPredicated;
+
+    /** Sat solver search flags */
+    Match _flags;
+    bool _match_word;
+    bool _require_all;
+
+    /** Sat solver status flags */
+    StatusFilter _status_flags;
+
+    /** Edition condition operand */
+    Edition _edition;
+    /** Operator for edition condition */
+    Rel _op;
 
     /** Repos to search. */
     StrContainer _repos;
+
     /** Kinds to search */
     Kinds _kinds;
+    //@}
 
-    /** Sat solver search flags */
-    int _flags;
-    /** Backup of search flags. compile() may change the flags if needed, so
-     * in order to reuse the query, the original flags need to be stored
-     * at the start of compile() */
-    int _flags_old;
-    /** Sat solver status flags */
-    int _status_flags;
+  public:
 
-    bool _match_word;
+    bool operator==( const PoolQuery::Impl & rhs ) const
+    {
+      if ( _flags == rhs._flags
+       // bnc#792901: while libzypp uses exact match mode for a single
+       // package name lock, zypper always uses glob. :(
+       // We unify those two forms to enable zypper to remove zypp locks
+       // without need to actually evaluate the query (which would require
+       // repos to be loaded).
+       || ( ( ( _flags.isModeString() && rhs._flags.isModeGlob() )
+           || ( _flags.isModeGlob() && rhs._flags.isModeString() ) )
+         && _strings.empty()
+         && _attrs.size() == 1
+         && _attrs.begin()->first == sat::SolvAttr::name ) )
+      {
+       return ( _strings == rhs._strings
+             && _attrs == rhs._attrs
+             && _uncompiledPredicated == rhs._uncompiledPredicated
+             && _match_word == rhs._match_word
+             && _require_all == rhs._require_all
+             && _status_flags == rhs._status_flags
+             && _edition == rhs._edition
+             && _op == rhs._op
+             && _repos == rhs._repos
+             && _kinds == rhs._kinds );
+      }
+      return false;
+    }
 
-    bool _require_all;
+    bool operator!=( const PoolQuery::Impl & rhs ) const
+    { return ! operator==( rhs ); }
+
+  public:
+    /** Compile the regex.
+     * Basically building the \ref _attrMatchList from strings.
+     * \throws MatchException Any of the exceptions thrown by \ref StrMatcher::compile.
+     */
+    void compile() const;
 
-    /** Sat solver Dataiterator structure */
-    ::_Dataiterator _rdit;
+    /** StrMatcher per attribtue. */
+    mutable AttrMatchList _attrMatchList;
 
-    bool _compiled;
+  private:
+    /** Pass flags from \ref compile, as they may have been changed. */
+    string createRegex( const StrContainer & container, const Match & flags ) const;
 
-    /** Function for processing found solvables. Used in execute(). */
-    mutable PoolQuery::ProcessResolvable _fnc;
   private:
     friend Impl * rwcowClone<Impl>( const Impl * rhs );
     /** clone for RWCOW_pointer */
     Impl * clone() const
     { return new Impl( *this ); }
   };
-/*
-  template <class _OutputIterator>
-  struct CollectNonEmpty
+
+  ///////////////////////////////////////////////////////////////////
+
+  struct MyInserter
   {
-    CollectNonEmpty( _OutputIterator iter_r ) : _iter( iter_r ) {}
+    MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
 
-    template<class _Tp>
-    bool operator()( const _Tp & value_r ) const
+    bool operator()(const string & str)
     {
-      if (value_r.empty())
-        return true;
-      *_iter++ = value_r;
+      _cont.insert(str);
       return true;
     }
 
-    private:
-      mutable _OutputIterator _iter;
+    PoolQuery::StrContainer & _cont;
   };
-*/
-  void PoolQuery::Impl::compile()
+
+
+  struct EmptyFilter
   {
-    // backup the flags
-    _flags_old = _flags;
+    bool operator()(const string & str)
+    {
+      return !str.empty();
+    }
+  };
+
+  void PoolQuery::Impl::compile() const
+  {
+    _attrMatchList.clear();
+
+    Match cflags( _flags );
+    if ( cflags.mode() == Match::OTHER ) // this will never succeed...
+      ZYPP_THROW( MatchUnknownModeException( cflags ) );
+
+    /** Compiled search strings. */
+    string rcstrings;
+
 
     // 'different'         - will have to iterate through all and match by ourselves (slow)
     // 'same'              - will pass the compiled string to dataiterator_init
     // 'one-attr'          - will pass it to dataiterator_init
     // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
-    
+
     // // NO ATTRIBUTE
     // else
     //   for all _strings
-    //     create regex; store in _rcstrings; if more strings flag regex;
+    //     create regex; store in rcstrings; if more strings flag regex;
     if (_attrs.empty())
     {
-      _rcstrings = createRegex(_strings);
-      if (_strings.size() > 1)
-        _flags = (_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;//setMatchRegex();
+      ; // A default 'query-all' will be added after all sources are processed.
     }
 
-    // // ONE ATTRIBUTE 
+    // // ONE ATTRIBUTE
     // else if _attrs is not empty but it contains just one attr
     //   for all _strings and _attr[key] strings
-    //     create regex; store in _rcattrs; flag 'one-attr'; if more strings flag regex;
+    //     create regex; flag 'one-attr'; if more strings flag regex;
     else if (_attrs.size() == 1)
     {
       StrContainer joined;
-      for(StrContainer::const_iterator it = _strings.begin(); it != _strings.end(); ++it)
-        if (!it->empty())
-          joined.insert(*it);
-      for(StrContainer::const_iterator it = _attrs.begin()->second.begin(); it != _attrs.begin()->second.end(); ++it)
-        if (!it->empty())
-          joined.insert(*it);
-      _rcstrings = createRegex(joined);
-      _rcattrs.insert(pair<sat::SolvAttr, string>(_attrs.begin()->first, string()));
+      invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
+      invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
+      rcstrings = createRegex(joined, cflags);
+      if (joined.size() > 1) // switch to regex for multiple strings
+        cflags.setModeRegex();
+      _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first,
+                                StrMatcher( rcstrings, cflags ) ) );
     }
 
-
     // // MULTIPLE ATTRIBUTES
     else
     {
+      // check whether there are any per-attribute strings
       bool attrvals_empty = true;
-      for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
+      for (AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
         if (!ai->second.empty())
           for(StrContainer::const_iterator it = ai->second.begin();
               it != ai->second.end(); it++)
@@ -215,54 +531,127 @@ namespace zypp
               attrvals_empty = false;
               goto attremptycheckend;
             }
-
 attremptycheckend:
 
+      // chceck whether the per-attribute strings are all the same
       bool attrvals_thesame = true;
-      for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
+      AttrRawStrMap::const_iterator ai = _attrs.begin();
+      const StrContainer & set1 = ai->second;
+      ++ai;
+      for (; ai != _attrs.end(); ++ai)
       {
-        
+        StrContainer result;
+        set_difference(
+          set1.begin(), set1.end(),
+          ai->second.begin(), ai->second.end(),
+          inserter(result, result.begin())/*, ltstr()*/);
+        if (!result.empty())
+        {
+          attrvals_thesame = false;
+          break;
+        }
       }
 
       // // THE SAME STRINGS FOR DIFFERENT ATTRS
       // else if _attrs is not empty but it does not contain strings
       //   for each key in _attrs take all _strings
-      //     create regex; store in _rcattrs and _rcstrings; flag 'same'; if more strings flag regex;
+      //     create regex; store in rcstrings; flag 'same'; if more strings flag regex;
       if (attrvals_empty || attrvals_thesame)
       {
+        StrContainer joined;
         if (attrvals_empty)
         {
-          // compile the search string
-          StrContainer joined;
-          for(StrContainer::const_iterator it = _strings.begin(); it != _strings.end(); ++it)
-            if (!it->empty())
-              joined.insert(*it);
-          _rcstrings = createRegex(joined);
-
-          // copy the _attrs keys to _rcattrs
-          for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
-            _rcattrs.insert(pair<sat::SolvAttr, string>(ai->first, string()));
+          invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
+          rcstrings = createRegex(joined, cflags);
+        }
+        else
+        {
+          invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
+          invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
+          rcstrings = createRegex(joined, cflags);
+        }
+        if (joined.size() > 1) // switch to regex for multiple strings
+          cflags.setModeRegex();
+        // May use the same StrMatcher for all
+        StrMatcher matcher( rcstrings, cflags );
+        for_( ai, _attrs.begin(), _attrs.end() )
+        {
+          _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) );
         }
       }
+
       // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
       // if _attrs is not empty and it contains non-empty vectors with non-empty strings
       //   for each key in _attrs take all _strings + all _attrs[key] strings
-      //     create regex; store in _rcattrs; flag 'different'; if more strings flag regex;
+      //     create regex; flag 'different'; if more strings flag regex;
       else
       {
-        
+        for_(ai, _attrs.begin(), _attrs.end())
+        {
+          StrContainer joined;
+          invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
+          invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
+          string s = createRegex(joined, cflags);
+          if (joined.size() > 1) // switch to regex for multiple strings
+            cflags.setModeRegex();
+          _attrMatchList.push_back( AttrMatchData( ai->first,
+                                    StrMatcher( s, cflags ) ) );
+        }
+      }
+    }
+
+    // Now handle any predicated queries
+    if ( ! _uncompiledPredicated.empty() )
+    {
+      StrContainer global;
+      invokeOnEach( _strings.begin(), _strings.end(), EmptyFilter(), MyInserter(global) );
+      for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
+      {
+        if ( it->strMatcher.flags().mode() == Match::OTHER )
+        {
+          // need to compile:
+          StrContainer joined( global );
+          const std::string & mstr( it->strMatcher.searchstring() );
+          if ( ! mstr.empty() )
+            joined.insert( mstr );
+
+          cflags = _flags;
+          rcstrings = createRegex( joined, cflags );
+          if ( joined.size() > 1 ) // switch to regex for multiple strings
+            cflags.setModeRegex();
+
+          _attrMatchList.push_back( AttrMatchData( it->attr,
+                                    StrMatcher( rcstrings, cflags ),
+                                                      it->predicate, it->predicateStr ) );
+        }
+        else
+        {
+          // copy matcher
+         _attrMatchList.push_back( *it );
+        }
       }
     }
 
-    // tell the Dataiterator to search only in one repo if only one specified
-    if (_repos.size() == 1)
-      _flags &= ~SEARCH_ALL_REPOS;
+    // If no attributes defined at all, then add 'query all'
+    if ( _attrMatchList.empty() )
+    {
+      cflags = _flags;
+      rcstrings = createRegex( _strings, cflags );
+      if ( _strings.size() > 1 ) // switch to regex for multiple strings
+        cflags.setModeRegex();
+      _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr,
+                                StrMatcher( rcstrings, cflags ) ) );
+    }
 
-    _compiled = true;
-    
-    DBG << asString() << endl;
+    // Finally check here, whether all involved regex compile.
+    for_( it, _attrMatchList.begin(), _attrMatchList.end() )
+    {
+      it->strMatcher.compile(); // throws on error
+    }
+    //DBG << asString() << endl;
   }
 
+
   /**
    * Converts '*' and '?' wildcards within str into their regex equivalents.
    */
@@ -270,8 +659,6 @@ attremptycheckend:
   {
     string regexed = str;
 
-    str::regex all("\\*"); // regex to search for '*'
-    str::regex one("\\?"); // regex to search for '?'
     string r_all(".*"); // regex equivalent of '*'
     string r_one(".");  // regex equivalent of '?'
     string::size_type pos;
@@ -284,16 +671,13 @@ attremptycheckend:
     for (pos = 0; (pos = regexed.find('?', pos)) != std::string::npos; ++pos)
       regexed = regexed.replace(pos, 1, r_one);
 
-    DBG << " -> " << regexed << endl;
-
     return regexed;
   }
 
+  string PoolQuery::Impl::createRegex( const StrContainer & container, const Match & flags ) const
+  {
 //! macro for word boundary tags for regexes
 #define WB (_match_word ? string("\\b") : string())
-
-  string PoolQuery::Impl::createRegex(StrContainer & container)
-  {
     string rstr;
 
     if (container.empty())
@@ -301,47 +685,45 @@ attremptycheckend:
 
     if (container.size() == 1)
     {
-      if (_match_word)
-        return ".*" + WB + *container.begin() + WB + ".*";
-
-      return *container.begin();
+      return WB + *container.begin() + WB;
     }
 
     // multiple strings
 
-    bool _use_wildcards = (_flags & SEARCH_STRINGMASK) == SEARCH_GLOB;
+    bool use_wildcards = flags.isModeGlob();
     StrContainer::const_iterator it = container.begin();
     string tmp;
 
-    if (_use_wildcards)
+    if (use_wildcards)
       tmp = wildcards2regex(*it);
+    else
+      tmp = *it;
 
     if (_require_all)
     {
-      if (!(_flags & SEARCH_STRING)) // not match exact
+      if ( ! flags.isModeString() ) // not match exact
         tmp += ".*" + WB + tmp;
       rstr = "(?=" + tmp + ")";
     }
     else
     {
-      if (_flags & SEARCH_STRING) // match exact
+      if ( flags.isModeString() || flags.isModeGlob() )
         rstr = "^";
-      else
-        rstr = ".*" + WB;
-
-      rstr += "(" + tmp;
+      rstr += WB + "(" + tmp;
     }
 
     ++it;
 
     for (; it != container.end(); ++it)
     {
-      if (_use_wildcards)
+      if (use_wildcards)
         tmp = wildcards2regex(*it);
+      else
+        tmp = *it;
 
       if (_require_all)
       {
-        if (!(_flags & SEARCH_STRING)) // not match exact
+        if ( ! flags.isModeString() ) // not match exact
           tmp += ".*" + WB + tmp;
         rstr += "(?=" + tmp + ")";
       }
@@ -353,109 +735,53 @@ attremptycheckend:
 
     if (_require_all)
     {
-      if (!(_flags & SEARCH_STRING)) // not match exact
+      if ( ! flags.isModeString() ) // not match exact
         rstr += WB + ".*";
     }
     else
     {
-      rstr += ")";
-      if (_flags & SEARCH_STRING) // match exact
+      rstr += ")" + WB;
+      if ( flags.isModeString() || flags.isModeGlob() )
         rstr += "$";
-      else
-        rstr += WB + ".*";
     }
 
     return rstr;
+#undef WB
   }
 
-
-  PoolQuery::ResultIterator PoolQuery::Impl::begin()
+  string PoolQuery::Impl::asString() const
   {
-    compile();
+    ostringstream o;
 
-    // if only one repository has been specified, find it in the pool
-    sat::Pool pool(sat::Pool::instance());
-    sat::Pool::RepositoryIterator itr = pool.reposBegin();
-    if (!(_flags & SEARCH_ALL_REPOS) && _repos.size() == 1)
+    o << "kinds: ";
+    if ( _kinds.empty() )
+      o << "ALL";
+    else
     {
-      string theone = *_repos.begin();
-      for (; itr->info().alias() != theone && itr != pool.reposEnd(); ++itr);
-      if (itr == pool.reposEnd())
-      {
-        RepoInfo info; info.setAlias(theone);
-        ERR << "Repository not found in sat pool." <<  endl;
-        ZYPP_THROW(repo::RepoNotFoundException(info));
-      }
+      for(Kinds::const_iterator it = _kinds.begin();
+          it != _kinds.end(); ++it)
+        o << *it << " ";
     }
+    o << endl;
 
-    DBG << "_flags:" << _flags << endl;
-
-    if (_rcattrs.empty())
-    {
-    ::dataiterator_init(&_rdit,
-      _flags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), // repository \todo fix this
-      0,                                           // search all solvables
-      0,                                           // attribute id - only if 1 attr key specified
-      _rcstrings.empty() ? 0 : _rcstrings.c_str(), // compiled search string
-      _flags);
-    }
-    else if (_rcattrs.size() == 1)
-    {
-      ::dataiterator_init(&_rdit,
-        _flags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), // repository \todo fix this 
-        0,                                           // search all solvables
-        _rcattrs.begin()->first.id(),                // keyname - attribute id - only if 1 attr key specified
-        _rcstrings.empty() ? 0 : _rcstrings.c_str(), // compiled search string 
-        _flags);
-    }
+    o << "repos: ";
+    if ( _repos.empty() )
+      o << "ALL";
     else
     {
-      ::dataiterator_init(&_rdit,
-        _flags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), /* repository - switch to next at the end of current one in increment() */ 
-        0, /*search all resolvables */
-        0, /*keyname - if only 1 attr key specified, pass it here, otherwise do more magic */
-        0, //qs.empty() ? 0 : qs.c_str(), /* create regex, pass it here */
-        _flags);
+      for(StrContainer::const_iterator it = _repos.begin();
+          it != _repos.end(); ++it)
+        o << *it << " ";
     }
-
-    PoolQuery::ResultIterator it(this);
-    it.increment();
-    return it;
-  }
-
-  PoolQuery::ResultIterator PoolQuery::Impl::end()
-  {
-    INT << "end" << endl;
-    return PoolQuery::ResultIterator();
-  }
-
-
-  string PoolQuery::Impl::asString() const
-  {
-    ostringstream o;
-
-    o << "compiled: " << _compiled << endl;
-    
-    o << "kinds: ";
-    for(Kinds::const_iterator it = _kinds.begin();
-        it != _kinds.end(); ++it)
-      o << *it << " ";
     o << endl;
 
-    o << "repos: ";
-    for(StrContainer::const_iterator it = _repos.begin();
-        it != _repos.end(); ++it)
-      o << *it << " ";
-    o << endl;
+    o << "version: "<< _op << " " << _edition.asString() << endl;
+    o << "status: " << ( _status_flags ? ( _status_flags == INSTALLED_ONLY ? "INSTALLED_ONLY" : "UNINSTALLED_ONLY" )
+                                       : "ALL" ) << endl;
 
-    o << "string match flags:" << endl;
-    o << "* string/substring/glob/regex: " << (_flags & SEARCH_STRINGMASK) << endl; 
-    o << "* SEARCH_NOCASE: " << ((_flags & SEARCH_NOCASE) ? "yes" : "no") << endl;
-    o << "* SEARCH_ALL_REPOS: " << ((_flags & SEARCH_ALL_REPOS) ? "yes" : "no") << endl;
-    o << "status filter flags:" << _status_flags << endl;
+    o << "string match flags: " << Match(_flags) << endl;
 
     // raw
-
     o << "strings: ";
     for(StrContainer::const_iterator it = _strings.begin();
         it != _strings.end(); ++it)
@@ -463,7 +789,7 @@ attremptycheckend:
     o << endl;
 
     o << "attributes: " << endl;
-    for(AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
+    for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
     {
       o << "* " << ai->first << ": ";
       for(StrContainer::const_iterator vi = ai->second.begin();
@@ -472,215 +798,31 @@ attremptycheckend:
       o << endl;
     }
 
-    // compiled
-
-    o << "regex compiled strings: " << _rcstrings << endl;
-    o << "regex compiled attributes:" << endl;
-    for (CompiledAttrMap::const_iterator ai = _rcattrs.begin(); ai != _rcattrs.end(); ++ai)
-      o << "* " << ai->first << ": " << ai->second << endl;
-
-    return o.str();
-  }
-
-  /** \relates PoolQuery::Impl Stream output *//*
-  inline std::ostream & operator<<( std::ostream & str, const PoolQuery::Impl & obj )
-  {
-    return str << "PoolQuery::Impl";
-  }
-  */
-  ///////////////////////////////////////////////////////////////////
-
-  ///////////////////////////////////////////////////////////////////
-  //
-  //  CLASS NAME : PoolQuery::ResultIterator
-  //
-  ///////////////////////////////////////////////////////////////////
-
-  PoolQuery::ResultIterator::ResultIterator(Impl * pqimpl)
-  : PoolQuery::ResultIterator::iterator_adaptor_(pqimpl ? &pqimpl->_rdit : 0)
-  , _rdit(pqimpl ? &pqimpl->_rdit : 0)
-  , _pqimpl(pqimpl)
-  , _sid(0)
-  , _has_next(true)
-  , _attrs(pqimpl->_rcattrs)
-  , _do_matching(false)
-  , _pool((sat::Pool::instance()))
-  {
-    if (_attrs.size() > 1)
-      _do_matching = true;
-  }
-
-  void PoolQuery::ResultIterator::increment()
-  {
-    if (!_rdit)
-      return;
-
-    bool got_match = false;
-    if (_has_next)
+    o << "predicated: " << endl;
+    for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
     {
-      DBG << "last: " << _sid << endl;
-      while (_has_next && !(got_match = matchSolvable()));
+      o << "* " << *it << endl;
     }
 
-    // no more solvables and the last did not match
-    if (!got_match && !_has_next)
+    // compiled
+    o << "last attribute matcher compiled: " << endl;
+    if ( _attrMatchList.empty() )
     {
-      base_reference() = 0;
-      _sid = 0;
+      o << "not yet compiled" << endl;
     }
-
-    DBG << "next: " << _sid << endl;
-  }
-
-  bool PoolQuery::ResultIterator::matchSolvable()
-  {
-    _sid = _rdit->solvid;
-
-    bool new_solvable = true;
-    bool matches = !_do_matching;
-    bool in_repo;
-    bool drop_by_kind_status = false;
-    bool drop_by_repo = false;
-    do
+    else
     {
-      //! \todo FIXME Dataiterator returning resolvables belonging to current repo?
-      in_repo = _sid >= _rdit->repo->start;
-
-      if (in_repo && new_solvable)
-      {
-        while(1)
-        {
-          drop_by_repo = false;
-          if (!_pqimpl->_repos.empty() && 
-            _pqimpl->_repos.find(_rdit->repo->name) == _pqimpl->_repos.end())
-          {
-            drop_by_repo = true;
-            break;
-          }
-
-          drop_by_kind_status = false;
-
-          // whether to drop an uninstalled (repo) solvable
-          if ( (_pqimpl->_status_flags & INSTALLED_ONLY) &&
-               _rdit->repo->name != _pool.systemRepoName() )
-          {
-            drop_by_kind_status = true;
-            break;
-          }
-
-          // whether to drop an installed (target) solvable
-          if ((_pqimpl->_status_flags & UNINSTALLED_ONLY) &&
-               _rdit->repo->name == _pool.systemRepoName())
-          {
-            drop_by_kind_status = true;
-            break;
-          }
-
-          // whether to drop unwanted kind
-          if (!_pqimpl->_kinds.empty())
-          {
-            sat::Solvable s(_sid);
-            // the user wants to filter by kind.
-            if (_pqimpl->_kinds.find(s.kind()) == _pqimpl->_kinds.end())
-              drop_by_kind_status = true;
-          }
-
-          break;
-        }
-
-        matches = matches && !drop_by_kind_status && !drop_by_repo;
-      }
-
-      if (_do_matching && !drop_by_kind_status)
-      {
-        if (!matches && in_repo)
-        {
-          SolvAttr attr(_rdit->key->name);
-          CompiledAttrMap::const_iterator ai = _attrs.find(attr);
-          if (ai != _attrs.end())
-          {
-            const string & sstr =
-              _pqimpl->_rcstrings.empty() ? ai->second : _pqimpl->_rcstrings;
-            const IdString & value =
-              IdString(_rdit->kv.id);
-
-            switch(_pqimpl->_flags & SEARCH_STRINGMASK)
-            {
-            case SEARCH_STRING:
-              if (_pqimpl->_flags & SEARCH_NOCASE)
-                matches = ! str::compareCI(sstr.c_str(), value.c_str());
-              else
-                matches = (sstr == value.asString());
-              break;
-            case SEARCH_SUBSTRING:
-              if (_pqimpl->_flags & SEARCH_NOCASE)
-                matches = ::strcasestr(value.c_str(), sstr.c_str());
-              else
-                matches = (value.asString().find(sstr) != string::npos);
-              break;
-            case SEARCH_GLOB:
-              matches = !::fnmatch(sstr.c_str(), value.c_str(),
-                  (_pqimpl->_flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
-              break;
-            case SEARCH_REGEX:
-              matches = false;
-              break;
-            default:
-              matches = false;
-              ERR << "invalid string matching type: "
-                  << (_pqimpl->_flags & SEARCH_STRINGMASK) << endl;
-            }
-            if (matches)
-              INT << "value: " << value.asString() << endl
-                  << " mstr: " <<  sstr << endl; 
-          }
-        }
-      }
-
-      if (drop_by_repo)
+      for_( it, _attrMatchList.begin(), _attrMatchList.end() )
       {
-        Repository nextRepo(Repository(_rdit->repo).nextInPool());
-        ::dataiterator_skip_repo(_rdit);
-        if (nextRepo)
-          ::dataiterator_jump_to_repo(_rdit, nextRepo.get());
-        drop_by_repo = false;
-      }
-      else if (drop_by_kind_status)
-      {
-        ::dataiterator_skip_solvable(_rdit);
-        drop_by_kind_status = false;
-      }
-
-      if ((_has_next = ::dataiterator_step(_rdit)))
-      {
-        new_solvable = _rdit->solvid != _sid;
-        if (!in_repo)
-          _sid = _rdit->solvid;
-      }
-      // no more attributes in this repo, return
-      else
-      {
-        // check for more repos to jump to
-        if (!_pqimpl->_repos.empty())
-        {
-          Repository nextRepo(Repository(_rdit->repo).nextInPool());
-          if (nextRepo)
-          {
-            ::dataiterator_jump_to_repo(_rdit, nextRepo.get());
-            _has_next = ::dataiterator_step(_rdit);
-          }
-        }
-
-        // did the last solvable match conditions?
-        return matches && in_repo;
+        o << "* " << *it << endl;
       }
     }
-    while (!new_solvable || !in_repo);
-
-    return matches;
+    return o.str();
   }
 
   ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
   //
   //   CLASS NAME : PoolQuery
   //
@@ -690,54 +832,98 @@ attremptycheckend:
     : _pimpl(new Impl())
   {}
 
-
   PoolQuery::~PoolQuery()
   {}
 
-
   void PoolQuery::addRepo(const std::string &repoalias)
   {
+    if (repoalias.empty())
+    {
+      WAR << "ignoring an empty repository alias" << endl;
+      return;
+    }
     _pimpl->_repos.insert(repoalias);
-    _pimpl->_flags &= ~SEARCH_ALL_REPOS;
   }
 
-
-  void PoolQuery::addKind(const Resolvable::Kind &kind)
+  void PoolQuery::addKind(const ResKind & kind)
   { _pimpl->_kinds.insert(kind); }
 
-
   void PoolQuery::addString(const string & value)
   { _pimpl->_strings.insert(value); }
 
-
   void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
   { _pimpl->_attrs[attr].insert(value); }
 
+  void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition )
+  { return addDependency( attr, name, op, edition, Arch_empty ); }
 
-  void PoolQuery::setCaseSensitive(const bool value)
+  void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
   {
-    if (value)
-      _pimpl->_flags &= ~SEARCH_NOCASE;
+    switch ( op.inSwitch() )
+    {
+      case Rel::ANY_e: // no additional constraint on edition.
+        if ( arch.empty() )    // no additional constraint on arch.
+       {
+         addAttribute( attr, name );
+         return;
+       }
+       break;
+
+      case Rel::NONE_e:        // will never match.
+        return;
+
+      default: // go and add the predicated query (uncompiled)
+        break;
+    }
+
+    // Match::OTHER indicates need to compile
+    // (merge global search strings into name).
+    AttrMatchData attrMatchData( attr, StrMatcher( name, Match::OTHER ) );
+
+    if ( isDependencyAttribute( attr ) )
+      attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
     else
-      _pimpl->_flags |= SEARCH_NOCASE;
+      attrMatchData.addPredicate( SolvableRangePredicate( op, edition, arch ) );
+
+    _pimpl->_uncompiledPredicated.insert( attrMatchData );
   }
 
+  void PoolQuery::addDependency( const sat::SolvAttr & attr, Capability cap_r )
+  {
+    CapDetail cap( cap_r );
+    if ( ! cap.isSimple() ) // will never match.
+      return;
+
+    // Matches STRING per default. (won't get compiled!)
+    AttrMatchData attrMatchData( attr, StrMatcher( cap.name().asString() ) );
+
+    if ( isDependencyAttribute( attr ) )
+      attrMatchData.addPredicate( CapabilityMatchPredicate( cap_r ) );
+    else
+      attrMatchData.addPredicate( SolvableRangePredicate( cap.op(), cap.ed() ) );
+
+    _pimpl->_uncompiledPredicated.insert( attrMatchData );
+  }
+
+  void PoolQuery::setEdition(const Edition & edition, const Rel & op)
+  {
+    _pimpl->_edition = edition;
+    _pimpl->_op = op;
+  }
 
-  void PoolQuery::setMatchSubstring()
-  { _pimpl->_flags |= (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_SUBSTRING; }
-  void PoolQuery::setMatchExact()
-  { _pimpl->_flags |= (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_STRING; }
-  void PoolQuery::setMatchRegex()
-  { _pimpl->_flags |= (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX; }
-  void PoolQuery::setMatchGlob()
-  { _pimpl->_flags |= (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_GLOB; }
+  void PoolQuery::setMatchSubstring()  { _pimpl->_flags.setModeSubstring(); }
+  void PoolQuery::setMatchExact()      { _pimpl->_flags.setModeString(); }
+  void PoolQuery::setMatchRegex()      { _pimpl->_flags.setModeRegex(); }
+  void PoolQuery::setMatchGlob()       { _pimpl->_flags.setModeGlob(); }
   void PoolQuery::setMatchWord()
   {
     _pimpl->_match_word = true;
-    _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;
+    _pimpl->_flags.setModeRegex();
   }
 
-  void PoolQuery::setFlags(int flags)
+  Match PoolQuery::flags() const
+  { return _pimpl->_flags; }
+  void PoolQuery::setFlags( const Match & flags )
   { _pimpl->_flags = flags; }
 
 
@@ -745,11 +931,11 @@ attremptycheckend:
   { _pimpl->_status_flags = INSTALLED_ONLY; }
   void PoolQuery::setUninstalledOnly()
   { _pimpl->_status_flags = UNINSTALLED_ONLY; }
-  void PoolQuery::setStatusFilterFlags( int flags )
+  void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
   { _pimpl->_status_flags = flags; }
 
 
-  void PoolQuery::setRequireAll(const bool require_all)
+  void PoolQuery::setRequireAll(bool require_all)
   { _pimpl->_require_all = require_all; }
 
 
@@ -757,10 +943,24 @@ attremptycheckend:
   PoolQuery::strings() const
   { return _pimpl->_strings; }
 
-  const PoolQuery::AttrMap &
+  const PoolQuery::AttrRawStrMap &
   PoolQuery::attributes() const
   { return _pimpl->_attrs; }
 
+  const PoolQuery::StrContainer &
+  PoolQuery::attribute(const sat::SolvAttr & attr) const
+  {
+    static const PoolQuery::StrContainer nocontainer;
+    AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
+    return it != _pimpl->_attrs.end() ? it->second : nocontainer;
+  }
+
+  const Edition PoolQuery::edition() const
+  { return _pimpl->_edition; }
+  const Rel PoolQuery::editionRel() const
+  { return _pimpl->_op; }
+
+
   const PoolQuery::Kinds &
   PoolQuery::kinds() const
   { return _pimpl->_kinds; }
@@ -769,19 +969,21 @@ attremptycheckend:
   PoolQuery::repos() const
   { return _pimpl->_repos; }
 
+
   bool PoolQuery::caseSensitive() const
-  { return _pimpl->_flags & SEARCH_NOCASE; }
-
-  bool PoolQuery::matchExact() const
-  { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_STRING; }
-  bool PoolQuery::matchSubstring() const
-  { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_SUBSTRING; }
-  bool PoolQuery::matchGlob() const
-  { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_GLOB; }
-  bool PoolQuery::matchRegex() const
-  { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_REGEX; }
-  int PoolQuery::matchType() const
-  { return _pimpl->_flags & SEARCH_STRINGMASK; }
+  { return !_pimpl->_flags.test( Match::NOCASE ); }
+  void PoolQuery::setCaseSensitive( bool value )
+  { _pimpl->_flags.turn( Match::NOCASE, !value ); }
+
+  bool PoolQuery::filesMatchFullPath() const
+  { return _pimpl->_flags.test( Match::FILES ); }
+  void PoolQuery::setFilesMatchFullPath( bool value )
+  { _pimpl->_flags.turn( Match::FILES, value ); }
+
+  bool PoolQuery::matchExact() const           { return _pimpl->_flags.isModeString(); }
+  bool PoolQuery::matchSubstring() const       { return _pimpl->_flags.isModeSubstring(); }
+  bool PoolQuery::matchGlob() const            { return _pimpl->_flags.isModeGlob(); }
+  bool PoolQuery::matchRegex() const           { return _pimpl->_flags.isModeRegex(); }
 
   bool PoolQuery::matchWord() const
   { return _pimpl->_match_word; }
@@ -789,66 +991,36 @@ attremptycheckend:
   bool PoolQuery::requireAll() const
   { return _pimpl->_require_all; }
 
+  PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
+  { return _pimpl->_status_flags; }
 
-
-  PoolQuery::ResultIterator PoolQuery::begin()
-  { return _pimpl->begin(); }
-
-
-  PoolQuery::ResultIterator PoolQuery::end()
-  { return _pimpl->end(); }
-
-
-  bool PoolQuery::empty()
-  { return _pimpl->begin() == _pimpl->end(); }
-
-  //! \todo collect the result, reuse if not dirty
-  PoolQuery::size_type PoolQuery::size()
+  bool PoolQuery::empty() const
   {
-    size_type count = 0;
-    for(ResultIterator it = _pimpl->begin(); it != _pimpl->end(); ++it, ++count);
-    return count;
+    try { return begin() == end(); }
+    catch (const Exception & ex) {}
+    return true;
   }
 
-
-  void PoolQuery::execute(ProcessResolvable fnc)
+  PoolQuery::size_type PoolQuery::size() const
   {
-    invokeOnEach(_pimpl->begin(), _pimpl->end(), fnc);
-    /*
-    _pimpl->_fnc = fnc;
-    string term;
-    if (!_pimpl->_strings.empty())
-      term = *_pimpl->_strings.begin();
-
-    sat::Pool pool(sat::Pool::instance());
-    for ( sat::Pool::RepositoryIterator itr = pool.reposBegin();
-          itr != pool.reposEnd();
-          ++itr )
+    try
     {
-      // filter by installed uninstalled
-      if ( ( _pimpl->_status_flags & INSTALLED_ONLY ) && (itr->name() != sat::Pool::instance().systemRepoName()) )
-        continue;
-
-      if ( ( _pimpl->_status_flags & UNINSTALLED_ONLY ) && (itr->name() == sat::Pool::instance().systemRepoName()) )
-        continue;
-
-      // is this repo in users repos?
-      bool included = ( find(_pimpl->_repos.begin(), _pimpl->_repos.end(), itr->name()) != _pimpl->_repos.end() );
-
-      // only look in user repos filter if the filter is not empty
-      // in this case we search in all
-      if ( _pimpl->_repos.empty() || included  )
-      {
-        repo_search( itr->get(), 0, 0, term.c_str(), _pimpl->_flags, Impl::repo_search_cb, (void*) (this));
-      }
-
+      size_type count = 0;
+      for_( it, begin(), end() )
+        ++count;
+      return count;
     }
-    */
+    catch (const Exception & ex) {}
+    return 0;
   }
 
+  void PoolQuery::execute(ProcessResolvable fnc)
+  { invokeOnEach( begin(), end(), fnc); }
+
+
   ///////////////////////////////////////////////////////////////////
   //
-  //  CLASS NAME : PoolQuery::Impl
+  //  CLASS NAME : PoolQuery::Attr
   //
   /**
    * represents all atributes in PoolQuery except SolvAtributes, which are
@@ -856,52 +1028,83 @@ attremptycheckend:
    */
   struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
   {
-    private:
-      friend class IdStringType<PoolQueryAttr>;
-      IdString _str;
+  private:
+    friend class IdStringType<PoolQueryAttr>;
+    IdString _str;
+  public:
 
-    public:
-    
     //noAttr
-    PoolQueryAttr():isSolvAttr(false){}
+    PoolQueryAttr(){}
 
     explicit PoolQueryAttr( const char* cstr_r )
-        : _str( cstr_r ),isSolvAttr(false){}
+        : _str( cstr_r )
+      {}
 
     explicit PoolQueryAttr( const std::string & str_r )
-        : _str( str_r ),isSolvAttr(false)
-    {
-      if( _str==noAttr ){
-        sat::SolvAttr sa(str_r);
-        if( sa != sat::SolvAttr::noAttr )
-        {
-          isSolvAttr = true; 
-        }
-      }
-    }
+        : _str( str_r )
+      {}
 
-    //unknown atributes
+    // unknown atributes
     static const PoolQueryAttr noAttr;
 
-    // own attributes
-    static const PoolQueryAttr nameAttr;
+    // PoolQuery's own attributes
     static const PoolQueryAttr repoAttr;
     static const PoolQueryAttr kindAttr;
-
-    // exported attributes from SolvAtributes
-    bool isSolvAttr;
+    static const PoolQueryAttr stringAttr;
+    static const PoolQueryAttr stringTypeAttr;
+    static const PoolQueryAttr requireAllAttr;
+    static const PoolQueryAttr caseSensitiveAttr;
+    static const PoolQueryAttr installStatusAttr;
+    static const PoolQueryAttr editionAttr;
+    static const PoolQueryAttr complexAttr;
   };
 
   const PoolQueryAttr PoolQueryAttr::noAttr;
 
-  const PoolQueryAttr PoolQueryAttr::nameAttr( "name" );
   const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
-  const PoolQueryAttr PoolQueryAttr::kindAttr( "kind" );
+  const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
+  const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
+  const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
+  const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");
+  const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
+  const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
+  const PoolQueryAttr PoolQueryAttr::editionAttr("version");
+  const PoolQueryAttr PoolQueryAttr::complexAttr("complex");
+
+  class StringTypeAttr : public IdStringType<PoolQueryAttr>
+  {
+    friend class IdStringType<StringTypeAttr>;
+    IdString _str;
+
+  public:
+    StringTypeAttr(){}
+    explicit StringTypeAttr( const char* cstr_r )
+            : _str( cstr_r ){}
+    explicit StringTypeAttr( const std::string & str_r )
+             : _str( str_r ){}
+
+    static const StringTypeAttr noAttr;
+
+    static const StringTypeAttr exactAttr;
+    static const StringTypeAttr substringAttr;
+    static const StringTypeAttr regexAttr;
+    static const StringTypeAttr globAttr;
+    static const StringTypeAttr wordAttr;
+  };
+
+  const StringTypeAttr StringTypeAttr::noAttr;
+
+  const StringTypeAttr StringTypeAttr::exactAttr("exact");
+  const StringTypeAttr StringTypeAttr::substringAttr("substring");
+  const StringTypeAttr StringTypeAttr::regexAttr("regex");
+  const StringTypeAttr StringTypeAttr::globAttr("glob");
+  const StringTypeAttr StringTypeAttr::wordAttr("word");
 
   ///////////////////////////////////////////////////////////////////
 
 
   //\TODO maybe ctor with stream can be usefull
+  //\TODO let it throw, let it throw, let it throw.
   bool PoolQuery::recover( istream &str, char delim )
   {
     bool finded_something = false; //indicates some atributes is finded
@@ -932,41 +1135,154 @@ attremptycheckend:
 
       finded_something = true;
 
-      string atrName(str::trim(string(s,0,pos))); // trimmed name of atribute
-      string atrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
+      string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
+      string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
 
-      PoolQueryAttr attribute( atrName );
+      PoolQueryAttr attribute( attrName );
 
-      if ( attribute==PoolQueryAttr::nameAttr)
+      if ( attribute==PoolQueryAttr::repoAttr )
       {
-        //setName...maybe some regex test
-        break;
+        addRepo( attrValue );
       }
-      else if ( attribute==PoolQueryAttr::repoAttr )
+      /* some backwards compatibility */
+      else if ( attribute==PoolQueryAttr::kindAttr || attribute=="kind" )
       {
-        addRepo( atrValue );
+        addKind( ResKind(attrValue) );
       }
-      else if ( attribute==PoolQueryAttr::kindAttr )
+      else if ( attribute==PoolQueryAttr::stringAttr
+        || attribute=="global_string")
       {
-        addKind( Resolvable::Kind(atrValue) );
+        addString( attrValue );
       }
-      else if ( attribute==PoolQueryAttr::noAttr )
+      else if ( attribute==PoolQueryAttr::stringTypeAttr
+        || attribute=="string_type" )
       {
-        if (attribute.isSolvAttr)
+        StringTypeAttr s(attrValue);
+        if( s == StringTypeAttr::regexAttr )
+        {
+          setMatchRegex();
+        }
+        else if ( s == StringTypeAttr::globAttr )
+        {
+          setMatchGlob();
+        }
+        else if ( s == StringTypeAttr::exactAttr )
+        {
+          setMatchExact();
+        }
+        else if ( s == StringTypeAttr::substringAttr )
+        {
+          setMatchSubstring();
+        }
+        else if ( s == StringTypeAttr::wordAttr )
+        {
+          setMatchWord();
+        }
+        else if ( s == StringTypeAttr::noAttr )
+        {
+          WAR << "unknown string type " << attrValue << endl;
+        }
+        else
+        {
+          WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
+        }
+      }
+      else if ( attribute==PoolQueryAttr::requireAllAttr )
+      {
+        if ( str::strToTrue(attrValue) )
+        {
+          setRequireAll(true);
+        }
+        else if ( !str::strToFalse(attrValue) )
+        {
+          setRequireAll(false);
+        }
+        else
+        {
+          WAR << "unknown boolean value " << attrValue << endl;
+        }
+      }
+      else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
+      {
+        if ( str::strToTrue(attrValue) )
+        {
+          setCaseSensitive(true);
+        }
+        else if ( !str::strToFalse(attrValue) )
+        {
+          setCaseSensitive(false);
+        }
+        else
+        {
+          WAR << "unknown boolean value " << attrValue << endl;
+        }
+      }
+      else if ( attribute==PoolQueryAttr::installStatusAttr )
+      {
+        if( attrValue == "all" )
+        {
+          setStatusFilterFlags( ALL );
+        }
+        else if( attrValue == "installed" )
+        {
+          setInstalledOnly();
+        }
+        else if( attrValue == "not-installed" )
         {
-          //setAtribute
+          setUninstalledOnly();
         }
         else
         {
-          //log unknwon atribute
+          WAR << "Unknown value for install status " << attrValue << endl;
         }
       }
+      else if ( attribute == PoolQueryAttr::editionAttr)
+      {
+        string::size_type pos;
+        Rel rel("==");
+        if (attrValue.find_first_of("=<>!") == 0)
+        {
+          pos = attrValue.find_last_of("=<>");
+          rel = Rel(attrValue.substr(0, pos+1));
+          attrValue = str::trim(attrValue.substr(pos+1, attrValue.npos));
+        }
+
+        setEdition(Edition(attrValue), rel);
+      }
+      else if ( attribute == PoolQueryAttr::complexAttr )
+      {
+        try
+        {
+          _pimpl->_uncompiledPredicated.insert( AttrMatchData::deserialize( attrValue ) );
+        }
+        catch ( const Exception & err )
+        {
+          WAR << "Unparsable value for complex: " << err.asUserHistory() << endl;
+
+        }
+      }
+      else if ( attribute==PoolQueryAttr::noAttr )
+      {
+        WAR << "empty attribute name" << endl;
+      }
       else
       {
-        //some forget handle new atribute
-        ;
+        string s = attrName;
+        str::replaceAll( s,"_",":" );
+        SolvAttr a(s);
+       if ( a == SolvAttr::name || isDependencyAttribute( a ) )
+       {
+         Capability c( attrValue );
+         CapDetail d( c );
+         if ( d.isVersioned() )
+           addDependency( a, d.name().asString(), d.op(), d.ed() );
+         else
+           addDependency( a, attrValue );
+       }
+       else
+         addAttribute( a, attrValue );
       }
-      
+
     } while ( true );
 
     return finded_something;
@@ -975,79 +1291,451 @@ attremptycheckend:
   void PoolQuery::serialize( ostream &str, char delim ) const
   {
     //separating delim
-    str << delim; 
+    str << delim;
     //iterate thrue all settings and write it
-    
-    for_( it, _pimpl->_repos.begin(), _pimpl->_repos.end() )
+    static const zypp::PoolQuery q; //not save default options, so create default query example
+
+    for_( it, repos().begin(), repos().end() )
     {
       str << "repo: " << *it << delim ;
     }
 
-    for_( it, _pimpl->_kinds.begin(), _pimpl->_kinds.end() )
+    for_( it, kinds().begin(), kinds().end() )
     {
-      str << "kind: " << it->idStr() << delim ;
+      str << PoolQueryAttr::kindAttr.asString() << ": "
+          << it->idStr() << delim ;
     }
 
-    //separating delim - protection
-    str << delim; 
+    if (editionRel() != Rel::ANY && edition() != Edition::noedition)
+      str << PoolQueryAttr::editionAttr.asString() << ": " << editionRel() << " " << edition() << delim;
 
-  }
+    if (matchMode()!=q.matchMode())
+    {
+      switch( matchMode() )
+      {
+      case Match::STRING:
+        str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
+        break;
+      case Match::SUBSTRING:
+        str << PoolQueryAttr::stringTypeAttr.asString()
+            << ": substring" << delim;
+        break;
+      case Match::GLOB:
+        str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
+        break;
+      case Match::REGEX:
+        str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
+        break;
+      default:
+        WAR << "unknown match type "  << matchMode() << endl;
+      }
+    }
 
+    if( caseSensitive() != q.caseSensitive() )
+    {
+      str << "case_sensitive: ";
+      if (caseSensitive())
+      {
+        str << "on" << delim;
+      }
+      else
+      {
+        str << "off" << delim;
+      }
+    }
+
+    if( requireAll() != q.requireAll() )
+    {
+      str << "require_all: ";
+      if (requireAll())
+      {
+        str << "on" << delim;
+      }
+      else
+      {
+        str << "off" << delim;
+      }
+    }
+
+    if( statusFilterFlags() != q.statusFilterFlags() )
+    {
+      switch( statusFilterFlags() )
+      {
+      case ALL:
+        str << "install_status: all" << delim;
+        break;
+      case INSTALLED_ONLY:
+        str << "install_status: installed" << delim;
+        break;
+      case UNINSTALLED_ONLY:
+        str << "install_status: not-installed" << delim;
+        break;
+      }
+    }
+
+    for_( it, strings().begin(), strings().end() )
+    {
+      str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
+    }
+
+    for_( it, attributes().begin(), attributes().end() )
+    {
+      string s = it->first.asString();
+      str::replaceAll(s,":","_");
+      for_( it2,it->second.begin(),it->second.end() )
+      {
+        str << s <<": "<< *it2 << delim;
+      }
+    }
+
+    for_( it, _pimpl->_uncompiledPredicated.begin(), _pimpl->_uncompiledPredicated.end() )
+    {
+      str << "complex: "<< it->serialize() << delim;
+    }
+
+    //separating delim - protection
+    str << delim;
+  }
 
   string PoolQuery::asString() const
   { return _pimpl->asString(); }
 
-
   ostream & operator<<( ostream & str, const PoolQuery & obj )
   { return str << obj.asString(); }
 
-  bool operator==(const PoolQuery& a, const PoolQuery& b)
-  {
-    return equal(a,b);
-  }
+  std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj )
+  { return dumpRange( str << obj, obj.begin(), obj.end() ); }
 
-  //internal matching two containers O(n^2)
-  template <class Container>
-  bool equalContainers(const Container& a, const Container& b)
-  {
-    for_(it,a.begin(),a.end())
+  bool PoolQuery::operator==( const PoolQuery & rhs ) const
+  { return *_pimpl == *rhs._pimpl; }
+
+  ///////////////////////////////////////////////////////////////////
+  namespace detail
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //  CLASS NAME : PoolQueryMatcher
+    //
+    /** Store \ref PoolQuery settings and assist \ref PoolQueryIterator.
+     *
+     * Basically the matcher performs a base query, which should preselect
+     * candidates for a match. And has some filter conditions on top of it.
+     * Query and fileter depend on the \ref PoolQuery settings.
+     *
+     * Matcher must be stateless, as it is shared between multiple
+     * \ref PoolQueryIterator instances.
+     *
+     * If \ref base_iterator is at the \ref end, \ref advance moves it
+     * to the first match. Otherwise advance moves to the next match, or
+     * to the \ref end, if there is no more match.
+     *
+     * \note The original implementation treated an empty search string as
+     * <it>"match always"</it>. We stay compatible.
+     */
+    class PoolQueryMatcher
     {
-      bool finded = false;
-      for_( it2, b.begin(),b.end() )
-      {
-        if (*it==*it2)
+      public:
+       typedef sat::LookupAttr::iterator base_iterator;
+
+      public:
+       const base_iterator & end() const
+       {
+         static base_iterator _end;
+         return _end;
+       }
+
+       bool advance( base_iterator & base_r ) const
+       {
+         if ( base_r == end() )
+           base_r = startNewQyery(); // first candidate
+         else
+          {
+            base_r.nextSkipSolvable(); // assert we don't visit this Solvable again
+           ++base_r; // advance to next candidate
+          }
+
+         while ( base_r != end() )
+         {
+           if ( isAMatch( base_r ) )
+             return true;
+           // No match: try next
+            ++base_r;
+         }
+         return false;
+       }
+
+        /** Provide all matching attributes within this solvable.
+         *
+         */
+        void matchDetail( const base_iterator & base_r, std::vector<base_iterator> & return_r ) const
         {
-          finded = true;
-          break;
+          if ( base_r == end() )
+            return;
+
+          sat::Solvable inSolvable( base_r.inSolvable() );
+
+          if ( _attrMatchList.size() == 1 )
+          {
+            // base_r is already on the 1st matching attribute!
+            // String matching is done by the base iterator. We must check the predicate here.
+            // Let's see if there are more matches for this solvable:
+            base_iterator base( base_r );
+            base.stayInThisSolvable(); // avoid discarding matches we found far away from here.
+            return_r.push_back( base );
+
+            const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
+            for ( ++base; base.inSolvable() == inSolvable; ++base ) // safe even if base == end()
+            {
+              if ( ! predicate || predicate( base ) )
+                return_r.push_back( base );
+            }
+          }
+          else
+          {
+            // Here: search all attributes ;(
+            for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
+            {
+              const AttrMatchData & matchData( *mi );
+              sat::LookupAttr q( matchData.attr, inSolvable );
+              if ( matchData.strMatcher ) // an empty searchstring matches always
+                q.setStrMatcher( matchData.strMatcher );
+
+              if ( ! q.empty() ) // there are matches.
+              {
+                // now check any predicate:
+                const AttrMatchData::Predicate & predicate( matchData.predicate );
+                for_( it, q.begin(), q.end() )
+                {
+                  if ( ! predicate || predicate( it ) )
+                    return_r.push_back( it );
+                }
+              }
+            }
+          }
         }
+
+      public:
+       /** Ctor stores the \ref PoolQuery settings.
+         * \throw MatchException Any of the exceptions thrown by \ref PoolQuery::Impl::compile.
+         */
+       PoolQueryMatcher( const shared_ptr<const PoolQuery::Impl> & query_r )
+       {
+         query_r->compile();
+
+         // Repo restriction:
+         sat::Pool satpool( sat::Pool::instance() );
+
+         for_( it, query_r->_repos.begin(), query_r->_repos.end() )
+         {
+           Repository r( satpool.reposFind( *it ) );
+           if ( r )
+             _repos.insert( r );
+           else
+             _neverMatchRepo = true;
+         }
+         // _neverMatchRepo: we just need to catch the case that no repo
+         // matched, so we'd interpret the empty list as 'take from all'
+         if ( _neverMatchRepo && ! _repos.empty() )
+           _neverMatchRepo = false;
+
+         // Kind restriction:
+         _kinds = query_r->_kinds;
+         // Edition restriction:
+         _op      = query_r->_op;
+         _edition = query_r->_edition;
+         // Status restriction:
+         _status_flags = query_r->_status_flags;
+          // StrMatcher
+          _attrMatchList = query_r->_attrMatchList;
+       }
+
+       ~PoolQueryMatcher()
+       {}
+
+      private:
+       /** Initialize a new base query. */
+       base_iterator startNewQyery() const
+       {
+         sat::LookupAttr q;
+
+         if ( _neverMatchRepo )
+           return q.end();
+
+         // Repo restriction:
+         if ( _repos.size() == 1 )
+           q.setRepo( *_repos.begin() );
+         // else: handled in isAMatch.
+
+         // Attribute restriction:
+         if ( _attrMatchList.size() == 1 ) // all (SolvAttr::allAttr) or 1 attr
+         {
+            const AttrMatchData & matchData( _attrMatchList.front() );
+           q.setAttr( matchData.attr );
+            if ( matchData.strMatcher ) // empty searchstring matches always
+              q.setStrMatcher( matchData.strMatcher );
+         }
+          else // more than 1 attr (but not all)
+          {
+            // no restriction, it's all handled in isAMatch.
+            q.setAttr( sat::SolvAttr::allAttr );
+          }
+
+         return q.begin();
+       }
+
+
+       /** Check whether we are on a match.
+        *
+        * The check covers the whole Solvable, not just the current
+        * attribute \c base_r points to. If there's no match, also
+        * prepare \c base_r to advance appropriately. If there is
+        * a match, simply return \c true. \ref advance always moves
+        * to the next Solvable if there was a match.
+        *
+        * \note: Caller asserts we're not at \ref end.
+       */
+       bool isAMatch( base_iterator & base_r ) const
+       {
+         /////////////////////////////////////////////////////////////////////
+         Repository inRepo( base_r.inRepo() );
+         // Status restriction:
+         if ( _status_flags
+            && ( (_status_flags == PoolQuery::INSTALLED_ONLY) != inRepo.isSystemRepo() ) )
+         {
+           base_r.nextSkipRepo();
+           return false;
+         }
+         // Repo restriction:
+         if ( _repos.size() > 1 && _repos.find( inRepo ) == _repos.end() )
+         {
+           base_r.nextSkipRepo();
+           return false;
+         }
+         /////////////////////////////////////////////////////////////////////
+         sat::Solvable inSolvable( base_r.inSolvable() );
+         // Kind restriction:
+         if ( ! _kinds.empty() && ! inSolvable.isKind( _kinds.begin(), _kinds.end() ) )
+         {
+            base_r.nextSkipSolvable();
+            return false;
+         }
+
+         // Edition restriction:
+         if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
+         {
+           base_r.nextSkipSolvable();
+           return false;
+         }
+         /////////////////////////////////////////////////////////////////////
+         // string and predicate matching:
+
+          if ( _attrMatchList.size() == 1 )
+          {
+            // String matching was done by the base iterator.
+            // Now check any predicate:
+            const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
+            if ( ! predicate || predicate( base_r ) )
+              return true;
+
+            return false; // no skip as there may be more occurrences od this attr.
+          }
+
+          // Here: search all attributes ;(
+          for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
+          {
+            const AttrMatchData & matchData( *mi );
+            sat::LookupAttr q( matchData.attr, inSolvable );
+            if ( matchData.strMatcher ) // an empty searchstring matches always
+              q.setStrMatcher( matchData.strMatcher );
+
+            if ( ! q.empty() ) // there are matches.
+            {
+              // now check any predicate:
+              const AttrMatchData::Predicate & predicate( matchData.predicate );
+              if ( predicate )
+              {
+                for_( it, q.begin(), q.end() )
+                {
+                  if ( predicate( it ) )
+                    return true;
+                }
+              }
+              else
+                return true;
+            }
+          }
+          base_r.nextSkipSolvable();
+          return false;
+       }
+
+      private:
+        /** Repositories include in the search. */
+        std::set<Repository> _repos;
+       DefaultIntegral<bool,false> _neverMatchRepo;
+        /** Resolvable kinds to include. */
+        std::set<ResKind> _kinds;
+        /** Edition filter. */
+        Rel _op;
+        Edition _edition;
+        /** Installed status filter flags. \see PoolQuery::StatusFilter */
+        int _status_flags;
+        /** StrMatcher per attribtue. */
+        AttrMatchList _attrMatchList;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    void PoolQueryIterator::increment()
+    {
+      // matcher restarts if at end! It is called from the ctor
+      // to get the 1st match. But if the end is reached, it should
+      // be deleted, otherwise we'd start over again.
+      if ( !_matcher )
+        return; // at end
+      if ( _matches )
+        _matches.reset(); // invalidate old matches
+      if ( ! _matcher->advance( base_reference() ) )
+        _matcher.reset();
+    }
+
+    const PoolQueryIterator::Matches & PoolQueryIterator::matches() const
+    {
+      if ( _matches )
+        return *_matches;
+
+      if ( !_matcher )
+      {
+        // at end of query:
+        static const Matches _none;
+        return _none;
       }
 
-      if (!finded)
-        return false;
+      _matches.reset( new Matches );
+      _matcher->matchDetail( base_reference(), *_matches );
+      return *_matches;
     }
-    return true;
-  }
 
-  bool equal(const PoolQuery& a, const PoolQuery& b)
-  {
-    if( a.matchType()!=b.matchType() )
-      return false;
-    if( a.matchWord()!=b.matchWord())
-      return false;
-    if( a.requireAll()!=b.requireAll() )
-      return false;
-    if(!equalContainers(a.strings(), b.strings()))
-      return false;
-    if(!equalContainers(a.kinds(), b.kinds()))
-      return false;
-    if(!equalContainers(a.repos(), b.repos()))
-      return false;
-    if(!equalContainers(a.attributes(), b.attributes()))
-      return false;
+    std::ostream & dumpOn( std::ostream & str, const PoolQueryIterator & obj )
+    {
+      str << *obj;
+      if ( ! obj.matchesEmpty() )
+      {
+        for_( it, obj.matchesBegin(), obj.matchesEnd() )
+        {
+          str << endl << "    " << it->inSolvAttr() << "\t" << it->asString();
+        }
+      }
+      return str;
+    }
 
-    return true;
-  }
+    ///////////////////////////////////////////////////////////////////
+  } //namespace detail
+  ///////////////////////////////////////////////////////////////////
 
+  detail::PoolQueryIterator PoolQuery::begin() const
+  {
+    return shared_ptr<detail::PoolQueryMatcher>( new detail::PoolQueryMatcher( _pimpl.getPtr() ) );
+  }
 
   /////////////////////////////////////////////////////////////////
 } // namespace zypp