]> arthur.barton.de Git - netatalk.git/commitdiff
Add filetype searching and searching by dates
authorRalph Boehme <sloowfranklin@gmail.com>
Tue, 19 Feb 2013 09:56:47 +0000 (10:56 +0100)
committerRalph Boehme <sloowfranklin@gmail.com>
Tue, 19 Feb 2013 09:56:47 +0000 (10:56 +0100)
etc/spotlight/slmod_rdf_map.c
etc/spotlight/slmod_rdf_map.h
etc/spotlight/slmod_rdf_parser.c
etc/spotlight/slmod_rdf_parser.y

index 80ac2c7dbe383c2830fb2e2788656aec4171fbe8..2edfb2ad8838aba6050f89330cc1574170169fe2 100644 (file)
@@ -32,70 +32,69 @@ struct spotlight_rdf_map spotlight_rdf_map[] = {
     {"kMDItemFSLabel",                  srmt_num,   NOTSUPPORTED},
     {"kMDItemDisplayName",              srmt_str,   "File:Name"},
     {"kMDItemFSName",                   srmt_str,   "File:Name"},
-#if 0
-    {"kMDItemFSContentChangeDate",      ssmt_date,  "nfo:fileLastModified"},
+    {"kMDItemFSContentChangeDate",      srmt_date,  "File:Modified"},
 
     /* Common metadata */
-    {"kMDItemContentCreationDate",      ssmt_date,  "nie:contentCreated"},
-    {"kMDItemContentModificationDate",  ssmt_date,  "nfo:fileLastModified"},
-    {"kMDItemAttributeChangeDate",      ssmt_date,  "nfo:fileLastModified"},
-    {"kMDItemAuthors",                  ssmt_str,   "dc:creator"},
-    {"kMDItemCopyright",                ssmt_str,   "nie:copyright"},
-    {"kMDItemCountry",                  ssmt_str,   "nco:country"},
-    {"kMDItemCreator",                  ssmt_str,   "dc:creator"},
-    {"kMDItemDurationSeconds",          ssmt_num,   "nfo:duration"},
-    {"kMDItemNumberOfPages",            ssmt_num,   "nfo:pageCount"},
-    {"kMDItemTitle",                    ssmt_str,   "nie:title"},
-    {"_kMDItemGroupId",                 ssmt_type,  SPECIAL},
-    {"kMDItemContentTypeTree",          ssmt_type,  SPECIAL},
+    {"kMDItemContentCreationDate",      srmt_date,  "Doc:Created"},
+    {"kMDItemContentModificationDate",  srmt_date,  "File:Modified"},
+    {"kMDItemAttributeChangeDate",      srmt_date,  "File:Modified"},
+    {"kMDItemAuthors",                  srmt_str,   "Doc:Author"},
+    {"kMDItemCopyright",                srmt_str,   "File:Copyright"},
+    {"kMDItemCountry",                  srmt_str,   "Image:Country"},
+    {"kMDItemCreator",                  srmt_str,   "DC:Creator"},
+    {"kMDItemDurationSeconds",          srmt_num,   "Audio:Duration"},
+    {"kMDItemNumberOfPages",            srmt_num,   "Doc:PageCount"},
+    {"kMDItemTitle",                    srmt_str,   "DC:Title"},
+    {"_kMDItemGroupId",                 srmt_type,  "File:Mime"},
+    {"kMDItemContentTypeTree",          srmt_type,  "File:Mime"},
 
     /* Image metadata */
-    {"kMDItemPixelWidth",               ssmt_num,   "nfo:width"},
-    {"kMDItemPixelHeight",              ssmt_num,   "nfo:height"},
-    {"kMDItemColorSpace",               ssmt_str,   "nexif:colorSpace"},
-    {"kMDItemBitsPerSample",            ssmt_num,   "nfo:colorDepth"},
-    {"kMDItemFocalLength",              ssmt_num,   "nmm:focalLength"},
-    {"kMDItemISOSpeed",                 ssmt_num,   "nmm:isoSpeed"},
-    {"kMDItemOrientation",              ssmt_bool,  "nfo:orientation"},
-    {"kMDItemResolutionWidthDPI",       ssmt_num,   "nfo:horizontalResolution"},
-    {"kMDItemResolutionHeightDPI",      ssmt_num,   "nfo:verticalResolution"},
-    {"kMDItemExposureTimeSeconds",      ssmt_num,   "nmm:exposureTime"},
+    {"kMDItemPixelWidth",               srmt_num,   "Image:Width"},
+    {"kMDItemPixelHeight",              srmt_num,   "Image:Height"},
+    {"kMDItemColorSpace",               srmt_str,   NOTSUPPORTED},
+    {"kMDItemBitsPerSample",            srmt_num,   NOTSUPPORTED},
+    {"kMDItemFocalLength",              srmt_num,   "Image:FocalLength"}, /* RDF: float */
+    {"kMDItemISOSpeed",                 srmt_num,   "Image:ISOSpeed"},
+    {"kMDItemOrientation",              srmt_bool,  "Image:Orientation"},
+    {"kMDItemResolutionWidthDPI",       srmt_num,   NOTSUPPORTED},
+    {"kMDItemResolutionHeightDPI",      srmt_num,   NOTSUPPORTED},
+    {"kMDItemExposureTimeSeconds",      srmt_num,   "Image:ExposureTime"}, /* RDF: fload */
 
     /* Audio metadata */
-    {"kMDItemComposer",                 ssmt_str,   "nmm:composer"},
-    {"kMDItemMusicalGenre",             ssmt_str,   "nfo:genre"},
-#endif
+    {"kMDItemComposer",                 srmt_str,   NOTSUPPORTED},
+    {"kMDItemMusicalGenre",             srmt_str,   "Audio:Genre"},
+
     {NULL, srmt_str, NULL}
 };
 
 struct MDTypeMap MDTypeMap[] = {
-    {"1",                       "http://www.semanticdesktop.org/ontologies/2007/03/22/nmo#Email"},
-    {"2",                       "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#Contact"},
-    {"3",                       NULL}, /* PrefPane */
-    {"4",                       "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Font"},
-    {"5",                       "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Bookmark"},
-    {"6",                       "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#Contact"},
-    {"7",                       "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Video"},
-    {"8",                       "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Executable"},
-    {"9",                       "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Folder"},
-    {"10",                      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Audio"},
-    {"11",                      "application/pdf"},
-    {"12",                      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Presentation"},
-    {"13",                      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Image"},
-    {"public.jpeg",             "image/jpeg"},
-    {"public.tiff",             "image/tiff"},
-    {"com.compuserve.gif",      "image/gif"},
-    {"public.png",              "image/png"},
-    {"com.microsoft.bmp",       "image/bmp"},
-    {"public.content",          "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Document"},
-    {"public.mp3",              "audio/mpeg"},
-    {"public.mpeg-4-audio",     "audio/x-aac"},
-    {"com.apple.application",   "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Software"},
-    {"public.text",             "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#TextDocument"},
-    {"public.plain-text",       "text/plain"},
-    {"public.rtf",              "text/rtf"},
-    {"public.html",             "text/html"},
-    {"public.xml",              "text/xml"},
-    {"public.source-code",      "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#SourceCode"},
+    {"1",                       "equals",      "message/rfc822"},
+    {"2",                       "equals",      "text/x-vcard"},
+    {"3",                       NOTSUPPORTED}, /* PrefPane */
+    {"4",                       NOTSUPPORTED}, /* Font. There's no single mime type to match all font formats, ugh! */
+    {"5",                       NOTSUPPORTED}, /* Bookmark */
+    {"6",                       "equals",      "text/x-vcard"},
+    {"7",                       "startsWith",  "video"},
+    {"8",                       NOTSUPPORTED}, /* Executable */
+    {"9",                       NOTSUPPORTED}, /* Folder */
+    {"10",                      "startsWith",  "audio"},
+    {"11",                      "equals",      "application/pdf"},
+    {"12",                      NOTSUPPORTED}, /* Presentation */
+    {"13",                      "startsWith",  "image"},
+    {"public.jpeg",             "equals",      "image/jpeg"},
+    {"public.tiff",             "equals",      "image/tiff"},
+    {"com.compuserve.gif",      "equals",      "image/gif"},
+    {"public.png",              "equals",      "image/png"},
+    {"com.microsoft.bmp",       "equals",      "image/bmp"},
+    {"public.content",          "inSet",       "application/msword,application/pdf,application/vnd.ms-excel,application/vnd.oasis.opendocument.text,application/vnd.sun.xml.writer"},
+    {"public.mp3",              "equals",      "audio/mpeg"},
+    {"public.mpeg-4-audio",     "equals",      "audio/x-aac"},
+    {"com.apple.application",   NOTSUPPORTED},
+    {"public.text",             "startsWith",  "text"},
+    {"public.plain-text",       "equals",      "text/plain"},
+    {"public.rtf",              "equals",      "text/rtf"},
+    {"public.html",             "equals",      "text/html"},
+    {"public.xml",              "equals",      "text/xml"},
+    {"public.source-code",      NOTSUPPORTED},
     {NULL,                      NULL}
 };
index 2096c756c9c7e5dde7fff265ae8fb102284b728b..4bfc78a6e80526e594df04d1b32fe33b17db7393 100644 (file)
@@ -37,6 +37,7 @@ struct spotlight_rdf_map {
 
 struct MDTypeMap {
     const char  *mdtm_value;     /* Value of '_kMDItemGroupId' or 'kMDItemContentTypeTree' */
+    const char  *mdtm_rdfop;     /* RDF query operator */
     const char  *mdtm_type;      /* MIME type */
 };
 
index 874f1251919eb3de9c05998860194cab1e628402..b3d5706be7a98e22cadc889521e2a4abb5647872 100644 (file)
@@ -524,8 +524,8 @@ static const yytype_int8 yyrhs[] =
 static const yytype_uint8 yyrline[] =
 {
        0,    67,    67,    69,    73,    90,    96,    97,    98,    99,
-     100,   121,   122,   123,   124,   125,   126,   127,   128,   132,
-     136,   137
+     100,   125,   126,   127,   128,   129,   130,   131,   132,   136,
+     140,   141
 };
 #endif
 
@@ -1504,7 +1504,11 @@ yyreduce:
          * The default Spotlight search term issued by the Finder (10.8) is:
          * '* == "searchterm" || kMDItemTextContent == "searchterm"'
          * As it isn't mappable to a single Tracker RDF query, we silently
-         * map this to just a filename search
+         * map ANY FTS query expression being part of an OR compound
+         * expression to a simple filename search.
+         * FTS queries are thus only possible by explicitly requesting
+         * file content FTS search in the Finder on the client (resulting
+         * in a 'kMDItemTextContent == "searchterm"' query).
          */
         if (strcmp((yyvsp[(1) - (3)].sval), "") == 0)
             (yyval.sval) = talloc_asprintf(srp_slq, (yyvsp[(3) - (3)].sval));
@@ -1520,73 +1524,73 @@ yyreduce:
 
   case 11:
 /* Line 1792 of yacc.c  */
-#line 121 "slmod_rdf_parser.y"
+#line 125 "slmod_rdf_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '=', (yyvsp[(4) - (5)].sval));}
     break;
 
   case 12:
 /* Line 1792 of yacc.c  */
-#line 122 "slmod_rdf_parser.y"
+#line 126 "slmod_rdf_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '!', (yyvsp[(4) - (5)].sval));}
     break;
 
   case 13:
 /* Line 1792 of yacc.c  */
-#line 123 "slmod_rdf_parser.y"
+#line 127 "slmod_rdf_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '<', (yyvsp[(4) - (5)].sval));}
     break;
 
   case 14:
 /* Line 1792 of yacc.c  */
-#line 124 "slmod_rdf_parser.y"
+#line 128 "slmod_rdf_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (5)].sval), '>', (yyvsp[(4) - (5)].sval));}
     break;
 
   case 15:
 /* Line 1792 of yacc.c  */
-#line 125 "slmod_rdf_parser.y"
+#line 129 "slmod_rdf_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '=', (yyvsp[(4) - (6)].sval));}
     break;
 
   case 16:
 /* Line 1792 of yacc.c  */
-#line 126 "slmod_rdf_parser.y"
+#line 130 "slmod_rdf_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '!', (yyvsp[(4) - (6)].sval));}
     break;
 
   case 17:
 /* Line 1792 of yacc.c  */
-#line 127 "slmod_rdf_parser.y"
+#line 131 "slmod_rdf_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '<', (yyvsp[(4) - (6)].sval));}
     break;
 
   case 18:
 /* Line 1792 of yacc.c  */
-#line 128 "slmod_rdf_parser.y"
+#line 132 "slmod_rdf_parser.y"
     {(yyval.sval) = map_expr((yyvsp[(1) - (6)].sval), '>', (yyvsp[(4) - (6)].sval));}
     break;
 
   case 19:
 /* Line 1792 of yacc.c  */
-#line 132 "slmod_rdf_parser.y"
+#line 136 "slmod_rdf_parser.y"
     {(yyval.sval) = map_daterange((yyvsp[(3) - (8)].sval), (yyvsp[(5) - (8)].tval), (yyvsp[(7) - (8)].tval));}
     break;
 
   case 20:
 /* Line 1792 of yacc.c  */
-#line 136 "slmod_rdf_parser.y"
+#line 140 "slmod_rdf_parser.y"
     {(yyval.tval) = isodate2unix((yyvsp[(3) - (4)].sval));}
     break;
 
   case 21:
 /* Line 1792 of yacc.c  */
-#line 137 "slmod_rdf_parser.y"
+#line 141 "slmod_rdf_parser.y"
     {(yyval.tval) = atoi((yyvsp[(1) - (1)].sval)) + SPRAW_TIME_OFFSET;}
     break;
 
 
 /* Line 1792 of yacc.c  */
-#line 1590 "slmod_rdf_parser.c"
+#line 1594 "slmod_rdf_parser.c"
       default: break;
     }
   /* User semantic actions sometimes alter yychar, and that requires
@@ -1818,7 +1822,7 @@ yyreturn:
 
 
 /* Line 2055 of yacc.c  */
-#line 140 "slmod_rdf_parser.y"
+#line 144 "slmod_rdf_parser.y"
 
 
 static time_t isodate2unix(const char *s)
@@ -1845,7 +1849,19 @@ static const char *map_daterange(const char *dateattr, time_t date1, time_t date
 
     for (p = spotlight_rdf_map; p->srm_spotlight_attr; p++) {
         if (strcmp(dateattr, p->srm_spotlight_attr) == 0) {
-            /* do something */
+                result = talloc_asprintf(srp_slq,
+                                         "<rdfq:and>\n"
+                                         "  <rdfq:greaterThan>\n"
+                                         "    <rdfq:Property name=\"%s\" />\n"
+                                         "    <rdf:Date>%s</rdf:Date>\n"
+                                         "  </rdfq:greaterThan>\n"
+                                         "  <rdfq:lessThan>\n"
+                                         "    <rdfq:Property name=\"%s\" />\n"
+                                         "    <rdf:Date>%s</rdf:Date>\n"
+                                         "  </rdfq:lessThan>\n"
+                                         "</rdfq:and>\n",
+                                         p->srm_rdf_attr, buf1,
+                                         p->srm_rdf_attr, buf2);
             break;
         }
     }
@@ -1856,6 +1872,28 @@ EC_CLEANUP:
     return result;
 }
 
+static char *map_type_search(const char *attr, char op, const char *val)
+{
+    char *result = NULL;
+
+    for (struct MDTypeMap *p = MDTypeMap; p->mdtm_value; p++) {
+        if (strcmp(p->mdtm_value, val) == 0) {
+            if (!p->mdtm_type)
+                return NULL;
+            result = talloc_asprintf(srp_slq,
+                                     "<rdfq:%s>\n"
+                                     "  <rdfq:Property name=\"File:Mime\" />\n"
+                                     "  <rdf:String>%s</rdf:String>\n"
+                                     "</rdfq:%s>\n",
+                                     p->mdtm_rdfop,
+                                     p->mdtm_type,
+                                     p->mdtm_rdfop);
+            break;
+        }
+    }
+    return result;
+}
+
 static const char *map_expr(const char *attr, char op, const char *val)
 {
     EC_INIT;
@@ -1870,14 +1908,21 @@ static const char *map_expr(const char *attr, char op, const char *val)
     for (p = spotlight_rdf_map; p->srm_spotlight_attr; p++) {
         if (p->srm_rdf_attr && strcmp(p->srm_spotlight_attr, attr) == 0) {
             switch (p->srm_type) {
-#if 0
-            case srmt_bool:
-                /* do something */
-                break;
             case srmt_num:
-                /* do something */
+                q = bformat("^%s$", val);
+                search = bfromcstr("*");
+                replace = bfromcstr(".*");
+                bfindreplace(q, search, replace, 0);
+                result = talloc_asprintf(srp_slq,
+                                         "<rdfq:regex>\n"
+                                         "  <rdfq:Property name=\"%s\" />\n"
+                                         "  <rdf:String>%s</rdf:String>\n"
+                                         "</rdfq:regex>\n",
+                                         p->srm_rdf_attr,
+                                         bdata(q));
+                bdestroy(q);
                 break;
-#endif
+
             case srmt_str:
                 q = bformat("^%s$", val);
                 search = bfromcstr("*");
@@ -1885,9 +1930,10 @@ static const char *map_expr(const char *attr, char op, const char *val)
                 bfindreplace(q, search, replace, 0);
                 result = talloc_asprintf(srp_slq,
                                          "<rdfq:regex>\n"
-                                         "  <rdfq:Property name=\"File:Name\" />\n"
+                                         "  <rdfq:Property name=\"%s\" />\n"
                                          "  <rdf:String>%s</rdf:String>\n"
                                          "</rdfq:regex>\n",
+                                         p->srm_rdf_attr,
                                          bdata(q));
                 bdestroy(q);
                 break;
@@ -1905,14 +1951,38 @@ static const char *map_expr(const char *attr, char op, const char *val)
                 result = "";
                 break;
 
-#if 0
             case srmt_date:
                 t = atoi(val) + SPRAW_TIME_OFFSET;
                 EC_NULL( tmp = localtime(&t) );
                 strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp);
-                /* do something */
+
+                switch (op) {
+                case '=':
+                    rdfop = "equals";
+                case '<':
+                    rdfop = "lessThan";
+                case '>':
+                    rdfop = "greaterThan";
+                default:
+                    yyerror("unknown date comparison");
+                    EC_FAIL;
+                }
+                result = talloc_asprintf(srp_slq,
+                                         "<rdfq:%s>\n"
+                                         "  <rdfq:Property name=\"%s\" />\n"
+                                         "  <rdf:Date>%s</rdf:Date>\n"
+                                         "</rdfq:%s>\n",
+                                         rdfop,
+                                         p->srm_rdf_attr,
+                                         buf1,
+                                         rdfop);
+
                 break;
-#endif
+
+            case srmt_type:
+                result = map_type_search(attr, op, val);
+                break;
+
             default:
                 yyerror("unknown Spotlight attribute type");
                 EC_FAIL;
index 4b6ea18e31a38e9772e76f66f826f02c64076622..99d6b568c5812fd47ee8e9b7ba1c3c8d129032ee 100644 (file)
@@ -103,7 +103,11 @@ BOOL                             {
          * The default Spotlight search term issued by the Finder (10.8) is:
          * '* == "searchterm" || kMDItemTextContent == "searchterm"'
          * As it isn't mappable to a single Tracker RDF query, we silently
-         * map this to just a filename search
+         * map ANY FTS query expression being part of an OR compound
+         * expression to a simple filename search.
+         * FTS queries are thus only possible by explicitly requesting
+         * file content FTS search in the Finder on the client (resulting
+         * in a 'kMDItemTextContent == "searchterm"' query).
          */
         if (strcmp($1, "") == 0)
             $$ = talloc_asprintf(srp_slq, $3);
@@ -163,7 +167,19 @@ static const char *map_daterange(const char *dateattr, time_t date1, time_t date
 
     for (p = spotlight_rdf_map; p->srm_spotlight_attr; p++) {
         if (strcmp(dateattr, p->srm_spotlight_attr) == 0) {
-            /* do something */
+                result = talloc_asprintf(srp_slq,
+                                         "<rdfq:and>\n"
+                                         "  <rdfq:greaterThan>\n"
+                                         "    <rdfq:Property name=\"%s\" />\n"
+                                         "    <rdf:Date>%s</rdf:Date>\n"
+                                         "  </rdfq:greaterThan>\n"
+                                         "  <rdfq:lessThan>\n"
+                                         "    <rdfq:Property name=\"%s\" />\n"
+                                         "    <rdf:Date>%s</rdf:Date>\n"
+                                         "  </rdfq:lessThan>\n"
+                                         "</rdfq:and>\n",
+                                         p->srm_rdf_attr, buf1,
+                                         p->srm_rdf_attr, buf2);
             break;
         }
     }
@@ -174,6 +190,28 @@ EC_CLEANUP:
     return result;
 }
 
+static char *map_type_search(const char *attr, char op, const char *val)
+{
+    char *result = NULL;
+
+    for (struct MDTypeMap *p = MDTypeMap; p->mdtm_value; p++) {
+        if (strcmp(p->mdtm_value, val) == 0) {
+            if (!p->mdtm_type)
+                return NULL;
+            result = talloc_asprintf(srp_slq,
+                                     "<rdfq:%s>\n"
+                                     "  <rdfq:Property name=\"File:Mime\" />\n"
+                                     "  <rdf:String>%s</rdf:String>\n"
+                                     "</rdfq:%s>\n",
+                                     p->mdtm_rdfop,
+                                     p->mdtm_type,
+                                     p->mdtm_rdfop);
+            break;
+        }
+    }
+    return result;
+}
+
 static const char *map_expr(const char *attr, char op, const char *val)
 {
     EC_INIT;
@@ -188,14 +226,21 @@ static const char *map_expr(const char *attr, char op, const char *val)
     for (p = spotlight_rdf_map; p->srm_spotlight_attr; p++) {
         if (p->srm_rdf_attr && strcmp(p->srm_spotlight_attr, attr) == 0) {
             switch (p->srm_type) {
-#if 0
-            case srmt_bool:
-                /* do something */
-                break;
             case srmt_num:
-                /* do something */
+                q = bformat("^%s$", val);
+                search = bfromcstr("*");
+                replace = bfromcstr(".*");
+                bfindreplace(q, search, replace, 0);
+                result = talloc_asprintf(srp_slq,
+                                         "<rdfq:regex>\n"
+                                         "  <rdfq:Property name=\"%s\" />\n"
+                                         "  <rdf:String>%s</rdf:String>\n"
+                                         "</rdfq:regex>\n",
+                                         p->srm_rdf_attr,
+                                         bdata(q));
+                bdestroy(q);
                 break;
-#endif
+
             case srmt_str:
                 q = bformat("^%s$", val);
                 search = bfromcstr("*");
@@ -203,9 +248,10 @@ static const char *map_expr(const char *attr, char op, const char *val)
                 bfindreplace(q, search, replace, 0);
                 result = talloc_asprintf(srp_slq,
                                          "<rdfq:regex>\n"
-                                         "  <rdfq:Property name=\"File:Name\" />\n"
+                                         "  <rdfq:Property name=\"%s\" />\n"
                                          "  <rdf:String>%s</rdf:String>\n"
                                          "</rdfq:regex>\n",
+                                         p->srm_rdf_attr,
                                          bdata(q));
                 bdestroy(q);
                 break;
@@ -223,14 +269,38 @@ static const char *map_expr(const char *attr, char op, const char *val)
                 result = "";
                 break;
 
-#if 0
             case srmt_date:
                 t = atoi(val) + SPRAW_TIME_OFFSET;
                 EC_NULL( tmp = localtime(&t) );
                 strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp);
-                /* do something */
+
+                switch (op) {
+                case '=':
+                    rdfop = "equals";
+                case '<':
+                    rdfop = "lessThan";
+                case '>':
+                    rdfop = "greaterThan";
+                default:
+                    yyerror("unknown date comparison");
+                    EC_FAIL;
+                }
+                result = talloc_asprintf(srp_slq,
+                                         "<rdfq:%s>\n"
+                                         "  <rdfq:Property name=\"%s\" />\n"
+                                         "  <rdf:Date>%s</rdf:Date>\n"
+                                         "</rdfq:%s>\n",
+                                         rdfop,
+                                         p->srm_rdf_attr,
+                                         buf1,
+                                         rdfop);
+
                 break;
-#endif
+
+            case srmt_type:
+                result = map_type_search(attr, op, val);
+                break;
+
             default:
                 yyerror("unknown Spotlight attribute type");
                 EC_FAIL;