Page MenuHomeSoftware Heritage

D8020.id29033.diff
No OneTemporary

D8020.id29033.diff

diff --git a/swh/graphql/app.py b/swh/graphql/app.py
--- a/swh/graphql/app.py
+++ b/swh/graphql/app.py
@@ -33,6 +33,7 @@
resolvers.branch_target,
resolvers.release_target,
resolvers.directory_entry_target,
+ resolvers.search_result_target,
resolvers.binary_string,
scalars.id_scalar,
scalars.datetime_scalar,
diff --git a/swh/graphql/backends/archive.py b/swh/graphql/backends/archive.py
--- a/swh/graphql/backends/archive.py
+++ b/swh/graphql/backends/archive.py
@@ -3,7 +3,10 @@
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information
+from typing import List
+
from swh.graphql import server
+from swh.model.swhids import CoreSWHID
class Archive:
@@ -19,6 +22,7 @@
if url_pattern is None:
return self.storage.origin_list(page_token=after, limit=first)
+ # FIXME, change to use swh-search in a different backend
return self.storage.origin_search(
url_pattern=url_pattern, page_token=after, limit=first
)
@@ -77,3 +81,21 @@
def get_content(self, content_id):
# FIXME, only for tests
return self.storage.content_find({"sha1_git": content_id})
+
+ def search_in_swhids(self, swhid) -> List:
+ # query iff a valid swhid
+ swhid = CoreSWHID.from_string(swhid)
+ swhid_type = swhid.object_type.value
+ mapping = {
+ "rev": self.get_revisions,
+ "rel": self.get_releases,
+ }
+ return mapping[swhid_type]([swhid.object_id])
+
+ def search_in_metadata(self, query) -> List:
+ return []
+
+ def search_in_origins(self, query) -> List:
+ # use this with the get_origins method
+ # validate the query language as a key expression in the utils
+ return self.get_origins(url_pattern=query, first=1)
diff --git a/swh/graphql/resolvers/release.py b/swh/graphql/resolvers/release.py
--- a/swh/graphql/resolvers/release.py
+++ b/swh/graphql/resolvers/release.py
@@ -42,6 +42,12 @@
return self._get_release_by_id(self.kwargs.get("swhid").object_id)
+class SearchReleaseNode(BaseReleaseNode):
+ """
+ Node resolver for a release requested from a search result node
+ """
+
+
class TargetReleaseNode(BaseReleaseNode):
"""
Node resolver for a release requested as a target
diff --git a/swh/graphql/resolvers/resolver_factory.py b/swh/graphql/resolvers/resolver_factory.py
--- a/swh/graphql/resolvers/resolver_factory.py
+++ b/swh/graphql/resolvers/resolver_factory.py
@@ -14,6 +14,7 @@
RevisionNode,
TargetRevisionNode,
)
+from .search import SearchResultConnection
from .snapshot import (
OriginSnapshotConnection,
SnapshotNode,
@@ -67,6 +68,7 @@
"revision-parents": ParentRevisionConnection,
"revision-log": LogRevisionConnection,
"directory-entries": DirectoryEntryConnection,
+ "search": SearchResultConnection,
}
if resolver_type not in mapping:
raise AttributeError(f"Invalid connection type: {resolver_type}")
diff --git a/swh/graphql/resolvers/resolvers.py b/swh/graphql/resolvers/resolvers.py
--- a/swh/graphql/resolvers/resolvers.py
+++ b/swh/graphql/resolvers/resolvers.py
@@ -39,6 +39,7 @@
branch_target: UnionType = UnionType("BranchTarget")
release_target: UnionType = UnionType("ReleaseTarget")
directory_entry_target: UnionType = UnionType("DirectoryEntryTarget")
+search_result_target: UnionType = UnionType("SearchResultTarget")
# Node resolvers
# A node resolver should return an instance of BaseNode
@@ -239,12 +240,21 @@
return resolver(obj, info, **kw)
+@query.field("search")
+def search_resolver(
+ obj, info: GraphQLResolveInfo, **kw
+) -> rs.search.SearchResultConnection:
+ resolver = get_connection_resolver("search")
+ return resolver(obj, info, **kw)
+
+
# Any other type of resolver
@release_target.type_resolver
@directory_entry_target.type_resolver
@branch_target.type_resolver
+@search_result_target.type_resolver
def union_resolver(obj, *_) -> str:
"""
Generic resolver for all the union types
diff --git a/swh/graphql/resolvers/revision.py b/swh/graphql/resolvers/revision.py
--- a/swh/graphql/resolvers/revision.py
+++ b/swh/graphql/resolvers/revision.py
@@ -55,6 +55,15 @@
return self._get_revision_by_id(self.kwargs.get("swhid").object_id)
+class SearchRevisionNode(BaseRevisionNode):
+ """
+ Node resolver for a revision requested from a search result node
+ """
+
+ # the parent obj (self.obj) (SearchResultNode) has the model revision object
+ # no need to get data from backend again
+
+
class TargetRevisionNode(BaseRevisionNode):
"""
Node resolver for a revision requested as a target
diff --git a/swh/graphql/resolvers/search.py b/swh/graphql/resolvers/search.py
new file mode 100644
--- /dev/null
+++ b/swh/graphql/resolvers/search.py
@@ -0,0 +1,82 @@
+# Copyright (C) 2022 The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+from swh.graphql.backends import archive
+from swh.storage.interface import PagedResult
+
+from .base_connection import BaseConnection
+from .base_node import BaseNode
+from .release import SearchReleaseNode
+from .revision import SearchRevisionNode
+
+
+class SearchResultNode(BaseNode):
+ @property
+ def targetType(self):
+ return self._node.__class__.__name__.lower()
+
+ @property
+ def match(self):
+ return None
+
+ @property
+ def target(self):
+ # return the right node resolver
+
+ # not using the ariadne resolver here
+ # it is not needed as the result field will never
+ # accept extra arguments
+
+ # node_data is already avialble here
+ resolvers = {"revision": SearchRevisionNode, "release": SearchReleaseNode}
+ resolver = resolvers[self.targetType]
+ return resolver(obj=self, info=self.info, node_data=self._node)
+
+
+class SearchResultConnection(BaseConnection):
+ """
+ Category is a mandatory argument, get one category per
+ query
+ different categories can be requested in one request with
+ alias in query
+ eg:
+ query mysearch {
+ ori-search: search(query: "test", category: "origin") {
+ type
+ }
+ meta-search: search(query: "test", category: "metadata") {
+ type
+ }
+ swhid-search: search(query: "test", category: "swhid") {
+ type
+ }
+ }
+ """
+
+ # add more categories as needed
+ # advanced query language will be handled implicitly in
+ # metadata and origins
+
+ _node_class = SearchResultNode
+
+ def _get_paged_result(self):
+ query = self.kwargs.get("query")
+ category = self.kwargs.get("category")
+ # FIXME, use a search factory instead, either in this module
+ # or in a separate package
+ if category == "swhid":
+ results = PagedResult(
+ results=archive.Archive().search_in_swhids(query), next_page_token=None
+ )
+ elif category == "metadata":
+ # FIXME, validate the query language as a key expression in the utils
+ results = archive.Archive().search_in_metadata(query)
+ elif category == "origin":
+ # FIXME, validate the query language as a key expression in the utils
+ # this will return a homogeneous list of origin types
+ # still using the same SearchResult type to return result
+ # FIXME, maybe it is better to add a type OriginSearchResult
+ results = archive.Archive().search_in_origins(query)
+ return results
diff --git a/swh/graphql/schema/schema.graphql b/swh/graphql/schema/schema.graphql
--- a/swh/graphql/schema/schema.graphql
+++ b/swh/graphql/schema/schema.graphql
@@ -805,6 +805,54 @@
status: String
}
+"""
+Connection to search results
+"""
+type SearchResultConnection {
+ """
+ List of search result edges
+ """
+ edges: [SearchResultEdge]
+
+ """
+ List of search result objects
+ """
+ nodes: [SearchResult]
+
+ """
+ Information for pagination
+ """
+ pageInfo: PageInfo!
+
+ """
+ Total number of origin objects in the connection
+ """
+ totalCount: Int
+}
+
+type SearchResultEdge {
+ """
+ Cursor to request the next page after the item
+ """
+ cursor: String!
+
+ """
+ Search result object
+ """
+ node: SearchResult
+}
+
+union SearchResultTarget = Revision | Release | Branch | Content | Directory | Snapshot | Origin
+
+"""
+A search result object
+"""
+type SearchResult {
+ match: Int
+ target: SearchResultTarget
+ targetType: String! # FIXME, use enum
+}
+
"""
The query root of the GraphQL interface.
"""
@@ -903,4 +951,12 @@
"""
swhid: SWHID!
): Content
+
+ """
+ Search the archive
+ """
+ search(
+ query: String!
+ category: String! # FIXME, use enum
+ ): SearchResultConnection
}

File Metadata

Mime Type
text/plain
Expires
Nov 5 2024, 2:05 AM (10 w, 6 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3228766

Event Timeline