summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am32
-rw-r--r--docs/reference/gdata-docs.xml2
-rw-r--r--docs/reference/gdata-sections.txt60
-rw-r--r--gdata/gdata.h3
-rw-r--r--gdata/gdata.symbols25
-rw-r--r--gdata/services/freebase/gdata-freebase-search-query.c602
-rw-r--r--gdata/services/freebase/gdata-freebase-search-query.h97
-rw-r--r--gdata/services/freebase/gdata-freebase-search-result.c395
-rw-r--r--gdata/services/freebase/gdata-freebase-search-result.h93
-rw-r--r--gdata/services/freebase/gdata-freebase-service.c62
-rw-r--r--gdata/services/freebase/gdata-freebase-service.h7
11 files changed, 1377 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index b2b685de..0a472d44 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -26,6 +26,8 @@ GDATA_ENUM_FILES = \
gdata/media/gdata-media-enums.h \
gdata/services/documents/gdata-documents-enums.c \
gdata/services/documents/gdata-documents-enums.h \
+ gdata/services/freebase/gdata-freebase-enums.c \
+ gdata/services/freebase/gdata-freebase-enums.h \
gdata/services/picasaweb/gdata-picasaweb-enums.c \
gdata/services/picasaweb/gdata-picasaweb-enums.h \
gdata/services/youtube/gdata-youtube-enums.c \
@@ -95,6 +97,29 @@ gdata/services/documents/gdata-documents-enums.c: $(gdata_documents_headers) Mak
&& sed "s/g_data/gdata/" gdata/services/documents/gdata-documents-enums.c.tmp > gdata/services/documents/gdata-documents-enums.c \
&& rm -f gdata/services/documents/gdata-documents-enums.c.tmp)
+gdata/services/freebase/gdata-freebase-enums.h: $(gdata_freebase_headers) Makefile
+ $(AM_V_GEN)($(GLIB_MKENUMS) \
+ --fhead "#ifndef GDATA_FREEBASE_ENUMS_H\n#define GDATA_FREEBASE_ENUMS_H\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
+ --fprod "/* enumerations from \"@filename@\" */\n" \
+ --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define GDATA_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \
+ --ftail "G_END_DECLS\n\n#endif /* !GDATA_FREEBASE_ENUMS_H */" \
+ $(addprefix $(srcdir)/,$(gdata_freebase_headers)) > gdata/services/freebase/gdata-freebase-enums.h.tmp \
+ && sed "s/g_data_freebase/gdata_freebase/" gdata/services/freebase/gdata-freebase-enums.h.tmp > gdata/services/freebase/gdata-freebase-enums.h.tmp2 \
+ && sed "s/GDATA_TYPE_DATA_FREEBASE/GDATA_TYPE_FREEBASE/" gdata/services/freebase/gdata-freebase-enums.h.tmp2 > gdata/services/freebase/gdata-freebase-enums.h \
+ && rm -f gdata/services/freebase/gdata-freebase-enums.h.tmp \
+ && rm -f gdata/services/freebase/gdata-freebase-enums.h.tmp2)
+
+gdata/services/freebase/gdata-freebase-enums.c: $(gdata_freebase_headers) Makefile gdata/services/freebase/gdata-freebase-enums.h
+ $(AM_V_GEN)($(GLIB_MKENUMS) \
+ --fhead "#include \"gdata-freebase-service.h\"\n#include \"gdata-freebase-search-query.h\"\n#include \"gdata-freebase-result.h\"\n#include \"gdata-freebase-enums.h\"" \
+ --fprod "\n/* enumerations from \"@filename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType etype = 0;\n if (etype == 0) {\n static const G@Type@Value values[] = {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (\"@EnumName@\", values);\n }\n return etype;\n}\n" \
+ $(addprefix $(srcdir)/,$(gdata_freebase_headers)) > gdata/services/freebase/gdata-freebase-enums.c.tmp \
+ && sed "s/g_data_freebase/gdata_freebase/" gdata/services/freebase/gdata-freebase-enums.c.tmp > gdata/services/freebase/gdata-freebase-enums.c \
+ && rm -f gdata/services/freebase/gdata-freebase-enums.c.tmp)
+
gdata/services/picasaweb/gdata-picasaweb-enums.h: $(gdata_picasaweb_headers) Makefile
$(AM_V_GEN)($(GLIB_MKENUMS) \
--fhead "#ifndef GDATA_PICASAWEB_ENUMS_H\n#define GDATA_PICASAWEB_ENUMS_H\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
@@ -309,11 +334,14 @@ gdatafreebaseincludedir = $(gdataincludedir)/services/freebase
gdata_freebase_headers = \
gdata/services/freebase/gdata-freebase-service.h \
gdata/services/freebase/gdata-freebase-result.h \
+ gdata/services/freebase/gdata-freebase-search-query.h \
+ gdata/services/freebase/gdata-freebase-search-result.h \
gdata/services/freebase/gdata-freebase-topic-query.h \
gdata/services/freebase/gdata-freebase-topic-result.h \
gdata/services/freebase/gdata-freebase-query.h
gdatafreebaseinclude_HEADERS = \
- $(gdata_freebase_headers)
+ $(gdata_freebase_headers) \
+ gdata/services/freebase/gdata-freebase-enums.h
gdata_sources = \
gdata/gdata-entry.c \
@@ -426,6 +454,8 @@ gdata_sources = \
\
gdata/services/freebase/gdata-freebase-service.c \
gdata/services/freebase/gdata-freebase-result.c \
+ gdata/services/freebase/gdata-freebase-search-query.c \
+ gdata/services/freebase/gdata-freebase-search-result.c \
gdata/services/freebase/gdata-freebase-topic-query.c \
gdata/services/freebase/gdata-freebase-topic-result.c \
gdata/services/freebase/gdata-freebase-query.c
diff --git a/docs/reference/gdata-docs.xml b/docs/reference/gdata-docs.xml
index 262ec281..c4edade9 100644
--- a/docs/reference/gdata-docs.xml
+++ b/docs/reference/gdata-docs.xml
@@ -191,6 +191,8 @@
<xi:include href="xml/gdata-freebase-result.xml"/>
<xi:include href="xml/gdata-freebase-topic-query.xml"/>
<xi:include href="xml/gdata-freebase-topic-result.xml"/>
+ <xi:include href="xml/gdata-freebase-search-query.xml"/>
+ <xi:include href="xml/gdata-freebase-search-result.xml"/>
</chapter>
</part>
diff --git a/docs/reference/gdata-sections.txt b/docs/reference/gdata-sections.txt
index 4cc46044..2e9dbc64 100644
--- a/docs/reference/gdata-sections.txt
+++ b/docs/reference/gdata-sections.txt
@@ -2577,6 +2577,8 @@ gdata_freebase_service_query
gdata_freebase_service_query_async
gdata_freebase_service_get_topic
gdata_freebase_service_get_topic_async
+gdata_freebase_service_search
+gdata_freebase_service_search_async
<SUBSECTION Standard>
gdata_freebase_service_get_type
GDATA_FREEBASE_SERVICE
@@ -2694,3 +2696,61 @@ GDATA_TYPE_FREEBASE_TOPIC_VALUE
<SUBSECTION Private>
GDataFreebaseTopicResultPrivate
</SECTION>
+
+<SECTION>
+<FILE>gdata-freebase-search-query</FILE>
+<TITLE>GDataFreebaseSearchQuery</TITLE>
+GDataFreebaseSearchQuery
+GDataFreebaseSearchQueryClass
+gdata_freebase_search_query_new
+gdata_freebase_search_query_set_language
+gdata_freebase_search_query_get_language
+gdata_freebase_search_query_set_stemmed
+gdata_freebase_search_query_get_stemmed
+GDataFreebaseSearchFilterType
+gdata_freebase_search_query_open_filter
+gdata_freebase_search_query_close_filter
+gdata_freebase_search_query_add_filter
+gdata_freebase_search_query_add_location
+<SUBSECTION Standard>
+gdata_freebase_search_query_get_type
+GDATA_FREEBASE_SEARCH_QUERY
+GDATA_FREEBASE_SEARCH_QUERY_CLASS
+GDATA_FREEBASE_SEARCH_QUERY_GET_CLASS
+GDATA_IS_FREEBASE_SEARCH_QUERY
+GDATA_IS_FREEBASE_SEARCH_QUERY_CLASS
+GDATA_TYPE_FREEBASE_SEARCH_QUERY
+gdata_freebase_search_filter_type_get_type
+GDATA_TYPE_FREEBASE_SEARCH_FILTER_TYPE
+<SUBSECTION Private>
+GDataFreebaseSearchQueryPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gdata-freebase-search-result</FILE>
+<TITLE>GDataFreebaseSearchResult</TITLE>
+GDataFreebaseSearchResult
+GDataFreebaseSearchResultClass
+GDataFreebaseSearchResultItem
+gdata_freebase_search_result_new
+gdata_freebase_search_result_get_num_items
+gdata_freebase_search_result_get_total_hits
+gdata_freebase_search_result_get_item
+gdata_freebase_search_result_item_get_mid
+gdata_freebase_search_result_item_get_id
+gdata_freebase_search_result_item_get_name
+gdata_freebase_search_result_item_get_language
+gdata_freebase_search_result_item_get_notable_id
+gdata_freebase_search_result_item_get_notable_name
+gdata_freebase_search_result_item_get_score
+<SUBSECTION Standard>
+gdata_freebase_search_result_get_type
+GDATA_FREEBASE_SEARCH_RESULT
+GDATA_FREEBASE_SEARCH_RESULT_CLASS
+GDATA_FREEBASE_SEARCH_RESULT_GET_CLASS
+GDATA_IS_FREEBASE_SEARCH_RESULT
+GDATA_IS_FREEBASE_SEARCH_RESULT_CLASS
+GDATA_TYPE_FREEBASE_SEARCH_RESULT
+<SUBSECTION Private>
+GDataFreebaseSearchResultPrivate
+</SECTION>
diff --git a/gdata/gdata.h b/gdata/gdata.h
index 579ecade..158a7651 100644
--- a/gdata/gdata.h
+++ b/gdata/gdata.h
@@ -147,7 +147,10 @@
#include <gdata/services/freebase/gdata-freebase-service.h>
#include <gdata/services/freebase/gdata-freebase-query.h>
#include <gdata/services/freebase/gdata-freebase-result.h>
+#include <gdata/services/freebase/gdata-freebase-search-query.h>
+#include <gdata/services/freebase/gdata-freebase-search-result.h>
#include <gdata/services/freebase/gdata-freebase-topic-query.h>
#include <gdata/services/freebase/gdata-freebase-topic-result.h>
+#include <gdata/services/freebase/gdata-freebase-enums.h>
#endif /* !GDATA_H */
diff --git a/gdata/gdata.symbols b/gdata/gdata.symbols
index b149eb66..5fd48156 100644
--- a/gdata/gdata.symbols
+++ b/gdata/gdata.symbols
@@ -1026,9 +1026,34 @@ gdata_freebase_service_query
gdata_freebase_service_query_async
gdata_freebase_service_get_topic
gdata_freebase_service_get_topic_async
+gdata_freebase_service_search
+gdata_freebase_service_search_async
gdata_freebase_query_get_type
gdata_freebase_query_new
gdata_freebase_query_new_from_variant
+gdata_freebase_search_filter_type_get_type
+gdata_freebase_search_query_get_type
+gdata_freebase_search_query_new
+gdata_freebase_search_query_open_filter
+gdata_freebase_search_query_close_filter
+gdata_freebase_search_query_add_filter
+gdata_freebase_search_query_add_location
+gdata_freebase_search_query_set_language
+gdata_freebase_search_query_get_language
+gdata_freebase_search_query_set_stemmed
+gdata_freebase_search_query_get_stemmed
+gdata_freebase_search_result_get_type
+gdata_freebase_search_result_new
+gdata_freebase_search_result_get_num_items
+gdata_freebase_search_result_get_total_hits
+gdata_freebase_search_result_get_item
+gdata_freebase_search_result_item_get_mid
+gdata_freebase_search_result_item_get_id
+gdata_freebase_search_result_item_get_name
+gdata_freebase_search_result_item_get_language
+gdata_freebase_search_result_item_get_notable_name
+gdata_freebase_search_result_item_get_notable_id
+gdata_freebase_search_result_item_get_score
gdata_freebase_topic_query_get_type
gdata_freebase_topic_query_new
gdata_freebase_topic_query_set_filter
diff --git a/gdata/services/freebase/gdata-freebase-search-query.c b/gdata/services/freebase/gdata-freebase-search-query.c
new file mode 100644
index 00000000..de6bf599
--- /dev/null
+++ b/gdata/services/freebase/gdata-freebase-search-query.c
@@ -0,0 +1,602 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) 2014 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GData Client. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gdata-freebase-search-query
+ * @short_description: GData Freebase query object
+ * @stability: Unstable
+ * @include: gdata/services/freebase/gdata-freebase-query.h
+ *
+ * #GDataFreebaseQuery represents a collection of query parameters specific to the Google Freebase service.
+ * a #GDataFreebaseQuery is built on top of a search term, further filters can be set on the search query
+ * through gdata_freebase_search_query_add_filter() or gdata_freebase_search_query_add_location(). The filters
+ * can be nested in sublevels, created through gdata_freebase_search_query_open_filter()
+ * and gdata_freebase_search_query_close_filter().
+ *
+ * For more details of Google Freebase API, see the <ulink type="http" url="https://developers.google.com/freebase/v1/">
+ * online documentation</ulink>.
+ *
+ * Since: UNRELEASED
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <json-glib/json-glib.h>
+
+#include "gdata-freebase-search-query.h"
+#include "gdata-query.h"
+#include "gdata-parser.h"
+
+static void gdata_freebase_search_query_finalize (GObject *self);
+static void gdata_freebase_search_query_set_property (GObject *self, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void gdata_freebase_search_query_get_property (GObject *self, guint prop_id, GValue *value, GParamSpec *pspec);
+static void get_query_uri (GDataQuery *self, const gchar *feed_uri, GString *query_uri, gboolean *params_started);
+
+typedef enum {
+ NODE_CONTAINER,
+ NODE_VALUE,
+ NODE_LOCATION
+} FilterNodeType;
+
+typedef union {
+ FilterNodeType type;
+
+ struct {
+ FilterNodeType type;
+ GDataFreebaseSearchFilterType filter_type;
+ GPtrArray *child_nodes; /* Contains owned FilterNode structs */
+ } container;
+
+ struct {
+ FilterNodeType type;
+ gchar *property;
+ gchar *value;
+ } value;
+
+ struct {
+ FilterNodeType type;
+ guint64 radius;
+ gdouble lat;
+ gdouble lon;
+ } location;
+} FilterNode;
+
+struct _GDataFreebaseSearchQueryPrivate {
+ FilterNode *filter;
+ GList *filter_stack; /* Contains unowned FilterNode structs */
+
+ gchar *lang;
+ guint stemmed : 1;
+};
+
+enum {
+ PROP_LANGUAGE = 1,
+ PROP_STEMMED
+};
+
+G_DEFINE_TYPE (GDataFreebaseSearchQuery, gdata_freebase_search_query, GDATA_TYPE_QUERY)
+
+static void
+gdata_freebase_search_query_class_init (GDataFreebaseSearchQueryClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GDataQueryClass *query_class = GDATA_QUERY_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GDataFreebaseSearchQueryPrivate));
+
+ gobject_class->finalize = gdata_freebase_search_query_finalize;
+ gobject_class->set_property = gdata_freebase_search_query_set_property;
+ gobject_class->get_property = gdata_freebase_search_query_get_property;
+
+ query_class->get_query_uri = get_query_uri;
+
+ /**
+ * GDataFreebaseSearchQuery:language:
+ *
+ * Language used for search results, in ISO-639-1 format.
+ *
+ * Since: UNRELEASED
+ */
+ g_object_class_install_property (gobject_class, PROP_LANGUAGE,
+ g_param_spec_string ("language",
+ "Language used for results",
+ "Language in ISO-639-1 format.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GDataFreebaseSearchQuery:stemmed:
+ *
+ * Whether word stemming should happen on the search terms. If this property is enabled,
+ * words like eg. "natural", "naturally" or "nature" would be all reduced to the root "natur"
+ * for search purposes.
+ *
+ * Since: UNRELEASED
+ */
+ g_object_class_install_property (gobject_class, PROP_STEMMED,
+ g_param_spec_boolean ("stemmed",
+ "Stem search terms",
+ "Whether the search terms should be stemmed",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdata_freebase_search_query_init (GDataFreebaseSearchQuery *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_FREEBASE_SEARCH_QUERY, GDataFreebaseSearchQueryPrivate);
+}
+
+static void
+_free_filter_node (FilterNode *node)
+{
+ switch (node->type) {
+ case NODE_CONTAINER:
+ g_ptr_array_unref (node->container.child_nodes);
+ break;
+ case NODE_VALUE:
+ g_free (node->value.property);
+ g_free (node->value.value);
+ break;
+ case NODE_LOCATION:
+ default:
+ break;
+ }
+
+ g_slice_free (FilterNode, node);
+}
+
+static void
+gdata_freebase_search_query_finalize (GObject *self)
+{
+ GDataFreebaseSearchQueryPrivate *priv = GDATA_FREEBASE_SEARCH_QUERY (self)->priv;
+
+ g_free (priv->lang);
+ g_list_free (priv->filter_stack);
+
+ if (priv->filter != NULL)
+ _free_filter_node (priv->filter);
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (gdata_freebase_search_query_parent_class)->finalize (self);
+}
+
+static void
+gdata_freebase_search_query_set_property (GObject *self, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ GDataFreebaseSearchQuery *query = GDATA_FREEBASE_SEARCH_QUERY (self);
+
+ switch (prop_id) {
+ case PROP_LANGUAGE:
+ gdata_freebase_search_query_set_language (query, g_value_get_string (value));
+ break;
+ case PROP_STEMMED:
+ gdata_freebase_search_query_set_stemmed (query, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdata_freebase_search_query_get_property (GObject *self, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ GDataFreebaseSearchQueryPrivate *priv = GDATA_FREEBASE_SEARCH_QUERY (self)->priv;
+
+ switch (prop_id) {
+ case PROP_LANGUAGE:
+ g_value_set_string (value, priv->lang);
+ break;
+ case PROP_STEMMED:
+ g_value_set_boolean (value, priv->stemmed);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+build_filter_string (FilterNode *node,
+ GString *str)
+{
+ switch (node->type) {
+ case NODE_CONTAINER:
+ {
+ /* Array matches GDataFreebaseSearchFilterType */
+ const gchar *type_str[] = { "all", "any", "not" };
+ guint i;
+
+ g_assert (node->container.filter_type >=0 &&
+ node->container.filter_type < G_N_ELEMENTS (type_str));
+
+ g_string_append_printf (str, "(%s", type_str[node->container.type]);
+
+ for (i = 0; i < node->container.child_nodes->len; i++)
+ build_filter_string (g_ptr_array_index (node->container.child_nodes, i), str);
+
+ g_string_append (str, ")");
+ break;
+ }
+ case NODE_VALUE:
+ {
+ gchar *escaped;
+
+ escaped = g_strescape (node->value.value, NULL);
+ g_string_append_printf (str, " %s:\"%s\"", node->value.property, escaped);
+ g_free (escaped);
+ break;
+ }
+ case NODE_LOCATION:
+ g_string_append_printf (str, "(within radius:%" G_GUINT64_FORMAT "m lon:%.4f lat:%.4f)",
+ node->location.radius, node->location.lon, node->location.lat);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+get_query_uri (GDataQuery *self, const gchar *feed_uri, GString *query_uri, gboolean *params_started)
+{
+ GDataFreebaseSearchQueryPrivate *priv = GDATA_FREEBASE_SEARCH_QUERY (self)->priv;
+ const gchar *query, *lang = NULL;
+ gint64 updated_max;
+ guint cur, limit;
+
+#define APPEND_SEP g_string_append_c (query_uri, (*params_started == FALSE) ? '?' : '&'); *params_started = TRUE;
+
+ query = gdata_query_get_q (self);
+
+ if (query != NULL) {
+ APPEND_SEP;
+ g_string_append (query_uri, "query=");
+ g_string_append (query_uri, query);
+ }
+
+ if (priv->filter != NULL) {
+ GString *str = g_string_new (NULL);
+
+ build_filter_string (priv->filter, str);
+
+ APPEND_SEP;
+ g_string_append (query_uri, "filter=");
+ g_string_append (query_uri, str->str);
+ g_string_free (str, TRUE);
+ }
+
+ updated_max = gdata_query_get_updated_max (self);
+
+ if (updated_max != -1) {
+ gchar *date_str;
+
+ date_str = gdata_parser_int64_to_json_iso8601 (updated_max);
+
+ APPEND_SEP;
+ g_string_append (query_uri, "as_of_time=");
+ g_string_append (query_uri, date_str);
+ g_free (date_str);
+ }
+
+ if (priv->lang != NULL) {
+ lang = priv->lang;
+ } else {
+ const gchar * const *user_languages;
+ GString *lang_str = NULL;
+ gint i;
+
+ user_languages = g_get_language_names ();
+
+ for (i = 0; user_languages[i] != NULL; i++) {
+ if (strlen (user_languages[i]) != 2)
+ continue;
+
+ if (!lang_str)
+ lang_str = g_string_new (user_languages[i]);
+ else
+ g_string_append_printf (lang_str, ",%s", user_languages[i]);
+ }
+
+ lang = g_string_free (lang_str, FALSE);
+ }
+
+ APPEND_SEP;
+ g_string_append (query_uri, "lang=");
+ g_string_append (query_uri, lang);
+
+ if (priv->stemmed) {
+ APPEND_SEP;
+ g_string_append (query_uri, "stemmed=true");
+ }
+
+ cur = gdata_query_get_start_index (self);
+
+ if (cur > 0) {
+ APPEND_SEP;
+ g_string_append_printf (query_uri, "cursor=%d", cur);
+ }
+
+ limit = gdata_query_get_max_results (self);
+
+ if (limit > 0) {
+ APPEND_SEP;
+ g_string_append_printf (query_uri, "limit=%d", limit);
+ }
+
+ /* We don't chain up with parent class get_query_uri because it uses
+ * GData protocol parameters and they aren't compatible with newest API family
+ */
+#undef APPEND_SEP
+}
+
+/**
+ * gdata_freebase_search_query_new:
+ * @search_terms: string to search for
+ *
+ * Creates a new #GDataFreebaseSearchQuery prepared to search for Freebase elements that
+ * match the given @search_terms. Further filters on the query can be set through
+ * gdata_freebase_search_query_add_filter() or gdata_freebase_search_query_add_location().
+ *
+ * Return value: (transfer full): a new #GDataFreebaseSearchQuery; unref with g_object_unref()
+ *
+ * Since: UNRELEASED
+ */
+GDataFreebaseSearchQuery *
+gdata_freebase_search_query_new (const gchar *search_terms)
+{
+ g_return_val_if_fail (search_terms != NULL, NULL);
+ return g_object_new (GDATA_TYPE_FREEBASE_SEARCH_QUERY, "q", search_terms, NULL);
+}
+
+/**
+ * gdata_freebase_search_query_open_filter:
+ * @self: a #GDataFreebaseSearchQuery
+ * @filter_type: filter type
+ *
+ * Opens a container of filter rules, those are applied according to the behavior specified by @filter_type.
+ * Every call to this function must be paired by a call to gdata_freebase_search_query_close_filter().
+ *
+ * Since: UNRELEASED
+ **/
+void
+gdata_freebase_search_query_open_filter (GDataFreebaseSearchQuery *self, GDataFreebaseSearchFilterType filter_type)
+{
+ GDataFreebaseSearchQueryPrivate *priv;
+ FilterNode *current_node, *node;
+
+ g_return_if_fail (GDATA_IS_FREEBASE_SEARCH_QUERY (self));
+
+ priv = GDATA_FREEBASE_SEARCH_QUERY (self)->priv;
+
+ node = g_slice_new0 (FilterNode);
+ node->type = NODE_CONTAINER;
+ node->container.filter_type = filter_type;
+ node->container.child_nodes = g_ptr_array_new_with_free_func ((GDestroyNotify) _free_filter_node);
+
+ if (priv->filter_stack != NULL) {
+ current_node = priv->filter_stack->data;
+ g_ptr_array_add (current_node->container.child_nodes, node);
+ } else if (priv->filter == NULL) {
+ priv->filter = node;
+ } else {
+ g_assert_not_reached ();
+ }
+
+ priv->filter_stack = g_list_prepend (priv->filter_stack, node);
+}
+
+/**
+ * gdata_freebase_search_query_close_filter:
+ * @self: a #GDataFreebaseSearchQuery
+ *
+ * Closes a filter level.
+ *
+ * Since: UNRELEASED
+ **/
+void
+gdata_freebase_search_query_close_filter (GDataFreebaseSearchQuery *self)
+{
+ GDataFreebaseSearchQueryPrivate *priv;
+
+ g_return_if_fail (GDATA_IS_FREEBASE_SEARCH_QUERY (self));
+
+ priv = GDATA_FREEBASE_SEARCH_QUERY (self)->priv;
+
+ if (priv->filter_stack == NULL)
+ g_assert_not_reached ();
+
+ priv->filter_stack = g_list_delete_link (priv->filter_stack, priv->filter_stack);
+}
+
+/**
+ * gdata_freebase_search_query_add_filter:
+ * @self: a #GDataFreebaseSearchQuery
+ * @property: Freebase property ID
+ * @value: match string
+ *
+ * Adds a property filter to the query. property filters are always nested in
+ * containers, opened and closed through gdata_freebase_search_query_open_filter()
+ * and gdata_freebase_search_query_close_filter().
+ *
+ * Since: UNRELEASED
+ **/
+void
+gdata_freebase_search_query_add_filter (GDataFreebaseSearchQuery *self, const gchar *property, const gchar *value)
+{
+ GDataFreebaseSearchQueryPrivate *priv;
+ FilterNode *current_node, *node;
+
+ g_return_if_fail (GDATA_IS_FREEBASE_SEARCH_QUERY (self));
+ g_return_if_fail (property != NULL && value != NULL);
+
+ priv = GDATA_FREEBASE_SEARCH_QUERY (self)->priv;
+
+ if (priv->filter_stack == NULL) {
+ g_critical ("A filter container must be opened before through "
+ "gdata_freebase_search_query_open_filter()");
+ g_assert_not_reached ();
+ }
+
+ node = g_slice_new0 (FilterNode);
+ node->type = NODE_VALUE;
+ node->value.property = g_strdup (property);
+ node->value.value = g_strdup (value);
+
+ current_node = priv->filter_stack->data;
+ g_ptr_array_add (current_node->container.child_nodes, node);
+}
+
+/**
+ * gdata_freebase_search_query_add_location:
+ * @self: a #GDataFreebaseSearchQuery
+ * @radius: radius in meters
+ * @lat: latitude
+ * @lon: longitude
+ *
+ * Adds a geolocation filter to the query. location filters are always nested in
+ * containers, opened and closed through gdata_freebase_search_query_open_filter()
+ * and gdata_freebase_search_query_close_filter().
+ *
+ * Since: UNRELEASED
+ **/
+void
+gdata_freebase_search_query_add_location (GDataFreebaseSearchQuery *self, guint64 radius, gdouble lat, gdouble lon)
+{
+ GDataFreebaseSearchQueryPrivate *priv = GDATA_FREEBASE_SEARCH_QUERY (self)->priv;
+ FilterNode *current_node, *node;
+
+ g_return_if_fail (GDATA_IS_FREEBASE_SEARCH_QUERY (self));
+
+ if (priv->filter_stack == NULL) {
+ g_critical ("A filter container must be opened before through "
+ "gdata_freebase_search_query_open_filter()");
+ g_assert_not_reached ();
+ }
+
+ node = g_slice_new0 (FilterNode);
+ node->type = NODE_LOCATION;
+ node->location.radius = radius;
+ node->location.lat = lat;
+ node->location.lon = lon;
+
+ current_node = priv->filter_stack->data;
+ g_ptr_array_add (current_node->container.child_nodes, node);
+}
+
+/**
+ * gdata_freebase_search_query_set_language:
+ * @self: a #GDataFreebaseSearchQuery
+ * @lang: (allow-none): Language used on the search terms and results, in ISO-639-1 format, or %NULL to unset.
+ *
+ * Sets the language used, both on the search terms and the results. If unset,
+ * the locale preferences will be respected.
+ *
+ * Since: UNRELEASED
+ **/
+void
+gdata_freebase_search_query_set_language (GDataFreebaseSearchQuery *self,
+ const gchar *lang)
+{
+ GDataFreebaseSearchQueryPrivate *priv;
+
+ g_return_if_fail (GDATA_IS_FREEBASE_SEARCH_QUERY (self));
+ g_return_if_fail (!lang || strlen (lang) == 2);
+
+ priv = self->priv;
+
+ if (g_strcmp0 (priv->lang, lang) == 0)
+ return;
+
+ g_free (priv->lang);
+ priv->lang = g_strdup (lang);
+ g_object_notify (G_OBJECT (self), "language");
+}
+
+/**
+ * gdata_freebase_search_query_get_language:
+ * @self: a #GDataFreebaseSearchQuery
+ *
+ * Gets the language set on the search query, or %NULL if unset.
+ *
+ * Return value: (allow-none): The language used on the query.
+ *
+ * Since: UNRELEASED
+ **/
+const gchar *
+gdata_freebase_search_query_get_language (GDataFreebaseSearchQuery *self)
+{
+ GDataFreebaseSearchQueryPrivate *priv;
+
+ g_return_val_if_fail (GDATA_IS_FREEBASE_SEARCH_QUERY (self), NULL);
+
+ priv = self->priv;
+ return priv->lang;
+}
+
+/**
+ * gdata_freebase_search_query_set_stemmed:
+ * @self: a #GDataFreebaseSearchQuery
+ * @stemmed: %TRUE to perform stemming on the search results
+ *
+ * Sets whether stemming is performed on the provided search terms. If @stemmed is %TRUE,
+ * words like eg. "natural", "naturally" or "nature" would be all reduced to the root "natur"
+ * for search purposes.
+ *
+ * Since: UNRELEASED
+ **/
+void
+gdata_freebase_search_query_set_stemmed (GDataFreebaseSearchQuery *self,
+ gboolean stemmed)
+{
+ GDataFreebaseSearchQueryPrivate *priv;
+
+ g_return_if_fail (GDATA_IS_FREEBASE_SEARCH_QUERY (self));
+
+ priv = self->priv;
+
+ if (priv->stemmed == stemmed)
+ return;
+
+ priv->stemmed = stemmed;
+ g_object_notify (G_OBJECT (self), "stemmed");
+}
+
+/**
+ * gdata_freebase_search_query_get_stemmed:
+ * @self: a #GDataFreebaseSearchQuery
+ *
+ * Returns whether the #GDataFreebaseSearchQuery will perform stemming on the search terms.
+ *
+ * Return value: %TRUE if the #GDataFreebaseSearchQuery performs stemming
+ *
+ * Since: UNRELEASED
+ **/
+gboolean
+gdata_freebase_search_query_get_stemmed (GDataFreebaseSearchQuery *self)
+{
+ GDataFreebaseSearchQueryPrivate *priv;
+
+ g_return_val_if_fail (GDATA_IS_FREEBASE_SEARCH_QUERY (self), FALSE);
+
+ priv = self->priv;
+ return priv->stemmed;
+}
diff --git a/gdata/services/freebase/gdata-freebase-search-query.h b/gdata/services/freebase/gdata-freebase-search-query.h
new file mode 100644
index 00000000..58172a96
--- /dev/null
+++ b/gdata/services/freebase/gdata-freebase-search-query.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) 2014 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GData Client. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDATA_FREEBASE_SEARCH_QUERY_H
+#define GDATA_FREEBASE_SEARCH_QUERY_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-query.h>
+#include <gdata/gdata-types.h>
+
+G_BEGIN_DECLS
+
+#define GDATA_TYPE_FREEBASE_SEARCH_QUERY (gdata_freebase_search_query_get_type ())
+#define GDATA_FREEBASE_SEARCH_QUERY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_FREEBASE_SEARCH_QUERY, GDataFreebaseSearchQuery))
+#define GDATA_FREEBASE_SEARCH_QUERY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_FREEBASE_SEARCH_QUERY, GDataFreebaseSearchQueryClass))
+#define GDATA_IS_FREEBASE_SEARCH_QUERY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_FREEBASE_SEARCH_QUERY))
+#define GDATA_IS_FREEBASE_SEARCH_QUERY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_FREEBASE_SEARCH_QUERY))
+#define GDATA_FREEBASE_SEARCH_QUERY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_FREEBASE_SEARCH_QUERY, GDataFreebaseSearchQueryClass))
+
+typedef struct _GDataFreebaseSearchQueryPrivate GDataFreebaseSearchQueryPrivate;
+
+/**
+ * GDataFreebaseSearchFilterType:
+ * @GDATA_FREEBASE_SEARCH_FILTER_ALL: all enclosed elements must match, logically an AND
+ * @GDATA_FREEBASE_SEARCH_FILTER_ANY: any of the enclosed elements must match, logically an OR
+ * @GDATA_FREEBASE_SEARCH_FILTER_NOT: the match is inverted.
+ *
+ * Search filter container types.
+ *
+ * Since: UNRELEASED
+ */
+typedef enum {
+ GDATA_FREEBASE_SEARCH_FILTER_ALL,
+ GDATA_FREEBASE_SEARCH_FILTER_ANY,
+ GDATA_FREEBASE_SEARCH_FILTER_NOT
+} GDataFreebaseSearchFilterType;
+
+/**
+ * GDataFreebaseSearchQuery:
+ *
+ * All the fields in the #GDataFreebaseSearchQuery structure are private and should never be accessed directly.
+ *
+ * Since: UNRELEASED
+ */
+typedef struct {
+ GDataQuery parent;
+ GDataFreebaseSearchQueryPrivate *priv;
+} GDataFreebaseSearchQuery;
+
+/**
+ * GDataFreebaseSearchQueryClass:
+ *
+ * All the fields in the #GDataFreebaseSearchQueryClass structure are private and should never be accessed directly.
+ *
+ * Since: UNRELEASED
+ */
+typedef struct {
+ /*< private >*/
+ GDataQueryClass parent;
+} GDataFreebaseSearchQueryClass;
+
+GType gdata_freebase_search_query_get_type (void) G_GNUC_CONST;
+
+GDataFreebaseSearchQuery *gdata_freebase_search_query_new (const gchar *search_terms) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+
+void gdata_freebase_search_query_open_filter (GDataFreebaseSearchQuery *self, GDataFreebaseSearchFilterType filter_type);
+void gdata_freebase_search_query_close_filter (GDataFreebaseSearchQuery *self);
+void gdata_freebase_search_query_add_filter (GDataFreebaseSearchQuery *self, const gchar *property, const gchar *value);
+void gdata_freebase_search_query_add_location (GDataFreebaseSearchQuery *self, guint64 radius, gdouble lat, gdouble lon);
+
+void gdata_freebase_search_query_set_language (GDataFreebaseSearchQuery *self, const gchar *lang);
+const gchar * gdata_freebase_search_query_get_language (GDataFreebaseSearchQuery *self);
+
+void gdata_freebase_search_query_set_stemmed (GDataFreebaseSearchQuery *self, gboolean stemmed);
+gboolean gdata_freebase_search_query_get_stemmed (GDataFreebaseSearchQuery *self);
+
+G_END_DECLS
+
+#endif /* !GDATA_FREEBASE_SEARCH_QUERY_H */
diff --git a/gdata/services/freebase/gdata-freebase-search-result.c b/gdata/services/freebase/gdata-freebase-search-result.c
new file mode 100644
index 00000000..8c212370
--- /dev/null
+++ b/gdata/services/freebase/gdata-freebase-search-result.c
@@ -0,0 +1,395 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) 2014 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GData Client. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gdata-freebase-search-result
+ * @short_description: GData Freebase search result object
+ * @stability: Unstable
+ * @include: gdata/services/freebase/gdata-freebase-result.h
+ *
+ * #GDataFreebaseSearchResult is a subclass of #GDataEntry to represent the result of a Freebase search query.
+ *
+ * For more details of Google Freebase API, see the <ulink type="http" url="https://developers.google.com/freebase/v1/">
+ * online documentation</ulink>.
+ *
+ * Since: UNRELEASED
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+
+#include "gdata-freebase-search-result.h"
+#include "gdata-private.h"
+#include "gdata-types.h"
+
+#define URLBASE "https://www.googleapis.com/freebase/v1"
+
+struct _GDataFreebaseSearchResultItem {
+ gchar *mid;
+ gchar *id;
+ gchar *name;
+ gchar *lang;
+ gchar *notable_id;
+ gchar *notable_name;
+ gdouble score;
+};
+
+struct _GDataFreebaseSearchResultPrivate {
+ GPtrArray *items; /* contains owned GDataFreebaseSearchResultItem structs */
+ guint total_hits;
+};
+
+static void gdata_freebase_search_result_finalize (GObject *self);
+static gboolean parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error);
+
+G_DEFINE_TYPE (GDataFreebaseSearchResult, gdata_freebase_search_result, GDATA_TYPE_FREEBASE_RESULT)
+
+static void
+gdata_freebase_search_result_class_init (GDataFreebaseSearchResultClass *klass)
+{
+ GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GDataFreebaseSearchResultPrivate));
+
+ gobject_class->finalize = gdata_freebase_search_result_finalize;
+ parsable_class->parse_json = parse_json;
+}
+
+static GDataFreebaseSearchResultItem *
+item_new (void)
+{
+ return g_slice_new0 (GDataFreebaseSearchResultItem);
+}
+
+static void
+item_free (GDataFreebaseSearchResultItem *item)
+{
+ g_free (item->mid);
+ g_free (item->id);
+ g_free (item->name);
+ g_free (item->lang);
+ g_free (item->notable_id);
+ g_free (item->notable_name);
+ g_slice_free (GDataFreebaseSearchResultItem, item);
+}
+
+static void
+gdata_freebase_search_result_init (GDataFreebaseSearchResult *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_FREEBASE_SEARCH_RESULT, GDataFreebaseSearchResultPrivate);
+ self->priv->items = g_ptr_array_new_with_free_func ((GDestroyNotify) item_free);
+}
+
+static void
+gdata_freebase_search_result_finalize (GObject *self)
+{
+ GDataFreebaseSearchResultPrivate *priv = GDATA_FREEBASE_SEARCH_RESULT (self)->priv;
+
+ g_ptr_array_unref (priv->items);
+
+ G_OBJECT_CLASS (gdata_freebase_search_result_parent_class)->finalize (self);
+}
+
+static gboolean
+parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error)
+{
+ GDataFreebaseSearchResultPrivate *priv = GDATA_FREEBASE_SEARCH_RESULT (parsable)->priv;
+ const GError *inner_error = NULL;
+ const gchar *member_name;
+ gint count, i;
+
+ GDATA_PARSABLE_CLASS (gdata_freebase_search_result_parent_class)->parse_json (parsable, reader, user_data, error);
+
+#define ITEM_SET_STRING(id,field,mandatory) \
+ json_reader_read_member (reader, #id); \
+ item->field = g_strdup (json_reader_get_string_value (reader)); \
+ if (mandatory) { \
+ inner_error = json_reader_get_error (reader); \
+ if (inner_error != NULL) goto item_error; \
+ } \
+ json_reader_end_member (reader);
+
+#define ITEM_SET_DOUBLE(id,field) \
+ json_reader_read_member (reader, #id); \
+ item->field = json_reader_get_double_value (reader); \
+ inner_error = json_reader_get_error (reader); \
+ if (inner_error != NULL) goto item_error; \
+ json_reader_end_member (reader);
+
+ member_name = json_reader_get_member_name (reader);
+
+ if (member_name == NULL)
+ return FALSE;
+
+ if (strcmp (member_name, "hits") == 0) {
+ priv->total_hits = json_reader_get_int_value (reader);
+ return TRUE;
+ } else if (strcmp (member_name, "result") != 0) {
+ /* Avoid anything else besides hits/result */
+ return TRUE;
+ }
+
+ if (!json_reader_is_array (reader))
+ return FALSE;
+
+ count = json_reader_count_elements (reader);
+
+ for (i = 0; i < count; i++) {
+ GDataFreebaseSearchResultItem *item;
+
+ item = item_new ();
+ json_reader_read_element (reader, i);
+
+ ITEM_SET_STRING (mid, mid, TRUE);
+ ITEM_SET_STRING (id, id, FALSE);
+ ITEM_SET_STRING (name, name, TRUE);
+ ITEM_SET_STRING (lang, lang, FALSE);
+ ITEM_SET_DOUBLE (score, score);
+
+ /* Read "notable" */
+ json_reader_read_member (reader, "notable");
+
+ if (json_reader_get_error (reader) == NULL) {
+ ITEM_SET_STRING (id, notable_id, TRUE);
+ ITEM_SET_STRING (name, notable_name, TRUE);
+ }
+
+ json_reader_end_member (reader);
+ json_reader_end_element (reader);
+
+ g_ptr_array_add (priv->items, item);
+ continue;
+
+ item_error:
+ item_free (item);
+ gdata_parser_error_required_json_content_missing (reader, error);
+ return FALSE;
+ }
+
+#undef ITEM_SET_DOUBLE
+#undef ITEM_SET_STRING
+
+ return TRUE;
+}
+
+/**
+ * gdata_freebase_search_result_new:
+ *
+ * Creates a new #GDataFreebaseSearchResult with the given ID and default properties.
+ *
+ * Return value: (transfer full): a new #GDataFreebaseSearchResult; unref with g_object_unref()
+ *
+ * Since: UNRELEASED
+ */
+GDataFreebaseSearchResult *
+gdata_freebase_search_result_new (void)
+{
+ return g_object_new (GDATA_TYPE_FREEBASE_SEARCH_RESULT, NULL);
+}
+
+/**
+ * gdata_freebase_search_result_get_num_items:
+ * @self: a #GDataFreebaseSearchResult
+ *
+ * Returns the number of items contained in this result.
+ *
+ * Returns: The number of items
+ *
+ * Since: UNRELEASED
+ **/
+guint
+gdata_freebase_search_result_get_num_items (GDataFreebaseSearchResult *self)
+{
+ g_return_val_if_fail (GDATA_IS_FREEBASE_SEARCH_RESULT (self), 0);
+
+ return self->priv->items->len;
+}
+
+/**
+ * gdata_freebase_search_result_get_total_hits:
+ * @self: a #GDataFreebaseSearchResult
+ *
+ * Returns the total number of hits found for the search query.
+ *
+ * Returns: the total number of hits.
+ *
+ * Since: UNRELEASED
+ **/
+guint
+gdata_freebase_search_result_get_total_hits (GDataFreebaseSearchResult *self)
+{
+ g_return_val_if_fail (GDATA_IS_FREEBASE_SEARCH_RESULT (self), 0);
+
+ return self->priv->total_hits;
+}
+
+/**
+ * gdata_freebase_search_result_get_item:
+ * @self: a #GDataFreebaseSearchResult
+ * @i: number of item to retrieve
+ *
+ * Gets an item from the search result.
+ *
+ * Returns: (transfer none) (allow-none): a search result item, or %NULL on invalid item.
+ *
+ * Since: UNRELEASED
+ **/
+const GDataFreebaseSearchResultItem *
+gdata_freebase_search_result_get_item (GDataFreebaseSearchResult *self, guint i)
+{
+ GDataFreebaseSearchResultPrivate *priv;
+
+ g_return_val_if_fail (GDATA_IS_FREEBASE_SEARCH_RESULT (self), NULL);
+
+ priv = self->priv;
+ g_return_val_if_fail (i < priv->items->len, NULL);
+
+ return g_ptr_array_index (priv->items, i);
+}
+
+/**
+ * gdata_freebase_search_result_item_get_mid:
+ * @item: a #GDataFreebaseSearchResultItem
+ *
+ * Returns the machine-encoded ID (MID) of the search result item. Elements may
+ * have a single MID, as opposed to the potentially multiple Freebase IDs that
+ * may point to it. MIDs are usable interchangeably with Freebase IDs.
+ *
+ * Returns: (transfer none): The result item MID.
+ *
+ * Since: UNRELEASED
+ **/
+const gchar *
+gdata_freebase_search_result_item_get_mid (const GDataFreebaseSearchResultItem *item)
+{
+ g_return_val_if_fail (item != NULL, NULL);
+ return item->mid;
+}
+
+/**
+ * gdata_freebase_search_result_item_get_id:
+ * @item: a #GDataFreebaseSearchResultItem
+ *
+ * Returns the Freebase ID of the search result item.
+ *
+ * Returns: (transfer none): The search result item Freebase ID.
+ *
+ * Since: UNRELEASED
+ **/
+const gchar *
+gdata_freebase_search_result_item_get_id (const GDataFreebaseSearchResultItem *item)
+{
+ g_return_val_if_fail (item != NULL, NULL);
+
+ if (item->id != NULL)
+ return item->id;
+
+ return item->mid;
+}
+
+/**
+ * gdata_freebase_search_result_item_get_name:
+ * @item: a #GDataFreebaseSearchResultItem
+ *
+ * Returns the human readable name of the search result item.
+ *
+ * Returns: (transfer none): The human readable name of the item.
+ *
+ * Since: UNRELEASED
+ **/
+const gchar *
+gdata_freebase_search_result_item_get_name (const GDataFreebaseSearchResultItem *item)
+{
+ g_return_val_if_fail (item != NULL, NULL);
+ return item->name;
+}
+
+/**
+ * gdata_freebase_search_result_item_get_language:
+ * @item: a #GDataFreebaseSearchResultItem
+ *
+ * Gets the language of this search result item, in ISO-639-1 format.
+ *
+ * Returns: (transfer none): The language of the search result item.
+ *
+ * Since: UNRELEASED
+ **/
+const gchar *
+gdata_freebase_search_result_item_get_language (const GDataFreebaseSearchResultItem *item)
+{
+ g_return_val_if_fail (item != NULL, NULL);
+ return item->lang;
+}
+
+/**
+ * gdata_freebase_search_result_item_get_notable_id:
+ * @item: a #GDataFreebaseSearchResultItem
+ *
+ * If this search result item is notable in an specific topic, this function
+ * returns the Freebase ID of this topic.
+ *
+ * Returns: (transfer none) (allow-none): The topic the result item is most notable of, or %NULL.
+ *
+ * Since: UNRELEASED
+ **/
+const gchar *
+gdata_freebase_search_result_item_get_notable_id (const GDataFreebaseSearchResultItem *item)
+{
+ g_return_val_if_fail (item != NULL, NULL);
+ return item->notable_id;
+}
+
+/**
+ * gdata_freebase_search_result_item_get_notable_name:
+ * @item: a #GDataFreebaseSearchResultItem
+ *
+ * If this search result item is notable in an specific topic, this function
+ * returns the human readable name of this topic.
+ *
+ * Returns: (transfer none) (allow-none): The human readable topic name, or %NULL
+ *
+ * Since: UNRELEASED
+ **/
+const gchar *
+gdata_freebase_search_result_item_get_notable_name (const GDataFreebaseSearchResultItem *item)
+{
+ g_return_val_if_fail (item != NULL, NULL);
+ return item->notable_name;
+}
+
+/**
+ * gdata_freebase_search_result_item_get_score:
+ * @item: a #GDataFreebaseSearchResultItem
+ *
+ * Returns the score of this search result item. The higher, the more relevant this
+ * item seems, given the search terms.
+ *
+ * Returns: the result item score.
+ *
+ * Since: UNRELEASED
+ **/
+gdouble
+gdata_freebase_search_result_item_get_score (const GDataFreebaseSearchResultItem *item)
+{
+ g_return_val_if_fail (item != NULL, 0.0);
+ return item->score;
+}
diff --git a/gdata/services/freebase/gdata-freebase-search-result.h b/gdata/services/freebase/gdata-freebase-search-result.h
new file mode 100644
index 00000000..e8601b79
--- /dev/null
+++ b/gdata/services/freebase/gdata-freebase-search-result.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) 2014 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GData Client. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDATA_FREEBASE_SEARCH_RESULT_H
+#define GDATA_FREEBASE_SEARCH_RESULT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-types.h>
+#include "gdata-freebase-result.h"
+
+G_BEGIN_DECLS
+
+#define GDATA_TYPE_FREEBASE_SEARCH_RESULT (gdata_freebase_search_result_get_type ())
+#define GDATA_FREEBASE_SEARCH_RESULT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_FREEBASE_SEARCH_RESULT, GDataFreebaseSearchResult))
+#define GDATA_FREEBASE_SEARCH_RESULT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_FREEBASE_SEARCH_RESULT, GDataFreebaseSearchResultClass))
+#define GDATA_IS_FREEBASE_SEARCH_RESULT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_FREEBASE_SEARCH_RESULT))
+#define GDATA_IS_FREEBASE_SEARCH_RESULT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_FREEBASE_SEARCH_RESULT))
+#define GDATA_FREEBASE_SEARCH_RESULT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_FREEBASE_SEARCH_RESULT, GDataFreebaseSearchResultClass))
+
+typedef struct _GDataFreebaseSearchResultPrivate GDataFreebaseSearchResultPrivate;
+
+/**
+ * GDataFreebaseSearchResultItem:
+ *
+ * Opaque struct holding data for a single search result item.
+ *
+ * Since: UNRELEASED
+ */
+typedef struct _GDataFreebaseSearchResultItem GDataFreebaseSearchResultItem;
+
+/**
+ * GDataFreebaseSearchResult:
+ *
+ * All the fields in the #GDataFreebaseSearchResult structure are private and should never be accessed directly.
+ *
+ * Since: UNRELEASED
+ */
+typedef struct {
+ GDataFreebaseResult parent;
+ GDataFreebaseSearchResultPrivate *priv;
+} GDataFreebaseSearchResult;
+
+/**
+ * GDataFreebaseSearchResultClass:
+ *
+ * All the fields in the #GDataFreebaseSearchResultClass structure are private and should never be accessed directly.
+ *
+ * Since: UNRELEASED
+ */
+
+typedef struct {
+ /*< private >*/
+ GDataFreebaseResultClass parent;
+} GDataFreebaseSearchResultClass;
+
+GType gdata_freebase_search_result_get_type (void) G_GNUC_CONST;
+
+GDataFreebaseSearchResult *gdata_freebase_search_result_new (void) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+
+guint gdata_freebase_search_result_get_num_items (GDataFreebaseSearchResult *self);
+guint gdata_freebase_search_result_get_total_hits (GDataFreebaseSearchResult *self);
+
+const GDataFreebaseSearchResultItem *gdata_freebase_search_result_get_item (GDataFreebaseSearchResult *self, guint i);
+
+const gchar *gdata_freebase_search_result_item_get_mid (const GDataFreebaseSearchResultItem *item);
+const gchar *gdata_freebase_search_result_item_get_id (const GDataFreebaseSearchResultItem *item);
+const gchar *gdata_freebase_search_result_item_get_name (const GDataFreebaseSearchResultItem *item);
+const gchar *gdata_freebase_search_result_item_get_language (const GDataFreebaseSearchResultItem *item);
+const gchar *gdata_freebase_search_result_item_get_notable_id (const GDataFreebaseSearchResultItem *item);
+const gchar *gdata_freebase_search_result_item_get_notable_name (const GDataFreebaseSearchResultItem *item);
+gdouble gdata_freebase_search_result_item_get_score (const GDataFreebaseSearchResultItem *item);
+
+G_END_DECLS
+
+#endif /* !GDATA_FREEBASE_SEARCH_RESULT_H */
diff --git a/gdata/services/freebase/gdata-freebase-service.c b/gdata/services/freebase/gdata-freebase-service.c
index 96e1a1e9..165ec140 100644
--- a/gdata/services/freebase/gdata-freebase-service.c
+++ b/gdata/services/freebase/gdata-freebase-service.c
@@ -315,3 +315,65 @@ gdata_freebase_service_get_topic_async (GDataFreebaseService *self, GDataFreebas
gdata_service_query_single_entry_async (GDATA_SERVICE (self), get_freebase_authorization_domain (), "topic",
GDATA_QUERY (query), GDATA_TYPE_FREEBASE_RESULT, cancellable, callback, user_data);
}
+
+/**
+ * gdata_freebase_service_search:
+ * @self: a #GDataFreebaseService
+ * @query: a #GDataFreebaseSearchQuery containing the topic ID
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @error: (allow-none): a #GError, or %NULL
+ *
+ * Performs a search for any given search term, filters can be set on @query to narrow down the results. The results returned
+ * are ordered by relevance. You can find out more about topic queries in the
+ * <ulink type="http" url="https://developers.google.com/freebase/v1/search-cookbook">online documentation</ulink>.
+ *
+ * Return value: (transfer full): a #GDataFreebaseSearchResult containing the results for the given search query; unref with g_object_unref()
+ *
+ * Since: UNRELEASED
+ */
+GDataFreebaseSearchResult *
+gdata_freebase_service_search (GDataFreebaseService *self, GDataFreebaseSearchQuery *query, GCancellable *cancellable, GError **error)
+{
+ GDataEntry *entry;
+
+ g_return_val_if_fail (GDATA_IS_FREEBASE_SERVICE (self), NULL);
+ g_return_val_if_fail (GDATA_IS_FREEBASE_SEARCH_QUERY (query), NULL);
+ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ entry = gdata_service_query_single_entry (GDATA_SERVICE (self), get_freebase_authorization_domain (), "search",
+ GDATA_QUERY (query), GDATA_TYPE_FREEBASE_SEARCH_RESULT, cancellable, error);
+ if (entry == NULL)
+ return NULL;
+
+ return GDATA_FREEBASE_SEARCH_RESULT (entry);
+}
+
+/**
+ * gdata_freebase_service_search_async:
+ * @self: a #GDataFreebaseService
+ * @query: a #GDataFreebaseQuery with the MQL query
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when authentication is finished
+ * @user_data: (closure): data to pass to the @callback function
+ *
+ * Performs a search for any given search term. @self and @query are all reffed when this
+ * function is called, so can safely be unreffed after this function returns.
+ *
+ * For more details, see gdata_freebase_service_search(), which is the synchronous version of
+ * this function.
+ *
+ * Since: UNRELEASED
+ */
+void
+gdata_freebase_service_search_async (GDataFreebaseService *self, GDataFreebaseSearchQuery *query,
+ GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
+{
+ g_return_if_fail (GDATA_IS_FREEBASE_SERVICE (self));
+ g_return_if_fail (GDATA_IS_FREEBASE_SEARCH_QUERY (query));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+ g_return_if_fail (callback != NULL);
+
+ gdata_service_query_single_entry_async (GDATA_SERVICE (self), get_freebase_authorization_domain (), "search",
+ GDATA_QUERY (query), GDATA_TYPE_FREEBASE_SEARCH_RESULT, cancellable, callback, user_data);
+}
diff --git a/gdata/services/freebase/gdata-freebase-service.h b/gdata/services/freebase/gdata-freebase-service.h
index af553bbd..609a38c5 100644
--- a/gdata/services/freebase/gdata-freebase-service.h
+++ b/gdata/services/freebase/gdata-freebase-service.h
@@ -28,6 +28,8 @@
#include <gdata/gdata-download-stream.h>
#include "gdata-freebase-query.h"
#include "gdata-freebase-result.h"
+#include "gdata-freebase-search-query.h"
+#include "gdata-freebase-search-result.h"
#include "gdata-freebase-topic-query.h"
#include "gdata-freebase-topic-result.h"
@@ -83,6 +85,11 @@ GDataFreebaseTopicResult *gdata_freebase_service_get_topic (GDataFreebaseService
void gdata_freebase_service_get_topic_async (GDataFreebaseService *self, GDataFreebaseTopicQuery *query,
GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+GDataFreebaseSearchResult *gdata_freebase_service_search (GDataFreebaseService *self, GDataFreebaseSearchQuery *query,
+ GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+void gdata_freebase_service_search_async (GDataFreebaseService *self, GDataFreebaseSearchQuery *query,
+ GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+
G_END_DECLS
#endif /* !GDATA_FREEBASE_SERVICE_H */