File: aptxapianindex.rb

package info (click to toggle)
apt-xapian-index 0.53
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 424 kB
  • sloc: python: 2,896; ruby: 475; sh: 137; makefile: 31
file content (142 lines) | stat: -rw-r--r-- 4,793 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# This program is free software. It comes without any warranty, to
# the extent permitted by applicable law. You can redistribute it
# and/or modify it under the terms of the Do What The Fuck You Want
# To Public License, Version 2, as published by Sam Hocevar. See
# http://sam.zoy.org/wtfpl/COPYING for more details.

# Setup configuration
# This tells python-apt that we've seen the warning about the API not being
# stable yet, and we don't want to see every time we run the program

require 'xapian'

# Setup configuration
XAPIANDBPATH = '/var/lib/apt-xapian-index'
XAPIANDB = XAPIANDBPATH + '/index'
XAPIANDBVALUES = XAPIANDBPATH + '/values'

# This is our little database of simple Debtags filters we provide: the name
# entered by the user in "--type" maps to a piece of Xapian query
FILTER_DB = {

  # We can do simple AND queries...
  :game => Xapian::Query.new(Xapian::Query::OP_AND, ['XTuse::gameplaying', 'XTrole::program']),

  # Or we can do complicate binary expressions...
  :gui => Xapian::Query.new(
    Xapian::Query::OP_AND, Xapian::Query.new('XTrole::program'),
    Xapian::Query.new(Xapian::Query::OP_OR, 'XTinterface::x11', 'XTinterface::3d')),

  :cmdline => Xapian::Query.new(Xapian::Query::OP_AND, 'XTrole::program', 'XTinterface::commandline'),

  :editor => Xapian::Query.new(Xapian::Query::OP_AND, 'XTrole::program', 'XTuse::editing')

  # Feel free to invent more
}

=begin
    Given a list of user-supplied keywords, build the list of terms that will
    go in a simple Xapian query.

    If a term is lowercase and contains '::', then it's considered to be a
    Debtags tag.
=end
def terms_for_simple_query keywords
  stemmer = Xapian::Stem.new("english")
  terms = []
  keywords.each do |word|
    if not word.downcase! and word['::']
      # FIXME: A better way could be to look up arguments in
      # /var/lib/debtags/vocabulary
      #
      # According to /var/lib/apt-xapian-index/README, Debtags tags are
      # indexed with the 'XT' prefix.
      terms << ( "XT" << word )
    else
      # If it is not a Debtags tag, then we consider it a normal keyword.
      terms << word
      # If the word has a stemmed version, add it to the query.
      # /var/lib/apt-xapian-index/README tells us that stemmed terms have a
      # 'Z' prefix.
      stem = stemmer.call(word)

      terms << ( 'Z' << stem ) unless stem == word
    end
  end
  terms
end

=begin
    If filtername is not None, lookup the simple filter database for the name
    and add its filter to the query.  Returns the enhanced query.
=end
def add_simple_filter_to_query query, filtername
  # See if the user wants to use one of the result filters
  if filtername
    if FILTER_DB.include? filtername
      # If a filter was requested, AND it with the query
      Xapian::Query.new(Xapian::Query::OP_AND, FILTER_DB[filtername], query)
    else
      raise RuntimeError("Invalid filter type.  Try one of %s" % FILTER_DB.keys.join(', '))
    end
  else
    query
  end
end

# Show a Xapian result mset as a list of packages and their short descriptions
def show_mset mset
  # Display the top 20 results, sorted by how well they match
  puts "%i results found." % mset.matches_estimated
  puts "Results 1-%i:" % mset.size
  mset.matches.each do |m|
    # /var/lib/apt-xapian-index/README tells us that the Xapian document data
    # is the package name.
    name = m.document.data

    # Print the match, together with the short description
    puts "%i%% %s - %s" % [m.percent, name, 'summary not available']
  end
end

# Read the "/etc/services"-style database of value indices
def read_value_db pathname
  begin
    rmcomments = /\s*(#.*)?$/
    splitter = /\s+/
    values = {}
    File.open pathname, 'r' do |io|
      while line = io.gets
        # Remove comments and trailing spaces
        line = rmcomments.sub("", line)
        # Skip empty lines
        next if line.empty?
        # Split the line
        fields = splitter.split(line)
        if fields.length < 2
          stderr.puts "Ignoring line %s:%d: only 1 value found when I need at least the value name and number" % [pathname, io.lineno + 1]
          next
        end
        # Parse the number
        begin
          number = fields[1].to_i
        rescue NoMethodError
          $stderr.puts "Ignoring line %s:%d: the second column (\"%s\") must be a number" % [pathname, io.lineno + 1, fields[1]]
          next
        end
        values[fields[0]] = number
        fields[2..-1].each do |a|
          values[a] = number
        end
      end
    end
  rescue => e
    # If we can't read the database, fallback to defaults
    $stderr.puts "Cannot read %s: %s.  Using a minimal default configuration" % [pathname, e]
    values = {
      :installedsize => 1,
      :packagesize => 2
    }
  end
  values
end