Skip to content

Commit bff9ca1

Browse files
authored
Merge pull request typesense#4 from typesense/alias-curation-apis
Alias curation apis
2 parents ff53bf3 + 9593d08 commit bff9ca1

File tree

10 files changed

+277
-2
lines changed

10 files changed

+277
-2
lines changed

examples/alias_operations.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import typesense
2+
3+
# Aliasing is a Typesense Premium feature (see: https://typesense.org/premium)
4+
5+
client = typesense.Client({
6+
'master_node': {
7+
'host': 'localhost',
8+
'port': '8108',
9+
'protocol': 'http',
10+
'api_key': 'abcd'
11+
},
12+
'read_replica_nodes': [{
13+
'host': 'localhost',
14+
'port': '9108',
15+
'protocol': 'http',
16+
'api_key': 'abcd'
17+
}],
18+
'timeout_seconds': 2
19+
})
20+
21+
# Create a collection
22+
23+
create_response = client.collections.create({
24+
"name": "books_january",
25+
"fields": [
26+
{"name": "title", "type": "string" },
27+
{"name": "authors", "type": "string[]" },
28+
{"name": "authors_facet", "type": "string[]", "facet": True },
29+
{"name": "publication_year", "type": "int32" },
30+
{"name": "publication_year_facet", "type": "string", "facet": True },
31+
{"name": "ratings_count", "type": "int32" },
32+
{"name": "average_rating", "type": "float" },
33+
{"name": "image_url", "type": "string" }
34+
],
35+
"default_sorting_field": "ratings_count"
36+
})
37+
38+
print(create_response)
39+
40+
# Create or update an existing alias
41+
42+
create_alias_response = client.aliases.upsert('books', {
43+
"collection_name": "books_january"
44+
})
45+
46+
print(create_alias_response)
47+
48+
# Add a book using the alias name `books`
49+
50+
hunger_games_book = {
51+
'id': '1', 'original_publication_year': 2008, 'authors': ['Suzanne Collins'], 'average_rating': 4.34,
52+
'publication_year': 2008, 'publication_year_facet': '2008', 'authors_facet': ['Suzanne Collins'],
53+
'title': 'The Hunger Games',
54+
'image_url': 'https://images.gr-assets.com/books/1447303603m/2767052.jpg',
55+
'ratings_count': 4780653
56+
}
57+
58+
client.collections['books'].documents.create(hunger_games_book)
59+
60+
# Search using the alias
61+
62+
print(client.collections['books'].documents.search({
63+
'q': 'hunger',
64+
'query_by': 'title',
65+
'sort_by': 'ratings_count:desc'
66+
}))
67+
68+
# List all aliases
69+
70+
print(client.aliases.retrieve())
71+
72+
# Retrieve the configuration of a specific alias
73+
74+
print(client.aliases['books'].retrieve())
75+
76+
# Delete an alias
77+
78+
print(client.aliases['books'].delete())

examples/curation_operations.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import typesense
2+
3+
# Curation is a Typesense Premium feature (see: https://typesense.org/premium)
4+
5+
client = typesense.Client({
6+
'master_node': {
7+
'host': 'localhost',
8+
'port': '8108',
9+
'protocol': 'http',
10+
'api_key': 'abcd'
11+
},
12+
'read_replica_nodes': [{
13+
'host': 'localhost',
14+
'port': '9108',
15+
'protocol': 'http',
16+
'api_key': 'abcd'
17+
}],
18+
'timeout_seconds': 2
19+
})
20+
21+
# Create a collection
22+
23+
create_response = client.collections.create({
24+
"name": "books",
25+
"fields": [
26+
{"name": "title", "type": "string" },
27+
{"name": "authors", "type": "string[]" },
28+
{"name": "authors_facet", "type": "string[]", "facet": True },
29+
{"name": "publication_year", "type": "int32" },
30+
{"name": "publication_year_facet", "type": "string", "facet": True },
31+
{"name": "ratings_count", "type": "int32" },
32+
{"name": "average_rating", "type": "float" },
33+
{"name": "image_url", "type": "string" }
34+
],
35+
"default_sorting_field": "ratings_count"
36+
})
37+
38+
print(create_response)
39+
40+
# Create or update an override
41+
42+
create_override_response = client.collections["books"].overrides.upsert('hermione-exact', {
43+
"rule": {
44+
"query": "hermione",
45+
"match": "exact"
46+
},
47+
"includes": [
48+
{"id": "1", "position": 1},
49+
]
50+
})
51+
52+
print(create_override_response)
53+
54+
# List all overrides
55+
56+
print(client.collections["books"].overrides.retrieve())
57+
58+
# Retrieve the configuration of a specific override
59+
60+
print(client.collections["books"].overrides["hermione-exact"].retrieve())
61+
62+
# Add a book
63+
64+
hunger_games_book = {
65+
'id': '1', 'original_publication_year': 2008, 'authors': ['JK Rowling'], 'average_rating': 4.34,
66+
'publication_year': 2008, 'publication_year_facet': '2008', 'authors_facet': ['JK Rowling'],
67+
'title': 'Harry Potter and the Goblet of Fire',
68+
'image_url': 'https://images.gr-assets.com/books/1447303603m/2767052.jpg',
69+
'ratings_count': 4780653
70+
}
71+
72+
client.collections['books'].documents.create(hunger_games_book)
73+
74+
# Search for a book
75+
76+
print(client.collections['books'].documents.search({
77+
'q': 'hermione',
78+
'query_by': 'title',
79+
'sort_by': 'ratings_count:desc'
80+
}))
81+
82+
# Delete an override
83+
84+
print(client.collections['books'].overrides["hermione-exact"].delete())

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
requests==2.18.4
1+
requests==2.22.0

typesense/alias.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from .documents import Documents
2+
from .api_call import ApiCall
3+
4+
5+
class Alias(object):
6+
def __init__(self, config, name):
7+
self.config = config
8+
self.name = name
9+
self.api_call = ApiCall(config)
10+
11+
def _endpoint_path(self):
12+
from .aliases import Aliases
13+
return u"{0}/{1}".format(Aliases.RESOURCE_PATH, self.name)
14+
15+
def retrieve(self):
16+
return self.api_call.get(self._endpoint_path())
17+
18+
def delete(self):
19+
return self.api_call.delete(self._endpoint_path())

typesense/aliases.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from typesense.alias import Alias
2+
from .api_call import ApiCall
3+
4+
5+
class Aliases(object):
6+
RESOURCE_PATH = '/aliases'
7+
8+
def __init__(self, config):
9+
self.config = config
10+
self.api_call = ApiCall(config)
11+
self.aliases = {}
12+
13+
def __getitem__(self, name):
14+
if name not in self.aliases:
15+
self.aliases[name] = Alias(self.config, name)
16+
17+
return self.aliases.get(name)
18+
19+
def _endpoint_path(self, alias_name):
20+
return u"{0}/{1}".format(Aliases.RESOURCE_PATH, alias_name)
21+
22+
def upsert(self, name, mapping):
23+
return self.api_call.put(self._endpoint_path(name), mapping)
24+
25+
def retrieve(self):
26+
return self.api_call.get(Aliases.RESOURCE_PATH)

typesense/api_call.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,20 @@ def post(self, endpoint, body):
6666
timeout=self.config.timeout_seconds)
6767
if r.status_code != 201:
6868
error_message = r.json().get('message', 'API error.')
69-
print(url)
69+
raise ApiCall.get_exception(r.status_code)(error_message)
70+
71+
return r.json()
72+
73+
def put(self, endpoint, body):
74+
url = self.config.master_node.url() + endpoint
75+
api_key = self.config.master_node.api_key
76+
77+
r = requests.put(url, json=body,
78+
headers={ApiCall.API_KEY_HEADER_NAME: api_key},
79+
timeout=self.config.timeout_seconds)
80+
81+
if r.status_code != 200:
82+
error_message = r.json().get('message', 'API error.')
7083
raise ApiCall.get_exception(r.status_code)(error_message)
7184

7285
return r.json()

typesense/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from .aliases import Aliases
12
from .collections import Collections
23
from .configuration import Configuration
34

@@ -6,3 +7,4 @@ class Client(object):
67
def __init__(self, config_dict):
78
self.config = Configuration(config_dict)
89
self.collections = Collections(self.config)
10+
self.aliases = Aliases(self.config)

typesense/collection.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from .overrides import Overrides
12
from .documents import Documents
23
from .api_call import ApiCall
34

@@ -8,6 +9,7 @@ def __init__(self, config, name):
89
self.name = name
910
self.api_call = ApiCall(config)
1011
self.documents = Documents(config, name)
12+
self.overrides = Overrides(config, name)
1113

1214
def _endpoint_path(self):
1315
from .collections import Collections

typesense/override.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from .api_call import ApiCall
2+
3+
4+
class Override(object):
5+
def __init__(self, config, collection_name, override_id):
6+
self.config = config
7+
self.collection_name = collection_name
8+
self.override_id = override_id
9+
self.api_call = ApiCall(config)
10+
11+
def _endpoint_path(self):
12+
from .overrides import Overrides
13+
from .collections import Collections
14+
return u"{0}/{1}/{2}/{3}".format(Collections.RESOURCE_PATH, self.collection_name, Overrides.RESOURCE_PATH,
15+
self.override_id)
16+
17+
def retrieve(self):
18+
return self.api_call.get(self._endpoint_path())
19+
20+
def delete(self):
21+
return self.api_call.delete(self._endpoint_path())

typesense/overrides.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from .override import Override
2+
from .api_call import ApiCall
3+
4+
5+
class Overrides(object):
6+
RESOURCE_PATH = 'overrides'
7+
8+
def __init__(self, config, collection_name):
9+
self.config = config
10+
self.api_call = ApiCall(config)
11+
self.collection_name = collection_name
12+
self.overrides = {}
13+
14+
def __getitem__(self, override_id):
15+
if override_id not in self.overrides:
16+
self.overrides[override_id] = Override(self.config, self.collection_name, override_id)
17+
18+
return self.overrides[override_id]
19+
20+
def _endpoint_path(self, override_id=None):
21+
from .collections import Collections
22+
override_id = override_id or ''
23+
return u"{0}/{1}/{2}/{3}".format(Collections.RESOURCE_PATH, self.collection_name,
24+
Overrides.RESOURCE_PATH, override_id)
25+
26+
def upsert(self, id, config):
27+
return self.api_call.put(self._endpoint_path(id), config)
28+
29+
def retrieve(self):
30+
return self.api_call.get(self._endpoint_path())

0 commit comments

Comments
 (0)