Re: [PATCH 2/3] lib: Add support for automatically excluding tags from queries
authorAustin Clements <amdragon@MIT.EDU>
Wed, 11 Jan 2012 18:48:38 +0000 (13:48 +1900)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:41:55 +0000 (09:41 -0800)
a2/d9ef3a56fa1920c9ffa796906b7894a29e619f [new file with mode: 0644]

diff --git a/a2/d9ef3a56fa1920c9ffa796906b7894a29e619f b/a2/d9ef3a56fa1920c9ffa796906b7894a29e619f
new file mode 100644 (file)
index 0000000..c590654
--- /dev/null
@@ -0,0 +1,238 @@
+Return-Path: <amdragon@mit.edu>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id 780CA429E21\r
+       for <notmuch@notmuchmail.org>; Wed, 11 Jan 2012 10:48:35 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.7\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
+       tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id NvGxMEIV7LhK for <notmuch@notmuchmail.org>;\r
+       Wed, 11 Jan 2012 10:48:34 -0800 (PST)\r
+Received: from dmz-mailsec-scanner-6.mit.edu (DMZ-MAILSEC-SCANNER-6.MIT.EDU\r
+       [18.7.68.35])\r
+       by olra.theworths.org (Postfix) with ESMTP id 73D8D431FB6\r
+       for <notmuch@notmuchmail.org>; Wed, 11 Jan 2012 10:48:34 -0800 (PST)\r
+X-AuditID: 12074423-b7f9c6d0000008c3-a7-4f0dd9824e6c\r
+Received: from mailhub-auth-4.mit.edu ( [18.7.62.39])\r
+       by dmz-mailsec-scanner-6.mit.edu (Symantec Messaging Gateway) with SMTP\r
+       id 42.EC.02243.289DD0F4; Wed, 11 Jan 2012 13:48:34 -0500 (EST)\r
+Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
+       by mailhub-auth-4.mit.edu (8.13.8/8.9.2) with ESMTP id q0BImXt0013787; \r
+       Wed, 11 Jan 2012 13:48:34 -0500\r
+Received: from awakening.csail.mit.edu (awakening.csail.mit.edu [18.26.4.91])\r
+       (authenticated bits=0)\r
+       (User authenticated as amdragon@ATHENA.MIT.EDU)\r
+       by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id q0BImWho011482\r
+       (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
+       Wed, 11 Jan 2012 13:48:33 -0500 (EST)\r
+Received: from amthrax by awakening.csail.mit.edu with local (Exim 4.77)\r
+       (envelope-from <amdragon@mit.edu>)\r
+       id 1Rl3Dy-0004ET-Gz; Wed, 11 Jan 2012 13:48:38 -0500\r
+Date: Wed, 11 Jan 2012 13:48:38 -0500\r
+From: Austin Clements <amdragon@MIT.EDU>\r
+To: Jani Nikula <jani@nikula.org>\r
+Subject: Re: [PATCH 2/3] lib: Add support for automatically excluding tags\r
+       from queries\r
+Message-ID: <20120111184838.GT20796@mit.edu>\r
+References: <20120109043101.GH20796@mit.edu>\r
+       <1326258173-21163-1-git-send-email-amdragon@mit.edu>\r
+       <1326258173-21163-3-git-send-email-amdragon@mit.edu>\r
+       <87ty42v99m.fsf@nikula.org>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=us-ascii\r
+Content-Disposition: inline\r
+In-Reply-To: <87ty42v99m.fsf@nikula.org>\r
+User-Agent: Mutt/1.5.21 (2010-09-15)\r
+X-Brightmail-Tracker:\r
+ H4sIAAAAAAAAA+NgFmphleLIzCtJLcpLzFFi42IRYrdT1226yetv8G0Fu0XTdGeL6zdnMjsw\r
+       edy6/5rd49mqW8wBTFFcNimpOZllqUX6dglcGTsWnGcqWGBYcfVdJ1MD41m1LkZODgkBE4n1\r
+       7/pYIGwxiQv31rN1MXJxCAnsY5RY19vICOFsYJR49vUlVOYkk8SRd49ZIJwljBJzXj0D62cR\r
+       UJXYPekuG4jNJqAhsW3/ckYQW0RAUWLzyf1gNrOAtMS3381MILawQKTEh01/2UFsXgEdidub\r
+       dkGt28EoceXKVWaIhKDEyZlPWCCatSRu/HsJ1MwBNmj5Pw6QMCfQrr2TF4LNERVQkZhychvb\r
+       BEahWUi6ZyHpnoXQvYCReRWjbEpulW5uYmZOcWqybnFyYl5eapGumV5uZoleakrpJkZwYLso\r
+       72D8c1DpEKMAB6MSD++Ovbz+QqyJZcWVuYcYJTmYlER5V90ACvEl5adUZiQWZ8QXleakFh9i\r
+       lOBgVhLhdaoByvGmJFZWpRblw6SkOViUxHk1tN75CQmkJ5akZqemFqQWwWRlODiUJHg3ggwV\r
+       LEpNT61Iy8wpQUgzcXCCDOcBGr4cpIa3uCAxtzgzHSJ/ilFRSpx3EkhCACSRUZoH1wtLPK8Y\r
+       xYFeEebNB6niASYtuO5XQIOZgAZvWccDMrgkESEl1cAo8e7tdu6TH2te1ITEXm/eF/uAKyQ3\r
+       gOPh1puFVy/n3NyzJnnZ0fcf6wt0zW5GrP7SqdrT4L//+f/glCXPJCOC3i8yfJC/96VjY2JK\r
+       xt+sOWd2njt7RU5Rg/PGEg1GtZtxMStidoQt8bb9UVFb0bDz2vfuR+vWOrPILWzw12r9wrdk\r
+       gVbM64W/lFiKMxINtZiLihMBpsQZ9BcDAAA=\r
+Cc: notmuch@notmuchmail.org\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Wed, 11 Jan 2012 18:48:35 -0000\r
+\r
+Quoth Jani Nikula on Jan 11 at 10:11 am:\r
+> On Wed, 11 Jan 2012 00:02:52 -0500, Austin Clements <amdragon@MIT.EDU> wrote:\r
+> > This is useful for tags like "deleted" and "spam" that people\r
+> > generally want to exclude from query results.  These exclusions will\r
+> > be overridden if a tag is explicitly mentioned in a query.\r
+> > ---\r
+> >  lib/notmuch.h |    6 ++++++\r
+> >  lib/query.cc  |   33 +++++++++++++++++++++++++++++++++\r
+> >  2 files changed, 39 insertions(+), 0 deletions(-)\r
+> > \r
+> > diff --git a/lib/notmuch.h b/lib/notmuch.h\r
+> > index 9f23a10..0a3ae2b 100644\r
+> > --- a/lib/notmuch.h\r
+> > +++ b/lib/notmuch.h\r
+> > @@ -457,6 +457,12 @@ notmuch_query_set_sort (notmuch_query_t *query, notmuch_sort_t sort);\r
+> >  notmuch_sort_t\r
+> >  notmuch_query_get_sort (notmuch_query_t *query);\r
+> >  \r
+> > +/* Add a tag that will be excluded by default from the query results.\r
+> > + * This exclusion will be overridden if this tag appears explicitly in\r
+> > + * the query. */\r
+> > +void\r
+> > +notmuch_query_add_tag_exclude (notmuch_query_t *query, const char *tag);\r
+> > +\r
+> >  /* Execute a query for threads, returning a notmuch_threads_t object\r
+> >   * which can be used to iterate over the results. The returned threads\r
+> >   * object is owned by the query and as such, will only be valid until\r
+> > diff --git a/lib/query.cc b/lib/query.cc\r
+> > index b6c0f12..716db1c 100644\r
+> > --- a/lib/query.cc\r
+> > +++ b/lib/query.cc\r
+> > @@ -27,6 +27,7 @@ struct _notmuch_query {\r
+> >      notmuch_database_t *notmuch;\r
+> >      const char *query_string;\r
+> >      notmuch_sort_t sort;\r
+> > +    notmuch_string_list_t *exclude_terms;\r
+> >  };\r
+> >  \r
+> >  typedef struct _notmuch_mset_messages {\r
+> > @@ -76,6 +77,8 @@ notmuch_query_create (notmuch_database_t *notmuch,\r
+> >  \r
+> >      query->sort = NOTMUCH_SORT_NEWEST_FIRST;\r
+> >  \r
+> > +    query->exclude_terms = _notmuch_string_list_create (query);\r
+> > +\r
+> >      return query;\r
+> >  }\r
+> >  \r
+> > @@ -97,6 +100,13 @@ notmuch_query_get_sort (notmuch_query_t *query)\r
+> >      return query->sort;\r
+> >  }\r
+> >  \r
+> > +void\r
+> > +notmuch_query_add_tag_exclude (notmuch_query_t *query, const char *tag)\r
+> > +{\r
+> > +    char *term = talloc_asprintf (query, "%s%s", _find_prefix ("tag"), tag);\r
+> > +    _notmuch_string_list_append (query->exclude_terms, term);\r
+> > +}\r
+> > +\r
+> >  /* We end up having to call the destructors explicitly because we had\r
+> >   * to use "placement new" in order to initialize C++ objects within a\r
+> >   * block that we allocated with talloc. So C++ is making talloc\r
+> > @@ -112,6 +122,25 @@ _notmuch_messages_destructor (notmuch_mset_messages_t *messages)\r
+> >      return 0;\r
+> >  }\r
+> >  \r
+> \r
+> I'd like to have a comment here, or inline in the code, explaining the\r
+> following function a little bit.\r
+\r
+Done.\r
+\r
+> > +static Xapian::Query\r
+> > +_notmuch_exclude_tags (notmuch_query_t *query, Xapian::Query xquery)\r
+> > +{\r
+> > +    Xapian::TermIterator end = xquery.get_terms_end ();\r
+> > +\r
+> > +    for (notmuch_string_node_t *term = query->exclude_terms->head; term;\r
+> > +   term = term->next) {\r
+> > +  Xapian::TermIterator it = xquery.get_terms_begin ();\r
+> > +  for (; it != end; it++) {\r
+> > +      if (*it == term->string)\r
+> \r
+> [This is a double reminder to me why I'm not that enthusiastic about\r
+> operator overloading in C++.]\r
+\r
+Actually, that's a good point.  I had originally done this to prevent\r
+std::string from performing any internal allocation or copying (which\r
+calling c_str could do), but since it took me some digging to figure\r
+out if I had *actually* avoided this (turns out there's an overloaded\r
+string==char*, so I had), I've switched to using string.compare, which\r
+I think makes the behavior and performance more obvious.\r
+\r
+> > +          break;\r
+> > +  }\r
+> > +  if (it == end)\r
+> > +      xquery = Xapian::Query (Xapian::Query::OP_AND_NOT,\r
+> > +                              xquery, Xapian::Query (term->string));\r
+> \r
+> I briefly dug into Xapian documentation and source code, and became none\r
+> the wiser whether this copies the queries passed to it or not, i.e. does\r
+> this leak memory or not. I just presume you know what you're doing. ;)\r
+\r
+Since my code isn't doing any memory allocation, it can't leak memory.\r
+It could do a lot of copying, but it turns out that isn't a problem\r
+either because Xapian objects are internally reference-counted\r
+handles.  So, in other words, if you write the obvious thing, it will\r
+do the right thing, which is totally contrary to what C++ has\r
+conditioned us to expect.  ]:--8)\r
+\r
+> I think the function fails if someone is stupid enough to exclude the\r
+> same tag twice. I'm not sure if you should care. If you think so, you\r
+> could just check double add in notmuch_query_add_tag_exclude().\r
+\r
+It handles this correctly for two reasons.  Suppose tag x is excluded\r
+twice.  At worst, if tag:x doesn't appear in the query, the returned\r
+query will get two AND NOT tag:x's, but that doesn't affect the\r
+results.  It turns out it doesn't even get doubled up for a slightly\r
+subtle reason: when the loop reaches the second x in the exclude list,\r
+it will iterate over the new query, which already has the AND NOT\r
+tag:x in it, so it will see the tag:x as if it were in the original\r
+query and not further modify the query.\r
+\r
+However, this did point out a bug.  I was using the end iterator from\r
+the original query even when I started iterating over the modified\r
+query.\r
+\r
+I'll send out an updated series after someone looks at the third\r
+patch.\r
+\r
+> Otherwise, looks good.\r
+> \r
+> > +    }\r
+> > +    return xquery;\r
+> > +}\r
+> > +\r
+> >  notmuch_messages_t *\r
+> >  notmuch_query_search_messages (notmuch_query_t *query)\r
+> >  {\r
+> > @@ -157,6 +186,8 @@ notmuch_query_search_messages (notmuch_query_t *query)\r
+> >                                     mail_query, string_query);\r
+> >    }\r
+> >  \r
+> > +  final_query = _notmuch_exclude_tags (query, final_query);\r
+> > +\r
+> >    enquire.set_weighting_scheme (Xapian::BoolWeight());\r
+> >  \r
+> >    switch (query->sort) {\r
+> > @@ -436,6 +467,8 @@ notmuch_query_count_messages (notmuch_query_t *query)\r
+> >                                     mail_query, string_query);\r
+> >    }\r
+> >  \r
+> > +  final_query = _notmuch_exclude_tags (query, final_query);\r
+> > +\r
+> >    enquire.set_weighting_scheme(Xapian::BoolWeight());\r
+> >    enquire.set_docid_order(Xapian::Enquire::ASCENDING);\r
+> >  \r
+> \r