OBIP: 2 Title: 3rd Party Search Providers Author: Tyler Smith Discussions-To: Github Issues or #3rd-party-search on Slack Status: Accepted Type: Standards Track Created: 03/25/2017 Copyright: MIT
A specification for the API a search provider must comply with to work correctly within the reference OpenBazaar 2.0 client.
Decentralized discovery of content is a notoriously hard problem. Protocols like Bittorrent rely heavily upon centralized providers like MiniNova, Vodo, and Vuze to allow users to find the content they're looking for. OpenBazaar 1.0 relies heavily on duosear.ch to locate good content.
To decentralize this experience as much as possible the OB1 team has proposed to create a standard API that anybody may fulfill and plug into the client to give the user an in-app experience that is much richer than we can currently provide.
While some popular implementations may be provided by default in the reference client, any URL that serves a compliant HTTPS JSON API should work as well as any other implementation.
A search provider must publish an entry point into their service. I will use the
example example.com as the service provider in this OBIP.
The entry point response from curl example.com:
{
"name": "Example Search",
"logo": "https://example.com/logo.png",
"links": {
"self": "",
"listings": "https://search.ob1.io/search/listings"
},
"options": {
"origin": {
"type": "radio",
"label": "Origin",
"options": [
{
"value": "worldwide",
"label": "π Worldwide",
"checked": true,
"default": true
},
{
"value": "us",
"label": "πΊπΈ United States",
"checked": false,
"default": false
},
{
"value": "de",
"label": "π©πͺ Germany",
"checked": false,
"default": false
}
]
},
"rating": {
"type": "radio",
"label": "Rating",
"options": [
{
"value": "5",
"label": "βββββ",
"checked": true,
"default": true
},
{
"value": "4",
"label": "ββββ \u0026 up",
"checked": false,
"default": false
},
{
"value": "3",
"label": "βββ \u0026 up",
"checked": false,
"default": false
},
{
"value": "2",
"label": "ββ \u0026 up",
"checked": false,
"default": false
},
{
"value": "1",
"label": "β \u0026 up",
"checked": false,
"default": false
}
]
},
"shipping": {
"type": "dropdown",
"label": "Shipping",
"options": [
{
"value": "worldwide",
"label": "π Worldwide",
"checked": true,
"default": true
},
{
"value": "us",
"label": "πΊπΈ United States",
"checked": false,
"default": false
},
{
"value": "de",
"label": "π©πͺ Germany",
"checked": false,
"default": false
}
]
}
},
"sortBy": {
"price-asc": {
"label": "Price (Low to High)",
"selected": false,
"default": false
},
"price-desc": {
"label": "Price (High to Low)",
"selected": false,
"default": false
},
"relevance": {
"label": "Relevance",
"selected": false,
"default": true
}
}
}-
nameis the displayed name of the Service -
logois the displayed image next to the name for branding -
linksis an object providing the URI of important endpointsselfis the canonical URI for the endpoint of the Servicelistingsis the canonical URI for the listing search
-
optionsis a list of filters that are displayed with search results to allow for more refined searching. It is a completely optional value; they are not required. What options a Service will handle is completely up to them. The client simply requires the proper structure for announcing the filters so they may be displayed in the client application. There are no special options, they are treated equally and rendered in the order given. A few example options are shown above, but are not required or "blessed" in any way.- The key of an
optionsobject is the "name" of the object. It is what is sent in the URL of a search as the key in the query params. For checkboxes it is sent in the URL one time each for each checked box, e.g.?color=green&color=blue. - The
typeproperty of the object specifies the type of filter that option is. The currently supported option types are: radio, checkbox, and dropdown - The
labelproperty is the value displayed in the application - The
optionsproperty is a list of options for the filter. - Its
valueandlabelproperties work the same as the root-leveloptionskey andlabelvalues respectively. checkedis a boolean indicating that filter is applied in the response.defaultis a boolean indicating that filter is applied by default.
- The key of an
-
sortByis a list of sort options available for search. Likeoptionsit is completely optional and the Service is responsible for handling sorting correctly.- The key is the key used in the query paramaters of the search request
- The
labelproperty is the displayed value - The
selectedproperty denotes the selected search value - The
defaultproperty denotes the value is the default value
{
"name": "OB1",
"logo": "https://ob1.io/logo.png",
"options": {
"origin": {
"type": "radio",
"label": "Origin",
"options": [
{
"value": "worldwide",
"label": "π Worldwide",
"checked": true,
"default": true
},
{
"value": "us",
"label": "πΊπΈ United States",
"checked": false,
"default": false
},
{
"value": "de",
"label": "π©πͺ Germany",
"checked": false,
"default": false
}
]
},
"rating": {
"type": "radio",
"label": "Rating",
"options": [
{
"value": "5",
"label": "βββββ",
"checked": true,
"default": true
},
{
"value": "4",
"label": "ββββ \u0026 up",
"checked": false,
"default": false
},
{
"value": "3",
"label": "βββ \u0026 up",
"checked": false,
"default": false
},
{
"value": "2",
"label": "ββ \u0026 up",
"checked": false,
"default": false
},
{
"value": "1",
"label": "β \u0026 up",
"checked": false,
"default": false
}
]
},
"shipping": {
"type": "dropdown",
"label": "Shipping",
"options": [
{
"value": "worldwide",
"label": "π Worldwide",
"checked": true,
"default": true
},
{
"value": "us",
"label": "πΊπΈ United States",
"checked": false,
"default": false
},
{
"value": "de",
"label": "π©πͺ Germany",
"checked": false,
"default": false
}
]
}
},
"sortBy": {
"price-asc": {
"label": "Price (Low to High)",
"selected": false,
"default": false
},
"price-desc": {
"label": "Price (High to Low)",
"selected": false,
"default": false
},
"relevance": {
"label": "Relevance",
"selected": true,
"default": true
}
},
"links": {
"self": "https://search.ob1.io/search?q=air\u0026pretty\u0026shipping=us",
"search": "https://search.ob1.io/search",
"listings": "https://search.ob1.io/search/listings"
},
"results": {
"total": 5672,
"morePages": true,
"results": [
{
"type": "listing",
"relationships": {
"moderators": ["Qma1uogRbJqDfs68cxgKkrDymH1WAsnhKgjvEo7gWSULBq"],
"vendor": {
"data": {
"peerID": "QmWBSdbs7LLR7BdFgtKQA7CUq8aCzoTi74V7UwiwvfYpU5",
"handle": "@yPayne",
"avatarHashes": {
"tiny": "QmTHCaDpznJStWvPUSiKNDd53VVZmPtnrHx5diGP5gskCx",
"small": "QmfVecQaoy1pZD3wsHHC82kYg24yhVaqBsAzm5aJCabXPQ",
"medium": "QmS1z4qW1SiDNtLD1Bfno8WEbFUV8qwb5CQxgR6BREyVTJ",
"original": "QmRSJfL6rEuZ6fBaCfd1ZYXHTnPKj4ZZuEuMsNmbbFjLfH",
"large": "QmWoNr88v9dnsJAZBkuZJAXjPGn1MBLobp3S6F8NnuZ1m3"
}
}
}
},
"data": {
"hash": "",
"slug": "air-direct-amplifier",
"title": "Air Direct Amplifier",
"tags": [
"culpa",
"nihil",
"sit",
"nesciunt",
"ad",
"accusamus",
"sit"
],
"categories": [
"Digital Goods"
],
"contractType": "DIGITAL_GOOD",
"description": "unde illo tempore omnis corporis repellat non. et eum excepturi ipsam alias quae.\tquia voluptates sed et provident qui. inventore eligendi vel autem modi asperiores autem sed.",
"thumbnail": {
"tiny": "QmTHCaDpznJStWvPUSiKNDd53VVZmPtnrHx5diGP5gskCx",
"small": "QmfVecQaoy1pZD3wsHHC82kYg24yhVaqBsAzm5aJCabXPQ",
"medium": "QmS1z4qW1SiDNtLD1Bfno8WEbFUV8qwb5CQxgR6BREyVTJ",
"original": "QmRSJfL6rEuZ6fBaCfd1ZYXHTnPKj4ZZuEuMsNmbbFjLfH",
"large": "QmWoNr88v9dnsJAZBkuZJAXjPGn1MBLobp3S6F8NnuZ1m3"
},
"language": "",
"price": {
"currencyCode": "btc",
"amount": 34
},
"nsfw": true
}
},
{
"type": "listing",
"relationships": {
"moderators": ["Qma1uogRbJqDfs68cxgKkrDymH1WAsnhKgjvEo7gWSULBq"],
"vendor": {
"data": {
"peerID": "QmSJNNBacfjY9F6gPizFpfpgwpGfpRy8LWKRPN6At9AgbU",
"handle": "@KimberlyHicks",
"avatarHashes": {
"tiny": "QmeSjo2YMdEaMzt1JCviDCfVZ1aGJdFvRKfjQf3sHe5ZgG",
"small": "QmcyVgwCXg3phR6NbRHK5sDZFyyH4pBLh1TGGkWCWT68hV",
"medium": "QmX3fhZXxPUpNsqdGZtsdaSSJApDptfFcSKWbCoHXJHXLo",
"original": "QmZuZ7W2yJ8tsvhKTcXu2zyZZoMxTqSEK6zKQUpefQbt4t",
"large": "QmPrN4Li71nsmZ2SjAYhrVgDAaDYN34dWyg2gS4Ubz942d"
}
}
}
},
"data": {
"hash": "",
"slug": "air-air-compressor",
"title": "Air Air Compressor",
"tags": [
"rem",
"id",
"eum",
"doloribus",
"magnam",
"quaerat",
"et",
"quaerat"
],
"categories": [
"Arts"
],
"contractType": "SERVICE",
"description": "ut sunt suscipit labore dolore.\tet ab voluptates. sit reiciendis vero vel sit reiciendis. autem velit soluta voluptate vel quia.\toptio quia omnis voluptatem nemo.",
"thumbnail": {
"tiny": "QmeSjo2YMdEaMzt1JCviDCfVZ1aGJdFvRKfjQf3sHe5ZgG",
"small": "QmcyVgwCXg3phR6NbRHK5sDZFyyH4pBLh1TGGkWCWT68hV",
"medium": "QmX3fhZXxPUpNsqdGZtsdaSSJApDptfFcSKWbCoHXJHXLo",
"original": "QmZuZ7W2yJ8tsvhKTcXu2zyZZoMxTqSEK6zKQUpefQbt4t",
"large": "QmPrN4Li71nsmZ2SjAYhrVgDAaDYN34dWyg2gS4Ubz942d"
},
"language": "",
"price": {
"currencyCode": "btc",
"amount": 29
},
"nsfw": true
}
}
]
}
}name,logo,links,options, andsortByare exactly the same as the in the Init responseresultscontains the search results for the given querytotalis the total number of matching documents the Service can return (not the number it did return)morePagesspecifies that there is at least one more partial or full page of results availableresultsis an array of "listing" results of the following formattypeis a property denoting the type of the returned object. This must be "listing" for listing results. In the future it can be another type, e.g. "node"relationshipsis an object with useful information about related objects. Currently used relationships arevendorandmoderators- The
vendor'sdataproperties contains the following information about the vendorpeerIDThe PeerID of the vendorhandleis the available Blockstack (or other naming system's) handle of the vendoravatarHashesis an object of hashes to various sizes of the vendor's avatartiny,small, andmediumare planned for use.originalandlargeare reserved for use in the future but may be sent now preemptively
- The
moderatorfield should be a list of peer IDs of moderators for the listing
- The
- The
dataobject represents the listing data itselfhash,slug,title,tags,contractType,description,thumbnail,language,price, andnsfwshould be the same as provided by the listingcategoriesis an array of categories as determined by the Service provider. It may use categories given by the vendor or using a custom algorithm
The reference client shall send the follow parameters to search providers on all search requests, and search providers should respond accordingly.
| Flag | Type | Description |
|---|---|---|
p |
Integer | Page; controls which pages of results should be sent |
ps |
Integer | Page Size; controls how many results should be sent per page |
nsfw |
Boolean | Controls whether or not to return adult listings |