Cleanup child indexables in beagle-extract-content. Print timestamp with timezone...
[beagle.git] / tools / Query.cs
blobe08d70a6a78874e9d0a3b68366e67126f02076d3
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 (response.NumMatches >= 0)
70 Console.WriteLine ("Returned latest {0} results out of total {1} matches", response.Hits.Count, response.NumMatches);
72 if (! display_hits) {
73 count += response.Hits.Count;
74 return;
77 foreach (Hit hit in response.Hits) {
78 if (verbose)
79 Console.WriteLine (" Uri: {0}", hit.Uri);
80 else
81 Console.WriteLine (hit.Uri);
83 if (verbose) {
84 SnippetRequest sreq = new SnippetRequest (query, hit);
85 SnippetResponse sresp = (SnippetResponse) sreq.Send ();
86 Console.WriteLine ("PaUri: {0}", hit.ParentUri != null ? hit.ParentUri.ToString () : "(null)");
87 Console.WriteLine (" Snip: {0}", sresp.Snippet != null ? sresp.Snippet : "(null)");
88 Console.WriteLine (" Type: {0}", hit.Type);
89 Console.WriteLine ("MimeT: {0}", hit.MimeType == null ? "(null)" : hit.MimeType);
90 Console.WriteLine (" Src: {0}", hit.Source);
91 Console.WriteLine ("Score: {0}", hit.Score);
92 if (hit.ValidTimestamp)
93 Console.WriteLine (" Time: {0}", hit.Timestamp);
95 foreach (Property prop in hit.Properties)
96 Console.WriteLine (" {0} = '{1}'", prop.Key, prop.Value);
98 Console.WriteLine ();
101 ++count;
105 private static void OnHitsSubtracted (HitsSubtractedResponse response)
107 lastQueryTime = DateTime.Now;
109 if (! display_hits)
110 return;
112 foreach (Uri uri in response.Uris) {
113 Console.WriteLine ("Subtracted Uri '{0}'", uri);
114 Console.WriteLine ();
116 --count;
120 private static void OnFinished (FinishedResponse response)
122 if (verbose) {
123 Console.WriteLine ("Elapsed time: {0:0.000}s",
124 (DateTime.Now - queryStartTime).TotalSeconds);
125 Console.WriteLine ("Total hits: {0}", count);
128 if (flood)
129 SendQuery ();
130 else
131 main_loop.Quit ();
134 public static void PrintUsageAndExit ()
136 string usage =
137 "beagle-query: Command-line interface to the Beagle search system.\n" +
138 "Web page: http://www.gnome.org/projects/beagle\n" +
139 "Copyright (C) 2004-2006 Novell, Inc.\n\n";
140 usage +=
141 "Usage: beagle-query [OPTIONS] <query string>\n\n" +
142 "Options:\n" +
143 " --verbose\t\t\tPrint detailed information about each hit.\n" +
144 " --mime <mime type>\t\tConstrain search results to the specified mime\n" +
145 " \t\ttype. Can be used multiply.\n" +
146 " --type <hit type>\t\tConstrain search results to the specified hit\n" +
147 " \t\ttype. Can be used multiply.\n" +
148 " --source <source>\t\tConstrain query to the specified source.\n" +
149 " \t\tSources list available from beagle-status.\n" +
150 " --start <date>\t\tConstrain query to items after specified date.\n" +
151 " \t\tDate must be in the form \"yyyyMMdd\" or \"yyyyMMddHHmmss\"\n" +
152 " --end <date>\t\t\tConstrain query to items before specified date.\n" +
153 " \t\t\tDate must be in the form \"yyyyMMdd\" or \"yyyyMMddHHmmss\"\n" +
154 " --keywords\t\t\tLists the keywords allowed in 'query string'.\n" +
155 " \t\t\tKeyword queries can be specified as keywordname:value e.g. ext:jpg\n" +
156 " --live-query\t\t\tRun continuously, printing notifications if a\n" +
157 " \t\t\tquery changes.\n" +
158 " --stats-only\t\t\tOnly display statistics about the query, not\n" +
159 " \t\t\tthe actual results.\n" +
160 " --max-hits\t\t\tLimit number of search results per backend\n" +
161 " \t\t\t(default = 100, max = 100)\n" +
162 " --flood\t\t\tExecute the query over and over again. Don't do that.\n" +
163 " --listener\t\t\tExecute an index listener query. Don't do that either.\n" +
164 " --help\t\t\tPrint this usage message.\n" +
165 "\n" +
166 "Query string supports an advanced query syntax.\n" +
167 "For details of the query syntax, please see http://beagle-project.org/Searching_Data\n" +
168 "Note: Quotes (\" or \') need to be shell escaped if used.\n";
170 Console.WriteLine (usage);
172 System.Environment.Exit (0);
175 private static void ReadBackendMappings ()
177 ArrayList assemblies = ReflectionFu.ScanEnvironmentForAssemblies ("BEAGLE_BACKEND_PATH", PathFinder.BackendDir);
179 // Add BeagleDaemonLib if it hasn't already been added.
180 bool found_daemon_lib = false;
181 foreach (Assembly assembly in assemblies) {
182 if (assembly.GetName ().Name == "BeagleDaemonLib") {
183 found_daemon_lib = true;
184 break;
188 if (!found_daemon_lib) {
189 try {
190 assemblies.Add (Assembly.LoadFrom (Path.Combine (PathFinder.PkgLibDir, "BeagleDaemonLib.dll")));
191 } catch (FileNotFoundException) {
192 Console.WriteLine ("WARNING: Could not find backend list.");
193 Environment.Exit (1);
197 foreach (Assembly assembly in assemblies) {
198 foreach (Type type in ReflectionFu.GetTypesFromAssemblyAttribute (assembly, typeof (IQueryableTypesAttribute))) {
199 object[] attributes = type.GetCustomAttributes (false);
200 foreach (object attribute in attributes) {
201 PropertyKeywordMapping mapping = attribute as PropertyKeywordMapping;
202 if (mapping == null)
203 continue;
204 //Logger.Log.Debug (mapping.Keyword + " => "
205 // + mapping.PropertyName +
206 // + " is-keyword=" + mapping.IsKeyword + " ("
207 // + mapping.Description + ") "
208 // + "(" + type.FullName + ")");
209 PropertyKeywordFu.RegisterMapping (mapping);
215 private static void OnClosed ()
217 if (flood)
218 SendQuery ();
219 else
220 main_loop.Quit ();
223 private static int query_counter = 0;
224 private static void SendQuery ()
226 ++query_counter;
227 if (flood) {
228 if (query_counter > 1)
229 Console.WriteLine ();
230 Console.WriteLine ("Sending query #{0}", query_counter);
233 queryStartTime = DateTime.Now;
234 try {
235 query.SendAsync ();
236 } catch (Exception ex) {
237 Console.WriteLine ("Could not connect to the Beagle daemon. The daemon probably isn't running.");
238 Console.WriteLine (ex);
239 System.Environment.Exit (-1);
243 [DllImport("libgobject-2.0.so.0")]
244 static extern void g_type_init ();
246 public static void Main (string[] args)
248 // Initialize GObject type system
249 g_type_init ();
251 main_loop = new MainLoop ();
253 if (args.Length == 0 || Array.IndexOf (args, "--help") > -1 || Array.IndexOf (args, "--usage") > -1)
254 PrintUsageAndExit ();
256 StringBuilder query_str = new StringBuilder ();
258 string[] formats = {
259 "yyyyMMdd",
260 "yyyyMMddHHmmss"
263 query = new Query ();
265 // Parse args
266 int i = 0;
267 while (i < args.Length) {
268 switch (args [i]) {
270 case "--mime":
271 if (++i >= args.Length) PrintUsageAndExit ();
272 query.AddMimeType (args [i]);
273 break;
274 case "--type":
275 if (++i >= args.Length) PrintUsageAndExit ();
276 query.AddHitType (args [i]);
277 break;
278 case "--source":
279 if (++i >= args.Length) PrintUsageAndExit ();
280 query.AddSource (args [i]);
281 break;
282 case "--live-query":
283 keep_running = true;
284 break;
285 case "--verbose":
286 verbose = true;
287 break;
288 case "--stats-only":
289 verbose = true;
290 display_hits = false;
291 break;
292 case "--max-hits":
293 if (++i >= args.Length) PrintUsageAndExit ();
294 query.MaxHits = Int32.Parse (args[i]);
295 break;
296 case "--flood":
297 flood = true;
298 break;
299 case "--listener":
300 listener = true;
301 keep_running = true;
302 break;
303 case "--start":
304 if (++i >= args.Length) PrintUsageAndExit ();
305 try {
306 start_date = DateTime.ParseExact (args[i], formats,
307 CultureInfo.InvariantCulture,
308 DateTimeStyles.None);
309 } catch (FormatException) {
310 Console.WriteLine ("Invalid start date");
311 System.Environment.Exit (-1);
313 start_date = start_date.ToUniversalTime ();
314 break;
316 case "--end":
317 if (++i >= args.Length) PrintUsageAndExit ();
318 try {
319 end_date = DateTime.ParseExact (args[i], formats,
320 CultureInfo.InvariantCulture,
321 DateTimeStyles.None);
322 } catch (FormatException) {
323 Console.WriteLine ("Invalid end date");
324 System.Environment.Exit (-1);
326 end_date = end_date.ToUniversalTime ();
327 break;
329 case "--keywords":
330 ReadBackendMappings ();
331 QueryDriver.ReadKeywordMappings ();
333 Console.WriteLine ("Supported query keywords are:");
335 IDictionaryEnumerator property_keyword_enum = PropertyKeywordFu.MappingEnumerator;
336 while (property_keyword_enum.MoveNext ()) {
337 PropertyDetail prop = property_keyword_enum.Value as PropertyDetail;
338 if (prop.Description != null)
339 Console.WriteLine (" {0,-20} for {1}", property_keyword_enum.Key, prop.Description);
340 else
341 Console.WriteLine (" {0,-20}", property_keyword_enum.Key);
344 System.Environment.Exit (0);
345 break;
347 default:
348 if (query_str.Length > 0)
349 query_str.Append (' ');
350 query_str.Append (args [i]);
352 break;
356 ++i;
359 if (listener) {
361 query.IsIndexListener = true;
363 } else {
365 if (query_str.Length > 0)
366 query.AddText (query_str.ToString ());
368 if (start_date != DateTime.MinValue || end_date != DateTime.MinValue) {
369 QueryPart_DateRange part = new QueryPart_DateRange ();
371 if (start_date != DateTime.MinValue)
372 part.StartDate = start_date;
374 if (end_date != DateTime.MinValue)
375 part.EndDate = end_date;
377 query.AddPart (part);
381 query.HitsAddedEvent += OnHitsAdded;
382 query.HitsSubtractedEvent += OnHitsSubtracted;
385 if (! keep_running)
386 query.FinishedEvent += OnFinished;
387 else
388 query.ClosedEvent += OnClosed;
390 SendQuery ();
392 main_loop.Run ();