[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#761867: Webapp: Add conjunctive search for ctags



Hello,

I have attached the patch for the conjunctive search.
Raw sql commands are the following:

First I find files with the ctags. (there are 2 tags in the following case)

SELECT ctags.file_id AS file_id
FROM ctags JOIN files ON files.id = ctags.file_id
WHERE ctags.tag IN (:tag_1, :tag_2) GROUP BY ctags.file_id
HAVING count(DISTINCT ctags.tag) = 2

then i use the ids i recovered to find the package names etc. (there are
4 ids as the first command had 4 results.)

SELECT files.id AS file_id,
package_names.name AS package,
packages.version AS version, files.path AS path
FROM files, package_names, packages
WHERE files.package_id = packages.id
AND files.id IN (:id_1, :id_2, :id_3, :id_4)
AND packages.name_id = package_names.id

Cheers
From 34e6c018afeba3e633afebe80a77ca4e9a777a59 Mon Sep 17 00:00:00 2001
From: Orestis Ioannou <orestis@oioannou.com>
Date: Thu, 12 Mar 2015 13:53:34 +0100
Subject: [PATCH] Webapp: Add conjunctive search for ctags

Closes: #761867
---
 debsources/app/sources/routes.py                   |  9 +++++
 .../app/sources/templates/sources/test_ctag.html   | 43 ++++++++++++++++++++++
 debsources/app/views.py                            | 16 ++++++--
 debsources/models.py                               | 32 ++++++++++++++++
 debsources/tests/test_webapp.py                    |  7 ++++
 5 files changed, 104 insertions(+), 3 deletions(-)
 create mode 100644 debsources/app/sources/templates/sources/test_ctag.html

diff --git a/debsources/app/sources/routes.py b/debsources/app/sources/routes.py
index 1ac8282..eec69c3 100644
--- a/debsources/app/sources/routes.py
+++ b/debsources/app/sources/routes.py
@@ -198,6 +198,15 @@ bp_sources.add_url_rule(
         err_func=ErrorHandler('sources'),
         pagination=True))
 
+# CtagView
+bp_sources.add_url_rule(
+    '/test_ctag/',
+    view_func=CtagView.as_view(
+        'test_ctag',
+        render_func=bind_render('sources/test_ctag.html'),
+        err_func=ErrorHandler('sources'),
+        pagination=True))
+
 
 # api
 bp_sources.add_url_rule(
diff --git a/debsources/app/sources/templates/sources/test_ctag.html b/debsources/app/sources/templates/sources/test_ctag.html
new file mode 100644
index 0000000..fa5e41e
--- /dev/null
+++ b/debsources/app/sources/templates/sources/test_ctag.html
@@ -0,0 +1,43 @@
+{#
+  Copyright (C) 2013  Matthieu Caneill <matthieu.caneill@gmail.com>
+  License: GNU Affero General Public License, version 3 or above.
+#}
+{# copied from templates/ctag.html #}
+
+{% extends "sources/base.html" %}
+n
+{% block title %}Ctag: {{ ctag }}
+{% if package %}(in package {{ package }}){% endif %}
+(page {{ page }}){% endblock %}
+
+{% block breadcrumbs %}ctag / {{ sha256 }}{% endblock %}
+
+{% block content %}
+
+<h2>{{ self.title() }}</h2>
+
+{{ count }} result{% if count >= 2 %}s{% endif %}:
+
+<ul>
+  {% for result in results %}
+    <li>
+      {% if conjunctive %}
+      <a href="{{ url_for('.source',
+       path_to="%s/%s/%s" % (result.package, result.version,
+       result.path)) }}">
+  {{ result.package }}/{{ result.version }}/{{ result.path }}</a>
+      {% else %}
+       <a href="{{ url_for('.source',
+        path_to="%s/%s/%s" % (result.package, result.version,
+        result.path),
+        hl = result.line,
+        _anchor="L%d" % result.line) }}">
+   {{ result.package }}/{{ result.version }}/{{ result.path }}</a>
+      {% endif %}
+     </li>
+  {% endfor %}
+</ul>
+
+{{ macros.render_pagination(pagination) }}
+
+{% endblock %}
diff --git a/debsources/app/views.py b/debsources/app/views.py
index ba27494..99bc2b8 100644
--- a/debsources/app/views.py
+++ b/debsources/app/views.py
@@ -425,8 +425,17 @@ class CtagView(GeneralView):
         else:
             pagination = None
             slice_ = None
-        count, results = Ctag.find_ctag(session, ctag, slice_=slice_,
-                                        package=package)
+
+        conjunctive = False
+        if ',' in ctag:
+            ctags = ctag.split(',')
+            conjunctive = True
+            (count, results) = Ctag.conjunctive_search(session,
+                                                       ctags, slice_=slice_,
+                                                       package=package)
+        else:
+            (count, results) = Ctag.find_ctag(session, ctag, slice_=slice_,
+                                              package=package)
         if self.d.get('pagination'):
             pagination = Pagination(page, offset, count)
         else:
@@ -437,7 +446,8 @@ class CtagView(GeneralView):
                     count=count,
                     page=page,
                     package=package,
-                    pagination=pagination)
+                    pagination=pagination,
+                    conjunctive=conjunctive)
 
 
 class PrefixView(GeneralView):
diff --git a/debsources/models.py b/debsources/models.py
index e4d05fc..94d4f42 100644
--- a/debsources/models.py
+++ b/debsources/models.py
@@ -386,6 +386,38 @@ class Ctag(Base):
                    for res in results.all()]
         return (count, results)
 
+    @staticmethod
+    def conjunctive_search(session, ctags, package=None, slice_=None):
+        find_files = (session.query(Ctag.file_id.label("file_id"))
+                      .join(File)
+                      .filter(Ctag.tag.in_(ctags))
+                      .group_by(Ctag.file_id)
+                      .having(sql_func.count(Ctag.tag.distinct())
+                              == len(ctags))
+                      )
+
+        results = (session.query(File.id.label("file_id"),
+                                 PackageName.name.label("package"),
+                                 Package.version.label("version"),
+                                 File.path.label("path"),
+                                 )
+                   .filter(File.package_id == Package.id)
+                   .filter(File.id.in_(find_files.all()))
+                   .filter(Package.name_id == PackageName.id)
+                   )
+        if package is not None:
+            results = results.filter(PackageName.name == package)
+        results = results.order_by(File.id, File.path)
+        count = results.count()
+        if slice_ is not None:
+            results = results.slice(slice_[0], slice_[1])
+        results = [dict(package=res.package,
+                        version=res.version,
+                        path=res.path,
+                        id=res.file_id)
+                   for res in results.all()]
+        return (count, results)
+
 
 class Metric(Base):
     __tablename__ = 'metrics'
diff --git a/debsources/tests/test_webapp.py b/debsources/tests/test_webapp.py
index d4eb3c8..c5cf3bd 100644
--- a/debsources/tests/test_webapp.py
+++ b/debsources/tests/test_webapp.py
@@ -422,6 +422,13 @@ class DebsourcesTestCase(unittest.TestCase, DbTestFixture):
         self.assertEqual(rv["count"], 113)
         self.assertEqual(len(rv["results"]), 113)
 
+    def test_api_conjunctive_search_ctag(self):
+        rv = json.loads(self.app.get('/api/ctag/?ctag=size,main').data)
+        self.assertEqual(len(rv["results"]), 4)
+        self.assertEqual(rv["results"][3], {'path': 'examples/input.c',
+                                            'version': u'0.99.beta17-1',
+                                            'id': 3891, 'package': u'libcaca'})
+
     def test_api_search_ctag_within_package(self):
         rv = json.loads(self.app.get(
             '/api/ctag/?ctag=name&package=ledger').data)
-- 
2.1.4

Attachment: signature.asc
Description: OpenPGP digital signature


Reply to: