Sorry for the combined patch. The changes became too inter-dependent.
[beagle.git] / tools / Query.cs
blobdf37ff20c95db2aea48a0bb5c2426dea01a39d6e
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 GLib;
38 using Beagle;
39 using Beagle.Util;
40 using Beagle.Daemon;
42 class QueryTool {
44 private static int count = 0;
45 private static Query query = null;
46 private static DateTime queryStartTime;
47 private static DateTime lastQueryTime = DateTime.Now;
49 private static MainLoop main_loop = null;
50 // CLI args
51 private static bool keep_running = false;
52 private static bool verbose = false;
53 private static bool display_hits = true;
54 private static bool flood = false;
55 private static bool listener = false;
56 private static DateTime start_date = DateTime.MinValue;
57 private static DateTime end_date = DateTime.MinValue;
59 private static void OnHitsAdded (HitsAddedResponse response)
61 lastQueryTime = DateTime.Now;
63 if (count == 0 && verbose) {
64 Console.WriteLine ("First hit returned in {0:0.000}s",
65 (lastQueryTime - queryStartTime).TotalSeconds);
68 if (! display_hits) {
69 count += response.Hits.Count;
70 return;
73 foreach (Hit hit in response.Hits) {
74 if (verbose)
75 Console.WriteLine (" Uri: {0}", hit.Uri);
76 else
77 Console.WriteLine (hit.Uri);
79 if (verbose) {
80 SnippetRequest sreq = new SnippetRequest (query, hit);
81 SnippetResponse sresp = (SnippetResponse) sreq.Send ();
82 Console.WriteLine ("PaUri: {0}", hit.ParentUri != null ? hit.ParentUri.ToString () : "(null)");
83 Console.WriteLine (" Snip: {0}", sresp.Snippet != null ? sresp.Snippet : "(null)");
84 Console.WriteLine (" Type: {0}", hit.Type);
85 Console.WriteLine ("MimeT: {0}", hit.MimeType == null ? "(null)" : hit.MimeType);
86 Console.WriteLine (" Src: {0}", hit.Source);
87 Console.WriteLine ("Score: {0}", hit.Score);
88 if (hit.ValidTimestamp)
89 Console.WriteLine (" Time: {0}", hit.Timestamp.ToLocalTime ());
91 foreach (Property prop in hit.Properties)
92 Console.WriteLine (" {0} = '{1}'", prop.Key, prop.Value);
94 Console.WriteLine ();
97 ++count;
101 private static void OnHitsSubtracted (HitsSubtractedResponse response)
103 lastQueryTime = DateTime.Now;
105 if (! display_hits)
106 return;
108 foreach (Uri uri in response.Uris) {
109 Console.WriteLine ("Subtracted Uri '{0}'", uri);
110 Console.WriteLine ();
112 --count;
116 private static void OnFinished (FinishedResponse response)
118 if (verbose) {
119 Console.WriteLine ("Elapsed time: {0:0.000}s",
120 (DateTime.Now - queryStartTime).TotalSeconds);
121 Console.WriteLine ("Total hits: {0}", count);
124 if (flood)
125 SendQuery ();
126 else
127 main_loop.Quit ();
128 //Gtk.Application.Quit ();
131 public static void PrintUsageAndExit ()
133 string usage =
134 "beagle-query: Command-line interface to the Beagle search system.\n" +
135 "Web page: http://www.gnome.org/projects/beagle\n" +
136 "Copyright (C) 2004 Novell, Inc.\n\n";
137 usage +=
138 "Usage: beagle-query [OPTIONS] <query string>\n\n" +
139 "Options:\n" +
140 " --verbose\t\t\tPrint detailed information about each hit.\n" +
141 " --mime <mime type>\t\tConstrain search results to the specified mime\n" +
142 " \t\ttype. Can be used multiply.\n" +
143 " --type <hit type>\t\tConstrain search results to the specified hit\n" +
144 " \t\ttype. Can be used multiply.\n" +
145 " --source <source>\t\tConstrain query to the specified source.\n" +
146 " \t\tSources list available from beagle-status.\n" +
147 " --start <date>\t\tConstrain query to items after specified date.\n" +
148 " \t\tDate must be in the form \"yyyyMMdd\" or \"yyyyMMddHHmmss\"\n" +
149 " --end <date>\t\t\tConstrain query to items before specified date.\n" +
150 " \t\t\tDate must be in the form \"yyyyMMdd\" or \"yyyyMMddHHmmss\"\n" +
151 " --keywords\t\t\tLists the keywords allowed in 'query string'.\n" +
152 " \t\t\tKeyword queries can be specified as keywordname:value e.g. ext:jpg\n" +
153 " --live-query\t\t\tRun continuously, printing notifications if a\n" +
154 " \t\t\tquery changes.\n" +
155 " --stats-only\t\t\tOnly display statistics about the query, not\n" +
156 " \t\t\tthe actual results.\n" +
157 " --max-hits\t\t\tLimit number of search results per backend\n" +
158 " \t\t\t(default = 100, max = 100)\n" +
159 " --flood\t\t\tExecute the query over and over again. Don't do that.\n" +
160 " --listener\t\t\tExecute an index listener query. Don't do that either.\n" +
161 " --help\t\t\tPrint this usage message.\n";
163 Console.WriteLine (usage);
165 System.Environment.Exit (0);
168 private static void ReadBackendMappings ()
170 ArrayList assemblies = ReflectionFu.ScanEnvironmentForAssemblies ("BEAGLE_BACKEND_PATH", PathFinder.BackendDir);
172 // Add BeagleDaemonLib if it hasn't already been added.
173 bool found_daemon_lib = false;
174 foreach (Assembly assembly in assemblies) {
175 if (assembly.GetName ().Name == "BeagleDaemonLib") {
176 found_daemon_lib = true;
177 break;
181 if (!found_daemon_lib) {
182 try {
183 assemblies.Add (Assembly.LoadFrom (Path.Combine (PathFinder.PkgLibDir, "BeagleDaemonLib.dll")));
184 } catch (FileNotFoundException) {
185 Console.WriteLine ("WARNING: Could not find backend list.");
186 Environment.Exit (1);
190 foreach (Assembly assembly in assemblies) {
191 foreach (Type type in ReflectionFu.ScanAssemblyForInterface (assembly, typeof (Beagle.Daemon.IQueryable))) {
192 object[] attributes = type.GetCustomAttributes (false);
193 foreach (object attribute in attributes) {
194 PropertyKeywordMapping mapping = attribute as PropertyKeywordMapping;
195 if (mapping == null)
196 continue;
197 //Logger.Log.Debug (mapping.Keyword + " => "
198 // + mapping.PropertyName +
199 // + " is-keyword=" + mapping.IsKeyword + " ("
200 // + mapping.Description + ") "
201 // + "(" + type.FullName + ")");
202 PropertyKeywordFu.RegisterMapping (mapping);
208 private static void OnClosed ()
210 if (flood)
211 SendQuery ();
212 else
213 main_loop.Quit ();
214 //Gtk.Application.Quit ();
217 private static int query_counter = 0;
218 private static void SendQuery ()
220 ++query_counter;
221 if (flood) {
222 if (query_counter > 1)
223 Console.WriteLine ();
224 Console.WriteLine ("Sending query #{0}", query_counter);
227 queryStartTime = DateTime.Now;
228 try {
229 query.SendAsync ();
230 } catch (Exception ex) {
231 Console.WriteLine ("Could not connect to the Beagle daemon. The daemon probably isn't running.");
232 Console.WriteLine (ex);
233 System.Environment.Exit (-1);
237 public static void Main (string[] args)
239 main_loop = new MainLoop ();
241 if (args.Length == 0 || Array.IndexOf (args, "--help") > -1 || Array.IndexOf (args, "--usage") > -1)
242 PrintUsageAndExit ();
244 StringBuilder query_str = new StringBuilder ();
246 string[] formats = {
247 "yyyyMMdd",
248 "yyyyMMddHHmmss"
251 query = new Query ();
253 // Parse args
254 int i = 0;
255 while (i < args.Length) {
256 switch (args [i]) {
258 case "--mime":
259 if (++i >= args.Length) PrintUsageAndExit ();
260 query.AddMimeType (args [i]);
261 break;
262 case "--type":
263 if (++i >= args.Length) PrintUsageAndExit ();
264 query.AddHitType (args [i]);
265 break;
266 case "--source":
267 if (++i >= args.Length) PrintUsageAndExit ();
268 query.AddSource (args [i]);
269 break;
270 case "--live-query":
271 keep_running = true;
272 break;
273 case "--verbose":
274 verbose = true;
275 break;
276 case "--stats-only":
277 verbose = true;
278 display_hits = false;
279 break;
280 case "--max-hits":
281 if (++i >= args.Length) PrintUsageAndExit ();
282 query.MaxHits = Int32.Parse (args[i]);
283 break;
284 case "--flood":
285 flood = true;
286 break;
287 case "--listener":
288 listener = true;
289 keep_running = true;
290 break;
291 case "--start":
292 if (++i >= args.Length) PrintUsageAndExit ();
293 try {
294 start_date = DateTime.ParseExact (args[i], formats,
295 CultureInfo.InvariantCulture,
296 DateTimeStyles.None);
297 } catch (FormatException) {
298 Console.WriteLine ("Invalid start date");
299 System.Environment.Exit (-1);
301 start_date = start_date.ToUniversalTime ();
302 break;
304 case "--end":
305 if (++i >= args.Length) PrintUsageAndExit ();
306 try {
307 end_date = DateTime.ParseExact (args[i], formats,
308 CultureInfo.InvariantCulture,
309 DateTimeStyles.None);
310 } catch (FormatException) {
311 Console.WriteLine ("Invalid end date");
312 System.Environment.Exit (-1);
314 end_date = end_date.ToUniversalTime ();
315 break;
317 case "--keywords":
318 ReadBackendMappings ();
319 QueryDriver.ReadKeywordMappings ();
321 Console.WriteLine ("Supported query keywords are:");
323 IDictionaryEnumerator property_keyword_enum = PropertyKeywordFu.MappingEnumerator;
324 while (property_keyword_enum.MoveNext ()) {
325 PropertyDetail prop = property_keyword_enum.Value as PropertyDetail;
326 if (prop.Description != null)
327 Console.WriteLine (" {0,-20} for {1}", property_keyword_enum.Key, prop.Description);
328 else
329 Console.WriteLine (" {0,-20}", property_keyword_enum.Key);
332 System.Environment.Exit (0);
333 break;
335 default:
337 // We have to do some nastiness here to deal with shell quoting.
338 // See beagled/QueryStringParser.cs for an idea of how this works.
339 string Pattern = "(?<pm>[+-]?) (?<key>\\w+:)? (?<expr>.*)";
340 Regex r = new Regex (Pattern, RegexOptions.IgnorePatternWhitespace);
341 Match m = r.Match (args [i]);
343 string quoted_query = m.Groups ["pm"].ToString () +
344 m.Groups ["key"].ToString () +
345 "\"" + m.Groups ["expr"].ToString () + "\"";
347 if (query_str.Length > 0)
348 query_str.Append (' ');
349 query_str.Append (quoted_query);
351 break;
355 ++i;
358 if (listener) {
360 query.IsIndexListener = true;
362 } else {
364 if (query_str.Length > 0)
365 query.AddText (query_str.ToString ());
367 if (start_date != DateTime.MinValue || end_date != DateTime.MinValue) {
368 QueryPart_DateRange part = new QueryPart_DateRange ();
370 if (start_date != DateTime.MinValue)
371 part.StartDate = start_date;
373 if (end_date != DateTime.MinValue)
374 part.EndDate = end_date;
376 query.AddPart (part);
380 query.HitsAddedEvent += OnHitsAdded;
381 query.HitsSubtractedEvent += OnHitsSubtracted;
384 if (! keep_running)
385 query.FinishedEvent += OnFinished;
386 else
387 query.ClosedEvent += OnClosed;
389 SendQuery ();
391 main_loop.Quit ();