Ejemplo n.º 1
0
    def testParseUserQuery_QuickOr(self):
        # quick-or searches
        ast = query2ast.ParseUserQuery('milestone:2008,2009,2010', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(KEY_HAS, [BUILTIN_ISSUE_FIELDS['label']],
                     ['milestone-2008', 'milestone-2009', 'milestone-2010'],
                     []), cond1)

        ast = query2ast.ParseUserQuery(
            'label:milestone-2008,milestone-2009,milestone-2010', '',
            BUILTIN_ISSUE_FIELDS, self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']],
                     ['milestone-2008', 'milestone-2009', 'milestone-2010'],
                     []), cond1)

        ast = query2ast.ParseUserQuery('milestone=2008,2009,2010', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(EQ, [BUILTIN_ISSUE_FIELDS['label']],
                     ['milestone-2008', 'milestone-2009', 'milestone-2010'],
                     []), cond1)
Ejemplo n.º 2
0
    def testParseUserQuery_CodeSyntaxThatWeNeedToCopeWith(self):
        # positive phrases
        ast = query2ast.ParseUserQuery('Base::Tuple', '', BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(TEXT_HAS, [ANY_FIELD], ['"base::tuple"'], []), cond)

        # stuff we just ignore
        ast = query2ast.ParseUserQuery(':: - -- .', '', BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        self.assertEqual([], ast.conjunctions[0].conds)
Ejemplo n.º 3
0
 def testParseUserQuery_IsOperator(self):
     """Test is:open, is:spam, and is:blocked."""
     for keyword in ['open', 'spam', 'blocked']:
         ast = query2ast.ParseUserQuery('is:' + keyword, '',
                                        BUILTIN_ISSUE_FIELDS,
                                        self.default_config)
         cond1 = ast.conjunctions[0].conds[0]
         self.assertEqual(
             MakeCond(EQ, [BUILTIN_ISSUE_FIELDS[keyword]], [], []), cond1)
         ast = query2ast.ParseUserQuery('-is:' + keyword, '',
                                        BUILTIN_ISSUE_FIELDS,
                                        self.default_config)
         cond1 = ast.conjunctions[0].conds[0]
         self.assertEqual(
             MakeCond(NE, [BUILTIN_ISSUE_FIELDS[keyword]], [], []), cond1)
Ejemplo n.º 4
0
def EvaluateSubscriptions(
    cnxn, issue, users_to_queries, services, config):
  """Determine subscribers who have subs that match the given issue."""
  # Note: unlike filter rule, subscriptions see explicit & derived values.
  lower_labels = [lab.lower() for lab in tracker_bizobj.GetLabels(issue)]
  label_set = set(lower_labels)

  subscribers_to_notify = []
  for uid, saved_queries in users_to_queries.items():
    for sq in saved_queries:
      if sq.subscription_mode != 'immediate':
        continue
      if issue.project_id not in sq.executes_in_project_ids:
        continue
      cond = savedqueries_helpers.SavedQueryToCond(sq)
      # TODO(jrobbins): Support linked accounts me_user_ids.
      cond, _warnings = searchpipeline.ReplaceKeywordsWithUserIDs([uid], cond)
      cond_ast = query2ast.ParseUserQuery(
        cond, '', query2ast.BUILTIN_ISSUE_FIELDS, config)

      if filterrules_helpers.EvalPredicate(
          cnxn, services, cond_ast, issue, label_set, config,
          tracker_bizobj.GetOwnerId(issue), tracker_bizobj.GetCcIds(issue),
          tracker_bizobj.GetStatus(issue)):
        subscribers_to_notify.append(uid)
        break  # Don't bother looking at the user's other saved quereies.

  return subscribers_to_notify
Ejemplo n.º 5
0
  def testParseUserQuery_Phase(self):
    ast = query2ast.ParseUserQuery(
        'gate:Canary,Stable', '', BUILTIN_ISSUE_FIELDS, self.default_config)
    cond1 = ast.conjunctions[0].conds[0]
    self.assertEqual(
        MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['gate']],
                 ['canary', 'stable'], []),
        cond1)

    ast = query2ast.ParseUserQuery(
        '-gate:Canary,Stable', '', BUILTIN_ISSUE_FIELDS, self.default_config)
    cond1 = ast.conjunctions[0].conds[0]
    self.assertEqual(
        MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['gate']],
                 ['canary', 'stable'], []),
        cond1)
  def testGetSpamQueryResultIIDs(self):
    sd = ['project', 'id']
    slice_term = ('Issue.shard = %s', [2])
    query_ast = query2ast.ParseUserQuery(
      'Priority:High is:spam', 'is:open', query2ast.BUILTIN_ISSUE_FIELDS,
      self.config)

    query_ast = backendsearchpipeline._FilterSpam(query_ast)

    self.mox.StubOutWithMock(backendsearchpipeline, 'SearchProjectCan')
    backendsearchpipeline.SearchProjectCan(
      self.cnxn, self.services, [789], query_ast, 2, self.config,
      sort_directives=sd, where=[slice_term],
      query_desc='getting query issue IDs'
      ).AndReturn(([10002, 10052], False, None))
    self.mox.ReplayAll()
    result, capped, err = backendsearchpipeline._GetQueryResultIIDs(
      self.cnxn, self.services, 'is:open', 'Priority:High is:spam',
      [789], self.config, sd, slice_term, 2, 12345)
    self.mox.VerifyAll()
    self.assertEqual([10002, 10052], result)
    self.assertFalse(capped)
    self.assertEqual(None, err)
    self.assertEqual(
      ([10002, 10052], 12345),
      memcache.get('789;is:open;Priority:High is:spam;project id;2'))
Ejemplo n.º 7
0
    def _QueryToWhere(self, cnxn, services, project_config, query,
                      canned_query, project):
        """Parses a query string into LEFT JOIN and WHERE conditions.

    Args:
      cnxn: A MonorailConnection instance.
      services: A Services instance.
      project_config: The configuration for the given project.
      query (string): The query to parse.
      canned_query (string): The supplied canned query.
      project: The current project.

    Returns:
      1. A list of LEFT JOIN clauses for the SQL query.
      2. A list of WHERE clases for the SQL query.
      3. A list of query conditions that are unsupported with snapshots.
    """
        if not (query or canned_query):
            return [], [], []

        query = query or ''
        scope = canned_query or ''

        query_ast = query2ast.ParseUserQuery(query, scope,
                                             query2ast.BUILTIN_ISSUE_FIELDS,
                                             project_config)
        query_ast = ast2ast.PreprocessAST(cnxn, query_ast,
                                          [project.project_id], services,
                                          project_config)
        left_joins, where, unsupported = ast2select.BuildSQLQuery(
            query_ast, snapshot_mode=True)

        return left_joins, where, unsupported
Ejemplo n.º 8
0
    def testParseUserQuery_Components(self):
        """Parse user queries for components"""
        ast = query2ast.ParseUserQuery('component:UI', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['component']], ['ui'],
                     []), cond1)

        ast = query2ast.ParseUserQuery('Component:UI>AboutBox', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['component']],
                     ['ui>aboutbox'], []), cond1)
Ejemplo n.º 9
0
    def testParseUserQuery_SyntaxErrors(self):
        """Bad queries should report warnings."""
        warnings = []
        ast = query2ast.ParseUserQuery('one two (three four)', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config, warnings)
        self.assertEqual(4, len(ast.conjunctions[0].conds))
        self.assertEqual(['Parentheses are ignored in user queries.'],
                         warnings)

        warnings = []
        ast = query2ast.ParseUserQuery('', 'one two (three four)',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config, warnings)
        self.assertEqual(4, len(ast.conjunctions[0].conds))
        self.assertEqual(['Parentheses are ignored in saved queries.'],
                         warnings)
Ejemplo n.º 10
0
 def testParseUserQuery_BadDates(self):
   bad_dates = ['today-13h', 'yesterday', '2/2', 'm/y/d',
                '99/99/1999', '0-0-0']
   for val in bad_dates:
     with self.assertRaises(query2ast.InvalidQueryError) as cm:
       query2ast.ParseUserQuery(
           'modified>=' + val, '', BUILTIN_ISSUE_FIELDS,
           self.default_config)
     self.assertEqual('Could not parse date: ' + val, cm.exception.message)
Ejemplo n.º 11
0
    def testParseUserQuery_HasOperator(self):
        # Search for issues with at least one attachment
        ast = query2ast.ParseUserQuery('has:attachment', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(IS_DEFINED, [BUILTIN_ISSUE_FIELDS['attachment']], [], []),
            cond1)

        ast = query2ast.ParseUserQuery('-has:attachment', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(IS_NOT_DEFINED, [BUILTIN_ISSUE_FIELDS['attachment']], [],
                     []), cond1)

        ast = query2ast.ParseUserQuery('has=attachment', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(IS_DEFINED, [BUILTIN_ISSUE_FIELDS['attachment']], [], []),
            cond1)

        ast = query2ast.ParseUserQuery('-has=attachment', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(IS_NOT_DEFINED, [BUILTIN_ISSUE_FIELDS['attachment']], [],
                     []), cond1)

        # Search for numeric fields for searches with 'has' prefix
        ast = query2ast.ParseUserQuery('has:attachments', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(IS_DEFINED, [BUILTIN_ISSUE_FIELDS['attachments']], [],
                     []), cond1)

        ast = query2ast.ParseUserQuery('-has:attachments', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(IS_NOT_DEFINED, [BUILTIN_ISSUE_FIELDS['attachments']], [],
                     []), cond1)

        # If it is not a field, look for any key-value label.
        ast = query2ast.ParseUserQuery('has:Size', '', BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(IS_DEFINED, [BUILTIN_ISSUE_FIELDS['label']], ['size'],
                     []), cond1)
Ejemplo n.º 12
0
 def testParseUserQuery_OrClause(self):
   # an "OR" query, which should look like two separate simple querys
   # joined together by a pipe.
   ast = query2ast.ParseUserQuery(
       'ham OR fancy', '', BUILTIN_ISSUE_FIELDS, self.default_config)
   conj1 = ast.conjunctions[0]
   conj2 = ast.conjunctions[1]
   self.assertEqual([MakeCond(TEXT_HAS, [ANY_FIELD], ['ham'], [])],
                    conj1.conds)
   self.assertEqual([MakeCond(TEXT_HAS, [ANY_FIELD], ['fancy'], [])],
                    conj2.conds)
Ejemplo n.º 13
0
def ParsePredicateASTs(rules, config, me_user_ids):
  """Parse the given rules in QueryAST PBs."""
  predicates = [rule.predicate for rule in rules]
  if me_user_ids:
    predicates = [
      searchpipeline.ReplaceKeywordsWithUserIDs(me_user_ids, pred)[0]
      for pred in predicates]
  predicate_asts = [
      query2ast.ParseUserQuery(pred, '', query2ast.BUILTIN_ISSUE_FIELDS, config)
      for pred in predicates]
  return predicate_asts
Ejemplo n.º 14
0
 def testParseUserQuery_PhaseFields(self):
   fd = tracker_bizobj.MakeFieldDef(
       1, self.project_id, 'EstDays', tracker_pb2.FieldTypes.INT_TYPE,
       'applic', 'applic', False, False, False, None, None, None, False, None,
       None, None, 'no_action', 'doc', False, is_phase_field=True)
   self.default_config.field_defs.append(fd)
   ast = query2ast.ParseUserQuery(
       'UXReview.EstDays>3', '', BUILTIN_ISSUE_FIELDS, self.default_config)
   cond1 = ast.conjunctions[0].conds[0]
   self.assertEqual(
       MakeCond(GT, [fd], ['3'], [3], phase_name='uxreview'),
       cond1)
Ejemplo n.º 15
0
  def testParseUserQuery_Phrases(self):
    # positive phrases
    ast = query2ast.ParseUserQuery(
        '"one two"', '-label:deprecated', BUILTIN_ISSUE_FIELDS,
        self.default_config)
    scope_cond1, fulltext_cond = ast.conjunctions[0].conds
    self.assertEqual(
        MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']],
                 ['deprecated'], []),
        scope_cond1)
    self.assertEqual(
        MakeCond(TEXT_HAS, [ANY_FIELD], ['"one two"'], []), fulltext_cond)

    # negative phrases
    ast = query2ast.ParseUserQuery(
        '-"one two"', '-label:deprecated', BUILTIN_ISSUE_FIELDS,
        self.default_config)
    scope_cond1, fulltext_cond = ast.conjunctions[0].conds
    self.assertEqual(
        MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']],
                 ['deprecated'], []),
        scope_cond1)
    self.assertEqual(
        MakeCond(NOT_TEXT_HAS, [ANY_FIELD], ['"one two"'], []), fulltext_cond)

    # multiple phrases
    ast = query2ast.ParseUserQuery(
        '-"a b" "x y"', '-label:deprecated', BUILTIN_ISSUE_FIELDS,
        self.default_config)
    scope_cond1, ft_cond1, ft_cond2 = ast.conjunctions[0].conds
    self.assertEqual(
        MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']],
                 ['deprecated'], []),
        scope_cond1)
    self.assertEqual(
        MakeCond(NOT_TEXT_HAS, [ANY_FIELD], ['"a b"'], []), ft_cond1)
    self.assertEqual(
        MakeCond(TEXT_HAS, [ANY_FIELD], ['"x y"'], []), ft_cond2)
Ejemplo n.º 16
0
  def testParseUserQuery_OwnersReportersAndCc(self):
    """Parse user queries for owner:, reporter: and cc:."""
    ast = query2ast.ParseUserQuery(
        'owner:user', '', BUILTIN_ISSUE_FIELDS, self.default_config)
    cond1 = ast.conjunctions[0].conds[0]
    self.assertEqual(
        MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['owner']],
                 ['user'], []),
        cond1)

    ast = query2ast.ParseUserQuery(
        'owner:[email protected]', '', BUILTIN_ISSUE_FIELDS, self.default_config)
    cond1 = ast.conjunctions[0].conds[0]
    self.assertEqual(
        MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['owner']],
                 ['*****@*****.**'], []),
        cond1)

    ast = query2ast.ParseUserQuery(
        '[email protected]', '', BUILTIN_ISSUE_FIELDS, self.default_config)
    cond1 = ast.conjunctions[0].conds[0]
    self.assertEqual(
        MakeCond(EQ, [BUILTIN_ISSUE_FIELDS['owner']],
                 ['*****@*****.**'], []),
        cond1)

    ast = query2ast.ParseUserQuery(
        '[email protected]', '', BUILTIN_ISSUE_FIELDS,
        self.default_config)
    cond1 = ast.conjunctions[0].conds[0]
    self.assertEqual(
        MakeCond(NE, [BUILTIN_ISSUE_FIELDS['reporter']],
                 ['*****@*****.**'], []),
        cond1)

    ast = query2ast.ParseUserQuery(
        '[email protected],[email protected]', '', BUILTIN_ISSUE_FIELDS,
        self.default_config)
    cond1 = ast.conjunctions[0].conds[0]
    self.assertEqual(
        MakeCond(EQ, [BUILTIN_ISSUE_FIELDS['cc']],
                 ['*****@*****.**', '*****@*****.**'], []),
        cond1)

    ast = query2ast.ParseUserQuery(
        'cc:user,user2', '', BUILTIN_ISSUE_FIELDS, self.default_config)
    cond1 = ast.conjunctions[0].conds[0]
    self.assertEqual(
        MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['cc']],
                 ['user', 'user2'], []),
        cond1)
 def testSearchProjectCan_FTSCapped(self):
   query_ast = query2ast.ParseUserQuery(
     'Priority:High', 'is:open', query2ast.BUILTIN_ISSUE_FIELDS,
     self.config)
   simplified_query_ast = ast2ast.PreprocessAST(
     self.cnxn, query_ast, [789], self.services, self.config)
   conj = simplified_query_ast.conjunctions[0]
   self.mox.StubOutWithMock(tracker_fulltext, 'SearchIssueFullText')
   tracker_fulltext.SearchIssueFullText(
     [789], conj, 2).AndReturn(([10002, 10052], True))
   self.mox.StubOutWithMock(self.services.issue, 'RunIssueQuery')
   self.services.issue.RunIssueQuery(
     self.cnxn, mox.IsA(list), mox.IsA(list), mox.IsA(list),
     shard_id=2).AndReturn(([10002, 10052], False))
   self.mox.ReplayAll()
   result, capped, err = backendsearchpipeline.SearchProjectCan(
     self.cnxn, self.services, [789], query_ast, 2, self.config)
   self.mox.VerifyAll()
   self.assertEqual([10002, 10052], result)
   self.assertTrue(capped)
   self.assertEqual(None, err)
Ejemplo n.º 18
0
 def testParseUserQuery_SearchWithinCustomFields(self):
   """Enums are treated as labels, other fields are kept as fields."""
   fd1 = tracker_bizobj.MakeFieldDef(
       1, self.project_id, 'Size', tracker_pb2.FieldTypes.ENUM_TYPE,
       'applic', 'applic', False, False, False, None, None, None, False, None,
       None, None, 'no_action', 'doc', False)
   fd2 = tracker_bizobj.MakeFieldDef(
       1, self.project_id, 'EstDays', tracker_pb2.FieldTypes.INT_TYPE,
       'applic', 'applic', False, False, False, None, None, None, False, None,
       None, None, 'no_action', 'doc', False)
   self.default_config.field_defs.extend([fd1, fd2])
   ast = query2ast.ParseUserQuery(
       'Size:Small EstDays>3', '', BUILTIN_ISSUE_FIELDS, self.default_config)
   cond1 = ast.conjunctions[0].conds[0]
   cond2 = ast.conjunctions[0].conds[1]
   self.assertEqual(
       MakeCond(KEY_HAS, [BUILTIN_ISSUE_FIELDS['label']],
                ['size-small'], []),
       cond1)
   self.assertEqual(
       MakeCond(GT, [fd2], ['3'], [3]),
       cond2)
Ejemplo n.º 19
0
def ParseQuery(mr, config, services):
  """Parse the user's query.

  Args:
    mr: commonly used info parsed from the request.
    config: The ProjectConfig PB for the project.
    services: connections to backends.

  Returns:
    A pair (ast, is_fulltext) with the parsed query abstract syntax tree
    and a boolean that is True if the query included any fulltext terms.
  """
  canned_query = savedqueries_helpers.SavedQueryIDToCond(
    mr.cnxn, services.features, mr.can)
  query_ast = query2ast.ParseUserQuery(
    mr.query, canned_query, query2ast.BUILTIN_ISSUE_FIELDS, config)

  is_fulltext_query = bool(
    query_ast.conjunctions and
    fulltext_helpers.BuildFTSQuery(
      query_ast.conjunctions[0], tracker_fulltext.ISSUE_FULLTEXT_FIELDS))

  return query_ast, is_fulltext_query
Ejemplo n.º 20
0
 def testParseUserQuery_Approvals(self):
   """Test approval queries are parsed correctly."""
   fd1 = tracker_bizobj.MakeFieldDef(
       1, self.project_id, 'UIReview', tracker_pb2.FieldTypes.APPROVAL_TYPE,
       'applic', 'applic', False, False, False, None, None, None, False, None,
       None, None, 'no_action', 'doc', False)
   fd2 = tracker_bizobj.MakeFieldDef(
       2, self.project_id, 'EstDays', tracker_pb2.FieldTypes.INT_TYPE,
       'applic', 'applic', False, False, False, None, None, None, False, None,
       None, None, 'no_action', 'doc', False)
   fd3 = tracker_bizobj.MakeFieldDef(
       3, self.project_id, 'UXReview', tracker_pb2.FieldTypes.APPROVAL_TYPE,
       'applic', 'applic', False, False, False, None, None, None, False, None,
       None, None, 'no_action', 'doc', False)
   self.default_config.field_defs.extend([fd1, fd2, fd3])
   ast = query2ast.ParseUserQuery(
       'UXReview-approver:[email protected],[email protected] UIReview:Approved',
       '', BUILTIN_ISSUE_FIELDS, self.default_config)
   cond1 = ast.conjunctions[0].conds[0]
   cond2 = ast.conjunctions[0].conds[1]
   self.assertEqual(MakeCond(TEXT_HAS, [fd3],
                             ['*****@*****.**', '*****@*****.**'], [],
                             key_suffix='-approver'), cond1)
   self.assertEqual(MakeCond(TEXT_HAS, [fd1], ['approved'], []), cond2)
Ejemplo n.º 21
0
def _GetQueryResultIIDs(cnxn, services, canned_query, user_query,
                        query_project_ids, harmonized_config, sd, slice_term,
                        shard_id, invalidation_timestep):
    """Do a search and return a list of matching issue IDs.

  Args:
    cnxn: connection to the database.
    services: interface to issue storage backends.
    canned_query: string part of the query from the drop-down menu.
    user_query: string part of the query that the user typed in.
    query_project_ids: list of project IDs to search.
    harmonized_config: combined configs for all the queried projects.
    sd: list of sort directives.
    slice_term: additional query term to narrow results to a logical shard
        within a physical shard.
    shard_id: int number of the database shard to search.
    invalidation_timestep: int timestep to use keep memcached items fresh.

  Returns:
    Tuple consisting of:
      A list of issue issue_ids that match the user's query.  An empty list, [],
      is returned if no issues match the query.
      Boolean that is set to True if the search results limit of this shard is
      hit.
      An error (subclass of Exception) encountered during query processing. None
      means that no error was encountered.
  """
    query_ast = _FilterSpam(
        query2ast.ParseUserQuery(user_query, canned_query,
                                 query2ast.BUILTIN_ISSUE_FIELDS,
                                 harmonized_config))

    logging.info('query_project_ids is %r', query_project_ids)

    is_fulltext_query = bool(
        query_ast.conjunctions and fulltext_helpers.BuildFTSQuery(
            query_ast.conjunctions[0], tracker_fulltext.ISSUE_FULLTEXT_FIELDS))
    expiration = framework_constants.MEMCACHE_EXPIRATION
    if is_fulltext_query:
        expiration = framework_constants.FULLTEXT_MEMCACHE_EXPIRATION

    # Might raise ast2ast.MalformedQuery or ast2select.NoPossibleResults.
    result_iids, search_limit_reached, error = SearchProjectCan(
        cnxn,
        services,
        query_project_ids,
        query_ast,
        shard_id,
        harmonized_config,
        sort_directives=sd,
        where=[slice_term],
        query_desc='getting query issue IDs')
    logging.info('Found %d result_iids', len(result_iids))
    if error:
        logging.warn('Got error %r', error)

    projects_str = ','.join(str(pid) for pid in sorted(query_project_ids))
    projects_str = projects_str or 'all'
    memcache_key = ';'.join(
        [projects_str, canned_query, user_query, ' '.join(sd),
         str(shard_id)])
    memcache.set(memcache_key, (result_iids, invalidation_timestep),
                 time=expiration,
                 namespace=settings.memcache_namespace)
    logging.info('set memcache key %r', memcache_key)

    search_limit_memcache_key = ';'.join([
        projects_str, canned_query, user_query, ' '.join(sd),
        'search_limit_reached',
        str(shard_id)
    ])
    memcache.set(search_limit_memcache_key,
                 (search_limit_reached, invalidation_timestep),
                 time=expiration,
                 namespace=settings.memcache_namespace)
    logging.info('set search limit memcache key %r', search_limit_memcache_key)

    timestamps_for_projects = memcache.get_multi(
        keys=(['%d;%d' % (pid, shard_id)
               for pid in query_project_ids] + ['all:%d' % shard_id]),
        namespace=settings.memcache_namespace)

    if query_project_ids:
        for pid in query_project_ids:
            key = '%d;%d' % (pid, shard_id)
            if key not in timestamps_for_projects:
                memcache.set(key,
                             invalidation_timestep,
                             time=framework_constants.MEMCACHE_EXPIRATION,
                             namespace=settings.memcache_namespace)
    else:
        key = 'all;%d' % shard_id
        if key not in timestamps_for_projects:
            memcache.set(key,
                         invalidation_timestep,
                         time=framework_constants.MEMCACHE_EXPIRATION,
                         namespace=settings.memcache_namespace)

    return result_iids, search_limit_reached, error
Ejemplo n.º 22
0
    def testParseUserQuery_Words(self):
        # an "ORTerm" is actually anything appearing on either side of an
        # "OR" operator. So this could be thought of as "simple" query parsing.

        # a simple query with no spaces
        ast = query2ast.ParseUserQuery('hamfancy', '', BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        fulltext_cond = ast.conjunctions[0].conds[0]
        self.assertEqual(MakeCond(TEXT_HAS, [ANY_FIELD], ['hamfancy'], []),
                         fulltext_cond)

        # negative word
        ast = query2ast.ParseUserQuery('-hamfancy', '', BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        fulltext_cond = ast.conjunctions[0].conds[0]
        self.assertEqual(
            # note: not NOT_TEXT_HAS.
            MakeCond(NOT_TEXT_HAS, [ANY_FIELD], ['hamfancy'], []),
            fulltext_cond)

        # invalid fulltext term
        ast = query2ast.ParseUserQuery('ham=fancy\\', '', BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        self.assertEqual([], ast.conjunctions[0].conds)

        # an explicit "AND" query in the "featured" context
        warnings = []
        query2ast.ParseUserQuery('ham AND fancy',
                                 'label:featured',
                                 BUILTIN_ISSUE_FIELDS,
                                 self.default_config,
                                 warnings=warnings)
        self.assertEqual(
            ['The only supported boolean operator is OR (all capitals).'],
            warnings)

        # an implicit "AND" query
        ast = query2ast.ParseUserQuery('ham fancy', '-label:deprecated',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        scope_cond1, ft_cond1, ft_cond2 = ast.conjunctions[0].conds
        self.assertEqual(
            MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']],
                     ['deprecated'], []), scope_cond1)
        self.assertEqual(MakeCond(TEXT_HAS, [ANY_FIELD], ['ham'], []),
                         ft_cond1)
        self.assertEqual(MakeCond(TEXT_HAS, [ANY_FIELD], ['fancy'], []),
                         ft_cond2)

        # Use word with non-operator prefix.
        word_with_non_op_prefix = '%stest' % query2ast.NON_OP_PREFIXES[0]
        ast = query2ast.ParseUserQuery(word_with_non_op_prefix, '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        fulltext_cond = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(TEXT_HAS, [ANY_FIELD], ['"%s"' % word_with_non_op_prefix],
                     []), fulltext_cond)

        # mix positive and negative words
        ast = query2ast.ParseUserQuery('ham -fancy', '-label:deprecated',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        scope_cond1, ft_cond1, ft_cond2 = ast.conjunctions[0].conds
        self.assertEqual(
            MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']],
                     ['deprecated'], []), scope_cond1)
        self.assertEqual(MakeCond(TEXT_HAS, [ANY_FIELD], ['ham'], []),
                         ft_cond1)
        self.assertEqual(MakeCond(NOT_TEXT_HAS, [ANY_FIELD], ['fancy'], []),
                         ft_cond2)

        # converts terms to lower case
        ast = query2ast.ParseUserQuery('AmDude', '-label:deprecated',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        scope_cond1, fulltext_cond = ast.conjunctions[0].conds
        self.assertEqual(
            MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']],
                     ['deprecated'], []), scope_cond1)
        self.assertEqual(MakeCond(TEXT_HAS, [ANY_FIELD], ['amdude'], []),
                         fulltext_cond)
Ejemplo n.º 23
0
    def testParseUserQuery_Dates(self):
        # query with a daterange
        ast = query2ast.ParseUserQuery('modified>=2009-5-12', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        ts1 = int(time.mktime(datetime.datetime(2009, 5, 12).timetuple()))
        self.assertEqual(
            MakeCond(GE, [BUILTIN_ISSUE_FIELDS['modified']], [], [ts1]), cond1)

        # query with quick-or
        ast = query2ast.ParseUserQuery('modified=2009-5-12,2009-5-13', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        ts1 = int(time.mktime(datetime.datetime(2009, 5, 12).timetuple()))
        ts2 = int(time.mktime(datetime.datetime(2009, 5, 13).timetuple()))
        self.assertEqual(
            MakeCond(EQ, [BUILTIN_ISSUE_FIELDS['modified']], [], [ts1, ts2]),
            cond1)

        # query with multiple dateranges
        ast = query2ast.ParseUserQuery('modified>=2009-5-12 opened<2008/1/1',
                                       '', BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1, cond2 = ast.conjunctions[0].conds
        ts1 = int(time.mktime(datetime.datetime(2009, 5, 12).timetuple()))
        self.assertEqual(
            MakeCond(GE, [BUILTIN_ISSUE_FIELDS['modified']], [], [ts1]), cond1)
        ts2 = int(time.mktime(datetime.datetime(2008, 1, 1).timetuple()))
        self.assertEqual(
            MakeCond(LT, [BUILTIN_ISSUE_FIELDS['opened']], [], [ts2]), cond2)

        # query with multiple dateranges plus a search term
        ast = query2ast.ParseUserQuery(
            'one two modified>=2009-5-12 opened<2008/1/1', '',
            BUILTIN_ISSUE_FIELDS, self.default_config)
        ft_cond1, ft_cond2, cond1, cond2 = ast.conjunctions[0].conds
        ts1 = int(time.mktime(datetime.datetime(2009, 5, 12).timetuple()))
        self.assertEqual(MakeCond(TEXT_HAS, [ANY_FIELD], ['one'], []),
                         ft_cond1)
        self.assertEqual(MakeCond(TEXT_HAS, [ANY_FIELD], ['two'], []),
                         ft_cond2)
        self.assertEqual(
            MakeCond(GE, [BUILTIN_ISSUE_FIELDS['modified']], [], [ts1]), cond1)
        ts2 = int(time.mktime(datetime.datetime(2008, 1, 1).timetuple()))
        self.assertEqual(
            MakeCond(LT, [BUILTIN_ISSUE_FIELDS['opened']], [], [ts2]), cond2)

        # query with a date field compared to "today"
        ast = query2ast.ParseUserQuery('modified<today',
                                       '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config,
                                       now=NOW)
        cond1 = ast.conjunctions[0].conds[0]
        ts1 = query2ast._CalculatePastDate(0, now=NOW)
        self.assertEqual(
            MakeCond(LT, [BUILTIN_ISSUE_FIELDS['modified']], [], [ts1]), cond1)

        # query with a daterange using today-N alias
        ast = query2ast.ParseUserQuery('modified>=today-13',
                                       '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config,
                                       now=NOW)
        cond1 = ast.conjunctions[0].conds[0]
        ts1 = query2ast._CalculatePastDate(13, now=NOW)
        self.assertEqual(
            MakeCond(GE, [BUILTIN_ISSUE_FIELDS['modified']], [], [ts1]), cond1)

        ast = query2ast.ParseUserQuery('modified>today-13',
                                       '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config,
                                       now=NOW)
        cond1 = ast.conjunctions[0].conds[0]
        ts1 = query2ast._CalculatePastDate(13, now=NOW)
        self.assertEqual(
            MakeCond(GT, [BUILTIN_ISSUE_FIELDS['modified']], [], [ts1]), cond1)

        # query with multiple old date query terms.
        ast = query2ast.ParseUserQuery(
            'modified-after:2009-5-12 opened-before:2008/1/1 '
            'closed-after:2007-2-1', '', BUILTIN_ISSUE_FIELDS,
            self.default_config)
        cond1, cond2, cond3 = ast.conjunctions[0].conds
        ts1 = int(time.mktime(datetime.datetime(2009, 5, 12).timetuple()))
        self.assertEqual(
            MakeCond(GT, [BUILTIN_ISSUE_FIELDS['modified']], [], [ts1]), cond1)
        ts2 = int(time.mktime(datetime.datetime(2008, 1, 1).timetuple()))
        self.assertEqual(
            MakeCond(LT, [BUILTIN_ISSUE_FIELDS['opened']], [], [ts2]), cond2)
        ts3 = int(time.mktime(datetime.datetime(2007, 2, 1).timetuple()))
        self.assertEqual(
            MakeCond(GT, [BUILTIN_ISSUE_FIELDS['closed']], [], [ts3]), cond3)
Ejemplo n.º 24
0
    def testParseUserQuery_SearchWithinFields(self):
        # Search for issues with certain filenames
        ast = query2ast.ParseUserQuery('attachment:filename', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['attachment']],
                     ['filename'], []), cond1)

        ast = query2ast.ParseUserQuery('-attachment:filename', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(NOT_TEXT_HAS, [BUILTIN_ISSUE_FIELDS['attachment']],
                     ['filename'], []), cond1)

        # Search for issues with a certain number of attachments
        ast = query2ast.ParseUserQuery('attachments:2', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['attachments']], ['2'],
                     [2]), cond1)

        # Searches with '=' syntax
        ast = query2ast.ParseUserQuery('attachment=filename', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(EQ, [BUILTIN_ISSUE_FIELDS['attachment']], ['filename'],
                     []), cond1)

        ast = query2ast.ParseUserQuery('-attachment=filename', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(NE, [BUILTIN_ISSUE_FIELDS['attachment']], ['filename'],
                     []), cond1)

        ast = query2ast.ParseUserQuery('milestone=2009', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(EQ, [BUILTIN_ISSUE_FIELDS['label']], ['milestone-2009'],
                     []), cond1)

        ast = query2ast.ParseUserQuery('-milestone=2009', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(NE, [BUILTIN_ISSUE_FIELDS['label']], ['milestone-2009'],
                     []), cond1)

        ast = query2ast.ParseUserQuery('milestone=2009-Q1', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(EQ, [BUILTIN_ISSUE_FIELDS['label']],
                     ['milestone-2009-q1'], []), cond1)

        ast = query2ast.ParseUserQuery('-milestone=2009-Q1', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(NE, [BUILTIN_ISSUE_FIELDS['label']],
                     ['milestone-2009-q1'], []), cond1)

        # Searches with ':' syntax
        ast = query2ast.ParseUserQuery('summary:foo', '', BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['summary']], ['foo'], []),
            cond1)

        ast = query2ast.ParseUserQuery('summary:"greetings programs"', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['summary']],
                     ['greetings programs'], []), cond1)

        ast = query2ast.ParseUserQuery('summary:"&#1234;"', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['summary']], ['&#1234;'],
                     []), cond1)

        ast = query2ast.ParseUserQuery('priority:high', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(KEY_HAS, [BUILTIN_ISSUE_FIELDS['label']],
                     ['priority-high'], []), cond1)

        ast = query2ast.ParseUserQuery('type:security', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(KEY_HAS, [BUILTIN_ISSUE_FIELDS['label']],
                     ['type-security'], []), cond1)

        ast = query2ast.ParseUserQuery('label:priority-high', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['label']],
                     ['priority-high'], []), cond1)

        ast = query2ast.ParseUserQuery('blockedon:other:123', '',
                                       BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(TEXT_HAS, [BUILTIN_ISSUE_FIELDS['blockedon']],
                     ['other:123'], []), cond1)

        ast = query2ast.ParseUserQuery('cost=-2', '', BUILTIN_ISSUE_FIELDS,
                                       self.default_config)
        cond1 = ast.conjunctions[0].conds[0]
        self.assertEqual(
            MakeCond(EQ, [BUILTIN_ISSUE_FIELDS['label']], ['cost--2'], []),
            cond1)
    def testApplyRule(self):
        cnxn = 'fake sql connection'
        issue = fake.MakeTestIssue(789,
                                   1,
                                   ORIG_SUMMARY,
                                   'New',
                                   111L,
                                   labels=ORIG_LABELS)
        config = tracker_pb2.ProjectIssueConfig()
        # Empty label set cannot satisfy rule looking for labels.
        pred = 'label:a label:b'
        rule = filterrules_helpers.MakeRule(pred,
                                            default_owner_id=1,
                                            default_status='S')
        predicate_ast = query2ast.ParseUserQuery(
            pred, '', query2ast.BUILTIN_ISSUE_FIELDS, config)
        self.assertEquals(
            (None, None, [], [], []),
            filterrules_helpers._ApplyRule(cnxn, self.services, rule,
                                           predicate_ast, issue, set(),
                                           config))

        pred = 'label:a -label:b'
        rule = filterrules_helpers.MakeRule(pred,
                                            default_owner_id=1,
                                            default_status='S')
        predicate_ast = query2ast.ParseUserQuery(
            pred, '', query2ast.BUILTIN_ISSUE_FIELDS, config)
        self.assertEquals(
            (None, None, [], [], []),
            filterrules_helpers._ApplyRule(cnxn, self.services, rule,
                                           predicate_ast, issue, set(),
                                           config))

        # Empty label set will satisfy rule looking for missing labels.
        pred = '-label:a -label:b'
        rule = filterrules_helpers.MakeRule(pred,
                                            default_owner_id=1,
                                            default_status='S')
        predicate_ast = query2ast.ParseUserQuery(
            pred, '', query2ast.BUILTIN_ISSUE_FIELDS, config)
        self.assertEquals(
            (1, 'S', [], [], []),
            filterrules_helpers._ApplyRule(cnxn, self.services, rule,
                                           predicate_ast, issue, set(),
                                           config))

        # Label set has the needed labels.
        pred = 'label:a label:b'
        rule = filterrules_helpers.MakeRule(pred,
                                            default_owner_id=1,
                                            default_status='S')
        predicate_ast = query2ast.ParseUserQuery(
            pred, '', query2ast.BUILTIN_ISSUE_FIELDS, config)
        self.assertEquals(
            (1, 'S', [], [], []),
            filterrules_helpers._ApplyRule(cnxn, self.services, rule,
                                           predicate_ast, issue, {'a', 'b'},
                                           config))

        # Label set has the needed labels with test for unicode.
        pred = 'label:a label:b'
        rule = filterrules_helpers.MakeRule(pred,
                                            default_owner_id=1,
                                            default_status='S')
        predicate_ast = query2ast.ParseUserQuery(
            pred, '', query2ast.BUILTIN_ISSUE_FIELDS, config)
        self.assertEquals(
            (1, 'S', [], [], []),
            filterrules_helpers._ApplyRule(cnxn, self.services, rule,
                                           predicate_ast, issue, {u'a', u'b'},
                                           config))

        # Label set has the needed labels, capitalization irrelevant.
        pred = 'label:A label:B'
        rule = filterrules_helpers.MakeRule(pred,
                                            default_owner_id=1,
                                            default_status='S')
        predicate_ast = query2ast.ParseUserQuery(
            pred, '', query2ast.BUILTIN_ISSUE_FIELDS, config)
        self.assertEquals(
            (1, 'S', [], [], []),
            filterrules_helpers._ApplyRule(cnxn, self.services, rule,
                                           predicate_ast, issue, {'a', 'b'},
                                           config))

        # Label set has a label, the rule negates.
        pred = 'label:a -label:b'
        rule = filterrules_helpers.MakeRule(pred,
                                            default_owner_id=1,
                                            default_status='S')
        predicate_ast = query2ast.ParseUserQuery(
            pred, '', query2ast.BUILTIN_ISSUE_FIELDS, config)
        self.assertEquals(
            (None, None, [], [], []),
            filterrules_helpers._ApplyRule(cnxn, self.services, rule,
                                           predicate_ast, issue, {'a', 'b'},
                                           config))