search._pike
Go to the documentation of this file.
1 /*0*/
2 inherit Service.Service;
3 #include <events.h>
4 class search {
5 public:
6 
7 
8 
9 //#define DEBUG_SEARCH 1
10 
11 #ifdef DEBUG_SEARCH
12 #define SEARCH_LOG(s, args...) werror("search: "+s+"\n", args)
13 #else
14 #define SEARCH_LOG(s, args...)
15 #endif
16 
17 
18 string low_compose_value(array value)
19 {
20  switch ( value[0] ) {
21  case ">":
22  case "<":
23  case "=":
24  sscanf(value[1], "\"%s\"", value[1]);
25  return value[0]+" '" + value[1]+"'";
26  break;
27  case "like":
28  default:
29  sscanf(value[1], "\"%s\"", value[1]);
30  return value[0]+" '" + value[1]+"'";
31  }
32 }
33 
34 string compose_value_expression(mapping st)
35 {
36  string query;
37  if ( st->storage == "attrib" )
38  query = " (ob_ident = 'attrib' and ob_attr='"+st->key+"' and ob_data "+
39  st->value[0]+" '"+st->value[1]+"') ";
40  else if ( st->storage == "doc_ft" )
41  query = " (match(doc_data) against ('"+st->value +"')) ";
42  else {
43  query = " (ob_ident = '"+st->storage+"' and ob_attr='"+st->key+
44  "' and ob_data ";
45  query += low_compose_value(st->value) + ") ";
46  }
47  return query;
48 }
49 
50 string compose_value_expressions(array tokens, string andor)
51 {
52  string query = "";
53 
54  mapping last = tokens[0];
55  string exp = compose_value_expression(last);
56 
57  for ( int i = 1; i < sizeof(tokens); i++ ) {
58  mapping token = tokens[i];
59  query += exp + " " + andor + " ";
60  exp = compose_value_expression(token);
61  }
62  query += exp;
63 
64  return query;
65 }
66 
67 mixed run_query(object handle, string query)
68 {
69  SEARCH_LOG("Query: %s\n", query);
70  return handle->big_query(query);
71 }
72 
73 
74 void call_service(object user, mixed args, int|void id)
75 {
76  SEARCH_LOG("Search Service called with %O\n", args);
77  object handle = Sql.Sql(serverCfg["database"]);
78 
79  if ( sizeof(handle->list_tables("temp_search")) )
80  handle->big_query("drop table temp_search");
81 
82  array res = ({});
83  array classes, extends, limits, fulltext;
84  int maxresults, startresults;
85 
86  if ( mappingp(args) ) {
87  classes = args->classes;
88  extends = args->extends;
89  limits = args->limits;
90  fulltext = args->fulltext;
91  maxresults = args->maxresults;
92  startresults = args->startresults;
93  }
94  else {
95  classes = args[2] || ({ "/classes/Object.pike" });
96  extends = args[3] || ({ });
97  limits = args[4] || ({ });
98  fulltext = args[5];
99  }
100  int keywordsearch = 0;
101 
102  string _query;
103  array limitkeys = ({ });
104  array extendkeys = ({ });
105  foreach(limits, mapping l) {
106  limitkeys += ({ l->key });
107  }
108  foreach(extends, mapping e) {
109  extendkeys += ({ e->key });
110  }
111 
112  // make sure class names are quoted:
113  for ( int i=0; i<sizeof(classes); i++ ) {
114  if ( !has_prefix( classes[i], "\"" ) &&
115  !has_prefix( classes[i], "'" ) )
116  classes[i] = "\"" + classes[i];
117  if ( !has_suffix( classes[i], "\"" ) &&
118  !has_suffix( classes[i], "'" ) )
119  classes[i] += "\"";
120  }
121 
122  if ( equal(classes, ({ "\"/classes/User\"" })) &&
123  sizeof(extendkeys-({"OBJ_NAME", "USER_FIRSTNAME", "USER_EMAIL", "USER_FULLNAME" })) == 0 )
124  {
125  mapping keymap = ([ "OBJ_NAME": "login",
126  "USER_FIRSTNAME":"firstname",
127  "USER_FULLNAME":"lastname",
128  "USER_EMAIL":"email", ]);
129  string key, val;
130  _query = "select distinct ob_id from i_userlookup where ";
131  for ( int i = sizeof(extends) - 1; i >= 0; i-- ) {
132  key = extends[i]->key;
133  if ( !keymap[key] ) continue;
134  val = low_compose_value(extends[i]->value);
135  _query += " "+ keymap[key]+" " + val + (i>0?" OR ":"");
136  }
137  keywordsearch = 1;
138  }
139  else if ( equal(classes, ({ "\"/classes/Group\"" })) &&
140  sizeof(extendkeys-({"GROUP_NAME"})) == 0 )
141  {
142  mapping keymap = ([ "GROUP_NAME": "k", ]);
143  string key, val;
144  _query = "select distinct substring(v,2) from i_groups where ";
145  for ( int i = sizeof(extends) - 1; i >= 0; i-- ) {
146  key = extends[i]->key;
147  if ( !keymap[key] ) continue;
148  val = low_compose_value(extends[i]->value);
149  _query += " "+ keymap[key]+" " + val + (i>0?" OR ":"");
150  }
151  keywordsearch = 1;
152  }
153  else if ( sizeof(limitkeys) == 0 && sizeof(extendkeys) == 0 && sizeof(fulltext) > 0)
154  {
155  keywordsearch = 1;
156  _query = "select ob_id from doc_ft where " +
157  compose_value_expressions(fulltext, "or");
158  }
159  else if ( (sizeof(limitkeys-({"OBJ_VERSIONOF","DOC_MIME_TYPE"})) == 0 ) &&
160  (sizeof(extendkeys-({ "OBJ_NAME", "OBJ_DESC", "OBJ_KEYWORDS" })) == 0))
161  {
162  // this is the new searching in the optimized ob_class table
163  keywordsearch = 1;
164  _query = "select distinct ob_id from ob_class where (";
165  array searchterms = ({ });
166  foreach(extends, mapping e) {
167  //string sterm = low_compose_value(e->value);
168  if ( !arrayp(e->value) )
169  e->value = ({ "like", e->value });
170  string sterm = "against ('" + replace(e->value[1], "%", "")+"')";
171  if ( search(searchterms, sterm) == -1 )
172  searchterms += ({ sterm });
173  }
174  if ( search(extendkeys, "OBJ_NAME") >= 0 &&
175  search(extendkeys, "OBJ_DESC") >= 0 &&
176  search(extendkeys, "OBJ_KEYWORDS") >= 0 ) {
177  for ( int i = sizeof(searchterms) - 1; i >= 1; i-- )
178  _query += " match(obkeywords) " + searchterms[i] + " OR ";
179  _query += "match(obkeywords) " + searchterms[0] + ")";
180  }
181  else {
182  mapping keymap = ([ "OBJ_NAME": "obname",
183  "OBJ_DESC": "obdescription",
184  "OBJ_KEYWORDS": "obkeywords",
185  "DOC_MIME_TYPE": "obmimetype"]);
186  string key, val;
187  for ( int i = sizeof(extends) - 1; i >= 0; i-- ) {
188  key = extends[i]->key;
189  if ( key=="OBJ_KEYWORDS" ) {
190  _query += "(";
191  for ( int i = sizeof(searchterms) - 1; i >= 1; i-- )
192  _query += " match(obkeywords) " + searchterms[i] + " OR ";
193  _query += "match(obkeywords) " + searchterms[0] + ")";
194  }
195  else {
196  val = low_compose_value(extends[i]->value);
197  _query += " "+ keymap[key]+" " + val + (i>0?" OR ":"");
198  }
199  }
200  _query += ")";
201  }
202 
203  _query += " AND obversionof=0 ";
204  // and all classes
205  if ( sizeof(classes) > 0 ) {
206  string orclasses = classes * " OR ob_class=";
207  _query += " AND (ob_class="+ orclasses + ")";
208  }
209 
210  if ( search(limitkeys, "DOC_MIME_TYPE") >= 0 ) {
211  // and mimetype !
212  foreach(limits, mapping l) {
213  if ( l->key == "DOC_MIME_TYPE" ) {
214  string val = low_compose_value(l->value);
215  _query += " AND obmimetype "+val;
216  }
217  }
218  // end new searching - (else old and slow searching)
219  }
220  if ( sizeof(fulltext) > 0 ) {
221  _query += " UNION select ob_id from doc_ft where " +
222  compose_value_expressions(fulltext, "or");
223  }
224 
225  }
226  else {
227  // slow searching
228  if ( sizeof(fulltext) ) {
229  _query = "create table temp_search as select ob_id from ( doc_ft ";
230  classes = ({ }); // classes are documents
231  }
232  else
233  _query = "select distinct ob_data.ob_id from ( ob_data ";
234  if ( arrayp(classes) && sizeof(classes) > 0 ) {
235  _query += "INNER JOIN ob_class on ob_class.ob_id=ob_data.ob_id and ("+
236  ( "ob_class = "+classes*" or ob_class=")+")";
237  }
238  _query += ") where";
239 
240  if ( sizeof(fulltext) )
241  _query += compose_value_expressions(fulltext, "or");
242  else
243  _query += compose_value_expressions(extends, "or");
244  }
245  object result;
246 
247  result = run_query(handle, _query);
248 
249  if ( sizeof(fulltext) > 0 && keywordsearch==0 ) {
250  _query = "select distinct ob_id from temp_search";
251  result = run_query(handle, _query);
252  }
253 
254 
255  mixed row;
256  while (row = result->fetch_row()) {
257  if ( !arrayp(row) )
258  row = ({ row });
259  foreach(row, int oid) {
260  int ok = 1;
261  // check limits here
262  if ( !keywordsearch ) {
263  foreach ( limits, mapping limit ) {
264  _query = "select distinct ob_id from ob_data where ob_id="+oid+" and "+
265  compose_value_expression(limit);
266  object lres = run_query(handle, _query);
267  if ( !lres->fetch_row() ) {
268  ok = 0;
269  }
270  }
271  }
272  if ( ok )
273  res += ({ oid });
274  }
275  }
276  SEARCH_LOG("Result is %O\n",res);
277  int resultsize = sizeof(res);
278  if ( startresults > 0 ) {
279  startresults = max(startresults, resultsize);
280  res = res[startresults..];
281  }
282  if ( maxresults > 0 ) {
283  res = res[..maxresults-1];
284  }
285  async_result(id, res);
286 }
287 
288 protected:
289  void run()
290 {
291 }
292 
293 public:
294 
295 private:
296  private void got_kill(int sig)
297 {
298  _exit(1);
299 }
300 
301 public:
302 
303 int main(int argc, array argv)
304 {
305  signal(signum("QUIT"), got_kill);
306  init( "search", argv );
307  start();
308  return -17;
309 }
310 
311 
312 };