Ban (for now at least) searching of nodes, and searching for a key
[rails.git] / app / controllers / search_controller.rb
1 class SearchController < ApplicationController
2   # Support searching for nodes, ways, or all
3   # Can search by tag k, v, or both (type->k,value->v)
4   # Can search by name (k=name,v=....)
5
6   after_filter :compress_output
7
8   def search_all
9     do_search(true,true,true)
10   end
11
12   def search_ways
13     do_search(true,false,false)
14   end
15   def search_nodes
16     do_search(false,true,false)
17   end
18   def search_relations
19     do_search(false,false,true)
20   end
21
22   def do_search(do_ways,do_nodes,do_relations)
23     type = params['type']
24     value = params['value']
25     unless type or value
26       name = params['name']
27       if name
28         type = 'name'
29         value = name
30       end
31     end
32
33     if do_nodes
34       response.headers['Error'] = "Searching of nodes is currently unavailable"
35       render :nothing => true, :status => :service_unavailable
36       return false
37     end
38
39     unless value
40       response.headers['Error'] = "Searching for a key without value is currently unavailable"
41       render :nothing => true, :status => :service_unavailable
42       return false
43     end
44
45     way_ids = Array.new
46     ways = Array.new
47     nodes = Array.new
48     relations = Array.new
49
50     # Matching for tags table
51     cond_way = Array.new
52     sql = '1=1'
53     if type
54       sql += ' AND current_way_tags.k=?'
55       cond_way += [type]
56     end
57     if value
58       sql += ' AND current_way_tags.v=? AND MATCH (current_way_tags.v) AGAINST (? IN BOOLEAN MODE)'
59       cond_way += [value,'"' + value.sub(/[-+*<>"~()]/, ' ') + '"']
60     end
61     cond_way = [sql] + cond_way
62
63     # Matching for tags table
64     cond_rel = Array.new
65     sql = '1=1'
66     if type
67       sql += ' AND current_relation_tags.k=?'
68       cond_rel += [type]
69     end
70     if value
71       sql += ' AND current_relation_tags.v=? AND MATCH (current_relation_tags.v) AGAINST (? IN BOOLEAN MODE)'
72       cond_rel += [value,'"' + value.sub(/[-+*<>"~()]/, ' ') + '"']
73     end
74     cond_rel = [sql] + cond_rel
75
76     # Matching for tags column
77     if type and value
78       cond_tags = ['tags LIKE ? OR tags LIKE ? OR tags LIKE ? OR tags LIKE ?', 
79       ''+type+'='+value+'',
80       ''+type+'='+value+';%',
81       '%;'+type+'='+value+';%',
82       '%;'+type+'='+value+'' ]
83     elsif type
84       cond_tags = ['tags LIKE ? OR tags LIKE ?',
85       ''+type+'=%',
86       '%;'+type+'=%' ]
87     elsif value
88       cond_tags = ['tags LIKE ? OR tags LIKE ?',
89       '%='+value+';%',
90       '%='+value+'' ]
91     else
92       cond_tags = ['1=1']
93     end
94
95     # First up, look for the relations we want
96     if do_relations
97       relations = Relation.find(:all,
98                                 :joins => "INNER JOIN current_relation_tags ON current_relation_tags.id = current_relations.id",
99                                 :conditions => cond_rel, :limit => 100)
100     end
101
102     # then ways
103     if do_ways
104       ways = Way.find(:all,
105                       :joins => "INNER JOIN current_way_tags ON current_way_tags.id = current_ways.id",
106                       :conditions => cond_way, :limit => 100)
107     end
108
109     # Now, nodes
110     if do_nodes
111       nodes = Node.find(:all, :conditions => cond_tags, :limit => 2000)
112     end
113
114     # Fetch any node needed for our ways (only have matching nodes so far)
115     nodes += Node.find(ways.collect { |w| w.nds }.uniq)
116
117     # Print
118     visible_nodes = {}
119     user_display_name_cache = {}
120     doc = OSM::API.new.get_xml_doc
121     nodes.each do |node|
122       doc.root << node.to_xml_node(user_display_name_cache)
123       visible_nodes[node.id] = node
124     end
125
126     ways.each do |way|
127       doc.root << way.to_xml_node(visible_nodes, user_display_name_cache)
128     end 
129
130     relations.each do |rel|
131       doc.root << rel.to_xml_node(user_display_name_cache)
132     end 
133     render :text => doc.to_s, :content_type => "text/xml"
134   end
135 end