Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F7066225
D8020.id29033.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
8 KB
Subscribers
None
D8020.id29033.diff
View Options
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
Details
Attached
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
Attached To
D8020: [WIP] Add a search entrypoint
Event Timeline
Log In to Comment