For deserialization exception, debug print the actual XML message. Helps in debugging.
[beagle.git] / tools / Query.cs
blobe51a38efd184a41dcaf66f32a522aad66ace6a79
1 //
2 // Query.cs
3 //
4 // Copyright (C) 2004-2005 Novell, Inc.
5 //
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in all
16 // copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 // SOFTWARE.
27 using System;
28 using System.IO;
29 using System.Collections;
30 using System.Globalization;
31 using System.Reflection;
32 using System.Threading;
33 using System.Text;
34 using System.Text.RegularExpressions;
36 using Gtk;
38 using Beagle;
39 using Beagle.Util;
40 using Beagle.Daemon;
42 class QueryTool {
44 static int count = 0;
45 static Query query = null;
46 static DateTime queryStartTime;
47 static DateTime lastQueryTime = DateTime.Now;
49 // CLI args
50 static bool keep_running = false;
51 static bool verbose = false;
52 static bool display_hits = true;
53 static bool flood = false;
54 static bool listener = false;
55 static DateTime start_date = DateTime.MinValue;
56 static DateTime end_date = DateTime.MinValue;
58 static void OnHitsAdded (HitsAddedResponse response)
60 lastQueryTime = DateTime.Now;
62 if (count == 0 && verbose) {
63 Console.WriteLine ("First hit returned in {0:0.000}s",
64 (lastQueryTime - queryStartTime).TotalSeconds);
67 if (! display_hits) {
68 count += response.Hits.Count;
69 return;
72 foreach (Hit hit in response.Hits) {
73 if (verbose)
74 Console.WriteLine (" Uri: {0}", hit.Uri);
75 else
76 Console.WriteLine (hit.Uri);
78 if (verbose) {
79 SnippetRequest sreq = new SnippetRequest (query, hit);
80 SnippetResponse sresp = (SnippetResponse) sreq.Send ();
81 Console.WriteLine ("PaUri: {0}", hit.ParentUri != null ? hit.ParentUri.ToString () : "(null)");
82 Console.WriteLine (" Snip: {0}", sresp.Snippet != null ? sresp.Snippet : "(null)");
83 Console.WriteLine (" Type: {0}", hit.Type);
84 Console.WriteLine ("MimeT: {0}", hit.MimeType == null ? "(null)" : hit.MimeType);
85 Console.WriteLine (" Src: {0}", hit.Source);
86 Console.WriteLine ("Score: {0}", hit.Score);
87 if (hit.ValidTimestamp)
88 Console.WriteLine (" Time: {0}", hit.Timestamp.ToLocalTime ());
90 foreach (Property prop in hit.Properties)
91 Console.WriteLine (" {0} = '{1}'", prop.Key, prop.Value);
93 Console.WriteLine ();
96 ++count;
100 static void OnHitsSubtracted (HitsSubtractedResponse response)
102 lastQueryTime = DateTime.Now;
104 if (! display_hits)
105 return;
107 foreach (Uri uri in response.Uris) {
108 Console.WriteLine ("Subtracted Uri '{0}'", uri);
109 Console.WriteLine ();
111 --count;
115 static void OnFinished (FinishedResponse response)
117 if (verbose) {
118 Console.WriteLine ("Elapsed time: {0:0.000}s",
119 (DateTime.Now - queryStartTime).TotalSeconds);
120 Console.WriteLine ("Total hits: {0}", count);
123 if (flood)
124 SendQuery ();
125 else
126 Gtk.Application.Quit ();
129 public static void PrintUsageAndExit ()
131 string usage =
132 "beagle-query: Command-line interface to the Beagle search system.\n" +
133 "Web page: http://www.gnome.org/projects/beagle\n" +
134 "Copyright (C) 2004 Novell, Inc.\n\n";
135 usage +=
136 "Usage: beagle-query [OPTIONS] <query string>\n\n" +
137 "Options:\n" +
138 " --verbose\t\t\tPrint detailed information about each hit.\n" +
139 " --mime <mime type>\t\tConstrain search results to the specified mime\n" +
140 " \t\ttype. Can be used multiply.\n" +
141 " --type <hit type>\t\tConstrain search results to the specified hit\n" +
142 " \t\ttype. Can be used multiply.\n" +
143 " --source <source>\t\tConstrain query to the specified source.\n" +
144 " \t\tSources list available from beagle-status.\n" +
145 " --start <date>\t\tConstrain query to items after specified date.\n" +
146 " \t\tDate must be in the form \"yyyyMMdd\" or \"yyyyMMddHHmmss\"\n" +
147 " --end <date>\t\t\tConstrain query to items before specified date.\n" +
148 " \t\t\tDate must be in the form \"yyyyMMdd\" or \"yyyyMMddHHmmss\"\n" +
149 " --keywords\t\t\tLists the keywords allowed in 'query string'.\n" +
150 " \t\t\tKeyword queries can be specified as keywordname:value e.g. ext:jpg\n" +
151 " --live-query\t\t\tRun continuously, printing notifications if a\n" +
152 " \t\t\tquery changes.\n" +
153 " --stats-only\t\t\tOnly display statistics about the query, not\n" +
154 " \t\t\tthe actual results.\n" +
155 " --max-hits\t\t\tLimit number of search results per backend\n" +
156 " \t\t\t(default = 100, max = 100)\n" +
157 " --flood\t\t\tExecute the query over and over again. Don't do that.\n" +
158 " --listener\t\t\tExecute an index listener query. Don't do that either.\n" +
159 " --help\t\t\tPrint this usage message.\n";
161 Console.WriteLine (usage);
163 System.Environment.Exit (0);
166 static void ReadBackendMappings ()
168 ArrayList assemblies = ReflectionFu.ScanEnvironmentForAssemblies ("BEAGLE_BACKEND_PATH", PathFinder.BackendDir);
170 // Add BeagleDaemonLib if it hasn't already been added.
171 bool found_daemon_lib = false;
172 foreach (Assembly assembly in assemblies) {
173 if (assembly.GetName ().Name == "BeagleDaemonLib") {
174 found_daemon_lib = true;
175 break;
179 if (!found_daemon_lib) {
180 try {
181 assemblies.Add (Assembly.LoadFrom (Path.Combine (PathFinder.PkgLibDir, "BeagleDaemonLib.dll")));
182 } catch (FileNotFoundException) {
183 Console.WriteLine ("WARNING: Could not find backend list.");
184 Environment.Exit (1);
188 foreach (Assembly assembly in assemblies) {
189 foreach (Type type in ReflectionFu.ScanAssemblyForInterface (assembly, typeof (Beagle.Daemon.IQueryable))) {
190 object[] attributes = type.GetCustomAttributes (false);
191 foreach (object attribute in attributes) {
192 PropertyKeywordMapping mapping = attribute as PropertyKeywordMapping;
193 if (mapping == null)
194 continue;
195 //Logger.Log.Debug (mapping.Keyword + " => "
196 // + mapping.PropertyName +
197 // + " is-keyword=" + mapping.IsKeyword + " ("
198 // + mapping.Description + ") "
199 // + "(" + type.FullName + ")");
200 PropertyKeywordFu.RegisterMapping (mapping);
206 static void OnClosed ()
208 if (flood)
209 SendQuery ();
210 else
211 Gtk.Application.Quit ();
214 static int query_counter = 0;
215 static void SendQuery ()
217 ++query_counter;
218 if (flood) {
219 if (query_counter > 1)
220 Console.WriteLine ();
221 Console.WriteLine ("Sending query #{0}", query_counter);
224 queryStartTime = DateTime.Now;
225 try {
226 query.SendAsync ();
227 } catch (Exception ex) {
228 Console.WriteLine ("Could not connect to the Beagle daemon. The daemon probably isn't running.");
229 Console.WriteLine (ex);
230 System.Environment.Exit (-1);
234 static void Main (string[] args)
236 Gtk.Application.InitCheck ("beagle-query", ref args);
238 if (args.Length == 0 || Array.IndexOf (args, "--help") > -1 || Array.IndexOf (args, "--usage") > -1)
239 PrintUsageAndExit ();
241 StringBuilder query_str = new StringBuilder ();
243 string[] formats = {
244 "yyyyMMdd",
245 "yyyyMMddHHmmss"
248 query = new Query ();
250 // Parse args
251 int i = 0;
252 while (i < args.Length) {
253 switch (args [i]) {
255 case "--mime":
256 if (++i >= args.Length) PrintUsageAndExit ();
257 query.AddMimeType (args [i]);
258 break;
259 case "--type":
260 if (++i >= args.Length) PrintUsageAndExit ();
261 query.AddHitType (args [i]);
262 break;
263 case "--source":
264 if (++i >= args.Length) PrintUsageAndExit ();
265 query.AddSource (args [i]);
266 break;
267 case "--live-query":
268 keep_running = true;
269 break;
270 case "--verbose":
271 verbose = true;
272 break;
273 case "--stats-only":
274 verbose = true;
275 display_hits = false;
276 break;
277 case "--max-hits":
278 if (++i >= args.Length) PrintUsageAndExit ();
279 query.MaxHits = Int32.Parse (args[i]);
280 break;
281 case "--flood":
282 flood = true;
283 break;
284 case "--listener":
285 listener = true;
286 keep_running = true;
287 break;
288 case "--start":
289 if (++i >= args.Length) PrintUsageAndExit ();
290 try {
291 start_date = DateTime.ParseExact (args[i], formats,
292 CultureInfo.InvariantCulture,
293 DateTimeStyles.None);
294 } catch (FormatException) {
295 Console.WriteLine ("Invalid start date");
296 System.Environment.Exit (-1);
298 start_date = start_date.ToUniversalTime ();
299 break;
301 case "--end":
302 if (++i >= args.Length) PrintUsageAndExit ();
303 try {
304 end_date = DateTime.ParseExact (args[i], formats,
305 CultureInfo.InvariantCulture,
306 DateTimeStyles.None);
307 } catch (FormatException) {
308 Console.WriteLine ("Invalid end date");
309 System.Environment.Exit (-1);
311 end_date = end_date.ToUniversalTime ();
312 break;
314 case "--keywords":
315 ReadBackendMappings ();
316 QueryDriver.ReadKeywordMappings ();
318 Console.WriteLine ("Supported query keywords are:");
320 IDictionaryEnumerator property_keyword_enum = PropertyKeywordFu.MappingEnumerator;
321 while (property_keyword_enum.MoveNext ()) {
322 PropertyDetail prop = property_keyword_enum.Value as PropertyDetail;
323 if (prop.Description != null)
324 Console.WriteLine (" {0,-20} for {1}", property_keyword_enum.Key, prop.Description);
325 else
326 Console.WriteLine (" {0,-20}", property_keyword_enum.Key);
329 System.Environment.Exit (0);
330 break;
332 default:
334 // We have to do some nastiness here to deal with shell quoting.
335 // See beagled/QueryStringParser.cs for an idea of how this works.
336 string Pattern = "(?<pm>[+-]?) (?<key>\\w+:)? (?<expr>.*)";
337 Regex r = new Regex (Pattern, RegexOptions.IgnorePatternWhitespace);
338 Match m = r.Match (args [i]);
340 string quoted_query = m.Groups ["pm"].ToString () +
341 m.Groups ["key"].ToString () +
342 "\"" + m.Groups ["expr"].ToString () + "\"";
344 if (query_str.Length > 0)
345 query_str.Append (' ');
346 query_str.Append (quoted_query);
348 break;
352 ++i;
355 if (listener) {
357 query.IsIndexListener = true;
359 } else {
361 if (query_str.Length > 0)
362 query.AddText (query_str.ToString ());
364 if (start_date != DateTime.MinValue || end_date != DateTime.MinValue) {
365 QueryPart_DateRange part = new QueryPart_DateRange ();
367 if (start_date != DateTime.MinValue)
368 part.StartDate = start_date;
370 if (end_date != DateTime.MinValue)
371 part.EndDate = end_date;
373 query.AddPart (part);
377 query.HitsAddedEvent += OnHitsAdded;
378 query.HitsSubtractedEvent += OnHitsSubtracted;
381 if (! keep_running)
382 query.FinishedEvent += OnFinished;
383 else
384 query.ClosedEvent += OnClosed;
386 SendQuery ();
388 Gtk.Application.Run ();