How to work with the Search service¶
Search service as part of the Infocenter is designed to speed up database searches. We are using Azure Cognitive Search
Base url for search¶
https://api.discover.swiss/info/v2/search
There are GET and POST endpoints for search.
Search request¶
Parameter | Type | Description |
---|---|---|
searchText | string | Optional. Search for contained string by the searchable fields |
searchFields | string | Optional. When name of fields are specified as a comma separated string (e.g. "name, description, address/name") then only the selected fields will be used for searching. |
select | string | Optional. When name of fields are specified as a comma separated string (e.g. "name, description, @id, address") then only the selected fields will be returned. |
filters | string[] | Optional. Odata strings for filters |
currentPage | int | Optional. By default equal 1. Determines how many results are in response |
resultsPerPage | int | Optional. By default equal 10. Determines how many results are in response |
orderBy | string | Optional. Determines name of field by which result will be ordered |
facets | FacetRequest[] | List of facets which will be iin the response |
Custom filter properies¶
Parameter | Type | Description |
---|---|---|
datasource | string[] | Optional. Determines one or many datasource by which data will be filtered |
project | string[] | Optional. Determines one or many datasource by which data will be filtered |
campaignTag | string[] | Optional. Determines one or many campaign tag by which data will be filtered |
Facets filter properies¶
For filtering by facets is necessary to use porerty filterPropertyName
from Search facet response as a request property name. These properties are optional and have string[] type.
List of available facets filters¶
facet name | filter property name | description |
---|---|---|
address/addressLocality | addressLocality | |
address/postalCode | addressPostalCode | |
tag/id | tag | |
categoryTree | category | represent category tree list. For getting full list of categories don't forget to set big enough value for count property in facet request |
containedInPlace/id | containedInPlace | |
time | time | |
state | state | |
rating/condition | ratingСondition | |
rating/difficulty | ratingDifficulty | |
elevation/ascent | elevationAscent | |
elevation/descent | elevationDescent | |
elevation/minAltitude | elevationMinAltitude | |
elevation/maxAltitude | elevationMaxAltitude | |
seasons | season | |
type | type | |
combinedType | combinedType | represent array containing data from parentType and additionalType propeties |
leafType | leafType | contains additionalType or type |
Localization¶
default: de-CH
Language: Based on the Accept-Language header localized properties are translated. Search language is based on this header as well (e.g. for de-CH it will search only in german translations if the translation is available for this porperty.
Search response¶
Any search response contains list of possible filters and total number of results matching current query.
Property name | Type | Description |
---|---|---|
count | int (nullable) | actual count of results in actual query but not count possible results |
values | SearchValueResponse[] | paged count of results by query |
facets | {string, SearchFacetResponse}[] | Dictionary of facets by the query. Where the key is a string with name of facets from azure search, and the value is SearchFacetResponse |
Searching¶
Properties names should be in CamelCase.
Warning
When you are searching in specific language don't forget to setup necessary language in Accept-Language
header, otherwise you can receive an incorrect response.
Search samples¶
Search with pagination and ordering by property name¶
https://api.discover.swiss/info/v2/search?OrderBy=additionalType&ResultsPerPage=5&CurrentPage=1
Fulltext search¶
https://api.discover.swiss/info/v2/search?SearchText=Scuol, Gurl* +"Tarasp fontana" -spaceship
The query parser separates operators (such as *, + and - in the example) from search terms, and deconstructs the search query into subqueries of a supported type:
- term query for standalone terms (like Scuol)
- phrase query for quoted terms (like Tarasp fontana or)
- prefix query for terms followed by a prefix operator * (like Gurl)
Search by selected language¶
To search by language it is necessary language it is neccessary to specify Accept-Language
header, and set SearchText
.
HEADERS: Accept-Language: "en"
https://api.discover.swiss/info/v2/search?search?SearchText=Switzerland&Type=AdministrativeArea
Search by selected fields¶
There is a possibility to specify SearchFields
if you want to search by specific field.
Information
Search is using all searchable fields when SearchFields
is empty.
There are available searchable fields:
Field name | Translated |
---|---|
openingHours | - |
address/name | + |
address/addressLine | - |
address/streetAddress | - |
description | + |
name | + |
Translated field means that this property is available in different languages.
For declaring multiple fields in SearchFields
property they should be separated by comma:
-SearchFields=name, description
-SearchFields=name, address/name, description
-SearchFields=address/name
Sample of searching by single fields¶
https://api.discover.swiss/info/v2/search?search?SearchText=Schweiz&SearchFields=name&Type=AdministrativeArea
Sample of searching by multiple fields¶
https://api.discover.swiss/info/v2/search?search?SearchText=Schweiz&SearchFields=name, address/name
Facets¶
There are next facets in the response:
facet name | filter property name | description |
---|---|---|
address/addressLocality | addressLocality | |
address/postalCode | addressPostalCode | |
tag/id | tag | |
categoryTree | category | represent category tree list. For getting full list of categories don't forget to set big enough value for count property in facet request |
containedInPlace/id | containedInPlace | |
time | time | |
state | state | |
rating/condition | ratingСondition | |
rating/difficulty | ratingDifficulty | |
elevation/ascent | elevationAscent | |
elevation/descent | elevationDescent | |
elevation/minAltitude | elevationMinAltitude | |
elevation/maxAltitude | elevationMaxAltitude | |
seasons | season | |
type | type | |
combinedType | combinedType | represent array containing data from parentType and additionalType propeties |
leafType | leafType | contains additionalType or type |
see search facet value response
Attention
It is important to use single quotes with 'string' value type and don't use single quotes with 'int' value type
Warning
For getting full list of categories don't forget to set big enough value for count
property in facet request
Control of facets through facet request¶
You use POST requests to get more control under the facets reposnes.
In post request body you can pass array of facets request.
Search facet request¶
Parameter | Type | Description |
---|---|---|
name | string | name of facet that will be in the response |
values | string | property that represent borders for range response |
interval | int | property that represent interval borders for range response |
count | int | count of facets in the reponse |
selectValues | string[] | facet values which will be returned in the response. If selectValues is set then values or interval can't be used. If selectValues contains values then the value of count will be equals to max int (2,147,483,647) |
scope | string | represent type of scope which will be used for building response. Possible values: ["all", "parent", "current"] . Default value is current |
Facet scope defintions¶
Name | Description |
---|---|
current | standard behavior of Azure Search.Only facets of the current request/response get delivered |
all | all facets get delivered like if they would be without a selection on the current filter on this facet. |
parent | facets get delivered like if they would be the parent of the current filter on this facet. Only works with tree facets |
there is different responses based on request:
Search Request Body | Request type | Desscription |
---|---|---|
{ "facets": null } or { //don't set any facets } |
POST, GET | Returns all facets |
{ "facets": [] } |
POST | With empty array it doesn’t return any facets |
{ "facets": [{ "name": "category" }]} |
POST | Returns only categoryTree facets |
Usage of scope in facets¶
Information
Scope have influence only on the facets values, and haven't any affect on the search results.
Scope 'Current'¶
Selecting of current
scope have the same behavior as if you do not select anything, and values of facet in result will be filtered by all currently applyied filters.
// next POST search request bodies have the same results
// with current scope
{
"facets":[
{
"name":"addressLocality",
"scope":"current"
}
],
"addressLocality":["Chur"],
}
// without any scope
{
"facets":[
{
"name":"addressLocality",
}
],
"addressLocality":["Chur"],
}
// Response (addressLocality facet values) for both request bodies:
"address/addressLocality": {
"filterPropertyName": "addressLocality",
"values": [
{
"facetType": "value",
"filterType": "object",
"valueType": "string",
"value": "Chur",
"count": 408,
"query": "Chur",
"name": "Chur"
}
]
}
Scope 'All'¶
Selecting of all
scope means that for the facet values in results will be applied all selected filters except of the filter with name of facet.
{
"facets":[
{
"name":"addressLocality",
"scope": "all"
}
],
"addressLocality":["Chur"],
}
// Response
"address/addressLocality": {
"filterPropertyName": "addressLocality",
"values": [
{
"facetType": "value",
"filterType": "object",
"valueType": "string",
"value": "Zürich",
"count": 994,
"query": "Zürich",
"name": "Zürich"
},
{
"facetType": "value",
"filterType": "object",
"valueType": "string",
"value": "Arosa",
"count": 590,
"query": "Arosa",
"name": "Arosa"
},
{
"facetType": "value",
"filterType": "object",
"valueType": "string",
"value": "Lenzerheide",
"count": 482,
"query": "Lenzerheide",
"name": "Lenzerheide"
},
{
"facetType": "value",
"filterType": "object",
"valueType": "string",
"value": "Chur",
"count": 408,
"query": "Chur",
"name": "Chur"
},
... // a lot of other values wich you'll get when you don't use 'addressLocality' filters
]
}
Scope 'Parent'¶
Selecting of parent
scope means that for the selected facet insted of provided filters will be applyied their parents.
Attention
Parent scope works only with tree facets.
Sample: If request has categoryTree
facet with scope=parent
and category filter equal to ds_root|ds_06|ds_0615|ds_061501
then values of that facet will be filtered with filter equal ds_root|ds_06|ds_0615
.
How filters will be transformed:
ds_root|ds_06|ds_0615|ds_061501
->ds_root|ds_06|ds_0615
ds_root
-> filter will be removed
// Post search request body:
{
"facets": [
{
"name": "category",
"count": 100,
"scope":"parent"
}
],
"category":["ds_root|ds_06|ds_0615|ds_061501"]
}
// Response
"categoryTree": {
"filterPropertyName": "category",
"values": [
{
"facetType": "value",
"filterType": "collection",
"valueType": "string",
"value": "ds_root",
"count": 168,
"query": "ds_root",
"name": "discover.swiss category",
"namePlural": "discover.swiss categories"
},
{
"facetType": "value",
"filterType": "collection",
"valueType": "string",
"value": "ds_root|ds_06",
"count": 168,
"query": "ds_root|ds_06",
"name": "Unterkünfte",
"namePlural": "Unterkünftes"
},
{
"facetType": "value",
"filterType": "collection",
"valueType": "string",
"value": "ds_root|ds_06|ds_0615",
"count": 168,
"query": "ds_root|ds_06|ds_0615",
"name": "Hütte",
"namePlural": "Hüttes"
},
{
"facetType": "value",
"filterType": "collection",
"valueType": "string",
"value": "ds_root|ds_06|ds_0615|ds_061501",
"count": 146,
"query": "ds_root|ds_06|ds_0615|ds_061501",
"name": "Mountain hut",
"namePlural": "Mountain huts"
}
]
}
Usage of range and interval in facets¶
You can get facets grouped by the range of numbers.
For example range of rating conditions:
{
"rating/condition": {
"filterPropertyName": "ratingCondition",
"values": [{...},
{
"facetType": "range",
"filterType": "object",
"valueType": "int",
"from": 10,
"to": 25,
"count": 15,
"value": null,
"name": "10 - 25",
"query": "query:rating/condition gt 10 and rating/condition le 25"
},{...}]
}
}
Warning
Range facets appliable only for facets with int type.
You can get range facets by 2 ways:
- use
values
- use
interval
Get ranges through values¶
Property values
is a string which should be represented in the next format: number|number
Facet values
property has next restrictions:
values
andinterval
cannot exist in the same timevalues
could contain only int and should be delimited by |, e.g. 10|15|3 - valid, |5|10 - invalid
Here is example how it could be:
// POST search request body
{
"facets": {
"name": "ratingCondition",
"values": "5|15|34"
}
}
// alternative
{
"facets": {
"name": "rating/condition",
"values": "10|25|34"
}
} ```
Response for that request will have the next structure:
``` json
{
"rating/condition": {
"filterPropertyName": "ratingCondition",
"values": [{...},{
"facetType": "range",
"filterType": "object",
"valueType": "int",
"from": 10,
"to": 25,
"count": 15,
"value": null,
"name": "10 - 25",
"query": "query:rating/condition gt 10 and rating/condition le 25"
},{...}]
}
}
Get ranges through interval¶
Property interval
should be a number
Facet inreval
property has next restrictions:
values
andinterval
cannot exist in the same timeinterval
should be integerinterval
should be greater than or equal to 0
Here is example how it could be:
// POST search request body
{
"facets": {
"name": "ratingCondition",
"interval": "2"
}
}
// alternative
{
"facets": {
"name": "rating/condition",
"values": "2"
}
}
Response for that request will have the next structure:
{
"rating/condition": {
"filterPropertyName": "ratingCondition",
"values": [{...},{
"facetType": "range",
"filterType": "object",
"valueType": "int",
"from": 2,
"to": 4,
"count": 15,
"value": null,
"name": "2 - 4",
"query": "query:rating/condition gt 2 and rating/condition le 4"
},{...}]
}
}
Usage of selectValues¶
To specify which values do you want to see in the response you can use selectValues
property in the facetRequest.
Warning
It is not possible to use selectValues
with values
or interval
at the same time in one facet request.
Sample of the request with 1 facet and 2 values of that facet¶
Request body:
// POST search request body
{
"facets": {
"name": "categoryTree",
"selectValues": ["ds_root|ds_06", "ds_root|ds_089456"]
}
}
Response which contains only 2 values for categoryTree
facet:
"facets": {
"categoryTree": {
"filterPropertyName": "category",
"values": [
{
...
"value": "ds_root|ds_06",
"count": 2171,
...
},
{
...
"value": "ds_root|ds_089456",
"count": 0,
...
}
]
}
}
Filtering by the facet¶
Filtering by facets is easy with usage of filterPropertyName
property from search facet value response and query
property from search facet value response.
// sample facet response
{
"filterPropertyName": "ratingCondition",
"values": [
{
... // other facet value response properties
query = "2"
},
{
... // other facet value response properties
query = "9"
}
}
It is how we can use that as filters in get request:
https://api.discover.swiss/info/v2/search?ratingCondition=5&ratingCondition=9
It is how we can use that as filters in post request:
// request body with filter by single query:
{
"ratingCondition":"5"
}
// or
{
"ratingCondition": ["5"]
}
// and this is body for filtering by multiple query of one property:
{
"ratingCondition":["5", "9"]
}
Facet response¶
// search response
{
count: 15,
values: [{...},{...}]
facets:{
"categoryTree": {
filterPropertyName: "category",
values: [
{
...
"query": "query:item eq 'SomeCategory'"
}, {...}
]
},
"rating/condition" : {
filterPropertyName: "ratingCondition",
values: [
{
...
"query": "2"
},
{
...
"query": "6"
}
]
}
}
}
Request body based on facet¶
// search request by category and rating/conditions query
{
"category": "query:item eq 'SomeCategory'",
"ratingCondition": "2"
}
// search request by category and multiple rating/conditions query
{
"category": "query:item eq 'SomeCategory'",
"ratingCondition": ["2", "6"]
}
Sample of filtering by the facet with 'int' valueType¶
Get request:
https://api.discover.swiss/info/v2/search?filters=time eq 50
Post request:
{
"filters": "time eq 50"
}
Sample of filtering by the facet with 'string' valueType¶
Get request:
https://api.discover.swiss/info/v2/search?filters=address/addressLocality eq 'Martina'
Post request:
{
"filters": "address/addressLocality eq 'Martina'"
}
Filtering¶
A filter provides criteria for selecting entries used in an query. Unfiltered search includes all entries in the index. A filter scopes a search query to a subset of documents.
It is possible to use filtering by multiple ways:
- use OData for filtering by every filterable property. See filterable properties in search index schema.
Complex to use but gives you full control. - use custom filter properties for filtering. See request strucuture here
Simplified usage without Azure Search internal know-how. - use facets filter properies. For filtering by facets is necessary to use porerty
filterPropertyName
from Search facet response as a request property name. See Facets. Simplified usage without Azure Search internal know-how.
Filtering samples¶
Filtering by category with odata filters¶
https://api.discover.swiss/info/v2/search?resultsPerPage=5¤tPage=1&filters=categoryTree/any(category: category eq 'ds_root|ds_01|ds_0101')
Filtering by Category(-ies) (request parameter)¶
Get request:
https://api.discover.swiss/info/v2/search?category=ds_root|ds_01|ds_0101|ds_010102&category=ds_root|ds_01|ds_0101|ds_010105
Post request:
{
"category": ["ds_root|ds_01|ds_0101|ds_010102", "ds_root|ds_01|ds_0101|ds_010105"]
}
Filtering by Types and Datasources¶
Get request:
https://api.discover.swiss/info/v2/search?Type=Tour&type=Place&datasource=ZHT-CMS&datasource=OUA
Post request:
{
"type":["Place", "Tour"],
"datasource": ["OUA", "ZHT-CMS"]
}
Filtering by CombinedType¶
Get request:
https://api.discover.swiss/info/v2/search?combinedType=Place&combinedType=Guide
Post request:
{
"combinedType":["Place", "Guide"]
}
Filtering by the time range¶
Get request:
https://api.discover.swiss/info/v2/search?Type=Tour&filters=((time gt 50) and (time lt 60)) or ((time gt 100) and (time lt 150))
Post request:
{
"type":"Tour",
"filters": "((time gt 50) and (time lt 60)) or ((time gt 100) and (time lt 150))"
}
Filtering by geo with odatafilters¶
Search Api supports geo-spatial queries in OData filter expressions via the geo.distance
and geo.intersects
functions.
The geo.distance function returns the distance in kilometers between two points, one being a field or range variable, and one being a constant passed as part of the filter. The geo.intersects function returns true if a given point is within a given polygon, where the point is a field or range variable and the polygon is specified as a constant passed as part of the filter.
Filtreing by distance¶
The geo.distance
function takes two parameters of type Edm.GeographyPoint
and returns an Edm.Double
value that is the distance between them in kilometers. This differs from other services that support OData geo-spatial operations, which typically return distances in meters.
One of the parameters to geo.distance
must be a geography point constant, and the other must be a field path (or a range variable in the case of a filter iterating over a field of type Collection(Edm.GeographyPoint)). The order of these parameters doesn't matter.
The geography point constant is of the form geography'POINT(longitude latitude)'
, where the longitude and latitude are numeric constants.
Warning
When using geo.distance in a filter, you must compare the distance returned by the function with a constant using lt (lower than), le (lower or equal), gt (greater than), or ge (greater or equal). The operators eq and ne are not supported when comparing distances.
https://api.discover.swiss/info/v2/search?Type=Event&filters=geo.distance(geo, geography'POINT(9.7760155 46.6233053)') ge 5
Filtering by polygons¶
The geo.intersects
function takes a variable of type Edm.GeographyPoint
and a constant Edm.GeographyPolygon
and returns an Edm.Boolean
-- true if the point is within the bounds of the polygon, false otherwise.
Warning
Note that the polygon is closed (the first and last point sets must be the same)
https://api.discover.swiss/info/v2/search?Type=Event&filters=geo.intersects(geo, geography'POLYGON((9.7714376 46.6257781, 9.7614813 46.6244521, 9.7601938 46.6195606, 9.7598076 46.6149633, 9.7702360 46.6131950, 9.7800207 46.6132540, 9.7870588 46.6137550, 9.7913504 46.6189123, 9.7917795 46.6223305, 9.7904062 46.6254539, 9.7860289 46.6272218, 9.7823381 46.6279290, 9.7769308 46.6275754, 9.7736263 46.6261022, 9.7714376 46.6257781))')
Filtering by schedule¶
Filtering by schedule available only by filters
property.
Filteting by schedule/byDay¶
https://api.discover.swiss/info/v2/search?Type=Event&filters=schedule/any(item: item/byDay/any(day: day eq 'Monday'))
Post request:
{
"type": "Event",
"filters": "schedule/any(item: item/byDay/any(day: day eq 'Monday'))"
}
Filtering by schedule/startDate and schedule/endDate¶
Properties schedule/startDate and schedule/endDate has DateTimeOffset
type and value for filtering should be presented in the next format: yyyy-mm-ddThh:mm:ss('.'s+)?(zzzzzz)
Timezone use cases in filter value:
yyyy-mm-ddThh:mm:ss.000001Z
- where Z - UTC timezoneyyyy-mm-ddThh:mm:ss+00:00
- where 00:00 - UTC timezoneyyyy-mm-ddThh:mm:ss.000001-02:00
- where UTC-02:00yyyy-mm-ddThh:mm:ss+06:00
- where UTC+06:00
Get request:
https://api.discover.swiss/info/v2/search?Type=Event&filters=schedule/any(item: item/startDate lt 2020-04-14T00:00:00Z)
https://api.discover.swiss/info/v2/search?Type=Event&filters=schedule/any(item: item/endDate ge 2020-04-14T00:00:00Z)
Post request:
{
"type": "Event",
"filters": "schedule/any(item: item/startDate lt 2020-01-01T00:00:00Z and item/endDate ge 2020-04-01T00:00:00Z)"
}
Filtering by schedule/startTime and schedule/endTime¶
Azure Search doesn't support TimeSpan and that is why it is presented as string. That is why filtering available only as for strings.
Get request:
https://api.discover.swiss/info/v2/search?Type=Event&filters=schedule/any(item: item/startTime eq '12:00:00')
https://api.discover.swiss/info/v2/search?Type=Event&filters=schedule/any(item: item/endTime eq '12:00:00')
Post request:
{
"type": "Event",
"filters": "schedule/any(item: item/startTime eq '12:00:00')"
}
OData filtering¶
Read more about OData specification you can here
Structure samples¶
OData filter structure for property¶
identifier eq 'TSVM-OUA_9702544'
OData filter structure for sub-property¶
address/addressLocality eq 'Martina'
OData filter structure for array¶
results/any(item: item eq 'something')
OData filter structure for sub-property in array¶
categoryTree/any(category: category eq 'ZHT-CMS_restaurants' or category eq 'TSVM-OUA_14359510')
Category tree¶
You can build a tree from Categories provided in the facets. Property 'value' of each category contains full parent list separated by '|' from root to current category.
Category example:
{
"value": "ds_root|ds_01|ds_0101",
"name": "Wandern"
}
Selecting of fields for result¶
It is possible to specify which fields should be returned in the response.
For that necessary to specify Select
parameter with string of fields separated by comma.
All fields you see in response (results) are available for selecting.
For returning some nested fields of some other field it is possible to declare nesting by /
symbol.
In case when field and his nested fields are selected in one query, only root field will have affect.
Examples:
-Select=image/dataGovernance/source/link,image/dataGovernance
- in that case image/dataGovernance/source/link
will be ignored
-Select=image/dataGovernance
Information
Max depth of getting nested fields is 7. (e.g. for root/nested1/nested2/nested3/nested4/nested5/nested6/nested7/nested8
query nested7
and following will be ignored)
Selecting several root fields¶
Request:
https://api.discover.swiss/info/v2/search?Type=Tour&Select=@id, image, dataGovernance
Response:
"values": [
{
"@id": "https://api.discover.swiss/info/v2/tours/TOUR_ID",
"dataGovernance": {
"supplier": {
"acronym": "ACRONYM"
},
"source": {
"name": "Name",
"logo": {
"contentUrl": "link"
},
"link": [
{
"url": "link",
"type": "type"
}
]
},
"author": "author"
},
"image": {
"caption": "caption",
"dataGovernance": {
"supplier": {
"acronym": "ACRONYM"
},
"source": {
"link": [
{
"type": "type"
}
]
}
},
"contentUrl": "contentUrl",
"thumbnailUrl": "thumbnailUrl",
"@id": "https://api.discover.swiss/info/v2/imageObjects/IMAGE_ID",
"identifier": "IMAGE_ID"
}
},
{
...
}
]
Selecting root and nested fields¶
Request:
https://api.discover.swiss/info/v2/search?Type=Tour&Select=dataGovernance/source, dataGovernance/source/name
The response will return all properties under dataGovernance/source
because it counts as root property and the more specific property dataGovernance/source/name
is included in it:
"values": [
{
"dataGovernance": {
"source": {
"name": "name",
"logo": {
"contentUrl": "contentUrl"
},
"link": [
{
"url": "url",
"type": "type"
}
]
}
}
},
{
...
}
]
Sample application¶
You can see search in sample application here: demo.discover.swiss/search