Add Konqueror bookmark backend.
[beagle.git] / tools / Query.cs
blob52f35a0c29b65ce525db37d6557de13196a8f538
1 //
2 // Query.cs
3 //
4 // Copyright (C) 2004-2006 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;
35 using System.Runtime.InteropServices;
37 using GLib;
39 using Beagle;
40 using Beagle.Util;
41 using Beagle.Daemon;
43 class QueryTool {
45 private static int count = 0;
46 private static Query query = null;
47 private static DateTime queryStartTime;
48 private static DateTime lastQueryTime = DateTime.Now;
50 private static MainLoop main_loop = null;
51 // CLI args
52 private static bool keep_running = false;
53 private static bool verbose = false;
54 private static bool display_hits = true;
55 private static bool flood = false;
56 private static bool listener = false;
57 private static DateTime start_date = DateTime.MinValue;
58 private static DateTime end_date = DateTime.MinValue;
60 private static void OnHitsAdded (HitsAddedResponse response)
62 lastQueryTime = DateTime.Now;
64 if (count == 0 && verbose) {
65 Console.WriteLine ("First hit returned in {0:0.000}s",
66 (lastQueryTime - queryStartTime).TotalSeconds);
69 if (! display_hits) {
70 count += response.Hits.Count;
71 return;
74 foreach (Hit hit in response.Hits) {
75 if (verbose)
76 Console.WriteLine (" Uri: {0}", hit.Uri);
77 else
78 Console.WriteLine (hit.Uri);
80 if (verbose) {
81 SnippetRequest sreq = new SnippetRequest (query, hit);
82 SnippetResponse sresp = (SnippetResponse) sreq.Send ();
83 Console.WriteLine ("PaUri: {0}", hit.ParentUri != null ? hit.ParentUri.ToString () : "(null)");
84 Console.WriteLine (" Snip: {0}", sresp.Snippet != null ? sresp.Snippet : "(null)");
85 Console.WriteLine (" Type: {0}", hit.Type);
86 Console.WriteLine ("MimeT: {0}", hit.MimeType == null ? "(null)" : hit.MimeType);
87 Console.WriteLine (" Src: {0}", hit.Source);
88 Console.WriteLine ("Score: {0}", hit.Score);
89 if (hit.ValidTimestamp)
90 Console.WriteLine (" Time: {0}", hit.Timestamp.ToLocalTime ());
92 foreach (Property prop in hit.Properties)
93 Console.WriteLine (" {0} = '{1}'", prop.Key, prop.Value);
95 Console.WriteLine ();
98 ++count;
102 private static void OnHitsSubtracted (HitsSubtractedResponse response)
104 lastQueryTime = DateTime.Now;
106 if (! display_hits)
107 return;
109 foreach (Uri uri in response.Uris) {
110 Console.WriteLine ("Subtracted Uri '{0}'", uri);
111 Console.WriteLine ();
113 --count;
117 private static void OnFinished (FinishedResponse response)
119 if (verbose) {
120 Console.WriteLine ("Elapsed time: {0:0.000}s",
121 (DateTime.Now - queryStartTime).TotalSeconds);
122 Console.WriteLine ("Total hits: {0}", count);
125 if (flood)
126 SendQuery ();
127 else
128 main_loop.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-2006 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.GetTypesFromAssemblyAttribute (assembly, typeof (IQueryableTypesAttribute))) {
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 ();
216 private static int query_counter = 0;
217 private static void SendQuery ()
219 ++query_counter;
220 if (flood) {
221 if (query_counter > 1)
222 Console.WriteLine ();
223 Console.WriteLine ("Sending query #{0}", query_counter);
226 queryStartTime = DateTime.Now;
227 try {
228 query.SendAsync ();
229 } catch (Exception ex) {
230 Console.WriteLine ("Could not connect to the Beagle daemon. The daemon probably isn't running.");
231 Console.WriteLine (ex);
232 System.Environment.Exit (-1);
236 [DllImport("libgobject-2.0.so.0")]
237 static extern void g_type_init ();
239 public static void Main (string[] args)
241 // Initialize GObject type system
242 g_type_init ();
244 main_loop = new MainLoop ();
246 if (args.Length == 0 || Array.IndexOf (args, "--help") > -1 || Array.IndexOf (args, "--usage") > -1)
247 PrintUsageAndExit ();
249 StringBuilder query_str = new StringBuilder ();
251 string[] formats = {
252 "yyyyMMdd",
253 "yyyyMMddHHmmss"
256 query = new Query ();
258 // Parse args
259 int i = 0;
260 while (i < args.Length) {
261 switch (args [i]) {
263 case "--mime":
264 if (++i >= args.Length) PrintUsageAndExit ();
265 query.AddMimeType (args [i]);
266 break;
267 case "--type":
268 if (++i >= args.Length) PrintUsageAndExit ();
269 query.AddHitType (args [i]);
270 break;
271 case "--source":
272 if (++i >= args.Length) PrintUsageAndExit ();
273 query.AddSource (args [i]);
274 break;
275 case "--live-query":
276 keep_running = true;
277 break;
278 case "--verbose":
279 verbose = true;
280 break;
281 case "--stats-only":
282 verbose = true;
283 display_hits = false;
284 break;
285 case "--max-hits":
286 if (++i >= args.Length) PrintUsageAndExit ();
287 query.MaxHits = Int32.Parse (args[i]);
288 break;
289 case "--flood":
290 flood = true;
291 break;
292 case "--listener":
293 listener = true;
294 keep_running = true;
295 break;
296 case "--start":
297 if (++i >= args.Length) PrintUsageAndExit ();
298 try {
299 start_date = DateTime.ParseExact (args[i], formats,
300 CultureInfo.InvariantCulture,
301 DateTimeStyles.None);
302 } catch (FormatException) {
303 Console.WriteLine ("Invalid start date");
304 System.Environment.Exit (-1);
306 start_date = start_date.ToUniversalTime ();
307 break;
309 case "--end":
310 if (++i >= args.Length) PrintUsageAndExit ();
311 try {
312 end_date = DateTime.ParseExact (args[i], formats,
313 CultureInfo.InvariantCulture,
314 DateTimeStyles.None);
315 } catch (FormatException) {
316 Console.WriteLine ("Invalid end date");
317 System.Environment.Exit (-1);
319 end_date = end_date.ToUniversalTime ();
320 break;
322 case "--keywords":
323 ReadBackendMappings ();
324 QueryDriver.ReadKeywordMappings ();
326 Console.WriteLine ("Supported query keywords are:");
328 IDictionaryEnumerator property_keyword_enum = PropertyKeywordFu.MappingEnumerator;
329 while (property_keyword_enum.MoveNext ()) {
330 PropertyDetail prop = property_keyword_enum.Value as PropertyDetail;
331 if (prop.Description != null)
332 Console.WriteLine (" {0,-20} for {1}", property_keyword_enum.Key, prop.Description);
333 else
334 Console.WriteLine (" {0,-20}", property_keyword_enum.Key);
337 System.Environment.Exit (0);
338 break;
340 default:
342 // We have to do some nastiness here to deal with shell quoting.
343 // See beagled/QueryStringParser.cs for an idea of how this works.
344 string Pattern = "(?<pm>[+-]?) (?<key>\\w+:)? (?<expr>.*)";
345 Regex r = new Regex (Pattern, RegexOptions.IgnorePatternWhitespace);
346 Match m = r.Match (args [i]);
348 string quoted_query = m.Groups ["pm"].ToString () +
349 m.Groups ["key"].ToString () +
350 "\"" + m.Groups ["expr"].ToString () + "\"";
352 if (query_str.Length > 0)
353 query_str.Append (' ');
354 query_str.Append (quoted_query);
356 break;
360 ++i;
363 if (listener) {
365 query.IsIndexListener = true;
367 } else {
369 if (query_str.Length > 0)
370 query.AddText (query_str.ToString ());
372 if (start_date != DateTime.MinValue || end_date != DateTime.MinValue) {
373 QueryPart_DateRange part = new QueryPart_DateRange ();
375 if (start_date != DateTime.MinValue)
376 part.StartDate = start_date;
378 if (end_date != DateTime.MinValue)
379 part.EndDate = end_date;
381 query.AddPart (part);
385 query.HitsAddedEvent += OnHitsAdded;
386 query.HitsSubtractedEvent += OnHitsSubtracted;
389 if (! keep_running)
390 query.FinishedEvent += OnFinished;
391 else
392 query.ClosedEvent += OnClosed;
394 SendQuery ();
396 main_loop.Run ();